Files
espeak-ng/src/libespeak-ng/wave.c
T

906 lines
22 KiB
C

/*
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org>
* Copyright (C) 2015-2016 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 <espeak-ng/espeak_ng.h>
#include "speech.h"
#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>
#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
#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_open
static int pulse_running;
// wave.cpp (this file)
void *wave_port_open(int, const char *);
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();
void *wave_pulse_open(int, const char *);
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
void *wave_open(int srate, const char *device)
{
if (pulse_running)
return wave_pulse_open(srate, device);
else
return wave_port_open(srate, device);
}
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_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 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 PaDeviceID myOutputDevice = 0;
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 PaError 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
return err;
}
/* 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
{
(void)inputBuffer; // unused
(void)outTime; // unused
(void)userData; // unused
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)
{
(void)theHandler; // unused
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
err = Pa_OpenStream(&pa_stream,
// capture parameters
paNoDevice,
0,
paInt16,
NULL,
// playback parameters
myOutputDevice,
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;
err = Pa_OpenStream(&pa_stream,
// capture parameters
paNoDevice,
0,
paInt16,
NULL,
// playback parameters
myOutputDevice,
out_channels,
paInt16,
NULL,
// general parameters
wave_samplerate, FRAMES_PER_BUFFER, 0,
paNoFlag,
pa_callback, (void *)userdata);
}
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;
}
static void update_output_parameters(int selectedDevice, const PaDeviceInfo *deviceInfo)
{
#if (USE_PORTAUDIO == 19)
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;
#else
myOutputDevice = selectedDevice;
#endif
}
static const PaDeviceInfo *select_device(const char *device)
{
#if (USE_PORTAUDIO == 19)
int numDevices = Pa_GetDeviceCount();
#else
int numDevices = Pa_CountDevices();
#endif
if (numDevices < 0)
return NULL;
#if (USE_PORTAUDIO == 19)
PaDeviceIndex i = 0, selectedIndex = 0;
#else
PaDeviceID i = 0, selectedIndex = 0;
#endif
const PaDeviceInfo *deviceInfo = NULL;
const PaDeviceInfo *selectedDeviceInfo = NULL;
if (option_device_number >= 0) {
selectedIndex = option_device_number;
selectedDeviceInfo = Pa_GetDeviceInfo(selectedIndex);
}
if (device == NULL) {
#if (USE_PORTAUDIO == 19)
selectedIndex = Pa_GetDefaultOutputDevice();
#else
selectedIndex = Pa_GetDefaultOutputDeviceID();
#endif
selectedDeviceInfo = Pa_GetDeviceInfo(selectedIndex);
}
if (selectedDeviceInfo == NULL) {
for (i = 0; i < numDevices; i++) {
deviceInfo = Pa_GetDeviceInfo(i);
if (deviceInfo != NULL && !strcmp(device, deviceInfo->name)) {
selectedIndex = i;
selectedDeviceInfo = deviceInfo;
}
}
}
if (selectedDeviceInfo)
update_output_parameters(selectedIndex, selectedDeviceInfo);
return selectedDeviceInfo;
}
void wave_set_callback_is_output_enabled(t_wave_callback *cb)
{
my_callback_is_output_enabled = cb;
}
void *wave_open(int srate, const char *device)
{
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)
return NULL;
static int once = 0;
if (!once) {
if (!select_device(device))
return NULL;
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)
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)
{
(void)theHandler; // unused
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;
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)
{
(void)theHandler; // unused
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)
{
(void)theHandler; // unused
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)
{
(void)theHandler; // unused
return myReadPosition;
}
uint32_t wave_get_write_position(void *theHandler)
{
(void)theHandler; // unused
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
void *wave_open(int srate, const char *device)
{
(void)srate; // unused
(void)device; // unused
return (void *)1;
}
size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{
(void)theHandler; // unused
(void)theMono16BitsWaveBuffer; // unused
return theSize;
}
int wave_close(void *theHandler)
{
(void)theHandler; // unused
return 0;
}
int wave_is_busy(void *theHandler)
{
(void)theHandler; // unused
return 0;
}
void wave_terminate()
{
}
uint32_t wave_get_read_position(void *theHandler)
{
(void)theHandler; // unused
return 0;
}
uint32_t wave_get_write_position(void *theHandler)
{
(void)theHandler; // unused
return 0;
}
void wave_flush(void *theHandler)
{
(void)theHandler; // unused
}
typedef int (t_wave_callback)(void);
void wave_set_callback_is_output_enabled(t_wave_callback *cb)
{
(void)cb; // unused
}
extern void *wave_test_get_write_buffer()
{
return NULL;
}
int wave_get_remaining_time(uint32_t sample, uint32_t *time)
{
(void)sample; // unused
if (!time) return -1;
*time = (uint32_t)0;
return 0;
}
#endif