]> Repos - nodeaudio/commitdiff
cancel playback functionality, on write event
authorAndrew Gundersen <gundersena@xavier.edu>
Thu, 26 Aug 2021 14:04:25 +0000 (09:04 -0500)
committerAndrew Gundersen <gundersena@xavier.edu>
Thu, 26 Aug 2021 14:04:25 +0000 (09:04 -0500)
package.json
src/na_callbacks.c
src/na_callbacks.h
src/na_core.c
src/na_core.h
src/na_front.c
test/rwSwitchStream.js

index ba7f42d34bd7029a04ee8ba90bfb2b375fafd4d6..874d32e4722eb29c95ea3bcd47c78b80dd798e3b 100644 (file)
@@ -11,7 +11,7 @@
     "test": "node test.js"
   },
   "gypfile": true,
-  "publishConfig": { 
+  "publishConfig": {
     "registry": "https://gitlab.com/api/v4/projects/28849281/packages/npm/"
   }
 }
index 55d3b05debdabe2b4b9f53503c8dc73f85b00dca..3132b8c43c62cca979a39141f7926f9ece550a8c 100644 (file)
@@ -10,6 +10,7 @@
 
 /* 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,
@@ -56,11 +57,30 @@ 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;
@@ -70,7 +90,7 @@ void init_callbacks( napi_env env,
                                       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,
@@ -82,7 +102,19 @@ void init_callbacks( napi_env env,
                                               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, 
@@ -136,24 +168,18 @@ int output_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]);
+// }
index 88da11507d81a1a426de5d9a898fc14e43e97474..48de8e1dac66320321f15c9ca5c323d0fde1c2b6 100644 (file)
@@ -8,8 +8,8 @@ typedef struct
 } 
 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, 
index 5d1d15b50fef5bffa70222f1fbb7b49248377f68..87aec5e550bb550b416136bf402e9af4a45aacbb 100644 (file)
@@ -20,11 +20,15 @@ PaUtilRingBuffer outputBuffer;
 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;
@@ -132,6 +136,8 @@ PaError Na_CloseOutputStream()
     return err;
 }
 
+bool cancel_pb_th = false;
+
 static void WriteLargeDataToOutputBuffer(void* arg)
 {
     playback_data* pd = (playback_data*) arg;
@@ -140,9 +146,9 @@ static void WriteLargeDataToOutputBuffer(void* 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)
@@ -158,6 +164,7 @@ static void WriteLargeDataToOutputBuffer(void* arg)
             pd->data += framesWritten * 2; /* convert to bytes */
         }
     }
+    pthread_mutex_unlock(&mutex);
 
     free(arg);
 }
@@ -167,8 +174,6 @@ void Na_WriteToOutputBuffer(void* data, size_t size)
     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.
      */
@@ -178,15 +183,22 @@ void Na_WriteToOutputBuffer(void* data, size_t size)
         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");
@@ -205,25 +217,10 @@ PaError Na_Terminate()
     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]);
-    // }
-
-
index 0111b3b0970ed5c3e73ec7384e27d935d992bfda..252fef7502fb60c05622631daeb7db5e0c2d41c4 100644 (file)
@@ -8,7 +8,7 @@ typedef struct
 }
 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);
 
@@ -24,6 +24,8 @@ PaError Na_CloseOutputStream();
 
 void Na_WriteToOutputBuffer(void* data, size_t size);
 
+void Na_CancelPlayback();
+
 PaError Na_Terminate();
 
 #endif
\ No newline at end of file
index 3e6aafee118507aa189d5d5e0ed45b54a78e5860..f284d5aaaffec3a75a364f740de8ee0c182d30d8 100644 (file)
@@ -161,6 +161,16 @@ napi_value WriteToOutputStream(napi_env env, napi_callback_info info)
     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");
@@ -190,9 +200,10 @@ napi_value Init(napi_env env, napi_value exports)
     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;
 
index 5a6e288222ccb3b521123c72bc2b3744d96e3d58..ba5b59c694929c080e566115c8361232cffb2c05 100644 (file)
@@ -21,6 +21,10 @@ emitter.on("data", (int16Arr) => {
     }
 });
 
+emitter.on("write", () => {
+    console.log("Writing frames");
+});
+
 const setStreams = () => {
 
     const defaultInput = nodeAudio.core.GetDefaultInputDevice();
@@ -48,6 +52,18 @@ const stopRecording = () => {
     /* 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));
@@ -69,4 +85,4 @@ stdin.on( 'data', function( key ) {
 setTimeout(() => {
     nodeAudio.core.Terminate();
     process.exit();
-}, 30000);
\ No newline at end of file
+}, 20000);
\ No newline at end of file