/*************************************************************************** * Copyright (C) 2008, Sun Microsystems, Inc. * * eSpeak driver for Solaris Audio Device Architecture (SADA) * * Written by Willie Walker, based on the eSpeak PulseAudio driver * * from Gilles Casse * * * * 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "speech.h" #ifdef USE_ASYNC // This source file is only used for asynchronious modes #include #include #include #include #include #include #include #include #include "wave.h" #include "debug.h" enum {ONE_BILLION=1000000000}; #define SAMPLE_RATE 22050 #define SAMPLE_SIZE 16 #ifdef USE_SADA static t_wave_callback* my_callback_is_output_enabled=NULL; static const char *sun_audio_device = "/dev/audio"; static int sun_audio_fd = -1; // The total number of 16-bit samples sent to be played via the // wave_write method. // static uint32_t total_samples_sent; // The total number of samples sent to be played via the wave_write // method, but which were never played because of a call to // wave_close. // static uint32_t total_samples_skipped; // The last known playing index after a call to wave_close. // static uint32_t last_play_position=0; //> // wave_init // // DESCRIPTION: // // initializes the audio subsytem. // // GLOBALS USED/MODIFIED: // // sun_audio_fd: modified to hold the file descriptor of the opened // audio device. // // // wave_open // // DESCRIPTION: // // opens the audio subsystem given a specific API (e.g., "alsa", // "oss", ...). We ignore the_api and just return the sun_audio_fd we // opened in wave_init. This return value will be passed in as the // theHandler parameter in all other methods. // // PARAMETERS: // // the_api: "alsa", "oss" (ignored) // // GLOBALS USED/MODIFIED: // // sun_audio_fd: used as return value // // RETURNS: // // sun_audio_fd opened in wave_init, which is passed in as theHandler // parameter in all other methods // // // wave_write // // DESCRIPTION: // // Meant to be asynchronous, it supplies the wave sample to the lower // audio layer and returns. The sample is played later on. [[[WDW - // we purposely do not open the audio device as non-blocking because // managing that would be a pain. So, we rely a lot upon fifo.cpp and // event.cpp to not overload us, allowing us to get away with a // blocking write. event.cpp:polling_thread in particular appears to // use get_remaining_time to prevent flooding.]]] // // PARAMETERS: // // theHandler: the audio device file descriptor // theMono16BitsWaveBuffer: the audio data // theSize: the number of bytes (not 16-bit samples) // // GLOBALS USED/MODIFIED: // // total_samples_sent: modified based upon 16-bit samples sent // // RETURNS: // // the number of bytes (not 16-bit samples) sent // // my_callback_is_output_enabled: no!"); return 0; } 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; } //> // wave_close // // DESCRIPTION: // // Does what SADA normally would call a flush, which means to cease // all audio production in progress and throw any remaining audio // away. [[[WDW - see comment in wave_flush.]]] // // PARAMETERS: // // theHandler: the audio device file descriptor // // GLOBALS USED/MODIFIED: // // last_play_position: modified to reflect play position the last time // this method was called // total_samples_sent: used to help calculate total_samples_skipped // total_samples_skipped: modified to hold the total number of 16-bit // samples sent to wave_write, but which were // never played // sun_audio_fd: used because some calls to wave_close seem to // pass a NULL for theHandler for some odd reason // // RETURNS: // // The result of the ioctl call (non-0 means failure) // // LEAVE"); return ret; } //> // wave_is_busy // // DESCRIPTION: // // Returns a non-0 value if audio is being played. // // PARAMETERS: // // theHandler: the audio device file descriptor // // GLOBALS USED/MODIFIED: // // sun_audio_fd: used because some calls to wave_is_busy seem to // pass a NULL for theHandler for some odd reason // // RETURNS: // // A non-0 value if audio is being played // // LEAVE"); return ainfo.play.active; } //> // wave_terminate // // DESCRIPTION: // // Used to end our session with eSpeak. // // GLOBALS USED/MODIFIED: // // sun_audio_fd: modified - closed and set to -1 // // LEAVE"); } //> // wave_flush // // DESCRIPTION: // // Appears to want to tell the audio subsystem to make sure it plays // the audio. In our case, the system is already doing this, so this // is basically a no-op. [[[WDW - if you do a drain, you block, so // don't do that. In addition the typical SADA notion of flush is // currently handled by wave_close. I think this is most likely just // terminology conflict between eSpeak and SADA.]]] // // PARAMETERS: // // theHandler: the audio device file descriptor // // LEAVE"); } //> // wave_set_callback_is_output_enabled // // DESCRIPTION: // // Sets the callback to call from wave_write before it sends data to // be played. It helps wave_write determine if the data should be // thrown away or not. // // PARAMETERS: // // cb: the callback to call from wave_write // // // wave_test_get_write_buffer // // DESCRIPTION: // // Unnecessary and is used for debug output from // speak_lib.cpp:dispatch_audio. // // RETURNS: // // NULL // // // wave_get_read_position // // DESCRIPTION: // // Concerns the sample which is currently played by the audio layer, // where 'sample' is a small buffer of synthesized wave data, // identified so that the user callback could be called when the // 'sample' is really played. The identifier is returned by // wave_get_write_position. This method is unused. // // PARAMETERS: // // theHandler: the audio device file descriptor // // RETURNS: // // The total number of 16-bit samples played by the audio system // so far. // // LEAVE"); return ainfo.play.samples; } //> // wave_get_write_position // // DESCRIPTION: // // Returns an identifier for a new sample, where 'sample' is a small // buffer of synthesized wave data, identified so that the user // callback could be called when the 'sample' is really played. This // implementation views the audio as one long continuous stream of // 16-bit samples. // // PARAMETERS: // // theHandler: the audio device file descriptor // // GLOBALS USED/MODIFIED: // // total_samples_sent: used as the return value // // RETURNS: // // total_samples_sent, which is the index for the end of this long // continuous stream. [[[WDW: with a unit32_t managing 16-bit // samples at 22050Hz, we have about 54 hours of play time before // the index wraps back to 0. We don't handle that wrapping, so // the behavior after 54 hours of play time is undefined.]]] // // LEAVE"); return total_samples_sent; } //> // wave_get_remaining_time // // DESCRIPTION: // // Returns the remaining time (in ms) before the sample is played. // The sample in this case is a return value from a previous call to // wave_get_write_position. // // PARAMETERS: // // sample: an index returned from wave_get_write_position representing // an index into the long continuous stream of 16-bit samples // time: a return value representing the delay in milliseconds until // sample is played. A value of 0 means the sample is either // currently being played or it has already been played. // // GLOBALS USED/MODIFIED: // // sun_audio_fd: used to determine total number of samples played by // the audio system // total_samples_skipped: used in remaining time calculation // // RETURNS: // // Time in milliseconds before the sample is played or 0 if the sample // is currently playing or has already been played. // // 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) / SAMPLE_RATE; *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; } #else // notdef USE_SADA void wave_init() {} 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 //> //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 //>