Skip to content
This repository was archived by the owner on Jun 27, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb11d0e
Merge pull request #2 from jacobcpeters/input-functions
jacobcpeters May 7, 2016
18a7c17
Merge pull request #3 from JsAudioOrg/master
jacobcpeters May 8, 2016
0989630
Merge pull request #8 from jacobcpeters/master
jacobcpeters Jun 22, 2016
caeaf85
Revert "Merge with main fork"
jacobcpeters Jun 22, 2016
7c74060
Merge pull request #9 from JsAudioOrg/revert-8-master
jacobcpeters Jun 22, 2016
93e5c92
Merge pull request #4 from JsAudioOrg/master
jacobcpeters Jun 22, 2016
3556ef8
Initial implementation of JsPaStreamCallback class
jacobcpeters Jun 22, 2016
e6bc977
Added callback code to PaOpenDefaultStream
jacobcpeters Jun 22, 2016
e7cf0df
Created a test for the callback
jacobcpeters Jun 22, 2016
75104cb
Removed debugging code
jacobcpeters Jun 22, 2016
b78eedb
Added callback code to PaOpenStream
jacobcpeters Jun 22, 2016
b466329
Renamed JsPaStreamCallback to JsPaStreamCallbackBridge
jacobcpeters Jun 23, 2016
8788ebc
Further Implement JsPaStreamCallbackBridge
jacobcpeters Jun 23, 2016
13dcc8d
Update intermediary PortAudio callback
jacobcpeters Jun 23, 2016
86a9fa8
Add sine output to test script
jacobcpeters Jun 23, 2016
0f43c39
Refactoring of callback test into example
jacobcpeters Jun 23, 2016
0996790
Cleaned up example code
jacobcpeters Jun 24, 2016
80249ef
Refactored CallbackBridge to work with more sample formats
jacobcpeters Jun 24, 2016
a0f3d5d
Added comments to ReadStream and WriteStream to clarify code
jacobcpeters Jun 24, 2016
b6ae676
Removed unused variable
jacobcpeters Jun 24, 2016
4fbb10d
Added a callback wire example
jacobcpeters Jun 24, 2016
08e5045
Refactor JsPaStreamCallbackBridge to accept a userData Object
jacobcpeters Jun 24, 2016
d017f57
Call deconstructors when stream is stopped, and add support for userD…
jacobcpeters Jun 26, 2016
15c2426
Refactor example to show how to use a userData Object
jacobcpeters Jun 26, 2016
1afc64a
Added a few comments
jacobcpeters Jun 28, 2016
ba38b2c
Make Js callback synchronous with PortAudio callback
jacobcpeters Jun 30, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
'targets': [
{
'target_name': 'jsaudio',
'sources': ['src/addon.cc', 'src/jsaudio.cc', 'src/helpers.cc', 'src/stream.cc'],
'sources': [
'src/addon.cc',
'src/jsaudio.cc',
'src/helpers.cc',
'src/stream.cc',
'src/callback.cc'
],
'include_dirs': [
'<!(node -e "require(\'nan\')")',
'<(module_root_dir)/vendor/'
Expand Down
96 changes: 96 additions & 0 deletions examples/pa_api_callback_sine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict'

// Requires
const JsAudioExports = require('./../lib/jsaudio')
const JsAudio = JsAudioExports.JsAudioNative
const JsPaStream = JsAudioExports.JsPaStream

/* CALLBACK SINE EXAMPLE
http://portaudio.com/docs/v19-doxydocs/paex__sine_8c_source.html
*/

// Setup
const numSeconds = process.argv[2] || 5
const sampleRate = 48000
const channels = 2
const framesPerBuffer = 8192
const streamFlags = 0
const tableSize = 200
const formats = {
paFloat32: 1,
paInt32: 2,
paInt24: 4,
paInt16: 8,
paInt8: 16,
paUInt8: 32,
paCustomFormat: 65536,
paNonInterleaved: 2147483648
}



function callback (input, output, frameCount, data) {
var outputBufferView = new Float32Array(output)

for(var i=0; i<frameCount*2; i+=2 )
{
outputBufferView[i] = data.sine[data.left_phase] // left
outputBufferView[i+1] = data.sine[data.right_phase] // right
data.left_phase += 1
if( data.left_phase >= data.tableSize ) data.left_phase -= data.tableSize
data.right_phase += 3 // higher pitch so we can distinguish left and right.
if( data.right_phase >= data.tableSize ) data.right_phase -= data.tableSize
}
return 0
}

//Main function
function callbackSine () {
// initialize stream instance
let stream = new JsPaStream()
// initialize data that is sent to callback
let data = {
left_phase: 0,
right_phase: 0,
tableSize: tableSize,
sine: []
}
console.log(
'PortAudio Test: output sine wave\n',
`SR = ${sampleRate}, BufSize = ${framesPerBuffer}\n`
)

// initialise sinusoidal wavetable
for(var i=0; i<tableSize; i++ )
{
data.sine[i] = Math.sin((i/tableSize) * Math.PI * 2 )
}

// initialize PortAudio
JsAudio.initialize()
// setup stream options
let streamOpts = {
stream,
sampleRate,
framesPerBuffer,
streamFlags,
sampleFormat: formats.paFloat32,
numInputChannels: channels,
numOutputChannels: channels,
callback: callback,
userData: data
}
// open stream with options
console.log(JsAudio.openDefaultStream(streamOpts))
// start stream
JsAudio.startStream(stream)
// log what we're doing
console.log(`Play for ${numSeconds} seconds.\n`)
// stop stream when timeout fires
setTimeout(() => {
JsAudio.closeStream(stream)
}, numSeconds * 1000)
}

// Run it
callbackSine()
63 changes: 63 additions & 0 deletions examples/pa_api_callback_wire.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use strict'

// Requires
const JsAudioExports = require('./../lib/jsaudio')
const JsAudio = JsAudioExports.JsAudioNative
const JsPaStream = JsAudioExports.JsPaStream

/* CALLBACK WIRE EXAMPLE */

// Setup
const sampleRate = 48000
const channels = 2
const framesPerBuffer = 8192
const streamFlags = 0
const tableSize = 200
const formats = {
paFloat32: 1,
paInt32: 2,
paInt24: 4,
paInt16: 8,
paInt8: 16,
paUInt8: 32,
paCustomFormat: 65536,
paNonInterleaved: 2147483648
}


//Create callback function that copies input to output
function callback (input, output, frameCount) {
var outputBufferView = new Float32Array(output)
var inputBufferView = new Float32Array(input)

for(var i=0; i < frameCount * 2; ++i) {
outputBufferView[i] = inputBufferView[i]
}
}

//Main function
function callbackWire () {
// initialize stream instance
let stream = new JsPaStream()

// initialize PortAudio
JsAudio.initialize()
// setup stream options
let streamOpts = {
stream,
sampleRate,
framesPerBuffer,
streamFlags,
sampleFormat: formats.paFloat32,
numInputChannels: channels,
numOutputChannels: channels,
callback: callback
}
// open stream with options
JsAudio.openDefaultStream(streamOpts)
// start stream
JsAudio.startStream(stream)
}

// Run it
callbackWire()
80 changes: 80 additions & 0 deletions src/callback.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "callback.h"
#include "helpers.h"

JsPaStreamCallbackBridge::JsPaStreamCallbackBridge(Callback *callback_,
size_t bytesPerFrameIn,
size_t bytesPerFrameOut,
const LocalValue &userData)
: AsyncWorker(callback_), m_frameCount(0), m_callbackResult(0),
m_bytesPerFrameIn(bytesPerFrameIn), m_bytesPerFrameOut(bytesPerFrameOut),
m_inputBuffer(nullptr), m_outputBuffer(nullptr) {
async = new uv_async_t;
uv_async_init(
uv_default_loop()
, async
, UVCallback
);
async->data = this;
uv_barrier_init(&async_barrier, 2);

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

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

}

void JsPaStreamCallbackBridge::dispatchJSCallback() {
HandleScope scope;
unsigned long frameCount;
v8::Local<v8::ArrayBuffer> input;
v8::Local<v8::ArrayBuffer> output;
v8::Local<v8::Value> callbackReturn;


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);

// Wait for the asyncronous callback
uv_barrier_wait(&async_barrier);

return m_callbackResult;
}

41 changes: 41 additions & 0 deletions src/callback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef CALLBACK_H
#define CALLBack_H

#include "jsaudio.h"

class JsPaStreamCallbackBridge : public AsyncWorker {
public:
explicit JsPaStreamCallbackBridge(Callback *callback_,
size_t bytesPerFrameIn,
size_t bytesPerFrameOut,
const LocalValue &userData);
explicit JsPaStreamCallbackBridge(Callback *callback_, size_t bytesPerFrame,
const LocalValue &userData)
: JsPaStreamCallbackBridge(callback_, bytesPerFrame, bytesPerFrame, userData) {}

~JsPaStreamCallbackBridge();

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

private:
NAN_INLINE static NAUV_WORK_CB(UVCallback) {
JsPaStreamCallbackBridge *callback =
static_cast<JsPaStreamCallbackBridge*>(async->data);
callback->dispatchJSCallback();
}

void Execute() {}

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

};

#endif
34 changes: 34 additions & 0 deletions src/helpers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ LocalObject ToLocObject (MaybeLocalValue lvIn) {
return lvIn.ToLocalChecked()->ToObject();
}

LocalFunction ToLocFunction (MaybeLocalValue lvIn) {
return lvIn.ToLocalChecked().As<Function>();
}

LocalString ConstCharPointerToLocString (const char* constCharPointer) {
if (constCharPointer == NULL) return New("").ToLocalChecked();
std::string str(constCharPointer);
Expand Down Expand Up @@ -108,3 +112,33 @@ PaStreamParameters LocObjToPaStreamParameters (LocalObject obj) {
};
return params;
}

size_t bytesPerFrame (PaSampleFormat sampleFormat) {
size_t retVal;

switch (sampleFormat) {
case paFloat32:
retVal = sizeof(float) * 2;
break;
case paInt32:
retVal = sizeof(int32_t) * 2;
break;
case paInt24:
retVal = (sizeof(int16_t) + sizeof(int8_t)) * 2;
break;
case paInt16:
retVal = sizeof(int16_t) * 2;
break;
case paInt8:
retVal = sizeof(int8_t) * 2;
break;
case paUInt8:
retVal = sizeof(uint16_t) * 2;
break;
default:
retVal = 0;
break;
}

return retVal;
}
2 changes: 2 additions & 0 deletions src/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ double LocalizeDouble (MaybeLocalValue lvIn);
unsigned long LocalizeULong (MaybeLocalValue lvIn);
LocalString ToLocString (std::string str);
LocalObject ToLocObject (MaybeLocalValue lvIn);
LocalFunction ToLocFunction (MaybeLocalValue lvIn);
LocalString ConstCharPointerToLocString (const char* constCharPointer);
void HostApiInfoToLocalObject (LocalObject obj, const PaHostApiInfo* hai);
void DeviceInfoToLocalObject (LocalObject obj, const PaDeviceInfo* di);
PaStreamParameters LocObjToPaStreamParameters (LocalObject obj);
size_t bytesPerFrame (PaSampleFormat sampleFormat);

#endif
Loading