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
|
||||
|
||||
#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
124
deps/src/shim.c
vendored
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
Loading…
Reference in a new issue