first pass at synchronizing threads, using RemoteRef and semaphore

This commit is contained in:
Spencer Russell 2013-12-21 03:20:39 -05:00
parent ff043ec95f
commit ae77cd2260
3 changed files with 127 additions and 69 deletions

10
deps/src/Makefile vendored
View file

@ -9,18 +9,16 @@ endif
.PHONY: all clean check-env default
#check-env:
#ifndef JULIA_ROOT
# $(error Environment variable JULIA_ROOT is not set.)
#ifndef JULIA_INC
# $(error Environment variable JULIA_INC is not set.)
#endif
INC =-I"$(JULIA_ROOT)/usr/include"
#INC =-I"$(JULIA_INC)"
FLAGS =-Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fPIC
CFLAGS =-g
LIBS =
LIBS =-lportaudio -lrt
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
OS = $(shell uname)

124
deps/src/shim.c vendored
View file

@ -1,50 +1,80 @@
#include <portaudio.h>
#include <semaphore.h>
#include <julia/julia.h>
#include <math.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,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
// PaDeviceInfo *info;
// int numDevices;
// printf("Found Devices:\n");
// numDevices = Pa_GetDeviceCount();
// for(i = 0; i < numDevices; ++i)
// {
// info = Pa_GetDeviceInfo(i);
// printf("%s\n", info->name);
// }
JuliaRemote = jlRemote;
sem_init(&CSemaphore, 0, 0);
RemotePutFunc = jl_get_function(jl_base_module, "put");
RemotePutArg = jl_box_int32(0);
err = Pa_OpenDefaultStream(&sin_stream,
err = Pa_OpenDefaultStream(&AudioStream,
0, /* no input channels */
2, /* stereo output */
1, /* mono output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
1024, /* frames per buffer, i.e. the number of sample frames
sampleRate,
bufSize, /* frames per buffer, i.e. the number of sample frames
that PortAudio will request from the callback. Many
apps may want to use paFramesPerBufferUnspecified,
which tells PortAudio to pick the best, possibly
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*/
if(err != paNoError)
{
return err;
}
err = Pa_StartStream(sin_stream);
err = Pa_StartStream(AudioStream);
if(err != paNoError)
{
return err;
@ -53,22 +83,23 @@ PaError play_sin(void)
return paNoError;
}
PaError stop_sin(void)
{
PaError err;
err = Pa_StopStream(sin_stream);
if(err != paNoError)
{
return err;
}
err = Pa_CloseStream(sin_stream);
if( err != paNoError )
{
return err;
}
return paNoError;
}
//PaError stop_sin(void)
//{
// PaError err;
// err = Pa_StopStream(sin_stream);
// if(err != 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
* 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,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
sem_wait(&CSemaphore);
for(i=0; i<framesPerBuffer; i++)
{
/* should modulo by 2PI */
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);
((float *)outputBuffer)[i] = ((float *)OutData)[i];
}
// TODO: copy the input data somewhere
jl_call2(RemotePutFunc, JuliaRemote, RemotePutArg);
return 0;
}

View file

@ -1,5 +1,3 @@
# thanks to Gustavo Goretkin for the start on this
module PortAudio
export play_sin, stop_sin
@ -11,20 +9,66 @@ typealias PaStream Void
const PA_NO_ERROR = 0
############ Exported Functions #############
function play_sin()
err = ccall((:play_sin, libportaudio_shim), PaError, ())
handle_status(err)
function play_sin(sample_rate, buf_size)
jl_remote = RemoteRef()
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
function stop_sin()
err = ccall((:stop_sin, libportaudio_shim), PaError, ())
handle_status(err)
end
#function stop_sin()
# err = ccall((:stop_sin, libportaudio_shim), PaError, ())
# handle_status(err)
#end
############ 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)
if err != PA_NO_ERROR
msg = ccall((:Pa_GetErrorText, "libportaudio"),