"test": "node test.js"
},
"gypfile": true,
- "publishConfig": {
+ "publishConfig": {
"registry": "https://gitlab.com/api/v4/projects/28849281/packages/npm/"
}
}
/* JS callbacks */
napi_threadsafe_function js_on_data;
+napi_threadsafe_function js_on_playback;
/* Calls js_emit_ts from the main thread and translates data to node */
static void call_js_on_data( napi_env env,
status = napi_call_function(env, undefined, on_data, argc, &args[0], NULL);
}
+static void call_js_on_playback( napi_env env,
+ napi_value on_data,
+ void* context,
+ void* data )
+{
+ (void) context;
+ napi_status status;
+
+ napi_value channel;
+ status = napi_create_string_utf8(env, "write", NAPI_AUTO_LENGTH, &channel);
+
+ napi_value undefined;
+ status = napi_get_undefined(env, &undefined);
+
+ size_t argc = 1;
+ napi_value args[1] = { channel };
+ status = napi_call_function(env, undefined, on_data, argc, &args[0], NULL);
+}
+
/* Takes in a Node Event Emitter and creates JS threadsafe functions to be
* called from the PortAudio callbacks.
*/
-void init_callbacks( napi_env env,
- napi_value* js_emit,
+void init_callbacks( napi_env env,
+ napi_value* js_emit,
PaUtilRingBuffer* inputBuffer )
{
napi_status status;
NAPI_AUTO_LENGTH,
&resource_name );
- /* Create thread-safe callback js_cb_safe */
+ /* Create thread-safe js_on_data callback */
status = napi_create_threadsafe_function( env,
*js_emit,
NULL,
inputBuffer, /* context */
call_js_on_data,
&js_on_data );
- /* Optional: create another cb with js_emit here */
+
+ /* Create thread-safe js_on_write callback */
+ status = napi_create_threadsafe_function( env,
+ *js_emit,
+ NULL,
+ resource_name,
+ 0,
+ 1,
+ NULL,
+ NULL,
+ inputBuffer, /* context */
+ call_js_on_playback,
+ &js_on_playback );
}
int input_callback( const void* input,
if (framesToRead != 0)
{
PaUtil_ReadRingBuffer(rb, output, framesToRead);
- printf("Wrote %i frames\n", framesToRead);
+
+ napi_status status;
+ status = napi_call_threadsafe_function( js_on_playback,
+ NULL,
+ napi_tsfn_nonblocking );
}
return paContinue;
}
-
-
-
-
-
-
- // short* sample = (short*) output;
- // for (int i = 0; i < framesToRead * 2; ++i)
- // {
- // printf("%hu", sample[i]);
- // }
-
-
-
-
+// short* sample = (short*) output;
+// for (int i = 0; i < framesToRead * 2; ++i)
+// {
+// printf("%hu", sample[i]);
+// }
}
audio_info;
-void init_callbacks( napi_env env,
- napi_value* js_emit,
+void init_callbacks( napi_env env,
+ napi_value* js_emit,
PaUtilRingBuffer* inputBuffer );
int input_callback( const void* input,
void* inputBufferStore;
void* outputBufferStore;
-/* Sample format is hard-coded paInt16 (unsigned short, 2 bytes) */
+pthread_t pb_th;
+pthread_mutex_t mutex;
-PaError Na_Initialize(napi_env env, napi_value* js_cb)
+PaError Na_Initialize(napi_env env, napi_value* js_emit)
{
- init_callbacks(env, js_cb, &inputBuffer);
+ init_callbacks(env, js_emit, &inputBuffer);
+
+ pthread_mutex_init(&mutex, NULL);
+
PaError err;
err = Pa_Initialize();
return err;
return err;
}
+bool cancel_pb_th = false;
+
static void WriteLargeDataToOutputBuffer(void* arg)
{
playback_data* pd = (playback_data*) arg;
ring_buffer_size_t framesToWrite;
ring_buffer_size_t framesWritten;
- // printing data works here...
-
- while (pd->len > 0)
+ pthread_mutex_lock(&mutex);
+ cancel_pb_th = false;
+ while (pd->len > 0 && !cancel_pb_th)
{
writableSpace = PaUtil_GetRingBufferWriteAvailable(&outputBuffer);
if (writableSpace != 0)
pd->data += framesWritten * 2; /* convert to bytes */
}
}
+ pthread_mutex_unlock(&mutex);
free(arg);
}
ring_buffer_size_t writableSpace;
writableSpace = PaUtil_GetRingBufferWriteAvailable(&outputBuffer);
- // printing data works here...
-
/* If there is not enough space to write all the data at once, we must
* start a new thread in order to prevent blocking.
*/
playback_data* pd = (playback_data*) malloc(sizeof(playback_data));
pd->data = data;
pd->len = len;
- pthread_t w_th;
- pthread_create(&w_th, NULL, (void*) WriteLargeDataToOutputBuffer, pd);
+ pthread_create(&pb_th, NULL, (void*) WriteLargeDataToOutputBuffer, pd);
}
else
{
+ pthread_mutex_lock(&mutex);
PaUtil_WriteRingBuffer(&outputBuffer, data, writableSpace);
+ pthread_mutex_unlock(&mutex);
}
}
+void Na_CancelPlayback()
+{
+ printf("NACORE:Na_CancelPlayback\n");
+ cancel_pb_th = true;
+}
+
PaError Na_Terminate()
{
printf("NACORE:Na_Terminate\n");
free_buffer(&inputBuffer);
free_buffer(&outputBuffer);
+ pthread_mutex_destroy(&mutex);
+
return err;
}
-
- // /* Data being passed into the thread must be allocated to heap so that
- // * it isn't deleted when this function returns.
- // */
- // playback_data* pd = (playback_data*) malloc(sizeof(playback_data));
- // pd->data = (void*) malloc(len * 2);
- // memcpy(pd->data, data, 2);
- // pd->len = len;
-
- // short* sample = (short*) pd->data;
- // int count = (int) pd->len;
- // for (int i = 0; i < count; i++)
- // {
- // printf("%hu", sample[i]);
- // }
-
-
}
playback_data;
-PaError Na_Initialize(napi_env env, napi_value* js_cb);
+PaError Na_Initialize(napi_env env, napi_value* js_emit);
PaError Na_GetDefaultInputDevice(PaDeviceIndex* deviceIndex);
void Na_WriteToOutputBuffer(void* data, size_t size);
+void Na_CancelPlayback();
+
PaError Na_Terminate();
#endif
\ No newline at end of file
return NULL;
}
+napi_value CancelPlayback(napi_env env, napi_callback_info info)
+{
+ printf("NAPI::CancelPlayback\n");
+ (void) env;
+ (void) info;
+
+ Na_CancelPlayback();
+ return NULL;
+}
+
napi_value Terminate(napi_env env, napi_callback_info info)
{
printf("NAPI::Terminate\n");
DECLARE_NAPI_METHOD("CloseInputStream", CloseInputStream),
DECLARE_NAPI_METHOD("CloseOutputStream", CloseOutputStream),
DECLARE_NAPI_METHOD("WriteToOutputStream", WriteToOutputStream),
+ DECLARE_NAPI_METHOD("CancelPlayback", CancelPlayback)
};
- status = napi_define_properties(env, exports, 9, desc);
+ status = napi_define_properties(env, exports, 10, desc);
return exports;
}
});
+emitter.on("write", () => {
+ console.log("Writing frames");
+});
+
const setStreams = () => {
const defaultInput = nodeAudio.core.GetDefaultInputDevice();
/* Play recorded audio */
console.log("Playing recorded audio...");
nodeAudio.core.WriteToOutputStream(pcmAudio.buffer);
+
+ /* Two writes test */
+ // setTimeout(() => {
+ // console.log("Playing again...");
+ // nodeAudio.core.WriteToOutputStream(pcmAudio.buffer);
+ // }, 2000);
+
+ /* Cancel mid-playback test */
+ setTimeout(() => {
+ console.log("Cancelling...");
+ nodeAudio.core.CancelPlayback();
+ }, 2000);
}
nodeAudio.core.Initialize(emitter.emit.bind(emitter));
setTimeout(() => {
nodeAudio.core.Terminate();
process.exit();
-}, 30000);
\ No newline at end of file
+}, 20000);
\ No newline at end of file