From 19ea2a139f7dc9dcdc6a8d21000ec0d48c52639d Mon Sep 17 00:00:00 2001 From: c vw Date: Thu, 27 May 2021 09:29:32 +0200 Subject: [PATCH] Make the "microphone mutex" private (static) --- audio.c | 2 +- portaudio.c | 206 +++++++++++++++++++++++++++------------------------ pulseaudio.c | 44 ++++++----- 3 files changed, 130 insertions(+), 122 deletions(-) diff --git a/audio.c b/audio.c index 49ff5eb..7c54ed8 100644 --- a/audio.c +++ b/audio.c @@ -50,7 +50,7 @@ int audio = 0; int mic_buffer_size = 720; // samples (both left and right) -GMutex audio_mutex; +static GMutex audio_mutex; static snd_pcm_t *record_handle=NULL; static snd_pcm_format_t record_audio_format; diff --git a/portaudio.c b/portaudio.c index b0fed87..9ef19cc 100644 --- a/portaudio.c +++ b/portaudio.c @@ -45,7 +45,7 @@ AUDIO_DEVICE input_devices[MAX_AUDIO_DEVICES]; int n_output_devices; AUDIO_DEVICE output_devices[MAX_AUDIO_DEVICES]; -GMutex audio_mutex; +static GMutex audio_mutex; int n_input_devices=0; int n_output_devices=0; @@ -105,7 +105,7 @@ void audio_get_cards() err = Pa_Initialize(); if( err != paNoError ) { - fprintf(stderr, "PORTAUDIO ERROR: Pa_Initialize: %s\n", Pa_GetErrorText(err)); + g_print("%s: init error %s\n", __FUNCTION__,Pa_GetErrorText(err)); return; } numDevices = Pa_GetDeviceCount(); @@ -135,7 +135,7 @@ void audio_get_cards() input_devices[n_input_devices].index=i; n_input_devices++; } - fprintf(stderr,"PORTAUDIO INPUT DEVICE, No=%d, Name=%s\n", i, deviceInfo->name); + g_print("%s: INPUT DEVICE, No=%d, Name=%s\n", __FUNCTION__, i, deviceInfo->name); } outputParameters.device = i; @@ -150,7 +150,7 @@ void audio_get_cards() output_devices[n_output_devices].index=i; n_output_devices++; } - fprintf(stderr,"PORTAUDIO OUTPUT DEVICE, No=%d, Name=%s\n", i, deviceInfo->name); + g_print("%s: OUTPUT DEVICE, No=%d, Name=%s\n", __FUNCTION__, i, deviceInfo->name); } } } @@ -179,7 +179,6 @@ int audio_open_input() // // Look up device name and determine device ID // - g_mutex_lock(&audio_mutex); padev=-1; for (i=0; imicrophone_name, input_devices[i].name)) { @@ -187,15 +186,16 @@ int audio_open_input() break; } } - fprintf(stderr,"audio_open_input: name=%s PADEV=%d\n",transmitter->microphone_name,padev); + g_print("%s: name=%s PADEV=%d\n", __FUNCTION__, transmitter->microphone_name, padev); // - // Should not occur, but possibly device name not found + // Device name possibly came from props file and device is no longer there // if (padev < 0) { - g_mutex_unlock(&audio_mutex); return -1; } + g_mutex_lock(&audio_mutex); + bzero( &inputParameters, sizeof( inputParameters ) ); //not necessary if you are filling in all the fields inputParameters.channelCount = 1; // MONO inputParameters.device = padev; @@ -207,7 +207,7 @@ int audio_open_input() err = Pa_OpenStream(&record_handle, &inputParameters, NULL, 48000.0, (unsigned long) MY_AUDIO_BUFFER_SIZE, paNoFlag, pa_mic_cb, NULL); if (err != paNoError) { - fprintf(stderr, "PORTAUDIO ERROR: AOI open stream: %s\n",Pa_GetErrorText(err)); + g_print("%s: open stream error %s\n", __FUNCTION__, Pa_GetErrorText(err)); record_handle=NULL; g_mutex_unlock(&audio_mutex); return -1; @@ -215,22 +215,33 @@ int audio_open_input() mic_ring_buffer=(float *) g_new(float,MY_RING_BUFFER_SIZE); mic_ring_outpt = mic_ring_inpt=0; + if (mic_ring_buffer == NULL) { + Pa_CloseStream(record_handle); + record_handle=NULL; + g_print("%s: alloc buffer failed.\n", __FUNCTION__); g_mutex_unlock(&audio_mutex); - audio_close_input(); return -1; } err = Pa_StartStream(record_handle); if (err != paNoError) { - fprintf(stderr, "PORTAUDIO ERROR: AOI start stream:%s\n",Pa_GetErrorText(err)); + g_print("%s: start stream error %s\n", __FUNCTION__, Pa_GetErrorText(err)); + Pa_CloseStream(record_handle); + record_handle=NULL; + g_free(mic_ring_buffer); + mic_ring_buffer=NULL; g_mutex_unlock(&audio_mutex); - audio_close_input(); return -1; } + + // + // Finished! + // g_mutex_unlock(&audio_mutex); return 0; } + // // PortAudio call-back function for Audio output // @@ -242,28 +253,34 @@ int pa_out_cb(const void *inputBuffer, void *outputBuffer, unsigned long framesP float *out = (float *)outputBuffer; RECEIVER *rx = (RECEIVER *)userdata; int i, newpt; - float *buffer=rx->local_audio_buffer; int avail; // only used for debug if (out == NULL) { - fprintf(stderr,"PortAudio error: bogus audio buffer in callback\n"); + g_print("%s: bogus audio buffer in callback\n", __FUNCTION__); return paContinue; } - // DEBUG: report water mark - //avail = rx->local_audio_buffer_inpt - rx->local_audio_buffer_outpt; - //if (avail < 0) avail += MY_RING_BUFFER_SIZE; - //fprintf(stderr,"AVAIL=%d\n", avail); - newpt=rx->local_audio_buffer_outpt; - for (i=0; i< framesPerBuffer; i++) { - if (rx->local_audio_buffer_inpt == newpt) { - // Ring buffer empty, send zero sample - *out++ = 0.0; - } else { - *out++ = buffer[newpt++]; - if (newpt >= MY_RING_BUFFER_SIZE) newpt=0; - rx->local_audio_buffer_outpt=newpt; + g_mutex_lock(&rx->local_audio_mutex); + if (rx->local_audio_buffer != NULL) { + // + // Mutex protection: if the buffer is non-NULL it cannot vanish + // util callback is completed + // DEBUG: report water mark + //avail = rx->local_audio_buffer_inpt - rx->local_audio_buffer_outpt; + //if (avail < 0) avail += MY_RING_BUFFER_SIZE; + //g_print("%s: AVAIL=%d\n", __FUNCTION__, avail); + newpt=rx->local_audio_buffer_outpt; + for (i=0; i< framesPerBuffer; i++) { + if (rx->local_audio_buffer_inpt == newpt) { + // Ring buffer empty, send zero sample + *out++ = 0.0; + } else { + *out++ = rx->local_audio_buffer[newpt++]; + if (newpt >= MY_RING_BUFFER_SIZE) newpt=0; + rx->local_audio_buffer_outpt=newpt; + } } } + g_mutex_unlock(&rx->local_audio_mutex); return paContinue; } @@ -277,41 +294,29 @@ int pa_mic_cb(const void *inputBuffer, void *outputBuffer, unsigned long framesP { float *in = (float *)inputBuffer; int i, newpt; - float sample; if (in == NULL) { // This should not happen, so we do not send silence etc. - fprintf(stderr,"PortAudio error: bogus audio buffer in callback\n"); + g_print("%s: bogus audio buffer in callback\n", __FUNCTION__); return paContinue; } - // - // send the samples in the buffer - // g_mutex_lock(&audio_mutex); - for (i=0; iaudio_name,padev); + g_print("%s: name=%s PADEV=%d\n", __FUNCTION__, rx->audio_name, padev); // - // Should not occur, but possibly device name not found + // Device name possibly came from props file and device is no longer there // if (padev < 0) { return -1; } g_mutex_lock(&rx->local_audio_mutex); + bzero( &outputParameters, sizeof( outputParameters ) ); //not necessary if you are filling in all the fields outputParameters.channelCount = 1; // Always MONO outputParameters.device = padev; @@ -384,6 +394,15 @@ int audio_open_output(RECEIVER *rx) outputParameters.suggestedLatency = 0.0; //Pa_GetDeviceInfo(padev)->defaultLowOutputLatency ; outputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host's API docs for info on using this field + err = Pa_OpenStream(&(rx->playstream), NULL, &outputParameters, 48000.0, (unsigned long) MY_AUDIO_BUFFER_SIZE, + paNoFlag, pa_out_cb, rx); + if (err != paNoError) { + g_print("%s: open stream error %s\n", __FUNCTION__, Pa_GetErrorText(err)); + rx->playstream = NULL; + g_mutex_unlock(&rx->local_audio_mutex); + return -1; + } + // // This is now a ring buffer much larger than a single audio buffer // @@ -392,26 +411,27 @@ int audio_open_output(RECEIVER *rx) rx->local_audio_buffer_outpt=0; rx->local_audio_cw=0; - err = Pa_OpenStream(&(rx->playback_handle), NULL, &outputParameters, 48000.0, (unsigned long) MY_AUDIO_BUFFER_SIZE, - paNoFlag, pa_out_cb, rx); - if (err != paNoError || rx->local_audio_buffer == NULL) { - fprintf(stderr,"PORTAUDIO ERROR: out open stream: %s\n",Pa_GetErrorText(err)); - rx->playback_handle = NULL; - if (rx->local_audio_buffer) g_free(rx->local_audio_buffer); - rx->local_audio_buffer = NULL; + if (rx->local_audio_buffer == NULL) { + g_print("%s: allocate buffer failed\n", __FUNCTION__); + Pa_CloseStream(rx->playstream); + rx->playstream=NULL; g_mutex_unlock(&rx->local_audio_mutex); return -1; } - err = Pa_StartStream(rx->playback_handle); + err = Pa_StartStream(rx->playstream); if (err != paNoError) { - fprintf(stderr,"PORTAUDIO ERROR: out start stream:%s\n",Pa_GetErrorText(err)); - rx->playback_handle=NULL; - if (rx->local_audio_buffer) g_free(rx->local_audio_buffer); + g_print("%s: error starting stream:%s\n",__FUNCTION__,Pa_GetErrorText(err)); + Pa_CloseStream(rx->playstream); + rx->playstream=NULL; + g_free(rx->local_audio_buffer); rx->local_audio_buffer = NULL; g_mutex_unlock(&rx->local_audio_mutex); return -1; } + // + // Finished! + // g_mutex_unlock(&rx->local_audio_mutex); return 0; } @@ -424,34 +444,25 @@ int audio_open_output(RECEIVER *rx) void audio_close_input() { PaError err; - void *p; - fprintf(stderr,"AudioCloseInput: %s\n", transmitter->microphone_name); + g_print("%s: micname=%s\n", __FUNCTION__,transmitter->microphone_name); + g_mutex_lock(&audio_mutex); if(record_handle!=NULL) { err = Pa_StopStream(record_handle); if (err != paNoError) { - fprintf(stderr,"PORTAUDIO ERROR: in stop stream: %s\n",Pa_GetErrorText(err)); + g_print("%s: error stopping stream: %s\n",__FUNCTION__,Pa_GetErrorText(err)); } err = Pa_CloseStream(record_handle); if (err != paNoError) { - fprintf(stderr,"PORTAUDIO ERROR: in close stream: %s\n",Pa_GetErrorText(err)); + g_print("%s: %s\n",__FUNCTION__,Pa_GetErrorText(err)); } record_handle=NULL; } - // - // We do not want to do a mutex lock/unlock for every single mic sample - // accessed. Since only the ring buffer is maintained by the functions - // audio_get_next_mic_sample() and in the "mic callback" function, - // it is more than enough to wait 2 msec after setting mic_ring_buffer to NULL - // before actually releasing the storage. - // if (mic_ring_buffer != NULL) { - p=mic_ring_buffer; - mic_ring_buffer=NULL; - usleep(2); - g_free(p); + g_free(mic_ring_buffer); } + g_mutex_unlock(&audio_mutex); } // @@ -462,7 +473,7 @@ void audio_close_input() void audio_close_output(RECEIVER *rx) { PaError err; - fprintf(stderr,"AudioCloseOutput: %s\n", rx->audio_name); + g_print("%s: device=%sn", __FUNCTION__,rx->audio_name); g_mutex_lock(&rx->local_audio_mutex); if(rx->local_audio_buffer!=NULL) { @@ -470,16 +481,16 @@ void audio_close_output(RECEIVER *rx) { rx->local_audio_buffer=NULL; } - if(rx->playback_handle!=NULL) { - err = Pa_StopStream(rx->playback_handle); + if(rx->playstream!=NULL) { + err = Pa_StopStream(rx->playstream); if (err != paNoError) { - fprintf(stderr,"PORTAUDIO ERROR: out stop stream: %s\n",Pa_GetErrorText(err)); + g_print("%s: stop stream error %s\n", __FUNCTION__, Pa_GetErrorText(err)); } - err = Pa_CloseStream(rx->playback_handle); + err = Pa_CloseStream(rx->playstream); if (err != paNoError) { - fprintf(stderr,"PORTAUDIO ERROR: out close stream: %s\n",Pa_GetErrorText(err)); + g_print("%s: close stream error %s\n",__FUNCTION__,Pa_GetErrorText(err)); } - rx->playback_handle=NULL; + rx->playstream=NULL; } g_mutex_unlock(&rx->local_audio_mutex); } @@ -516,7 +527,7 @@ int audio_write (RECEIVER *rx, float left, float right) } g_mutex_lock(&rx->local_audio_mutex); - if (rx->playback_handle != NULL && buffer != NULL) { + if (rx->playstream != NULL && buffer != NULL) { if (rx->local_audio_cw == 1) { // // We come from a TX->RX transition: @@ -590,21 +601,20 @@ int audio_write (RECEIVER *rx, float left, float right) // Thus we have an active latency management. // int cw_audio_write(RECEIVER *rx, float sample) { - float *buffer = rx->local_audio_buffer; int oldpt, newpt; static int count=0; int avail; int adjust; g_mutex_lock(&rx->local_audio_mutex); - if (rx->playback_handle != NULL && buffer != NULL) { + if (rx->playstream != NULL && rx->local_audio_buffer != NULL) { if (rx->local_audio_cw == 0 && cw_keyer_sidetone_volume > 0) { // // First time producing CW audio after RX/TX transition: // empty audio buffer and insert 512 samples of silence // rx->local_audio_cw=1; - bzero(buffer, 512*sizeof(float)); + bzero(rx->local_audio_buffer, 512*sizeof(float)); rx->local_audio_buffer_inpt=512; rx->local_audio_buffer_outpt=0; count=0; @@ -637,7 +647,7 @@ int cw_audio_write(RECEIVER *rx, float sample) { // // buffer space available // - buffer[oldpt] = sample; + rx->local_audio_buffer[oldpt] = sample; rx->local_audio_buffer_inpt=newpt; } break; @@ -647,9 +657,9 @@ int cw_audio_write(RECEIVER *rx, float sample) { // insert two samples of silence. No check on "buffer full" needed // oldpt=rx->local_audio_buffer_inpt; - buffer[oldpt++]=0.0; + rx->local_audio_buffer[oldpt++]=0.0; if (oldpt == MY_RING_BUFFER_SIZE) oldpt=0; - buffer[oldpt++]=0.0; + rx->local_audio_buffer[oldpt++]=0.0; if (oldpt == MY_RING_BUFFER_SIZE) oldpt=0; rx->local_audio_buffer_inpt=oldpt; break; diff --git a/pulseaudio.c b/pulseaudio.c index 6c13ff8..3ea4457 100644 --- a/pulseaudio.c +++ b/pulseaudio.c @@ -21,7 +21,7 @@ AUDIO_DEVICE output_devices[MAX_AUDIO_DEVICES]; // // Ring buffer for "local microphone" samples -// NOTE: lead large buffer for some "loopback" devices which produce +// NOTE: need large buffer for some "loopback" devices which produce // samples in large chunks if fed from digimode programs. // #define MICRINGLEN 6000 @@ -36,11 +36,11 @@ static pa_context *pa_ctx; static pa_simple* microphone_stream; static gint local_microphone_buffer_offset; static float *local_microphone_buffer=NULL; -static GThread *mic_read_thread_id; +static GThread *mic_read_thread_id=0; static gboolean running; gint local_microphone_buffer_size=720; -GMutex audio_mutex; +static GMutex audio_mutex; static void source_list_cb(pa_context *context,const pa_source_info *s,int eol,void *data) { int i; @@ -122,7 +122,7 @@ void audio_get_cards() { g_mutex_lock(&audio_mutex); main_loop=pa_glib_mainloop_new(NULL); main_loop_api=pa_glib_mainloop_get_api(main_loop); - pa_ctx=pa_context_new(main_loop_api,"linhpsdr"); + pa_ctx=pa_context_new(main_loop_api,"piHPSDR"); pa_context_connect(pa_ctx,NULL,0,NULL); pa_context_set_state_callback(pa_ctx, state_cb, NULL); } @@ -144,7 +144,7 @@ int audio_open_output(RECEIVER *rx) { char stream_id[16]; sprintf(stream_id,"RX-%d",rx->id); - rx->playback_handle=pa_simple_new(NULL, // Use the default server. + rx->playstream=pa_simple_new(NULL, // Use the default server. "piHPSDR", // Our application's name. PA_STREAM_PLAYBACK, rx->audio_name, @@ -155,7 +155,7 @@ int audio_open_output(RECEIVER *rx) { &err // error code if returns NULL ); - if(rx->playback_handle!=NULL) { + if(rx->playstream!=NULL) { rx->local_audio_buffer_offset=0; rx->local_audio_buffer=g_new0(float,2*rx->local_audio_buffer_size); g_print("%s: allocated local_audio_buffer %p size %ld bytes\n",__FUNCTION__,rx->local_audio_buffer,2*rx->local_audio_buffer_size*sizeof(float)); @@ -177,7 +177,7 @@ static void *mic_read_thread(gpointer arg) { while(running) { // // It is guaranteed that local_microphone_buffer, mic_ring_buffer, and microphone_stream - // will not be destroyes until this thread has terminated (and waited for via pthread_join) + // will not be destroyed until this thread has terminated (and waited for via thread joining) // rc=pa_simple_read(microphone_stream, local_microphone_buffer, @@ -276,9 +276,9 @@ int audio_open_input() { void audio_close_output(RECEIVER *rx) { g_mutex_lock(&rx->local_audio_mutex); - if(rx->playback_handle!=NULL) { - pa_simple_free(rx->playback_handle); - rx->playback_handle=NULL; + if(rx->playstream!=NULL) { + pa_simple_free(rx->playstream); + rx->playstream=NULL; } if(rx->local_audio_buffer!=NULL) { g_free(rx->local_audio_buffer); @@ -289,12 +289,18 @@ void audio_close_output(RECEIVER *rx) { void audio_close_input() { running=FALSE; + g_mutex_lock(&audio_mutex); + if(mic_read_thread_id!=NULL) { g_print("audio_close_input: wait for thread to complete\n"); + // + // wait for the mic read thread to terminate, + // then destroy the stream and the buffers + // This way, the buffers cannot "vanish" in the mic read thread + // g_thread_join(mic_read_thread_id); mic_read_thread_id=NULL; } - g_mutex_lock(&audio_mutex); if(microphone_stream!=NULL) { pa_simple_free(microphone_stream); microphone_stream=NULL; @@ -337,17 +343,13 @@ int cw_audio_write(RECEIVER *rx,float sample) { int err; g_mutex_lock(&rx->local_audio_mutex); - if (rx->playback_handle != NULL) { // Note this test must be done "mutex-protected" - if(rx->local_audio_buffer==NULL) { - rx->local_audio_buffer_offset=0; - rx->local_audio_buffer=g_new0(float,2*rx->local_audio_buffer_size); - } + if (rx->playstream != NULL && rx->local_audio_buffer != NULL) { // Note this test must be done "mutex-protected" rx->local_audio_buffer[rx->local_audio_buffer_offset*2]=sample; rx->local_audio_buffer[(rx->local_audio_buffer_offset*2)+1]=sample; rx->local_audio_buffer_offset++; if(rx->local_audio_buffer_offset>=rx->local_audio_buffer_size) { - rc=pa_simple_write(rx->playback_handle, + rc=pa_simple_write(rx->playstream, rx->local_audio_buffer, rx->local_audio_buffer_size*sizeof(float)*2, &err); @@ -368,17 +370,13 @@ int audio_write(RECEIVER *rx,float left_sample,float right_sample) { int err; g_mutex_lock(&rx->local_audio_mutex); - if (rx->playback_handle != NULL) { // Note this test must be done "mutex-protected" - if(rx->local_audio_buffer==NULL) { - rx->local_audio_buffer_offset=0; - rx->local_audio_buffer=g_new0(float,2*rx->local_audio_buffer_size); - } + if (rx->playstream != NULL && rx->local_audio_buffer != NULL) { // Note this test must be done "mutex-protected" rx->local_audio_buffer[rx->local_audio_buffer_offset*2]=left_sample; rx->local_audio_buffer[(rx->local_audio_buffer_offset*2)+1]=right_sample; rx->local_audio_buffer_offset++; if(rx->local_audio_buffer_offset>=rx->local_audio_buffer_size) { - rc=pa_simple_write(rx->playback_handle, + rc=pa_simple_write(rx->playstream, rx->local_audio_buffer, rx->local_audio_buffer_size*sizeof(float)*2, &err); -- 2.45.2