Skip to content
This repository has been archived by the owner on Jun 27, 2022. It is now read-only.

Commit

Permalink
Make Js callback synchronous with PortAudio callback
Browse files Browse the repository at this point in the history
This greatly reduces to memory operations, and negates the need for a mutex. It will eliminate the need for extra, complicated, code to implement paFramesPerBufferUnspecified.
  • Loading branch information
jacobcpeters committed Jun 30, 2016
1 parent 1afc64a commit 017f8f2
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 104 deletions.
6 changes: 3 additions & 3 deletions examples/pa_api_callback_sine.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ function callbackSine () {
// log what we're doing
console.log(`Play for ${numSeconds} seconds.\n`)
// stop stream when timeout fires
//setTimeout(() => {
//JsAudio.closeStream(stream)
//}, numSeconds * 1000)
setTimeout(() => {
JsAudio.closeStream(stream)
}, numSeconds * 1000)
}

// Run it
Expand Down
129 changes: 39 additions & 90 deletions src/callback.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,16 @@ JsPaStreamCallbackBridge::JsPaStreamCallbackBridge(Callback *callback_,
, UVCallback
);
async->data = this;
uv_mutex_init(&async_lock);
uv_barrier_init(&async_barrier, 2);

// Save userData to persistent object
SaveToPersistent(ToLocString("userData"), userData);
}

JsPaStreamCallbackBridge::~JsPaStreamCallbackBridge() {
uv_mutex_destroy(&async_lock);
uv_barrier_destroy(&async_barrier);
uv_close((uv_handle_t*)async, NULL);

//free buffer memory
if(m_inputBuffer != nullptr)
free(m_inputBuffer);
if(m_outputBuffer != nullptr)
free(m_outputBuffer);
}

int JsPaStreamCallbackBridge::sendToCallback(const void* input, unsigned long frameCount) {
uv_mutex_lock(&async_lock);
m_frameCount = frameCount;

if(m_inputBuffer != nullptr)
free(m_inputBuffer);
m_inputBuffer = malloc(sizeof(float) * frameCount * 2);

memmove(
m_inputBuffer,
input,
m_bytesPerFrameIn * frameCount
);
uv_mutex_unlock(&async_lock);

uv_async_send(async);
return 0;
}

void JsPaStreamCallbackBridge::dispatchJSCallback() {
Expand All @@ -58,74 +34,47 @@ void JsPaStreamCallbackBridge::dispatchJSCallback() {
v8::Local<v8::ArrayBuffer> output;
v8::Local<v8::Value> callbackReturn;

uv_mutex_lock(&async_lock);

frameCount = m_frameCount;

// Setup ArrayBuffer for input audio data
input = v8::ArrayBuffer::New(
v8::Isolate::GetCurrent(),
m_bytesPerFrameIn * frameCount
);
// Copy input audio data from bridge buffer to ArrayBuffer
memmove(
input->GetContents().Data(),
m_inputBuffer,
input->ByteLength()
);

// Setup ArrayBuffer for output audio data
output = v8::ArrayBuffer::New(
v8::Isolate::GetCurrent(),
m_bytesPerFrameOut * frameCount
);

// Create array of arguments and call the javascript callback
LocalValue argv[] = {
input,
output,
New<Number>(frameCount),
GetFromPersistent(ToLocString("userData"))
};
callbackReturn = callback->Call(4, argv);

if(m_outputBuffer != nullptr)
free(m_outputBuffer);
m_outputBuffer = malloc(output->ByteLength());
// Copy output audio data from bridge buffer to ArrayBuffer
memmove(
m_outputBuffer,
output->GetContents().Data(),
output->ByteLength()
);

// Store the return result of the javascript callback
// so it be sent to the PaStreamCallback function
m_callbackResult = LocalizeInt(callbackReturn);

uv_mutex_unlock(&async_lock);
frameCount = m_frameCount;

// Setup ArrayBuffer for input audio data
input = v8::ArrayBuffer::New(
v8::Isolate::GetCurrent(),
const_cast<void*>(m_inputBuffer),
m_bytesPerFrameIn * frameCount
);

// Setup ArrayBuffer for output audio data
output = v8::ArrayBuffer::New(
v8::Isolate::GetCurrent(),
m_outputBuffer,
m_bytesPerFrameOut * frameCount
);

// Create array of arguments and call the javascript callback
LocalValue argv[] = {
input,
output,
New<Number>(frameCount),
GetFromPersistent(ToLocString("userData"))
};
m_callbackResult = LocalizeInt(callback->Call(4, argv));

uv_barrier_wait(&async_barrier);
}

int JsPaStreamCallbackBridge::Execute(const void* input, void* output, unsigned long frameCount) {
m_frameCount = frameCount;

m_inputBuffer = input;
m_outputBuffer = output;

// Dispatch the asyncronous callback
uv_async_send(async);

void JsPaStreamCallbackBridge::consumeAudioData(void* output, unsigned long frameCount) {
// Wait for the asyncronous callback
uv_barrier_wait(&async_barrier);

if(m_outputBuffer != nullptr) {
memmove(
output,
m_outputBuffer,
m_bytesPerFrameOut * frameCount
);

// Free the output buffer and set it to nullptr to prevent it from sending the same output data twice
free(m_outputBuffer);
m_outputBuffer = nullptr;
}
return m_callbackResult;
}

int JsPaStreamCallbackBridge::getCallbackResult() {
int ret;
uv_mutex_lock(&async_lock);
ret = m_callbackResult;
uv_mutex_unlock(&async_lock);
return ret;
}
11 changes: 4 additions & 7 deletions src/callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,8 @@ class JsPaStreamCallbackBridge : public AsyncWorker {

~JsPaStreamCallbackBridge();

int sendToCallback(const void* input, unsigned long frameCount);
void dispatchJSCallback();
void consumeAudioData(void* output, unsigned long frameCount);
int getCallbackResult();

void dispatchJSCallback();
int Execute(const void* input, void* output, unsigned long frameCount);

private:
NAN_INLINE static NAUV_WORK_CB(UVCallback) {
Expand All @@ -31,11 +28,11 @@ class JsPaStreamCallbackBridge : public AsyncWorker {
void Execute() {}

uv_async_t *async;
uv_mutex_t async_lock;
uv_barrier_t async_barrier;
unsigned long m_frameCount;
size_t m_bytesPerFrameIn;
size_t m_bytesPerFrameOut;
void* m_inputBuffer;
const void* m_inputBuffer;
void* m_outputBuffer;
int m_callbackResult;

Expand Down
6 changes: 2 additions & 4 deletions src/jsaudio.cc
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,8 @@ static int StreamCallbackDispatcher (
) {
JsPaStreamCallbackBridge* bridge = static_cast<JsPaStreamCallbackBridge*>(userData);

bridge->sendToCallback(input, frameCount);
bridge->consumeAudioData(output, frameCount);

return bridge->getCallbackResult();
// Call Js callback
return bridge->Execute(input, output, frameCount);
}

void StreamFinishedCallback (void* userData) {
Expand Down

0 comments on commit 017f8f2

Please sign in to comment.