1 |
/***************************************************************************
|
2 |
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org> *
|
3 |
* based on AudioIO.cc (Audacity-1.2.4b) and wavegen.cpp *
|
4 |
* *
|
5 |
* This program is free software; you can redistribute it and/or modify *
|
6 |
* it under the terms of the GNU General Public License as published by *
|
7 |
* the Free Software Foundation; either version 3 of the License, or *
|
8 |
* (at your option) any later version. *
|
9 |
* *
|
10 |
* This program is distributed in the hope that it will be useful, *
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
13 |
* GNU General Public License for more details. *
|
14 |
* *
|
15 |
* You should have received a copy of the GNU General Public License *
|
16 |
* along with this program; if not, write to the *
|
17 |
* Free Software Foundation, Inc., *
|
18 |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
19 |
***************************************************************************/
|
20 |
|
21 |
#include "speech.h"
|
22 |
|
23 |
#ifdef USE_ASYNC
|
24 |
// This source file is only used for asynchronious modes
|
25 |
|
26 |
#include <stdio.h>
|
27 |
#include <string.h>
|
28 |
#include <stdlib.h>
|
29 |
#include <math.h>
|
30 |
#include <assert.h>
|
31 |
#include <sys/time.h>
|
32 |
#include <time.h>
|
33 |
|
34 |
#include "portaudio.h"
|
35 |
#ifndef PLATFORM_WINDOWS
|
36 |
#include <unistd.h>
|
37 |
#endif
|
38 |
#include "wave.h"
|
39 |
#include "debug.h"
|
40 |
|
41 |
//<Definitions
|
42 |
|
43 |
enum {ONE_BILLION=1000000000};
|
44 |
|
45 |
#ifdef USE_PORTAUDIO
|
46 |
|
47 |
#undef USE_PORTAUDIO
|
48 |
// determine portaudio version by looking for a #define which is not in V18
|
49 |
#ifdef paNeverDropInput
|
50 |
#define USE_PORTAUDIO 19
|
51 |
#else
|
52 |
#define USE_PORTAUDIO 18
|
53 |
#endif
|
54 |
|
55 |
|
56 |
static t_wave_callback* my_callback_is_output_enabled=NULL;
|
57 |
|
58 |
#define N_WAV_BUF 10
|
59 |
#define SAMPLE_RATE 22050
|
60 |
#define FRAMES_PER_BUFFER 512
|
61 |
#define BUFFER_LENGTH (SAMPLE_RATE*2*sizeof(uint16_t))
|
62 |
#define THRESHOLD (BUFFER_LENGTH/5)
|
63 |
static char myBuffer[BUFFER_LENGTH];
|
64 |
static char* myRead=NULL;
|
65 |
static char* myWrite=NULL;
|
66 |
static int out_channels=1;
|
67 |
static int my_stream_could_start=0;
|
68 |
|
69 |
static int mInCallbackFinishedState = false;
|
70 |
#if (USE_PORTAUDIO == 18)
|
71 |
static PortAudioStream *pa_stream=NULL;
|
72 |
#endif
|
73 |
#if (USE_PORTAUDIO == 19)
|
74 |
static struct PaStreamParameters myOutputParameters;
|
75 |
static PaStream *pa_stream=NULL;
|
76 |
#endif
|
77 |
|
78 |
static int userdata[4];
|
79 |
static PaError pa_init_err=0;
|
80 |
|
81 |
// time measurement
|
82 |
// The read and write position audio stream in the audio stream are measured in ms.
|
83 |
//
|
84 |
// * When the stream is opened, myReadPosition and myWritePosition are cleared.
|
85 |
// * myWritePosition is updated in wave_write.
|
86 |
// * myReadPosition is updated in pa_callback (+ sample delay).
|
87 |
|
88 |
static uint32_t myReadPosition = 0; // in ms
|
89 |
static uint32_t myWritePosition = 0;
|
90 |
|
91 |
//>
|
92 |
//<init_buffer, get_used_mem
|
93 |
|
94 |
static void init_buffer()
|
95 |
{
|
96 |
myWrite = myBuffer;
|
97 |
myRead = myBuffer;
|
98 |
memset(myBuffer,0,BUFFER_LENGTH);
|
99 |
myReadPosition = myWritePosition = 0;
|
100 |
SHOW("init_buffer > myRead=0x%x, myWrite=0x%x, BUFFER_LENGTH=0x%x, myReadPosition = myWritePosition = 0\n", (int)myRead, (int)myWrite, BUFFER_LENGTH);
|
101 |
}
|
102 |
|
103 |
static unsigned int get_used_mem()
|
104 |
{
|
105 |
char* aRead = myRead;
|
106 |
char* aWrite = myWrite;
|
107 |
unsigned int used = 0;
|
108 |
|
109 |
assert ((aRead >= myBuffer)
|
110 |
&& (aRead <= myBuffer + BUFFER_LENGTH)
|
111 |
&& (aWrite >= myBuffer)
|
112 |
&& (aWrite <= myBuffer + BUFFER_LENGTH));
|
113 |
|
114 |
if (aRead < aWrite)
|
115 |
{
|
116 |
used = aWrite - aRead;
|
117 |
}
|
118 |
else
|
119 |
{
|
120 |
used = aWrite + BUFFER_LENGTH - aRead;
|
121 |
}
|
122 |
SHOW("get_used_mem > %d\n", used);
|
123 |
|
124 |
return used;
|
125 |
}
|
126 |
|
127 |
//>
|
128 |
//<start stream
|
129 |
|
130 |
static void start_stream()
|
131 |
{
|
132 |
PaError err;
|
133 |
SHOW_TIME("start_stream");
|
134 |
|
135 |
my_stream_could_start=0;
|
136 |
mInCallbackFinishedState = false;
|
137 |
|
138 |
err = Pa_StartStream(pa_stream);
|
139 |
SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));
|
140 |
|
141 |
#if USE_PORTAUDIO == 19
|
142 |
if(err == paStreamIsNotStopped)
|
143 |
{
|
144 |
SHOW_TIME("start_stream > restart stream (begin)");
|
145 |
// not sure why we need this, but PA v19 seems to need it
|
146 |
err = Pa_StopStream(pa_stream);
|
147 |
SHOW("start_stream > Pa_StopStream=%d (%s)\n", err, Pa_GetErrorText(err));
|
148 |
err = Pa_StartStream(pa_stream);
|
149 |
SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));
|
150 |
SHOW_TIME("start_stream > restart stream (end)");
|
151 |
}
|
152 |
#endif
|
153 |
}
|
154 |
|
155 |
//>
|
156 |
//<pa_callback
|
157 |
|
158 |
/* This routine will be called by the PortAudio engine when audio is needed.
|
159 |
** It may called at interrupt level on some machines so don't do anything
|
160 |
** that could mess up the system like calling malloc() or free().
|
161 |
*/
|
162 |
#if USE_PORTAUDIO == 18
|
163 |
static int pa_callback(void *inputBuffer, void *outputBuffer,
|
164 |
unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
|
165 |
#else
|
166 |
static int pa_callback(const void *inputBuffer, void *outputBuffer,
|
167 |
long unsigned int framesPerBuffer, const PaStreamCallbackTimeInfo *outTime,
|
168 |
PaStreamCallbackFlags flags, void *userData )
|
169 |
#endif
|
170 |
{
|
171 |
int aResult=0; // paContinue
|
172 |
char* aWrite = myWrite;
|
173 |
size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;
|
174 |
|
175 |
myReadPosition += framesPerBuffer;
|
176 |
SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n",(int)myReadPosition, framesPerBuffer, n);
|
177 |
|
178 |
if (aWrite >= myRead)
|
179 |
{
|
180 |
if((size_t)(aWrite - myRead) >= n)
|
181 |
{
|
182 |
memcpy(outputBuffer, myRead, n);
|
183 |
myRead += n;
|
184 |
}
|
185 |
else
|
186 |
{
|
187 |
SHOW_TIME("pa_callback > underflow");
|
188 |
aResult=1; // paComplete;
|
189 |
mInCallbackFinishedState = true;
|
190 |
size_t aUsedMem=0;
|
191 |
aUsedMem = (size_t)(aWrite - myRead);
|
192 |
if (aUsedMem)
|
193 |
{
|
194 |
memcpy(outputBuffer, myRead, aUsedMem);
|
195 |
}
|
196 |
char* p = (char*)outputBuffer + aUsedMem;
|
197 |
memset(p, 0, n - aUsedMem);
|
198 |
// myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
|
199 |
myRead = aWrite;
|
200 |
}
|
201 |
}
|
202 |
else // myRead > aWrite
|
203 |
{
|
204 |
if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n)
|
205 |
{
|
206 |
memcpy(outputBuffer, myRead, n);
|
207 |
myRead += n;
|
208 |
}
|
209 |
else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n)
|
210 |
{
|
211 |
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
|
212 |
if (aTopMem)
|
213 |
{
|
214 |
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
|
215 |
memcpy(outputBuffer, myRead, aTopMem);
|
216 |
}
|
217 |
int aRest = n - aTopMem;
|
218 |
if (aRest)
|
219 |
{
|
220 |
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
|
221 |
char* p = (char*)outputBuffer + aTopMem;
|
222 |
memcpy(p, myBuffer, aRest);
|
223 |
}
|
224 |
myRead = myBuffer + aRest;
|
225 |
}
|
226 |
else
|
227 |
{
|
228 |
SHOW_TIME("pa_callback > underflow");
|
229 |
aResult=1; // paComplete;
|
230 |
|
231 |
int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
|
232 |
if (aTopMem)
|
233 |
{
|
234 |
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
|
235 |
memcpy(outputBuffer, myRead, aTopMem);
|
236 |
}
|
237 |
int aRest = aWrite - myBuffer;
|
238 |
if (aRest)
|
239 |
{
|
240 |
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
|
241 |
char* p = (char*)outputBuffer + aTopMem;
|
242 |
memcpy(p, myBuffer, aRest);
|
243 |
}
|
244 |
|
245 |
size_t aUsedMem = aTopMem + aRest;
|
246 |
char* p = (char*)outputBuffer + aUsedMem;
|
247 |
memset(p, 0, n - aUsedMem);
|
248 |
// myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
|
249 |
myRead = aWrite;
|
250 |
}
|
251 |
}
|
252 |
|
253 |
SHOW("pa_callback > myRead=%x\n",(int)myRead);
|
254 |
|
255 |
|
256 |
// #if USE_PORTAUDIO == 18
|
257 |
// if(aBufferEmpty)
|
258 |
// {
|
259 |
// static int end_timer = 0;
|
260 |
// if(end_timer == 0)
|
261 |
// end_timer = 4;
|
262 |
// if(end_timer > 0)
|
263 |
// {
|
264 |
// end_timer--;
|
265 |
// if(end_timer == 0)
|
266 |
// return(1);
|
267 |
// }
|
268 |
// }
|
269 |
// return(0);
|
270 |
// #else
|
271 |
|
272 |
#ifdef ARCH_BIG
|
273 |
{
|
274 |
// BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer
|
275 |
int c;
|
276 |
unsigned char *out_ptr;
|
277 |
unsigned char *out_end;
|
278 |
out_ptr = (unsigned char *)outputBuffer;
|
279 |
out_end = out_ptr + framesPerBuffer*2 * out_channels;
|
280 |
while(out_ptr < out_end)
|
281 |
{
|
282 |
c = out_ptr[0];
|
283 |
out_ptr[0] = out_ptr[1];
|
284 |
out_ptr[1] = c;
|
285 |
out_ptr += 2;
|
286 |
}
|
287 |
}
|
288 |
#endif
|
289 |
|
290 |
|
291 |
return(aResult);
|
292 |
//#endif
|
293 |
|
294 |
} // end of WaveCallBack
|
295 |
|
296 |
//>
|
297 |
|
298 |
|
299 |
void wave_flush(void* theHandler)
|
300 |
{
|
301 |
ENTER("wave_flush");
|
302 |
|
303 |
if (my_stream_could_start)
|
304 |
{
|
305 |
// #define buf 1024
|
306 |
// static char a_buffer[buf*2];
|
307 |
// memset(a_buffer,0,buf*2);
|
308 |
// wave_write(theHandler, a_buffer, buf*2);
|
309 |
start_stream();
|
310 |
}
|
311 |
}
|
312 |
|
313 |
//<wave_open_sound
|
314 |
|
315 |
static int wave_open_sound()
|
316 |
{
|
317 |
ENTER("wave_open_sound");
|
318 |
|
319 |
PaError err=paNoError;
|
320 |
PaError active;
|
321 |
|
322 |
#if USE_PORTAUDIO == 18
|
323 |
active = Pa_StreamActive(pa_stream);
|
324 |
#else
|
325 |
active = Pa_IsStreamActive(pa_stream);
|
326 |
#endif
|
327 |
|
328 |
if(active == 1)
|
329 |
{
|
330 |
SHOW_TIME("wave_open_sound > already active");
|
331 |
return(0);
|
332 |
}
|
333 |
if(active < 0)
|
334 |
{
|
335 |
out_channels = 1;
|
336 |
|
337 |
#if USE_PORTAUDIO == 18
|
338 |
// err = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,SAMPLE_RATE,FRAMES_PER_BUFFER,N_WAV_BUF,pa_callback,(void *)userdata);
|
339 |
|
340 |
PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID();
|
341 |
|
342 |
PaError err = Pa_OpenStream( &pa_stream,
|
343 |
/* capture parameters */
|
344 |
paNoDevice,
|
345 |
0,
|
346 |
paInt16,
|
347 |
NULL,
|
348 |
/* playback parameters */
|
349 |
playbackDevice,
|
350 |
out_channels,
|
351 |
paInt16,
|
352 |
NULL,
|
353 |
/* general parameters */
|
354 |
SAMPLE_RATE, FRAMES_PER_BUFFER, 0,
|
355 |
//paClipOff | paDitherOff,
|
356 |
paNoFlag,
|
357 |
pa_callback, (void *)userdata);
|
358 |
|
359 |
SHOW("wave_open_sound > Pa_OpenDefaultStream(1): err=%d (%s)\n",err, Pa_GetErrorText(err));
|
360 |
|
361 |
if(err == paInvalidChannelCount)
|
362 |
{
|
363 |
SHOW_TIME("wave_open_sound > try stereo");
|
364 |
// failed to open with mono, try stereo
|
365 |
out_channels = 2;
|
366 |
// myOutputParameters.channelCount = out_channels;
|
367 |
PaError err = Pa_OpenStream( &pa_stream,
|
368 |
/* capture parameters */
|
369 |
paNoDevice,
|
370 |
0,
|
371 |
paInt16,
|
372 |
NULL,
|
373 |
/* playback parameters */
|
374 |
playbackDevice,
|
375 |
out_channels,
|
376 |
paInt16,
|
377 |
NULL,
|
378 |
/* general parameters */
|
379 |
SAMPLE_RATE, FRAMES_PER_BUFFER, 0,
|
380 |
//paClipOff | paDitherOff,
|
381 |
paNoFlag,
|
382 |
pa_callback, (void *)userdata);
|
383 |
// err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,
|
384 |
// SAMPLE_RATE,
|
385 |
// FRAMES_PER_BUFFER,
|
386 |
// N_WAV_BUF,pa_callback,(void *)userdata);
|
387 |
SHOW("wave_open_sound > Pa_OpenDefaultStream(2): err=%d (%s)\n",err, Pa_GetErrorText(err));
|
388 |
err=0; // avoid warning
|
389 |
}
|
390 |
mInCallbackFinishedState = false; // v18 only
|
391 |
#else
|
392 |
myOutputParameters.channelCount = out_channels;
|
393 |
unsigned long framesPerBuffer = paFramesPerBufferUnspecified;
|
394 |
err = Pa_OpenStream(
|
395 |
&pa_stream,
|
396 |
NULL, /* no input */
|
397 |
&myOutputParameters,
|
398 |
SAMPLE_RATE,
|
399 |
framesPerBuffer,
|
400 |
paNoFlag,
|
401 |
// paClipOff | paDitherOff,
|
402 |
pa_callback,
|
403 |
(void *)userdata);
|
404 |
if ((err!=paNoError)
|
405 |
&& (err!=paInvalidChannelCount)) //err==paUnanticipatedHostError
|
406 |
{
|
407 |
fprintf(stderr, "wave_open_sound > Pa_OpenStream : err=%d (%s)\n",err,Pa_GetErrorText(err));
|
408 |
framesPerBuffer = FRAMES_PER_BUFFER;
|
409 |
err = Pa_OpenStream(
|
410 |
&pa_stream,
|
411 |
NULL, /* no input */
|
412 |
&myOutputParameters,
|
413 |
SAMPLE_RATE,
|
414 |
framesPerBuffer,
|
415 |
paNoFlag,
|
416 |
// paClipOff | paDitherOff,
|
417 |
pa_callback,
|
418 |
(void *)userdata);
|
419 |
}
|
420 |
if(err == paInvalidChannelCount)
|
421 |
{
|
422 |
SHOW_TIME("wave_open_sound > try stereo");
|
423 |
// failed to open with mono, try stereo
|
424 |
out_channels = 2;
|
425 |
myOutputParameters.channelCount = out_channels;
|
426 |
err = Pa_OpenStream(
|
427 |
&pa_stream,
|
428 |
NULL, /* no input */
|
429 |
&myOutputParameters,
|
430 |
SAMPLE_RATE,
|
431 |
framesPerBuffer,
|
432 |
paNoFlag,
|
433 |
// paClipOff | paDitherOff,
|
434 |
pa_callback,
|
435 |
(void *)userdata);
|
436 |
|
437 |
// err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)SAMPLE_RATE,FRAMES_PER_BUFFER,pa_callback,(void *)userdata);
|
438 |
}
|
439 |
mInCallbackFinishedState = false;
|
440 |
#endif
|
441 |
}
|
442 |
|
443 |
SHOW("wave_open_sound > %s\n","LEAVE");
|
444 |
|
445 |
return (err != paNoError);
|
446 |
}
|
447 |
|
448 |
//>
|
449 |
//<select_device
|
450 |
|
451 |
#if (USE_PORTAUDIO == 19)
|
452 |
static void update_output_parameters(int selectedDevice, const PaDeviceInfo *deviceInfo)
|
453 |
{
|
454 |
// const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i);
|
455 |
myOutputParameters.device = selectedDevice;
|
456 |
// myOutputParameters.channelCount = pdi->maxOutputChannels;
|
457 |
myOutputParameters.channelCount = 1;
|
458 |
myOutputParameters.sampleFormat = paInt16;
|
459 |
|
460 |
// Latency greater than 100ms for avoiding glitches
|
461 |
// (e.g. when moving a window in a graphical desktop)
|
462 |
// deviceInfo = Pa_GetDeviceInfo(selectedDevice);
|
463 |
if (deviceInfo)
|
464 |
{
|
465 |
double aLatency = deviceInfo->defaultLowOutputLatency;
|
466 |
double aCoeff = round(0.100 / aLatency);
|
467 |
// myOutputParameters.suggestedLatency = aCoeff * aLatency; // to avoid glitches ?
|
468 |
myOutputParameters.suggestedLatency = aLatency; // for faster response ?
|
469 |
SHOW("Device=%d, myOutputParameters.suggestedLatency=%f, aCoeff=%f\n",
|
470 |
selectedDevice,
|
471 |
myOutputParameters.suggestedLatency,
|
472 |
aCoeff);
|
473 |
}
|
474 |
else
|
475 |
{
|
476 |
myOutputParameters.suggestedLatency = (double)0.1; // 100ms
|
477 |
SHOW("Device=%d, myOutputParameters.suggestedLatency=%f (default)\n",
|
478 |
selectedDevice,
|
479 |
myOutputParameters.suggestedLatency);
|
480 |
}
|
481 |
//pdi->defaultLowOutputLatency;
|
482 |
|
483 |
myOutputParameters.hostApiSpecificStreamInfo = NULL;
|
484 |
}
|
485 |
#endif
|
486 |
|
487 |
static void select_device(const char* the_api)
|
488 |
{
|
489 |
ENTER("select_device");
|
490 |
|
491 |
#if (USE_PORTAUDIO == 19)
|
492 |
int numDevices = Pa_GetDeviceCount();
|
493 |
if( numDevices < 0 )
|
494 |
{
|
495 |
SHOW( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
|
496 |
assert(0);
|
497 |
}
|
498 |
|
499 |
PaDeviceIndex i=0, selectedIndex=0, defaultAlsaIndex=numDevices;
|
500 |
const PaDeviceInfo *deviceInfo=NULL;
|
501 |
const PaDeviceInfo *selectedDeviceInfo=NULL;
|
502 |
|
503 |
if(option_device_number >= 0)
|
504 |
{
|
505 |
selectedIndex = option_device_number;
|
506 |
selectedDeviceInfo = Pa_GetDeviceInfo(selectedIndex);
|
507 |
}
|
508 |
|
509 |
if(selectedDeviceInfo == NULL)
|
510 |
{
|
511 |
for( i=0; i<numDevices; i++ )
|
512 |
{
|
513 |
deviceInfo = Pa_GetDeviceInfo( i );
|
514 |
|
515 |
if (deviceInfo == NULL)
|
516 |
{
|
517 |
break;
|
518 |
}
|
519 |
const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
|
520 |
|
521 |
if (hostInfo && hostInfo->type == paALSA)
|
522 |
{
|
523 |
// Check (once) the default output device
|
524 |
if (defaultAlsaIndex == numDevices)
|
525 |
{
|
526 |
defaultAlsaIndex = hostInfo->defaultOutputDevice;
|
527 |
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( defaultAlsaIndex );
|
528 |
update_output_parameters(defaultAlsaIndex, deviceInfo);
|
529 |
if (Pa_IsFormatSupported(NULL, &myOutputParameters, SAMPLE_RATE) == 0)
|
530 |
{
|
531 |
SHOW( "select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex);
|
532 |
selectedIndex = defaultAlsaIndex;
|
533 |
selectedDeviceInfo = deviceInfo;
|
534 |
break;
|
535 |
}
|
536 |
}
|
537 |
|
538 |
// if the default output device does not match,
|
539 |
// look for the device with the highest number of output channels
|
540 |
SHOW( "select_device > ALSA, i=%d (numDevices=%d)\n", i, numDevices);
|
541 |
|
542 |
update_output_parameters(i, deviceInfo);
|
543 |
|
544 |
if (Pa_IsFormatSupported(NULL, &myOutputParameters, SAMPLE_RATE) == 0)
|
545 |
{
|
546 |
SHOW( "select_device > ALSA, name=%s (#%d)\n", deviceInfo->name, i);
|
547 |
|
548 |
if (!selectedDeviceInfo
|
549 |
|| (selectedDeviceInfo->maxOutputChannels < deviceInfo->maxOutputChannels))
|
550 |
{
|
551 |
selectedIndex = i;
|
552 |
selectedDeviceInfo = deviceInfo;
|
553 |
}
|
554 |
}
|
555 |
}
|
556 |
}
|
557 |
}
|
558 |
|
559 |
if (selectedDeviceInfo)
|
560 |
{
|
561 |
update_output_parameters(selectedIndex, selectedDeviceInfo);
|
562 |
}
|
563 |
else
|
564 |
{
|
565 |
i = Pa_GetDefaultOutputDevice();
|
566 |
deviceInfo = Pa_GetDeviceInfo( i );
|
567 |
update_output_parameters(i, deviceInfo);
|
568 |
}
|
569 |
|
570 |
#endif
|
571 |
}
|
572 |
|
573 |
//>
|
574 |
|
575 |
|
576 |
// int wave_Close(void* theHandler)
|
577 |
// {
|
578 |
// SHOW_TIME("WaveCloseSound");
|
579 |
|
580 |
// // PaError active;
|
581 |
|
582 |
// // check whether speaking has finished, and close the stream
|
583 |
// if(pa_stream != NULL)
|
584 |
// {
|
585 |
// Pa_CloseStream(pa_stream);
|
586 |
// pa_stream = NULL;
|
587 |
// init_buffer();
|
588 |
|
589 |
// // #if USE_PORTAUDIO == 18
|
590 |
// // active = Pa_StreamActive(pa_stream);
|
591 |
// // #else
|
592 |
// // active = Pa_IsStreamActive(pa_stream);
|
593 |
// // #endif
|
594 |
// // if(active == 0)
|
595 |
// // {
|
596 |
// // SHOW_TIME("WaveCloseSound > ok, not active");
|
597 |
// // Pa_CloseStream(pa_stream);
|
598 |
// // pa_stream = NULL;
|
599 |
// // return(1);
|
600 |
// // }
|
601 |
// }
|
602 |
// return(0);
|
603 |
// }
|
604 |
|
605 |
//<wave_set_callback_is_output_enabled
|
606 |
|
607 |
void wave_set_callback_is_output_enabled(t_wave_callback* cb)
|
608 |
{
|
609 |
my_callback_is_output_enabled = cb;
|
610 |
}
|
611 |
|
612 |
//>
|
613 |
//<wave_init
|
614 |
|
615 |
// TBD: the arg could be "alsa", "oss",...
|
616 |
void wave_init()
|
617 |
{
|
618 |
ENTER("wave_init");
|
619 |
PaError err;
|
620 |
|
621 |
pa_stream = NULL;
|
622 |
mInCallbackFinishedState = false;
|
623 |
init_buffer();
|
624 |
|
625 |
// PortAudio sound output library
|
626 |
err = Pa_Initialize();
|
627 |
pa_init_err = err;
|
628 |
if(err != paNoError)
|
629 |
{
|
630 |
SHOW_TIME("wave_init > Failed to initialise the PortAudio sound");
|
631 |
}
|
632 |
}
|
633 |
|
634 |
//>
|
635 |
//<wave_open
|
636 |
|
637 |
void* wave_open(const char* the_api)
|
638 |
{
|
639 |
ENTER("wave_open");
|
640 |
static int once=0;
|
641 |
|
642 |
// TBD: the_api (e.g. "alsa") is not used at the moment
|
643 |
// select_device is called once
|
644 |
if (!once)
|
645 |
{
|
646 |
select_device("alsa");
|
647 |
once=1;
|
648 |
}
|
649 |
return((void*)1);
|
650 |
}
|
651 |
|
652 |
//>
|
653 |
//<copyBuffer
|
654 |
|
655 |
|
656 |
static size_t copyBuffer(char* dest, char* src, const size_t theSizeInBytes)
|
657 |
{
|
658 |
size_t bytes_written = 0;
|
659 |
unsigned int i = 0;
|
660 |
uint16_t* a_dest = NULL;
|
661 |
uint16_t* a_src = NULL;
|
662 |
|
663 |
if ((src != NULL) && dest != NULL)
|
664 |
{
|
665 |
// copy for one channel (mono)?
|
666 |
if(out_channels==1)
|
667 |
{
|
668 |
SHOW("copyBuffer > 1 channel > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
|
669 |
memcpy(dest, src, theSizeInBytes);
|
670 |
bytes_written = theSizeInBytes;
|
671 |
}
|
672 |
else // copy for 2 channels (stereo)
|
673 |
{
|
674 |
SHOW("copyBuffer > 2 channels > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
|
675 |
i = 0;
|
676 |
a_dest = (uint16_t* )dest;
|
677 |
a_src = (uint16_t* )src;
|
678 |
|
679 |
for(i=0; i<theSizeInBytes/2; i++)
|
680 |
{
|
681 |
a_dest[2*i] = a_src[i];
|
682 |
a_dest[2*i+1] = a_src[i];
|
683 |
}
|
684 |
bytes_written = 2*theSizeInBytes;
|
685 |
} // end if(out_channels==1)
|
686 |
} // end if ((src != NULL) && dest != NULL)
|
687 |
|
688 |
return bytes_written;
|
689 |
}
|
690 |
|
691 |
//>
|
692 |
//<wave_write
|
693 |
|
694 |
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize)
|
695 |
{
|
696 |
ENTER("wave_write");
|
697 |
size_t bytes_written = 0;
|
698 |
// space in ringbuffer for the sample needed: 1x mono channel but 2x for 1 stereo channel
|
699 |
size_t bytes_to_write = (out_channels==1) ? theSize : theSize*2;
|
700 |
my_stream_could_start = 0;
|
701 |
|
702 |
if(pa_stream == NULL)
|
703 |
{
|
704 |
SHOW_TIME("wave_write > wave_open_sound\n");
|
705 |
if (0 != wave_open_sound())
|
706 |
{
|
707 |
SHOW_TIME("wave_write > wave_open_sound fails!");
|
708 |
return 0;
|
709 |
}
|
710 |
my_stream_could_start=1;
|
711 |
}
|
712 |
else if (!wave_is_busy(NULL))
|
713 |
{
|
714 |
my_stream_could_start = 1;
|
715 |
}
|
716 |
assert(BUFFER_LENGTH >= bytes_to_write);
|
717 |
|
718 |
if (myWrite >= myBuffer + BUFFER_LENGTH)
|
719 |
{
|
720 |
myWrite = myBuffer;
|
721 |
} // end if (myWrite >= myBuffer + BUFFER_LENGTH)
|
722 |
|
723 |
size_t aTotalFreeMem=0;
|
724 |
char* aRead = myRead;
|
725 |
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
|
726 |
|
727 |
while (1)
|
728 |
{
|
729 |
if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled()))
|
730 |
{
|
731 |
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
|
732 |
return 0;
|
733 |
}
|
734 |
|
735 |
aRead = myRead;
|
736 |
|
737 |
// write pointer is before read pointer?
|
738 |
if (myWrite >= aRead)
|
739 |
{
|
740 |
aTotalFreeMem = aRead + BUFFER_LENGTH - myWrite;
|
741 |
}
|
742 |
else // read pointer is before write pointer!
|
743 |
{
|
744 |
aTotalFreeMem = aRead - myWrite;
|
745 |
} // end if (myWrite >= aRead)
|
746 |
|
747 |
if (aTotalFreeMem>1)
|
748 |
{
|
749 |
// -1 because myWrite must be different of aRead
|
750 |
// otherwise buffer would be considered as empty
|
751 |
aTotalFreeMem -= 1;
|
752 |
} // end if (aTotalFreeMem>1)
|
753 |
|
754 |
if (aTotalFreeMem >= bytes_to_write)
|
755 |
{
|
756 |
break;
|
757 |
} // end if (aTotalFreeMem >= bytes_to_write)
|
758 |
|
759 |
//SHOW_TIME("wave_write > wait");
|
760 |
SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem);
|
761 |
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
|
762 |
usleep(10000);
|
763 |
} // end while (1)
|
764 |
|
765 |
aRead = myRead;
|
766 |
|
767 |
// write pointer is ahead the read pointer?
|
768 |
if (myWrite >= aRead)
|
769 |
{
|
770 |
SHOW_TIME("wave_write > myWrite >= aRead");
|
771 |
// determine remaining free memory to the end of the ringbuffer
|
772 |
size_t aFreeMem = myBuffer + BUFFER_LENGTH - myWrite;
|
773 |
// is enough linear space available (regardless 1 or 2 channels)?
|
774 |
if (aFreeMem >= bytes_to_write)
|
775 |
{
|
776 |
// copy direct - no wrap around at end of ringbuffer needed
|
777 |
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
|
778 |
}
|
779 |
else // not enough linear space available
|
780 |
{
|
781 |
// 2 channels (stereo)?
|
782 |
if (out_channels == 2)
|
783 |
{
|
784 |
// copy with wrap around at the end of ringbuffer
|
785 |
copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem/2);
|
786 |
myWrite = myBuffer;
|
787 |
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem/2, theSize - aFreeMem/2);
|
788 |
}
|
789 |
else // 1 channel (mono)
|
790 |
{
|
791 |
// copy with wrap around at the end of ringbuffer
|
792 |
copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem);
|
793 |
myWrite = myBuffer;
|
794 |
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem);
|
795 |
} // end if (out_channels == 2)
|
796 |
} // end if (aFreeMem >= bytes_to_write)
|
797 |
} // if (myWrite >= aRead)
|
798 |
else // read pointer is ahead the write pointer
|
799 |
{
|
800 |
SHOW_TIME("wave_write > myWrite <= aRead");
|
801 |
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
|
802 |
} // end if (myWrite >= aRead)
|
803 |
|
804 |
bytes_written = bytes_to_write;
|
805 |
myWritePosition += theSize/sizeof(uint16_t); // add number of samples
|
806 |
|
807 |
if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
|
808 |
{
|
809 |
start_stream();
|
810 |
} // end if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
|
811 |
|
812 |
SHOW_TIME("wave_write > LEAVE");
|
813 |
|
814 |
return bytes_written;
|
815 |
}
|
816 |
|
817 |
//>
|
818 |
//<wave_close
|
819 |
|
820 |
int wave_close(void* theHandler)
|
821 |
{
|
822 |
SHOW_TIME("wave_close > ENTER");
|
823 |
|
824 |
static int aStopStreamCount = 0;
|
825 |
|
826 |
#if (USE_PORTAUDIO == 19)
|
827 |
if( pa_stream == NULL )
|
828 |
{
|
829 |
SHOW_TIME("wave_close > LEAVE (NULL stream)");
|
830 |
return 0;
|
831 |
}
|
832 |
|
833 |
if( Pa_IsStreamStopped( pa_stream ) )
|
834 |
{
|
835 |
SHOW_TIME("wave_close > LEAVE (stopped)");
|
836 |
return 0;
|
837 |
}
|
838 |
#else
|
839 |
if( pa_stream == NULL )
|
840 |
{
|
841 |
SHOW_TIME("wave_close > LEAVE (NULL stream)");
|
842 |
return 0;
|
843 |
}
|
844 |
|
845 |
if( Pa_StreamActive( pa_stream ) == false && mInCallbackFinishedState == false )
|
846 |
{
|
847 |
SHOW_TIME("wave_close > LEAVE (not active)");
|
848 |
return 0;
|
849 |
}
|
850 |
#endif
|
851 |
|
852 |
// Avoid race condition by making sure this function only
|
853 |
// gets called once at a time
|
854 |
aStopStreamCount++;
|
855 |
if (aStopStreamCount != 1)
|
856 |
{
|
857 |
SHOW_TIME("wave_close > LEAVE (stopStreamCount)");
|
858 |
return 0;
|
859 |
}
|
860 |
|
861 |
// Comment from Audacity-1.2.4b adapted to the eSpeak context.
|
862 |
//
|
863 |
// We got here in one of two ways:
|
864 |
//
|
865 |
// 1. The calling program calls the espeak_Cancel function and we
|
866 |
// therefore want to stop as quickly as possible.
|
867 |
// So we use AbortStream(). If this is
|
868 |
// the case the portaudio stream is still in the Running state
|
869 |
// (see PortAudio state machine docs).
|
870 |
//
|
871 |
// 2. The callback told PortAudio to stop the stream since it had
|
872 |
// reached the end of the selection.
|
873 |
// The event polling thread discovered this by noticing that
|
874 |
// wave_is_busy() returned false.
|
875 |
// wave_is_busy() (which calls Pa_GetStreamActive()) will not return
|
876 |
// false until all buffers have finished playing, so we can call
|
877 |
// AbortStream without losing any samples. If this is the case
|
878 |
// we are in the "callback finished state" (see PortAudio state
|
879 |
// machine docs).
|
880 |
//
|
881 |
// The moral of the story: We can call AbortStream safely, without
|
882 |
// losing samples.
|
883 |
//
|
884 |
// DMM: This doesn't seem to be true; it seems to be necessary to
|
885 |
// call StopStream if the callback brought us here, and AbortStream
|
886 |
// if the user brought us here.
|
887 |
//
|
888 |
|
889 |
#if (USE_PORTAUDIO == 19)
|
890 |
if (pa_stream)
|
891 |
{
|
892 |
Pa_AbortStream( pa_stream );
|
893 |
SHOW_TIME("wave_close > Pa_AbortStream (end)");
|
894 |
|
895 |
Pa_CloseStream( pa_stream );
|
896 |
SHOW_TIME("wave_close > Pa_CloseStream (end)");
|
897 |
pa_stream = NULL;
|
898 |
mInCallbackFinishedState = false;
|
899 |
}
|
900 |
#else
|
901 |
if (pa_stream)
|
902 |
{
|
903 |
if (mInCallbackFinishedState)
|
904 |
{
|
905 |
Pa_StopStream( pa_stream );
|
906 |
SHOW_TIME("wave_close > Pa_StopStream (end)");
|
907 |
}
|
908 |
else
|
909 |
{
|
910 |
Pa_AbortStream( pa_stream );
|
911 |
SHOW_TIME("wave_close > Pa_AbortStream (end)");
|
912 |
}
|
913 |
Pa_CloseStream( pa_stream );
|
914 |
SHOW_TIME("wave_close > Pa_CloseStream (end)");
|
915 |
|
916 |
pa_stream = NULL;
|
917 |
mInCallbackFinishedState = false;
|
918 |
}
|
919 |
#endif
|
920 |
init_buffer();
|
921 |
|
922 |
aStopStreamCount = 0; // last action
|
923 |
SHOW_TIME("wave_close > LEAVE");
|
924 |
return 0;
|
925 |
}
|
926 |
|
927 |
// int wave_close(void* theHandler)
|
928 |
// {
|
929 |
// ENTER("wave_close");
|
930 |
|
931 |
// if(pa_stream != NULL)
|
932 |
// {
|
933 |
// PaError err = Pa_AbortStream(pa_stream);
|
934 |
// SHOW_TIME("wave_close > Pa_AbortStream (end)");
|
935 |
// SHOW("wave_close Pa_AbortStream > err=%d\n",err);
|
936 |
// while(1)
|
937 |
// {
|
938 |
// PaError active;
|
939 |
// #if USE_PORTAUDIO == 18
|
940 |
// active = Pa_StreamActive(pa_stream);
|
941 |
// #else
|
942 |
// active = Pa_IsStreamActive(pa_stream);
|
943 |
// #endif
|
944 |
// if (active != 1)
|
945 |
// {
|
946 |
// break;
|
947 |
// }
|
948 |
// SHOW("wave_close > active=%d\n",err);
|
949 |
// usleep(10000); /* sleep until playback has finished */
|
950 |
// }
|
951 |
// err = Pa_CloseStream( pa_stream );
|
952 |
// SHOW_TIME("wave_close > Pa_CloseStream (end)");
|
953 |
// SHOW("wave_close Pa_CloseStream > err=%d\n",err);
|
954 |
// pa_stream = NULL;
|
955 |
// init_buffer();
|
956 |
// }
|
957 |
// return 0;
|
958 |
// }
|
959 |
|
960 |
//>
|
961 |
//<wave_is_busy
|
962 |
|
963 |
int wave_is_busy(void* theHandler)
|
964 |
{
|
965 |
PaError active=0;
|
966 |
|
967 |
SHOW_TIME("wave_is_busy");
|
968 |
|
969 |
if (pa_stream)
|
970 |
{
|
971 |
#if USE_PORTAUDIO == 18
|
972 |
active = Pa_StreamActive(pa_stream)
|
973 |
&& (mInCallbackFinishedState == false);
|
974 |
#else
|
975 |
active = Pa_IsStreamActive(pa_stream)
|
976 |
&& (mInCallbackFinishedState == false);
|
977 |
#endif
|
978 |
}
|
979 |
|
980 |
SHOW("wave_is_busy: %d\n",active);
|
981 |
|
982 |
|
983 |
return (active==1);
|
984 |
}
|
985 |
|
986 |
//>
|
987 |
//<wave_terminate
|
988 |
|
989 |
void wave_terminate()
|
990 |
{
|
991 |
ENTER("wave_terminate");
|
992 |
|
993 |
Pa_Terminate();
|
994 |
|
995 |
}
|
996 |
|
997 |
//>
|
998 |
//<wave_get_read_position, wave_get_write_position, wave_get_remaining_time
|
999 |
|
1000 |
uint32_t wave_get_read_position(void* theHandler)
|
1001 |
{
|
1002 |
SHOW("wave_get_read_position > myReadPosition=%u\n", myReadPosition);
|
1003 |
return myReadPosition;
|
1004 |
}
|
1005 |
|
1006 |
uint32_t wave_get_write_position(void* theHandler)
|
1007 |
{
|
1008 |
SHOW("wave_get_write_position > myWritePosition=%u\n", myWritePosition);
|
1009 |
return myWritePosition;
|
1010 |
}
|
1011 |
|
1012 |
int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
1013 |
{
|
1014 |
double a_time=0;
|
1015 |
|
1016 |
if (!time || !pa_stream)
|
1017 |
{
|
1018 |
SHOW("event get_remaining_time> %s\n","audio device not available");
|
1019 |
return -1;
|
1020 |
}
|
1021 |
|
1022 |
if (sample > myReadPosition)
|
1023 |
{
|
1024 |
// TBD: take in account time suplied by portaudio V18 API
|
1025 |
a_time = sample - myReadPosition;
|
1026 |
a_time = 0.5 + (a_time * 1000.0) / SAMPLE_RATE;
|
1027 |
}
|
1028 |
else
|
1029 |
{
|
1030 |
a_time = 0;
|
1031 |
}
|
1032 |
|
1033 |
SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time);
|
1034 |
|
1035 |
*time = (uint32_t)a_time;
|
1036 |
|
1037 |
return 0;
|
1038 |
}
|
1039 |
|
1040 |
//>
|
1041 |
//<wave_test_get_write_buffer
|
1042 |
|
1043 |
void *wave_test_get_write_buffer()
|
1044 |
{
|
1045 |
return myWrite;
|
1046 |
}
|
1047 |
|
1048 |
|
1049 |
#else
|
1050 |
// notdef USE_PORTAUDIO
|
1051 |
|
1052 |
|
1053 |
void wave_init() {}
|
1054 |
void* wave_open(const char* the_api) {return (void *)1;}
|
1055 |
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
|
1056 |
int wave_close(void* theHandler) {return 0;}
|
1057 |
int wave_is_busy(void* theHandler) {return 0;}
|
1058 |
void wave_terminate() {}
|
1059 |
uint32_t wave_get_read_position(void* theHandler) {return 0;}
|
1060 |
uint32_t wave_get_write_position(void* theHandler) {return 0;}
|
1061 |
void wave_flush(void* theHandler) {}
|
1062 |
typedef int (t_wave_callback)(void);
|
1063 |
void wave_set_callback_is_output_enabled(t_wave_callback* cb) {}
|
1064 |
extern void* wave_test_get_write_buffer() {return NULL;}
|
1065 |
|
1066 |
int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
1067 |
{
|
1068 |
if (!time) return(-1);
|
1069 |
*time = (uint32_t)0;
|
1070 |
return 0;
|
1071 |
}
|
1072 |
|
1073 |
#endif // of USE_PORTAUDIO
|
1074 |
|
1075 |
//>
|
1076 |
//<clock_gettime2, add_time_in_ms
|
1077 |
|
1078 |
void clock_gettime2(struct timespec *ts)
|
1079 |
{
|
1080 |
struct timeval tv;
|
1081 |
|
1082 |
if (!ts)
|
1083 |
{
|
1084 |
return;
|
1085 |
}
|
1086 |
|
1087 |
assert (gettimeofday(&tv, NULL) != -1);
|
1088 |
ts->tv_sec = tv.tv_sec;
|
1089 |
ts->tv_nsec = tv.tv_usec*1000;
|
1090 |
}
|
1091 |
|
1092 |
void add_time_in_ms(struct timespec *ts, int time_in_ms)
|
1093 |
{
|
1094 |
if (!ts)
|
1095 |
{
|
1096 |
return;
|
1097 |
}
|
1098 |
|
1099 |
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms;
|
1100 |
while(t_ns >= ONE_BILLION)
|
1101 |
{
|
1102 |
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
|
1103 |
ts->tv_sec += 1;
|
1104 |
t_ns -= ONE_BILLION;
|
1105 |
}
|
1106 |
ts->tv_nsec = (long int)t_ns;
|
1107 |
}
|
1108 |
|
1109 |
|
1110 |
#endif // USE_ASYNC
|
1111 |
|
1112 |
//>
|