123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- /***************************************************************************
- * 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 <errno.h>
- #include <string.h>
- #include <stropts.h>
- #include <assert.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/audioio.h>
-
- #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_init
-
- void wave_init() {
- ENTER("wave_init");
-
- audio_info_t ainfo;
- char *audio_device = NULL;
-
- audio_device = getenv("AUDIODEV");
- if (audio_device != NULL) {
- if ((sun_audio_fd = open(audio_device, O_WRONLY)) < 0) {
- SHOW("wave_init() could not open: %s (%d)\n",
- audio_device, sun_audio_fd);
- }
- }
-
- if (sun_audio_fd < 0) {
- if ((sun_audio_fd = open(sun_audio_device, O_WRONLY)) < 0) {
- SHOW("wave_init() could not open: %s (%d)\n",
- sun_audio_device, sun_audio_fd);
- }
- }
-
- SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd);
-
- if (sun_audio_fd < 0) {
- return;
- }
-
- ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo);
- SHOW("wave_init() play buffer size: %d\n", ainfo.play.buffer_size);
- ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
- ainfo.play.channels = 1;
- ainfo.play.sample_rate = SAMPLE_RATE;
- ainfo.play.precision = SAMPLE_SIZE;
-
- if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) {
- SHOW("wave_init() failed to set audio params: %s\n", strerror(errno));
- close(sun_audio_fd);
- return;
- }
- }
-
- //>
- // 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_open
-
- void* wave_open(const char* the_api)
- {
- ENTER("wave_open");
- return((void*) sun_audio_fd);
- }
-
- //>
- // 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
- //
- //<wave_write
-
- 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;
- }
-
- //>
- // 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)
- //
- //<wave_close
-
- 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;
- }
-
- //>
- // 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
- //
- //<wave_is_busy
-
- int wave_is_busy(void* theHandler)
- {
- audio_info_t ainfo;
- int audio_fd = (int) theHandler;
- if (!audio_fd) {
- audio_fd = sun_audio_fd;
- }
- ENTER("wave_is_busy");
- ioctl(audio_fd, AUDIO_GETINFO, &ainfo);
- SHOW("wave_is_busy: active=%d", ainfo.play.active);
- SHOW_TIME("wave_is_busy > 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
- //
- //<wave_terminate
-
- void wave_terminate()
- {
- ENTER("wave_terminate");
- close(sun_audio_fd);
- sun_audio_fd = -1;
- SHOW_TIME("wave_terminate > 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
- //
- //<wave_flush
-
- void wave_flush(void* theHandler)
- {
- ENTER("wave_flush");
- //ioctl((int) theHandler, AUDIO_DRAIN, 0);
- SHOW_TIME("wave_flush > 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_set_callback_is_output_enabled
-
- void wave_set_callback_is_output_enabled(t_wave_callback* cb)
- {
- my_callback_is_output_enabled = cb;
- }
-
- //>
- // wave_test_get_write_buffer
- //
- // DESCRIPTION:
- //
- // Unnecessary and is used for debug output from
- // speak_lib.cpp:dispatch_audio.
- //
- // RETURNS:
- //
- // NULL
- //
- //<wave_test_get_write_buffer
-
- void *wave_test_get_write_buffer()
- {
- return 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.
- //
- //<wave_get_read_position
-
- 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;
- }
-
- //>
- // 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.]]]
- //
- //<wave_get_write_position
-
- 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;
- }
-
- //>
- // 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.
- //
- //<wave_get_remaining_time
-
- 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) / 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
-
- //>
- //<clock_gettime2, add_time_in_ms
-
- 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
-
- //>
|