int wave_get_remaining_time(uint32_t sample, uint32_t* time) { uint32_t a_time=0; uint32_t actual_index; audio_info_t ainfo; ENTER("wave_get_remaining_time"); if (!time) { return(-1); SHOW_TIME("wave_get_remaining_time > LEAVE"); } ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); // See if this sample has already been played or is currently // playing. // actual_index = sample - total_samples_skipped; if ((sample < total_samples_skipped) || (actual_index <= ainfo.play.samples)) { *time = 0; } else { a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate; *time = (uint32_t) a_time; } SHOW("wave_get_remaining_time for %d: %d\n", sample, *time); SHOW_TIME("wave_get_remaining_time > LEAVE"); return 0; }
//> //<fifo_init void fifo_init() { ENTER("fifo_init"); // security pthread_mutex_init( &my_mutex, (const pthread_mutexattr_t *)NULL); init(0); assert(-1 != sem_init(&my_sem_start_is_required, 0, 0)); assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0)); pthread_attr_t a_attrib; if (pthread_attr_init (& a_attrib) || pthread_attr_setdetachstate(&a_attrib, PTHREAD_CREATE_JOINABLE) || pthread_create( &my_thread, & a_attrib, say_thread, (void*)NULL)) { assert(0); } pthread_attr_destroy(&a_attrib); // leave once the thread is actually started SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n"); while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { continue; // Restart when interrupted by handler } SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n"); }
static void pulse_close(void) { ENTER(__FUNCTION__); drain(); connected = 0; if (mainloop) pa_threaded_mainloop_stop(mainloop); connected = 0; if (context) { SHOW_TIME("pa_context_disconnect (call)"); pa_context_disconnect(context); pa_context_unref(context); context = NULL; } if (mainloop) { SHOW_TIME("pa_threaded_mainloop_free (call)"); pa_threaded_mainloop_free(mainloop); mainloop = NULL; } SHOW_TIME("pulse_close (ret)"); }
int wave_close(void* theHandler) { SHOW_TIME("wave_close > ENTER"); static int aStopStreamCount = 0; // 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; } int a_status = pthread_mutex_lock(&pulse_mutex); if (a_status) { SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__); aStopStreamCount = 0; // last action return PULSE_ERROR; } drain(); pthread_mutex_unlock(&pulse_mutex); SHOW_TIME("wave_close (ret)"); aStopStreamCount = 0; // last action return PULSE_OK; }
static int sleep_until_start_request_or_inactivity() { SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > ENTER"); int a_start_is_required=0; // Wait for the start request (my_sem_start_is_required). // Besides this, if the audio stream is still busy, // check from time to time its end. // The end of the stream is confirmed by several checks // for filtering underflow. // int i=0; while((i<= MAX_INACTIVITY_CHECK) && !a_start_is_required) { if (wave_is_busy( NULL) ) { i = 0; } else { i++; } int err=0; struct timespec ts; struct timeval tv; clock_gettime2( &ts); #ifdef DEBUG_ENABLED struct timespec to; to.tv_sec = ts.tv_sec; to.tv_nsec = ts.tv_nsec; #endif add_time_in_ms( &ts, INACTIVITY_TIMEOUT); SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n", to.tv_sec, to.tv_nsec, ts.tv_sec, ts.tv_nsec); while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1 && errno == EINTR) { continue; } assert (gettimeofday(&tv, NULL) != -1); SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err, tv.tv_sec, tv.tv_usec*1000); if (err==0) { a_start_is_required = 1; } } SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE"); return a_start_is_required; }
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) { ENTER("wave_write"); size_t bytes_to_write = theSize; char* aBuffer=theMono16BitsWaveBuffer; assert(stream); size_t aTotalFreeMem=0; pthread_mutex_lock(&pulse_mutex); while (1) { if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled())) { SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); theSize=0; goto terminate; } aTotalFreeMem = pulse_free(); if (aTotalFreeMem >= bytes_to_write) { SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); break; } // TBD: check if really helpful if (aTotalFreeMem >= MAXLENGTH*2) { aTotalFreeMem = MAXLENGTH*2; } SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); // 500: threshold for avoiding too many calls to pulse_write if (aTotalFreeMem>500) { pulse_write(aBuffer, aTotalFreeMem); bytes_to_write -= aTotalFreeMem; aBuffer += aTotalFreeMem; } usleep(10000); } pulse_write(aBuffer, bytes_to_write); terminate: pthread_mutex_unlock(&pulse_mutex); SHOW("wave_write: theSize=%d", theSize); SHOW_TIME("wave_write > LEAVE"); return theSize; }
ESPEAK_API espeak_ERROR espeak_Synchronize(void) {//============================================= #ifdef USE_ASYNC SHOW_TIME("espeak_Synchronize > ENTER"); while (espeak_IsPlaying()) { usleep(20000); } #endif SHOW_TIME("espeak_Synchronize > LEAVE"); return EE_OK; } // end of espeak_Synchronize
espeak_ERROR event_declare (espeak_EVENT* event) { ENTER("event_declare"); event_display(event); if (!event) { return EE_INTERNAL_ERROR; } int a_status = pthread_mutex_lock(&my_mutex); espeak_ERROR a_error = EE_OK; if (!a_status) { SHOW_TIME("event_declare > locked\n"); espeak_EVENT* a_event = event_copy(event); a_error = push(a_event); if (a_error != EE_OK) { event_delete(a_event); } SHOW_TIME("event_declare > unlocking\n"); a_status = pthread_mutex_unlock(&my_mutex); } // TBD: remove the comment // reminder: code in comment. // This wait can lead to an underrun // // if (!a_status && !my_event_is_running && (a_error == EE_OK)) // { // // quit when command is actually started // // (for possible forthcoming 'end of command' checks) SHOW_TIME("event_declare > post my_sem_start_is_required\n"); sem_post(&my_sem_start_is_required); // int val=1; // while (val) // { // usleep(50000); // TBD: event? // sem_getvalue(&my_sem_start_is_required, &val); // } // } if (a_status != 0) { a_error = EE_INTERNAL_ERROR; } return a_error; }
void wave_terminate() { ENTER("wave_terminate"); close(sun_audio_fd); sun_audio_fd = -1; SHOW_TIME("wave_terminate > LEAVE"); }
uint32_t wave_get_write_position(void* theHandler) { ENTER("wave_get_write_position"); SHOW("wave_get_write_position: %d\n", total_samples_sent); SHOW_TIME("wave_get_write_position > LEAVE"); return total_samples_sent; }
espeak_ERROR sync_espeak_Synth_Mark(unsigned int unique_identifier, const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void* user_data) {//========================================================================= espeak_ERROR aStatus; InitText(flags); my_unique_identifier = unique_identifier; my_user_data = user_data; if(index_mark != NULL) { strncpy0(skip_marker, index_mark, sizeof(skip_marker)); skipping_text = 1; } end_character_position = end_position; aStatus = Synthesize(unique_identifier, text, flags | espeakSSML); SHOW_TIME("LEAVE sync_espeak_Synth_Mark"); return (aStatus); } // end of sync_espeak_Synth_Mark
void GetOptions (const char *filename) { AudioConfig *config = ParseAudioOptions (filename, MyName); int i ; START_TIME(option_time); /* Need to merge new config with what we have already :*/ /* now lets check the config sanity : */ /* mixing set and default flags : */ Config->set_flags |= config->set_flags; if( config->playcmd != NULL ) set_string_value( &(Config->playcmd), config->playcmd, NULL, 0 ); for( i = 0 ; i < AFTERSTEP_EVENTS_NUM ; ++i ) if( config->sounds[i] != NULL ) set_string_value( &(Config->sounds[i]), config->sounds[i], NULL, 0 ); if( get_flags(config->set_flags, AUDIO_SET_DELAY) ) Config->delay = config->delay; if( get_flags(config->set_flags, AUDIO_SET_RPLAY_HOST) && config->rplay_host != NULL ) set_string_value( &(Config->rplay_host), config->rplay_host, NULL, 0 ); if( get_flags(config->set_flags, AUDIO_SET_RPLAY_PRIORITY) ) Config->rplay_priority = config->rplay_priority; if( get_flags(config->set_flags, AUDIO_SET_RPLAY_VOLUME) ) Config->rplay_volume = config->rplay_volume; DestroyAudioConfig (config); SHOW_TIME("Config parsing",option_time); }
/*********************************************************************** * Configuration reading procedures : * GetOptions - read the sound configuration file. ***********************************************************************/ void GetBaseOptions (const char *filename/* unused*/) { START_TIME(started); ReloadASEnvironment( NULL, NULL, NULL, False, False ); SHOW_TIME("BaseConfigParsingTime",started); }
int wave_close(void* theHandler) { int ret; audio_info_t ainfo; int audio_fd = (int) theHandler; if (!audio_fd) { audio_fd = sun_audio_fd; } ENTER("wave_close"); // [[[WDW: maybe do a pause/resume ioctl???]]] ret = ioctl(audio_fd, I_FLUSH, FLUSHRW); ioctl(audio_fd, AUDIO_GETINFO, &ainfo); // Calculate the number of samples that won't get // played. We also keep track of the last_play_position // because wave_close can be called multiple times // before another call to wave_write. // if (last_play_position != ainfo.play.samples) { last_play_position = ainfo.play.samples; total_samples_skipped = total_samples_sent - last_play_position; } SHOW_TIME("wave_close > LEAVE"); return ret; }
espeak_ERROR fifo_add_commands (t_espeak_command* command1, t_espeak_command* command2) { ENTER("fifo_add_command"); int a_status = pthread_mutex_lock(&my_mutex); espeak_ERROR a_error = EE_OK; if (!a_status) { SHOW_TIME("fifo_add_commands > locked\n"); if (node_counter+1 >= MAX_NODE_COUNTER) { SHOW("push > %s\n", "EE_BUFFER_FULL"); a_error = EE_BUFFER_FULL; } else { push(command1); push(command2); } SHOW_TIME("fifo_add_command > unlocking\n"); a_status = pthread_mutex_unlock(&my_mutex); } if (!a_status && !my_command_is_running && (a_error == EE_OK)) { // quit when one command is actually started // (for possible forthcoming 'end of command' checks) SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n"); sem_post(&my_sem_start_is_required); int val=1; while (val > 0) { usleep(50000); // TBD: event? sem_getvalue(&my_sem_start_is_required, &val); } } if (a_status != 0) { a_error = EE_INTERNAL_ERROR; } SHOW_TIME("LEAVE fifo_add_commands"); return a_error; }
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) { size_t num; ENTER("wave_write"); if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled())) { SHOW_TIME("wave_write > my_callback_is_output_enabled: no!"); return 0; } #if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN { // BIG-ENDIAN, swap the order of bytes in each sound sample int c; char *out_ptr; char *out_end; out_ptr = (char *)theMono16BitsWaveBuffer; out_end = out_ptr + theSize; while(out_ptr < out_end) { c = out_ptr[0]; out_ptr[0] = out_ptr[1]; out_ptr[1] = c; out_ptr += 2; } } #endif num = write((int) theHandler, theMono16BitsWaveBuffer, theSize); // Keep track of the total number of samples sent -- we use this in // wave_get_read_position and also use it to help calculate the // total_samples_skipped in wave_close. // total_samples_sent += num / 2; if (num < theSize) { SHOW("ERROR: wave_write only wrote %d of %d bytes\n", num, theSize); } else { SHOW("wave_write wrote %d bytes\n", theSize); } SHOW_TIME("wave_write > LEAVE"); return num; }
int wave_close(void* theHandler) { SHOW_TIME("wave_close > ENTER"); int a_status = pthread_mutex_lock(&pulse_mutex); if (a_status) { SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__); return PULSE_ERROR; } drain(); pthread_mutex_unlock(&pulse_mutex); SHOW_TIME("wave_close (ret)"); return PULSE_OK; }
int wave_is_busy(void* theHandler) { SHOW_TIME("wave_is_busy"); pa_timing_info a_timing_info; int active = pulse_playing(&a_timing_info); SHOW("wave_is_busy: %d\n",active); return active; }
uint32_t wave_get_read_position(void* theHandler) { audio_info_t ainfo; ENTER("wave_get_read_position"); ioctl((int) theHandler, AUDIO_GETINFO, &ainfo); SHOW("wave_get_read_position: %d\n", ainfo.play.samples); SHOW_TIME("wave_get_read_position > LEAVE"); return ainfo.play.samples; }
void GetOptions (const char *filename) { START_TIME(option_time); IdentConfig *config = ParseIdentOptions( filename, MyName ); if (config->style_defs) ProcessMyStyleDefinitions (&(config->style_defs)); SHOW_TIME("Config parsing",option_time); }
static int drain(void) { pa_operation *o = NULL; int success = 0; int ret = PULSE_ERROR; ENTER(__FUNCTION__); CHECK_CONNECTED(ret); pa_threaded_mainloop_lock(mainloop); CHECK_DEAD_GOTO(fail, 0); SHOW_TIME("pa_stream_drain (call)"); if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) { SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); goto fail; } SHOW_TIME("pa_threaded_mainloop_wait (call)"); while (pa_operation_get_state(o) != PA_OPERATION_DONE) { CHECK_DEAD_GOTO(fail, 1); pa_threaded_mainloop_wait(mainloop); } SHOW_TIME("pa_threaded_mainloop_wait (ret)"); if (!success) { SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); } else { ret = PULSE_OK; } fail: SHOW_TIME("pa_operation_unref (call)"); if (o) pa_operation_unref(o); pa_threaded_mainloop_unlock(mainloop); SHOW_TIME("drain (ret)"); return ret; }
static void close_stream() { SHOW_TIME("fifo > close_stream > ENTER\n"); // Warning: a wave_close can be already required by // an external command (espeak_Cancel + fifo_stop), if so: // my_stop_is_required = 1; int a_status = pthread_mutex_lock(&my_mutex); assert (!a_status); int a_stop_is_required = my_stop_is_required; if (!a_stop_is_required) { my_command_is_running = 1; } a_status = pthread_mutex_unlock(&my_mutex); if (!a_stop_is_required) { wave_close(NULL); int a_status = pthread_mutex_lock(&my_mutex); assert (!a_status); my_command_is_running = 0; a_stop_is_required = my_stop_is_required; a_status = pthread_mutex_unlock(&my_mutex); if (a_stop_is_required) { // acknowledge the stop request SHOW_TIME("fifo > close_stream > post my_sem_stop_is_acknowledged\n"); int a_status = sem_post(&my_sem_stop_is_acknowledged); assert( a_status != -1); } } SHOW_TIME("fifo > close_stream > LEAVE\n"); }
espeak_ERROR fifo_add_command (t_espeak_command* the_command) { ENTER("fifo_add_command"); int a_status = pthread_mutex_lock(&my_mutex); espeak_ERROR a_error = EE_OK; if (!a_status) { SHOW_TIME("fifo_add_command > locked\n"); a_error = push(the_command); SHOW_TIME("fifo_add_command > unlocking\n"); a_status = pthread_mutex_unlock(&my_mutex); } if (!a_status && !my_command_is_running && (a_error == EE_OK)) { // quit when command is actually started // (for possible forthcoming 'end of command' checks) SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n"); sem_post(&my_sem_start_is_required); int val=1; while (val > 0) { usleep(50000); // TBD: event? sem_getvalue(&my_sem_start_is_required, &val); } } if (a_status != 0) { a_error = EE_INTERNAL_ERROR; } SHOW_TIME("LEAVE fifo_add_command"); return a_error; }
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 }
ESPEAK_API espeak_ERROR espeak_Cancel(void) {//=============================== #ifdef USE_ASYNC ENTER("espeak_Cancel"); fifo_stop(); event_clear_all(); if(my_mode == AUDIO_OUTPUT_PLAYBACK) { wave_close(my_audio); } SHOW_TIME("espeak_Cancel > LEAVE"); #endif embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements return EE_OK; } // end of espeak_Cancel
espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void* user_data) {//=========================================================================== #ifdef DEBUG_ENABLED ENTER("sync_espeak_Synth"); SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text); #endif espeak_ERROR aStatus; InitText(flags); my_unique_identifier = unique_identifier; my_user_data = user_data; for (int i=0; i < N_SPEECH_PARAM; i++) saved_parameters[i] = param_stack[0].parameter[i]; switch(position_type) { case POS_CHARACTER: skip_characters = position; break; case POS_WORD: skip_words = position; break; case POS_SENTENCE: skip_sentences = position; break; } if(skip_characters || skip_words || skip_sentences) skipping_text = 1; end_character_position = end_position; aStatus = Synthesize(unique_identifier, text, flags); #ifdef USE_ASYNC wave_flush(my_audio); #endif SHOW_TIME("LEAVE sync_espeak_Synth"); return aStatus; } // end of sync_espeak_Synth
void wave_terminate() { ENTER("wave_terminate"); // Pa_Terminate(); int a_status; pthread_mutex_t* a_mutex = NULL; a_mutex = &pulse_mutex; a_status = pthread_mutex_lock(a_mutex); pulse_close(); SHOW_TIME("unlock mutex"); a_status = pthread_mutex_unlock(a_mutex); pthread_mutex_destroy(a_mutex); }
// TBD: the arg could be "alsa", "oss",... void wave_init() { ENTER("wave_init"); PaError err; pa_stream = NULL; 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"); } }
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); }
espeak_ERROR fifo_stop () { ENTER("fifo_stop"); int a_command_is_running = 0; int a_status = pthread_mutex_lock(&my_mutex); SHOW_TIME("fifo_stop > locked\n"); if (a_status != 0) { return EE_INTERNAL_ERROR; } if (my_command_is_running) { a_command_is_running = 1; my_stop_is_required = 1; SHOW_TIME("fifo_stop > my_stop_is_required = 1\n"); } SHOW_TIME("fifo_stop > unlocking\n"); a_status = pthread_mutex_unlock(&my_mutex); if (a_status != 0) { return EE_INTERNAL_ERROR; } if (a_command_is_running) { SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n"); while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { continue; // Restart when interrupted by handler } SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n"); } SHOW_TIME("fifo_stop > my_stop_is_required = 0\n"); my_stop_is_required = 0; SHOW_TIME("LEAVE fifo_stop\n"); return EE_OK; }