/* * Copyright (C) 2007, Gilles Casse * Copyright (C) 2015 Reece H. Dunn * based on AudioIO.cc (Audacity-1.2.4b) and wavegen.cpp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see: . */ #include "config.h" #include "speech.h" #ifdef USE_ASYNC // This source file is only used for asynchronious modes #include #include #include #include #include #include #include #include #include "portaudio.h" #ifdef PLATFORM_WINDOWS #include #else #include #endif #include "wave.h" #include "debug.h" #ifdef NEED_STRUCT_TIMESPEC #define HAVE_STRUCT_TIMESPEC 1 struct timespec { long tv_sec; long tv_nsec; }; #endif /* HAVE_STRUCT_TIMESPEC */ enum { ONE_BILLION = 1000000000 }; #ifdef USE_PORTAUDIO #include "portaudio.h" #undef USE_PORTAUDIO // determine portaudio version by looking for a #define which is not in V18 #ifdef paNeverDropInput #define USE_PORTAUDIO 19 #else #define USE_PORTAUDIO 18 #endif #ifdef USE_PULSEAUDIO // create some wrappers for runtime detection // checked on wave_init static int pulse_running; // wave.cpp (this file) int wave_port_init(int); void *wave_port_open(const char *the_api); size_t wave_port_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize); int wave_port_close(void *theHandler); int wave_port_is_busy(void *theHandler); void wave_port_terminate(); uint32_t wave_port_get_read_position(void *theHandler); uint32_t wave_port_get_write_position(void *theHandler); void wave_port_flush(void *theHandler); void wave_port_set_callback_is_output_enabled(t_wave_callback *cb); void *wave_port_test_get_write_buffer(); int wave_port_get_remaining_time(uint32_t sample, uint32_t *time); // wave_pulse.cpp int is_pulse_running(); int wave_pulse_init(int); void *wave_pulse_open(const char *the_api); size_t wave_pulse_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize); int wave_pulse_close(void *theHandler); int wave_pulse_is_busy(void *theHandler); void wave_pulse_terminate(); uint32_t wave_pulse_get_read_position(void *theHandler); uint32_t wave_pulse_get_write_position(void *theHandler); void wave_pulse_flush(void *theHandler); void wave_pulse_set_callback_is_output_enabled(t_wave_callback *cb); void *wave_pulse_test_get_write_buffer(); int wave_pulse_get_remaining_time(uint32_t sample, uint32_t *time); // wrappers int wave_init(int srate) { pulse_running = is_pulse_running(); if (pulse_running) return wave_pulse_init(srate); else return wave_port_init(srate); } void *wave_open(const char *the_api) { if (pulse_running) return wave_pulse_open(the_api); else return wave_port_open(the_api); } size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) { if (pulse_running) return wave_pulse_write(theHandler, theMono16BitsWaveBuffer, theSize); else return wave_port_write(theHandler, theMono16BitsWaveBuffer, theSize); } int wave_close(void *theHandler) { if (pulse_running) return wave_pulse_close(theHandler); else return wave_port_close(theHandler); } int wave_is_busy(void *theHandler) { if (pulse_running) return wave_pulse_is_busy(theHandler); else return wave_port_is_busy(theHandler); } void wave_terminate() { if (pulse_running) wave_pulse_terminate(); else wave_port_terminate(); } uint32_t wave_get_read_position(void *theHandler) { if (pulse_running) return wave_pulse_get_read_position(theHandler); else return wave_port_get_read_position(theHandler); } uint32_t wave_get_write_position(void *theHandler) { if (pulse_running) return wave_pulse_get_write_position(theHandler); else return wave_port_get_write_position(theHandler); } void wave_flush(void *theHandler) { if (pulse_running) wave_pulse_flush(theHandler); else wave_port_flush(theHandler); } void wave_set_callback_is_output_enabled(t_wave_callback *cb) { if (pulse_running) wave_pulse_set_callback_is_output_enabled(cb); else wave_port_set_callback_is_output_enabled(cb); } void *wave_test_get_write_buffer() { if (pulse_running) return wave_pulse_test_get_write_buffer(); else return wave_port_test_get_write_buffer(); } int wave_get_remaining_time(uint32_t sample, uint32_t *time) { if (pulse_running) return wave_pulse_get_remaining_time(sample, time); else return wave_port_get_remaining_time(sample, time); } // rename functions to be wrapped #define wave_init wave_port_init #define wave_open wave_port_open #define wave_write wave_port_write #define wave_close wave_port_close #define wave_is_busy wave_port_is_busy #define wave_terminate wave_port_terminate #define wave_get_read_position wave_port_get_read_position #define wave_get_write_position wave_port_get_write_position #define wave_flush wave_port_flush #define wave_set_callback_is_output_enabled wave_port_set_callback_is_output_enabled #define wave_test_get_write_buffer wave_port_test_get_write_buffer #define wave_get_remaining_time wave_port_get_remaining_time #endif // USE_PULSEAUDIO static t_wave_callback *my_callback_is_output_enabled = NULL; #define N_WAV_BUF 10 #define MAX_SAMPLE_RATE 22050 #define FRAMES_PER_BUFFER 512 #define BUFFER_LENGTH (MAX_SAMPLE_RATE*2*sizeof(uint16_t)) static char myBuffer[BUFFER_LENGTH]; static char *myRead = NULL; static char *myWrite = NULL; static int out_channels = 1; static int my_stream_could_start = 0; static int wave_samplerate; static int mInCallbackFinishedState = false; #if (USE_PORTAUDIO == 18) static PortAudioStream *pa_stream = NULL; #endif #if (USE_PORTAUDIO == 19) static struct PaStreamParameters myOutputParameters; static PaStream *pa_stream = NULL; #endif static int userdata[4]; static PaError pa_init_err = 0; // time measurement // The read and write position audio stream in the audio stream are measured in ms. // // * When the stream is opened, myReadPosition and myWritePosition are cleared. // * myWritePosition is updated in wave_write. // * myReadPosition is updated in pa_callback (+ sample delay). static uint32_t myReadPosition = 0; // in ms static uint32_t myWritePosition = 0; static void init_buffer() { myWrite = myBuffer; myRead = myBuffer; memset(myBuffer, 0, BUFFER_LENGTH); myReadPosition = myWritePosition = 0; SHOW("init_buffer > myRead=0x%x, myWrite=0x%x, BUFFER_LENGTH=0x%x, myReadPosition = myWritePosition = 0\n", (int)myRead, (int)myWrite, BUFFER_LENGTH); } static unsigned int get_used_mem() { char *aRead = myRead; char *aWrite = myWrite; unsigned int used = 0; assert((aRead >= myBuffer) && (aRead <= myBuffer + BUFFER_LENGTH) && (aWrite >= myBuffer) && (aWrite <= myBuffer + BUFFER_LENGTH)); if (aRead < aWrite) { used = aWrite - aRead; } else { used = aWrite + BUFFER_LENGTH - aRead; } SHOW("get_used_mem > %d\n", used); return used; } static void start_stream() { PaError err; SHOW_TIME("start_stream"); my_stream_could_start = 0; mInCallbackFinishedState = false; err = Pa_StartStream(pa_stream); SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err)); #if USE_PORTAUDIO == 19 if (err == paStreamIsNotStopped) { SHOW_TIME("start_stream > restart stream (begin)"); // not sure why we need this, but PA v19 seems to need it err = Pa_StopStream(pa_stream); SHOW("start_stream > Pa_StopStream=%d (%s)\n", err, Pa_GetErrorText(err)); err = Pa_StartStream(pa_stream); SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err)); SHOW_TIME("start_stream > restart stream (end)"); } #endif } /* This routine will be called by the PortAudio engine when audio is needed. ** It may called at interrupt level on some machines so don't do anything ** that could mess up the system like calling malloc() or free(). */ #if USE_PORTAUDIO == 18 static int pa_callback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData) #else static int pa_callback(const void *inputBuffer, void *outputBuffer, long unsigned int framesPerBuffer, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags flags, void *userData) #endif { int aResult = 0; // paContinue char *aWrite = myWrite; size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer; myReadPosition += framesPerBuffer; SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n", (int)myReadPosition, framesPerBuffer, n); if (aWrite >= myRead) { if ((size_t)(aWrite - myRead) >= n) { memcpy(outputBuffer, myRead, n); myRead += n; } else { SHOW_TIME("pa_callback > underflow"); aResult = 1; // paComplete; mInCallbackFinishedState = true; size_t aUsedMem = 0; aUsedMem = (size_t)(aWrite - myRead); if (aUsedMem) { memcpy(outputBuffer, myRead, aUsedMem); } char *p = (char *)outputBuffer + aUsedMem; memset(p, 0, n - aUsedMem); myRead = aWrite; } } else { // myRead > aWrite if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n) { memcpy(outputBuffer, myRead, n); myRead += n; } else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n) { int aTopMem = myBuffer + BUFFER_LENGTH - myRead; if (aTopMem) { SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n", (int)myRead, (int)aTopMem); memcpy(outputBuffer, myRead, aTopMem); } int aRest = n - aTopMem; if (aRest) { SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n", (int)myRead, (int)aRest); char *p = (char *)outputBuffer + aTopMem; memcpy(p, myBuffer, aRest); } myRead = myBuffer + aRest; } else { SHOW_TIME("pa_callback > underflow"); aResult = 1; // paComplete; int aTopMem = myBuffer + BUFFER_LENGTH - myRead; if (aTopMem) { SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n", (int)myRead, (int)aTopMem); memcpy(outputBuffer, myRead, aTopMem); } int aRest = aWrite - myBuffer; if (aRest) { SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n", (int)myRead, (int)aRest); char *p = (char *)outputBuffer + aTopMem; memcpy(p, myBuffer, aRest); } size_t aUsedMem = aTopMem + aRest; char *p = (char *)outputBuffer + aUsedMem; memset(p, 0, n - aUsedMem); myRead = aWrite; } } SHOW("pa_callback > myRead=%x\n", (int)myRead); #ifdef ARCH_BIG { // BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer int c; unsigned char *out_ptr; unsigned char *out_end; out_ptr = (unsigned char *)outputBuffer; out_end = out_ptr + framesPerBuffer*2 * out_channels; while (out_ptr < out_end) { c = out_ptr[0]; out_ptr[0] = out_ptr[1]; out_ptr[1] = c; out_ptr += 2; } } #endif return aResult; } void wave_flush(void *theHandler) { ENTER("wave_flush"); if (my_stream_could_start) { start_stream(); } } static int wave_open_sound() { ENTER("wave_open_sound"); PaError err = paNoError; PaError active; #if USE_PORTAUDIO == 18 active = Pa_StreamActive(pa_stream); #else active = Pa_IsStreamActive(pa_stream); #endif if (active == 1) { SHOW_TIME("wave_open_sound > already active"); return 0; } if (active < 0) { out_channels = 1; #if USE_PORTAUDIO == 18 PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID(); PaError err = Pa_OpenStream(&pa_stream, /* capture parameters */ paNoDevice, 0, paInt16, NULL, /* playback parameters */ playbackDevice, out_channels, paInt16, NULL, /* general parameters */ wave_samplerate, FRAMES_PER_BUFFER, 0, paNoFlag, pa_callback, (void *)userdata); SHOW("wave_open_sound > Pa_OpenDefaultStream(1): err=%d (%s)\n", err, Pa_GetErrorText(err)); if (err == paInvalidChannelCount) { SHOW_TIME("wave_open_sound > try stereo"); // failed to open with mono, try stereo out_channels = 2; PaError err = Pa_OpenStream(&pa_stream, /* capture parameters */ paNoDevice, 0, paInt16, NULL, /* playback parameters */ playbackDevice, out_channels, paInt16, NULL, /* general parameters */ wave_samplerate, FRAMES_PER_BUFFER, 0, paNoFlag, pa_callback, (void *)userdata); SHOW("wave_open_sound > Pa_OpenDefaultStream(2): err=%d (%s)\n", err, Pa_GetErrorText(err)); err = 0; // avoid warning } mInCallbackFinishedState = false; // v18 only #else myOutputParameters.channelCount = out_channels; unsigned long framesPerBuffer = paFramesPerBufferUnspecified; err = Pa_OpenStream( &pa_stream, NULL, /* no input */ &myOutputParameters, wave_samplerate, framesPerBuffer, paNoFlag, pa_callback, (void *)userdata); if ((err != paNoError) && (err != paInvalidChannelCount)) { // err==paUnanticipatedHostError fprintf(stderr, "wave_open_sound > Pa_OpenStream : err=%d (%s)\n", err, Pa_GetErrorText(err)); framesPerBuffer = FRAMES_PER_BUFFER; err = Pa_OpenStream( &pa_stream, NULL, /* no input */ &myOutputParameters, wave_samplerate, framesPerBuffer, paNoFlag, // paClipOff | paDitherOff, pa_callback, (void *)userdata); } if (err == paInvalidChannelCount) { SHOW_TIME("wave_open_sound > try stereo"); // failed to open with mono, try stereo out_channels = 2; myOutputParameters.channelCount = out_channels; err = Pa_OpenStream( &pa_stream, NULL, /* no input */ &myOutputParameters, wave_samplerate, framesPerBuffer, paNoFlag, // paClipOff | paDitherOff, pa_callback, (void *)userdata); } mInCallbackFinishedState = false; #endif } SHOW("wave_open_sound > %s\n", "LEAVE"); return err != paNoError; } #if (USE_PORTAUDIO == 19) static void update_output_parameters(int selectedDevice, const PaDeviceInfo *deviceInfo) { myOutputParameters.device = selectedDevice; myOutputParameters.channelCount = 1; myOutputParameters.sampleFormat = paInt16; // Latency greater than 100ms for avoiding glitches // (e.g. when moving a window in a graphical desktop) // deviceInfo = Pa_GetDeviceInfo(selectedDevice); if (deviceInfo) { double aLatency = deviceInfo->defaultLowOutputLatency; myOutputParameters.suggestedLatency = aLatency; // for faster response ? SHOW("Device=%d, myOutputParameters.suggestedLatency=%f, aCoeff=%f\n", selectedDevice, myOutputParameters.suggestedLatency, aCoeff); } else { myOutputParameters.suggestedLatency = (double)0.1; // 100ms SHOW("Device=%d, myOutputParameters.suggestedLatency=%f (default)\n", selectedDevice, myOutputParameters.suggestedLatency); } myOutputParameters.hostApiSpecificStreamInfo = NULL; } #endif static void select_device(const char *the_api) { ENTER("select_device"); #if (USE_PORTAUDIO == 19) int numDevices = Pa_GetDeviceCount(); if (numDevices < 0) { SHOW("ERROR: Pa_CountDevices returned 0x%x\n", numDevices); assert(0); } PaDeviceIndex i = 0, selectedIndex = 0, defaultAlsaIndex = numDevices; const PaDeviceInfo *deviceInfo = NULL; const PaDeviceInfo *selectedDeviceInfo = NULL; if (option_device_number >= 0) { selectedIndex = option_device_number; selectedDeviceInfo = Pa_GetDeviceInfo(selectedIndex); } if (selectedDeviceInfo == NULL) { for (i = 0; i < numDevices; i++) { deviceInfo = Pa_GetDeviceInfo(i); if (deviceInfo == NULL) { break; } const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); if (hostInfo && hostInfo->type == paALSA) { // Check (once) the default output device if (defaultAlsaIndex == numDevices) { defaultAlsaIndex = hostInfo->defaultOutputDevice; const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(defaultAlsaIndex); update_output_parameters(defaultAlsaIndex, deviceInfo); if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) { SHOW("select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex); selectedIndex = defaultAlsaIndex; selectedDeviceInfo = deviceInfo; break; } } // if the default output device does not match, // look for the device with the highest number of output channels SHOW("select_device > ALSA, i=%d (numDevices=%d)\n", i, numDevices); update_output_parameters(i, deviceInfo); if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) { SHOW("select_device > ALSA, name=%s (#%d)\n", deviceInfo->name, i); if (!selectedDeviceInfo || (selectedDeviceInfo->maxOutputChannels < deviceInfo->maxOutputChannels)) { selectedIndex = i; selectedDeviceInfo = deviceInfo; } } } } } if (selectedDeviceInfo) { update_output_parameters(selectedIndex, selectedDeviceInfo); } else { i = Pa_GetDefaultOutputDevice(); deviceInfo = Pa_GetDeviceInfo(i); update_output_parameters(i, deviceInfo); } #endif } void wave_set_callback_is_output_enabled(t_wave_callback *cb) { my_callback_is_output_enabled = cb; } int wave_init(int srate) { ENTER("wave_init"); PaError err; pa_stream = NULL; wave_samplerate = srate; mInCallbackFinishedState = false; init_buffer(); // PortAudio sound output library err = Pa_Initialize(); pa_init_err = err; if (err != paNoError) { SHOW_TIME("wave_init > Failed to initialise the PortAudio sound"); } return err == paNoError; } void *wave_open(const char *the_api) { ENTER("wave_open"); static int once = 0; if (!once) { select_device("alsa"); once = 1; } return (void *)1; } static size_t copyBuffer(char *dest, char *src, const size_t theSizeInBytes) { size_t bytes_written = 0; unsigned int i = 0; uint16_t *a_dest = NULL; uint16_t *a_src = NULL; if ((src != NULL) && dest != NULL) { // copy for one channel (mono)? if (out_channels == 1) { SHOW("copyBuffer > 1 channel > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes); memcpy(dest, src, theSizeInBytes); bytes_written = theSizeInBytes; } else { // copy for 2 channels (stereo) SHOW("copyBuffer > 2 channels > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes); i = 0; a_dest = (uint16_t *)dest; a_src = (uint16_t *)src; for (i = 0; i < theSizeInBytes/2; i++) { a_dest[2*i] = a_src[i]; a_dest[2*i+1] = a_src[i]; } bytes_written = 2*theSizeInBytes; } // end if(out_channels==1) } // end if ((src != NULL) && dest != NULL) return bytes_written; } size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) { ENTER("wave_write"); size_t bytes_written = 0; // space in ringbuffer for the sample needed: 1x mono channel but 2x for 1 stereo channel size_t bytes_to_write = (out_channels == 1) ? theSize : theSize*2; my_stream_could_start = 0; if (pa_stream == NULL) { SHOW_TIME("wave_write > wave_open_sound\n"); if (0 != wave_open_sound()) { SHOW_TIME("wave_write > wave_open_sound fails!"); return 0; } my_stream_could_start = 1; } else if (!wave_is_busy(NULL)) { my_stream_could_start = 1; } assert(BUFFER_LENGTH >= bytes_to_write); if (myWrite >= myBuffer + BUFFER_LENGTH) { myWrite = myBuffer; } // end if (myWrite >= myBuffer + BUFFER_LENGTH) size_t aTotalFreeMem = 0; char *aRead = myRead; SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite); while (1) { if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled())) { SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); return 0; } aRead = myRead; // write pointer is before read pointer? if (myWrite >= aRead) { aTotalFreeMem = aRead + BUFFER_LENGTH - myWrite; } else { // read pointer is before write pointer! aTotalFreeMem = aRead - myWrite; } // end if (myWrite >= aRead) if (aTotalFreeMem > 1) { // -1 because myWrite must be different of aRead // otherwise buffer would be considered as empty aTotalFreeMem -= 1; } // end if (aTotalFreeMem>1) if (aTotalFreeMem >= bytes_to_write) { break; } // end if (aTotalFreeMem >= bytes_to_write) SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem); SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite); usleep(10000); } // end while (1) aRead = myRead; // write pointer is ahead the read pointer? if (myWrite >= aRead) { SHOW_TIME("wave_write > myWrite >= aRead"); // determine remaining free memory to the end of the ringbuffer size_t aFreeMem = myBuffer + BUFFER_LENGTH - myWrite; // is enough linear space available (regardless 1 or 2 channels)? if (aFreeMem >= bytes_to_write) { // copy direct - no wrap around at end of ringbuffer needed myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize); } else { // not enough linear space available // 2 channels (stereo)? if (out_channels == 2) { // copy with wrap around at the end of ringbuffer copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem/2); myWrite = myBuffer; myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem/2, theSize - aFreeMem/2); } else { // 1 channel (mono) // copy with wrap around at the end of ringbuffer copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem); myWrite = myBuffer; myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem); } // end if (out_channels == 2) } // end if (aFreeMem >= bytes_to_write) } // if (myWrite >= aRead) else { // read pointer is ahead the write pointer SHOW_TIME("wave_write > myWrite <= aRead"); myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize); } // end if (myWrite >= aRead) bytes_written = bytes_to_write; myWritePosition += theSize/sizeof(uint16_t); // add number of samples if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER)) { start_stream(); } // end if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER)) SHOW_TIME("wave_write > LEAVE"); return bytes_written; } int wave_close(void *theHandler) { SHOW_TIME("wave_close > ENTER"); static int aStopStreamCount = 0; #if (USE_PORTAUDIO == 19) if (pa_stream == NULL) { SHOW_TIME("wave_close > LEAVE (NULL stream)"); return 0; } if (Pa_IsStreamStopped(pa_stream)) { SHOW_TIME("wave_close > LEAVE (stopped)"); return 0; } #else if (pa_stream == NULL) { SHOW_TIME("wave_close > LEAVE (NULL stream)"); return 0; } if (Pa_StreamActive(pa_stream) == false && mInCallbackFinishedState == false) { SHOW_TIME("wave_close > LEAVE (not active)"); return 0; } #endif // Avoid race condition by making sure this function only // gets called once at a time aStopStreamCount++; if (aStopStreamCount != 1) { SHOW_TIME("wave_close > LEAVE (stopStreamCount)"); return 0; } // Comment from Audacity-1.2.4b adapted to the eSpeak context. // // We got here in one of two ways: // // 1. The calling program calls the espeak_Cancel function and we // therefore want to stop as quickly as possible. // So we use AbortStream(). If this is // the case the portaudio stream is still in the Running state // (see PortAudio state machine docs). // // 2. The callback told PortAudio to stop the stream since it had // reached the end of the selection. // The event polling thread discovered this by noticing that // wave_is_busy() returned false. // wave_is_busy() (which calls Pa_GetStreamActive()) will not return // false until all buffers have finished playing, so we can call // AbortStream without losing any samples. If this is the case // we are in the "callback finished state" (see PortAudio state // machine docs). // // The moral of the story: We can call AbortStream safely, without // losing samples. // // DMM: This doesn't seem to be true; it seems to be necessary to // call StopStream if the callback brought us here, and AbortStream // if the user brought us here. // #if (USE_PORTAUDIO == 19) if (pa_stream) { Pa_AbortStream(pa_stream); SHOW_TIME("wave_close > Pa_AbortStream (end)"); Pa_CloseStream(pa_stream); SHOW_TIME("wave_close > Pa_CloseStream (end)"); pa_stream = NULL; mInCallbackFinishedState = false; } #else if (pa_stream) { if (mInCallbackFinishedState) { Pa_StopStream(pa_stream); SHOW_TIME("wave_close > Pa_StopStream (end)"); } else { Pa_AbortStream(pa_stream); SHOW_TIME("wave_close > Pa_AbortStream (end)"); } Pa_CloseStream(pa_stream); SHOW_TIME("wave_close > Pa_CloseStream (end)"); pa_stream = NULL; mInCallbackFinishedState = false; } #endif init_buffer(); aStopStreamCount = 0; // last action SHOW_TIME("wave_close > LEAVE"); return 0; } int wave_is_busy(void *theHandler) { PaError active = 0; SHOW_TIME("wave_is_busy"); if (pa_stream) { #if USE_PORTAUDIO == 18 active = Pa_StreamActive(pa_stream) && (mInCallbackFinishedState == false); #else active = Pa_IsStreamActive(pa_stream) && (mInCallbackFinishedState == false); #endif } SHOW("wave_is_busy: %d\n", active); return active == 1; } void wave_terminate() { ENTER("wave_terminate"); Pa_Terminate(); } uint32_t wave_get_read_position(void *theHandler) { SHOW("wave_get_read_position > myReadPosition=%u\n", myReadPosition); return myReadPosition; } uint32_t wave_get_write_position(void *theHandler) { SHOW("wave_get_write_position > myWritePosition=%u\n", myWritePosition); return myWritePosition; } int wave_get_remaining_time(uint32_t sample, uint32_t *time) { double a_time = 0; if (!time || !pa_stream) { SHOW("event get_remaining_time> %s\n", "audio device not available"); return -1; } if (sample > myReadPosition) { // TBD: take in account time suplied by portaudio V18 API a_time = sample - myReadPosition; a_time = 0.5 + (a_time * 1000.0) / wave_samplerate; } else { a_time = 0; } SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time); *time = (uint32_t)a_time; return 0; } void *wave_test_get_write_buffer() { return myWrite; } #else int wave_init(int srate) { return 1; } void *wave_open(const char *the_api) { return (void *)1; } size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) { return theSize; } int wave_close(void *theHandler) { return 0; } int wave_is_busy(void *theHandler) { return 0; } void wave_terminate() { } uint32_t wave_get_read_position(void *theHandler) { return 0; } uint32_t wave_get_write_position(void *theHandler) { return 0; } void wave_flush(void *theHandler) { } typedef int (t_wave_callback)(void); void wave_set_callback_is_output_enabled(t_wave_callback *cb) { } extern void *wave_test_get_write_buffer() { return NULL; } int wave_get_remaining_time(uint32_t sample, uint32_t *time) { if (!time) return -1; *time = (uint32_t)0; return 0; } #endif // of USE_PORTAUDIO void clock_gettime2(struct timespec *ts) { struct timeval tv; if (!ts) { return; } assert(gettimeofday(&tv, NULL) != -1); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec*1000; } void add_time_in_ms(struct timespec *ts, int time_in_ms) { if (!ts) { return; } uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; while (t_ns >= ONE_BILLION) { SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns); ts->tv_sec += 1; t_ns -= ONE_BILLION; } ts->tv_nsec = (long int)t_ns; } #endif // USE_ASYNC