Files
espeak-ng/src/libespeak-ng/wave.c
T
Reece H. Dunn e69c936329 Remove the debugging code.
The debugging code is done inconsistently, mainly to trace the
event and audio output logic. This makes it harder to understand
the code flow for logic that is not enabled by default.

As such the debugging code makes the code harder to maintain.
2015-12-28 12:33:14 +00:00

937 lines
24 KiB
C

/*
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org>
* 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: <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "speech.h"
#ifdef USE_ASYNC
// This source file is only used for asynchronious modes
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#include "portaudio.h"
#ifdef PLATFORM_WINDOWS
#include <windows.h>
#else
#include <unistd.h>
#endif
#include "wave.h"
#ifdef NEED_STRUCT_TIMESPEC
#define HAVE_STRUCT_TIMESPEC 1
struct timespec {
long tv_sec;
long tv_nsec;
};
#endif
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
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;
}
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;
return used;
}
static void start_stream()
{
PaError err;
my_stream_could_start = 0;
mInCallbackFinishedState = false;
err = Pa_StartStream(pa_stream);
#if USE_PORTAUDIO == 19
if (err == paStreamIsNotStopped) {
// not sure why we need this, but PA v19 seems to need it
err = Pa_StopStream(pa_stream);
err = Pa_StartStream(pa_stream);
}
#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;
if (aWrite >= myRead) {
if ((size_t)(aWrite - myRead) >= n) {
memcpy(outputBuffer, myRead, n);
myRead += n;
} else {
// 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 {
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)
memcpy(outputBuffer, myRead, aTopMem);
int aRest = n - aTopMem;
if (aRest) {
char *p = (char *)outputBuffer + aTopMem;
memcpy(p, myBuffer, aRest);
}
myRead = myBuffer + aRest;
} else {
// underflow
aResult = 1; // paComplete;
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
if (aTopMem)
memcpy(outputBuffer, myRead, aTopMem);
int aRest = aWrite - myBuffer;
if (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;
}
}
#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)
{
if (my_stream_could_start)
start_stream();
}
static int 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)
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);
if (err == paInvalidChannelCount) {
// 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);
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)) {
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,
pa_callback,
(void *)userdata);
}
if (err == paInvalidChannelCount) {
// 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,
pa_callback,
(void *)userdata);
}
mInCallbackFinishedState = false;
#endif
}
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 ?
} else
myOutputParameters.suggestedLatency = (double)0.1; // 100ms
myOutputParameters.hostApiSpecificStreamInfo = NULL;
}
#endif
static void select_device(const char *the_api)
{
#if (USE_PORTAUDIO == 19)
int numDevices = Pa_GetDeviceCount();
if (numDevices < 0)
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) {
selectedIndex = defaultAlsaIndex;
selectedDeviceInfo = deviceInfo;
break;
}
}
// if the default output device does not match,
// look for the device with the highest number of output channels
update_output_parameters(i, deviceInfo);
if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) {
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)
{
PaError err;
pa_stream = NULL;
wave_samplerate = srate;
mInCallbackFinishedState = false;
init_buffer();
// PortAudio sound output library
err = Pa_Initialize();
pa_init_err = err;
return err == paNoError;
}
void *wave_open(const char *the_api)
{
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) {
memcpy(dest, src, theSizeInBytes);
bytes_written = theSizeInBytes;
} else { // copy for 2 channels (stereo)
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;
}
}
return bytes_written;
}
size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{
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) {
if (0 != wave_open_sound())
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;
size_t aTotalFreeMem = 0;
char *aRead = myRead;
while (1) {
if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled()))
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;
if (aTotalFreeMem > 1) {
// -1 because myWrite must be different of aRead
// otherwise buffer would be considered as empty
aTotalFreeMem -= 1;
}
if (aTotalFreeMem >= bytes_to_write)
break;
usleep(10000);
}
aRead = myRead;
// write pointer is ahead the read pointer?
if (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);
}
}
} else // read pointer is ahead the write pointer
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
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();
return bytes_written;
}
int wave_close(void *theHandler)
{
static int aStopStreamCount = 0;
#if (USE_PORTAUDIO == 19)
if (pa_stream == NULL)
return 0;
if (Pa_IsStreamStopped(pa_stream))
return 0;
#else
if (pa_stream == NULL)
return 0;
if (Pa_StreamActive(pa_stream) == false && mInCallbackFinishedState == false)
return 0;
#endif
// Avoid race condition by making sure this function only
// gets called once at a time
aStopStreamCount++;
if (aStopStreamCount != 1)
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);
Pa_CloseStream(pa_stream);
pa_stream = NULL;
mInCallbackFinishedState = false;
}
#else
if (pa_stream) {
if (mInCallbackFinishedState) {
Pa_StopStream(pa_stream);
} else {
Pa_AbortStream(pa_stream);
}
Pa_CloseStream(pa_stream);
pa_stream = NULL;
mInCallbackFinishedState = false;
}
#endif
init_buffer();
aStopStreamCount = 0; // last action
return 0;
}
int wave_is_busy(void *theHandler)
{
PaError active = 0;
if (pa_stream) {
#if USE_PORTAUDIO == 18
active = Pa_StreamActive(pa_stream)
&& (mInCallbackFinishedState == false);
#else
active = Pa_IsStreamActive(pa_stream)
&& (mInCallbackFinishedState == false);
#endif
}
return active == 1;
}
void wave_terminate()
{
Pa_Terminate();
}
uint32_t wave_get_read_position(void *theHandler)
{
return myReadPosition;
}
uint32_t wave_get_write_position(void *theHandler)
{
return myWritePosition;
}
int wave_get_remaining_time(uint32_t sample, uint32_t *time)
{
double a_time = 0;
if (!time || !pa_stream)
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;
*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
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) {
ts->tv_sec += 1;
t_ns -= ONE_BILLION;
}
ts->tv_nsec = (long int)t_ns;
}
#endif