first pass at synchronizing threads, using RemoteRef and semaphore
This commit is contained in:
parent
ff043ec95f
commit
ae77cd2260
3 changed files with 127 additions and 69 deletions
10
deps/src/Makefile
vendored
10
deps/src/Makefile
vendored
|
@ -9,18 +9,16 @@ endif
|
||||||
.PHONY: all clean check-env default
|
.PHONY: all clean check-env default
|
||||||
|
|
||||||
#check-env:
|
#check-env:
|
||||||
#ifndef JULIA_ROOT
|
#ifndef JULIA_INC
|
||||||
# $(error Environment variable JULIA_ROOT is not set.)
|
# $(error Environment variable JULIA_INC is not set.)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
INC =-I"$(JULIA_ROOT)/usr/include"
|
#INC =-I"$(JULIA_INC)"
|
||||||
FLAGS =-Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fPIC
|
FLAGS =-Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fPIC
|
||||||
CFLAGS =-g
|
CFLAGS =-g
|
||||||
LIBS =
|
LIBS =-lportaudio -lrt
|
||||||
|
|
||||||
OBJS = shim.o
|
OBJS = shim.o
|
||||||
# libm can be removed later, it's just for the sin function used in testing
|
|
||||||
LIBS += -lportaudio -lm
|
|
||||||
|
|
||||||
# Figure out OS and architecture
|
# Figure out OS and architecture
|
||||||
OS = $(shell uname)
|
OS = $(shell uname)
|
||||||
|
|
124
deps/src/shim.c
vendored
124
deps/src/shim.c
vendored
|
@ -1,50 +1,80 @@
|
||||||
#include <portaudio.h>
|
#include <portaudio.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <julia/julia.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define SAMPLE_RATE 44100
|
// some defines we need to include until PR 4997 is merged
|
||||||
|
STATIC_INLINE jl_function_t *jl_get_function(jl_module_t *m, const char *name)
|
||||||
|
{
|
||||||
|
return (jl_function_t*) jl_get_global(m, jl_symbol(name));
|
||||||
|
}
|
||||||
|
|
||||||
static int patestCallback(const void *inputBuffer, void *outputBuffer,
|
DLLEXPORT jl_value_t *jl_call2(jl_function_t *f, jl_value_t *a, jl_value_t *b)
|
||||||
|
{
|
||||||
|
jl_value_t *v;
|
||||||
|
JL_TRY {
|
||||||
|
JL_GC_PUSH3(&f,&a,&b);
|
||||||
|
jl_value_t *args[2] = {a,b};
|
||||||
|
v = jl_apply(f, args, 2);
|
||||||
|
JL_GC_POP();
|
||||||
|
}
|
||||||
|
JL_CATCH {
|
||||||
|
v = NULL;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 *sin_stream;
|
static PaStream *AudioStream;
|
||||||
|
static jl_value_t *JuliaRemote;
|
||||||
|
static sem_t CSemaphore;
|
||||||
|
static void *OutData = NULL;
|
||||||
|
static unsigned long OutFrames = 0;
|
||||||
|
static jl_function_t *RemotePutFunc = NULL;
|
||||||
|
static jl_value_t *RemotePutArg = NULL;
|
||||||
|
|
||||||
|
|
||||||
PaError play_sin(void)
|
void wake_callback_thread(void *outData, unsigned int outFrames)
|
||||||
|
{
|
||||||
|
OutData = outData;
|
||||||
|
OutFrames = outFrames;
|
||||||
|
sem_post(&CSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
PaError open_stream(unsigned int sampleRate, unsigned int bufSize,
|
||||||
|
jl_value_t *jlRemote)
|
||||||
{
|
{
|
||||||
PaError err;
|
PaError err;
|
||||||
// PaDeviceInfo *info;
|
|
||||||
// int numDevices;
|
|
||||||
|
|
||||||
// printf("Found Devices:\n");
|
JuliaRemote = jlRemote;
|
||||||
// numDevices = Pa_GetDeviceCount();
|
sem_init(&CSemaphore, 0, 0);
|
||||||
// for(i = 0; i < numDevices; ++i)
|
RemotePutFunc = jl_get_function(jl_base_module, "put");
|
||||||
// {
|
RemotePutArg = jl_box_int32(0);
|
||||||
// info = Pa_GetDeviceInfo(i);
|
|
||||||
// printf("%s\n", info->name);
|
|
||||||
// }
|
|
||||||
|
|
||||||
err = Pa_OpenDefaultStream(&sin_stream,
|
err = Pa_OpenDefaultStream(&AudioStream,
|
||||||
0, /* no input channels */
|
0, /* no input channels */
|
||||||
2, /* stereo output */
|
1, /* mono output */
|
||||||
paFloat32, /* 32 bit floating point output */
|
paFloat32, /* 32 bit floating point output */
|
||||||
SAMPLE_RATE,
|
sampleRate,
|
||||||
1024, /* 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
|
||||||
changing, buffer size.*/
|
changing, buffer size.*/
|
||||||
patestCallback, /* this is your callback function */
|
paCallback, /* this is your callback function */
|
||||||
NULL); /*This is a pointer that will be passed to your callback*/
|
NULL); /*This is a pointer that will be passed to your callback*/
|
||||||
if(err != paNoError)
|
if(err != paNoError)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Pa_StartStream(sin_stream);
|
err = Pa_StartStream(AudioStream);
|
||||||
if(err != paNoError)
|
if(err != paNoError)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
|
@ -53,22 +83,23 @@ PaError play_sin(void)
|
||||||
return paNoError;
|
return paNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
PaError stop_sin(void)
|
|
||||||
{
|
|
||||||
PaError err;
|
|
||||||
err = Pa_StopStream(sin_stream);
|
|
||||||
if(err != paNoError)
|
|
||||||
{
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = Pa_CloseStream(sin_stream);
|
//PaError stop_sin(void)
|
||||||
if( err != paNoError )
|
//{
|
||||||
{
|
// PaError err;
|
||||||
return err;
|
// err = Pa_StopStream(sin_stream);
|
||||||
}
|
// if(err != paNoError)
|
||||||
return paNoError;
|
// {
|
||||||
}
|
// return err;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// err = Pa_CloseStream(sin_stream);
|
||||||
|
// if( err != paNoError )
|
||||||
|
// {
|
||||||
|
// return err;
|
||||||
|
// }
|
||||||
|
// return paNoError;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -76,35 +107,20 @@ PaError stop_sin(void)
|
||||||
* 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 patestCallback(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)
|
||||||
{
|
{
|
||||||
float freq_l = 100;
|
|
||||||
float freq_r = 150;
|
|
||||||
static float phase_l = 0;
|
|
||||||
static float phase_r = 0;
|
|
||||||
|
|
||||||
float *out = (float*)outputBuffer;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
sem_wait(&CSemaphore);
|
||||||
for(i=0; i<framesPerBuffer; i++)
|
for(i=0; i<framesPerBuffer; i++)
|
||||||
{
|
{
|
||||||
/* should modulo by 2PI */
|
((float *)outputBuffer)[i] = ((float *)OutData)[i];
|
||||||
phase_l += (2 * M_PI * freq_l / SAMPLE_RATE);
|
|
||||||
if(phase_l > 2 * M_PI)
|
|
||||||
{
|
|
||||||
phase_l -= 2 * M_PI;
|
|
||||||
}
|
|
||||||
phase_r += (2 * M_PI * freq_r / SAMPLE_RATE);
|
|
||||||
if(phase_r > 2 * M_PI)
|
|
||||||
{
|
|
||||||
phase_r -= 2 * M_PI;
|
|
||||||
}
|
|
||||||
out[2*i] = sin(phase_l);
|
|
||||||
out[2*i + 1] = sin(phase_r);
|
|
||||||
}
|
}
|
||||||
|
// TODO: copy the input data somewhere
|
||||||
|
jl_call2(RemotePutFunc, JuliaRemote, RemotePutArg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# thanks to Gustavo Goretkin for the start on this
|
|
||||||
|
|
||||||
module PortAudio
|
module PortAudio
|
||||||
|
|
||||||
export play_sin, stop_sin
|
export play_sin, stop_sin
|
||||||
|
@ -11,20 +9,66 @@ typealias PaStream Void
|
||||||
|
|
||||||
const PA_NO_ERROR = 0
|
const PA_NO_ERROR = 0
|
||||||
|
|
||||||
|
|
||||||
############ Exported Functions #############
|
############ Exported Functions #############
|
||||||
|
|
||||||
function play_sin()
|
function play_sin(sample_rate, buf_size)
|
||||||
err = ccall((:play_sin, libportaudio_shim), PaError, ())
|
jl_remote = RemoteRef()
|
||||||
handle_status(err)
|
|
||||||
|
ccall((:open_stream, libportaudio_shim), PaError,
|
||||||
|
(Cuint, Cuint, RemoteRef),
|
||||||
|
sample_rate, buf_size, jl_remote)
|
||||||
|
|
||||||
|
info("Launching Audio Task...")
|
||||||
|
yieldto(Task(audio_task))
|
||||||
|
info("Audio Task Yielded")
|
||||||
end
|
end
|
||||||
|
|
||||||
function stop_sin()
|
#function stop_sin()
|
||||||
err = ccall((:stop_sin, libportaudio_shim), PaError, ())
|
# err = ccall((:stop_sin, libportaudio_shim), PaError, ())
|
||||||
handle_status(err)
|
# handle_status(err)
|
||||||
end
|
#end
|
||||||
|
|
||||||
############ Internal Functions ############
|
############ Internal Functions ############
|
||||||
|
|
||||||
|
const sample_rate = 44100
|
||||||
|
const buf_size = 512
|
||||||
|
const freq = 100
|
||||||
|
phase = 0.0
|
||||||
|
|
||||||
|
function process!(out_array, in_array)
|
||||||
|
global phase
|
||||||
|
for i in 1:buf_size
|
||||||
|
out_array[i] = sin(phase)
|
||||||
|
phase += 2pi * freq / sample_rate
|
||||||
|
if phase > 2pi
|
||||||
|
phase -= 2pi
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return buf_size
|
||||||
|
end
|
||||||
|
|
||||||
|
function wake_callback_thread(out_array)
|
||||||
|
ccall((:wake_callback_thread, libportaudio_shim), Void,
|
||||||
|
(Ptr{Void}, Cuint),
|
||||||
|
out_array, size(out_array, 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
function audio_task()
|
||||||
|
info("Audio Task Launched")
|
||||||
|
in_array = zeros(buf_size)
|
||||||
|
out_array = zeros(buf_size)
|
||||||
|
while true
|
||||||
|
process!(out_array, in_array)
|
||||||
|
# wake the C code so it knows we've given it some more data
|
||||||
|
wake_callback_thread(out_array)
|
||||||
|
# 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 should do
|
||||||
|
# something with the data we get from the callback
|
||||||
|
take(jl_remote)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function handle_status(err::PaError)
|
function handle_status(err::PaError)
|
||||||
if err != PA_NO_ERROR
|
if err != PA_NO_ERROR
|
||||||
msg = ccall((:Pa_GetErrorText, "libportaudio"),
|
msg = ccall((:Pa_GetErrorText, "libportaudio"),
|
||||||
|
|
Loading…
Reference in a new issue