Add mono audio input to PortAudio shim
The way the PortAudio callback works it is possible to reuse the buffer used for sharing output audio data between Julia and the C-library. The output data can be pushed from the shared buffer to the PortAudio's output buffer, after which the same location in the buffer can be used for storing the data read from the input buffer. This does assume equal lengths for the in- and output buffers.
This commit is contained in:
parent
eb34b85922
commit
7831578955
2 changed files with 21 additions and 31 deletions
37
deps/src/shim.c
vendored
37
deps/src/shim.c
vendored
|
@ -4,18 +4,16 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static int paCallback(const void *inputBuffer, void *outputBuffer,
|
static int paCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
unsigned long framesPerBuffer,
|
unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
PaStreamCallbackFlags statusFlags,
|
PaStreamCallbackFlags statusFlags,
|
||||||
void *userData);
|
void *userData);
|
||||||
|
|
||||||
static PaStream *AudioStream;
|
static PaStream *AudioStream;
|
||||||
static int JuliaPipeReadFD = 0;
|
static int JuliaPipeReadFD = 0;
|
||||||
static int JuliaPipeWriteFD = 0;
|
static int JuliaPipeWriteFD = 0;
|
||||||
static sem_t CSemaphore;
|
static sem_t CSemaphore;
|
||||||
static void *OutData = NULL;
|
static void *Buffer = NULL;
|
||||||
static unsigned long OutFrames = 0;
|
|
||||||
|
|
||||||
|
|
||||||
int make_pipe(void)
|
int make_pipe(void)
|
||||||
{
|
{
|
||||||
|
@ -27,11 +25,9 @@ int make_pipe(void)
|
||||||
return JuliaPipeReadFD;
|
return JuliaPipeReadFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void synchronize_buffer(void *buffer)
|
||||||
void wake_callback_thread(void *outData, unsigned int outFrames)
|
|
||||||
{
|
{
|
||||||
OutData = outData;
|
Buffer = buffer;
|
||||||
OutFrames = outFrames;
|
|
||||||
sem_post(&CSemaphore);
|
sem_post(&CSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +35,12 @@ PaError open_stream(unsigned int sampleRate, unsigned int bufSize)
|
||||||
{
|
{
|
||||||
PaError err;
|
PaError err;
|
||||||
|
|
||||||
|
|
||||||
err = Pa_OpenDefaultStream(&AudioStream,
|
err = Pa_OpenDefaultStream(&AudioStream,
|
||||||
0, /* no input channels */
|
1, /* single input channel */
|
||||||
1, /* mono output */
|
1, /* mono output */
|
||||||
paFloat32, /* 32 bit floating point output */
|
paFloat32, /* 32 bit floating point output */
|
||||||
sampleRate,
|
sampleRate,
|
||||||
bufSize, /* frames per buffer, i.e. the number of sample frames
|
bufSize, /* frames per buffer, i.e. the number of sample frames
|
||||||
that PortAudio will request from the callback. Many
|
that PortAudio will request from the callback. Many
|
||||||
apps may want to use paFramesPerBufferUnspecified,
|
apps may want to use paFramesPerBufferUnspecified,
|
||||||
which tells PortAudio to pick the best, possibly
|
which tells PortAudio to pick the best, possibly
|
||||||
|
@ -66,7 +61,6 @@ PaError open_stream(unsigned int sampleRate, unsigned int bufSize)
|
||||||
return paNoError;
|
return paNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//PaError stop_sin(void)
|
//PaError stop_sin(void)
|
||||||
//{
|
//{
|
||||||
// PaError err;
|
// PaError err;
|
||||||
|
@ -84,17 +78,16 @@ PaError open_stream(unsigned int sampleRate, unsigned int bufSize)
|
||||||
// return paNoError;
|
// return paNoError;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine will be called by the PortAudio engine when audio is needed.
|
* This routine will be called by the PortAudio engine when audio is needed.
|
||||||
* It may called at interrupt level on some machines so don't do anything that
|
* It may called at interrupt level on some machines so don't do anything that
|
||||||
* could mess up the system like calling malloc() or free().
|
* could mess up the system like calling malloc() or free().
|
||||||
*/
|
*/
|
||||||
static int paCallback(const void *inputBuffer, void *outputBuffer,
|
static int paCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
unsigned long framesPerBuffer,
|
unsigned long framesPerBuffer,
|
||||||
const PaStreamCallbackTimeInfo* timeInfo,
|
const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
PaStreamCallbackFlags statusFlags,
|
PaStreamCallbackFlags statusFlags,
|
||||||
void *userData)
|
void *userData)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned char fd_data = 0;
|
unsigned char fd_data = 0;
|
||||||
|
@ -102,9 +95,9 @@ static int paCallback(const void *inputBuffer, void *outputBuffer,
|
||||||
sem_wait(&CSemaphore);
|
sem_wait(&CSemaphore);
|
||||||
for(i=0; i<framesPerBuffer; i++)
|
for(i=0; i<framesPerBuffer; i++)
|
||||||
{
|
{
|
||||||
((float *)outputBuffer)[i] = ((float *)OutData)[i];
|
((float *)outputBuffer)[i] = ((float *)Buffer)[i];
|
||||||
|
((float *)Buffer)[i] = ((float *)inputBuffer)[i];
|
||||||
}
|
}
|
||||||
// TODO: copy the input data somewhere
|
|
||||||
write(JuliaPipeWriteFD, &fd_data, 1);
|
write(JuliaPipeWriteFD, &fd_data, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,10 @@ end
|
||||||
|
|
||||||
############ Internal Functions ############
|
############ Internal Functions ############
|
||||||
|
|
||||||
function wake_callback_thread(out_array)
|
function synchronize_buffer(buffer)
|
||||||
ccall((:wake_callback_thread, libportaudio_shim), Void,
|
ccall((:synchronize_buffer, libportaudio_shim), Void, (Ptr{Void},), buffer)
|
||||||
(Ptr{Void}, Cuint),
|
|
||||||
out_array, size(out_array, 1))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function init_portaudio()
|
function init_portaudio()
|
||||||
info("Initializing PortAudio. Expect errors as we scan devices")
|
info("Initializing PortAudio. Expect errors as we scan devices")
|
||||||
err = ccall((:Pa_Initialize, "libportaudio"), PaError, ())
|
err = ccall((:Pa_Initialize, "libportaudio"), PaError, ())
|
||||||
|
@ -83,17 +80,17 @@ end
|
||||||
|
|
||||||
function portaudio_task(jl_filedesc::Integer, stream::PortAudioStream)
|
function portaudio_task(jl_filedesc::Integer, stream::PortAudioStream)
|
||||||
info("Audio Task Launched")
|
info("Audio Task Launched")
|
||||||
in_array = zeros(AudioSample, stream.info.buf_size)
|
buffer = zeros(AudioSample, stream.info.buf_size)
|
||||||
desc_bytes = Cchar[0]
|
desc_bytes = Cchar[0]
|
||||||
jl_stream = fdio(jl_filedesc)
|
jl_stream = fdio(jl_filedesc)
|
||||||
jl_rawfd = RawFD(jl_filedesc)
|
jl_rawfd = RawFD(jl_filedesc)
|
||||||
try
|
try
|
||||||
while true
|
while true
|
||||||
# assume the root mixer is always active
|
# assume the root mixer is always active
|
||||||
out_array::AudioBuf, _::Bool = render(stream.mixer, in_array,
|
buffer::AudioBuf, _::Bool = render(stream.mixer, buffer, stream.info)
|
||||||
stream.info)
|
|
||||||
# wake the C code so it knows we've given it some more data
|
# wake the C code so it knows we've given it some more data
|
||||||
wake_callback_thread(out_array)
|
synchronize_buffer(buffer)
|
||||||
# wait for new data to be available from the sound card (and for it
|
# wait for new data to be available from the sound card (and for it
|
||||||
# to have processed our last frame of data). At some point we
|
# to have processed our last frame of data). At some point we
|
||||||
# should do something with the data we get from the callback
|
# should do something with the data we get from the callback
|
||||||
|
|
Loading…
Reference in a new issue