1 |
/***************************************************************************
|
2 |
* Copyright (C) 2007, Gilles Casse <gcasse@oralux.org> *
|
3 |
* eSpeak driver for PulseAudio *
|
4 |
* based on the XMMS PulseAudio Plugin *
|
5 |
* *
|
6 |
* This program is free software; you can redistribute it and/or modify *
|
7 |
* it under the terms of the GNU General Public License as published by *
|
8 |
* the Free Software Foundation; either version 3 of the License, or *
|
9 |
* (at your option) any later version. *
|
10 |
* *
|
11 |
* This program is distributed in the hope that it will be useful, *
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
14 |
* GNU General Public License for more details. *
|
15 |
* *
|
16 |
* You should have received a copy of the GNU General Public License *
|
17 |
* along with this program; if not, write to the *
|
18 |
* Free Software Foundation, Inc., *
|
19 |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
20 |
***************************************************************************/
|
21 |
// TBD:
|
22 |
// * ARCH_BIG
|
23 |
// * uint64? a_timing_info.read_index
|
24 |
// * prebuf,... size?
|
25 |
// * 0.9.6: pb pulse_free using tlength=8820 (max size never returned -> tlength=10000 ok, but higher drain).
|
26 |
//
|
27 |
#include "speech.h"
|
28 |
|
29 |
#ifdef USE_ASYNC
|
30 |
// This source file is only used for asynchronious modes
|
31 |
|
32 |
#include <stdio.h>
|
33 |
#include <string.h>
|
34 |
#include <stdlib.h>
|
35 |
#include <math.h>
|
36 |
#include <assert.h>
|
37 |
#include <sys/time.h>
|
38 |
#include <time.h>
|
39 |
#include <pulse/pulseaudio.h>
|
40 |
#include <pthread.h>
|
41 |
|
42 |
#ifndef PLATFORM_WINDOWS
|
43 |
#include <unistd.h>
|
44 |
#endif
|
45 |
#include "wave.h"
|
46 |
#include "debug.h"
|
47 |
|
48 |
//<Definitions
|
49 |
|
50 |
enum {ONE_BILLION=1000000000};
|
51 |
|
52 |
enum {
|
53 |
// /* 100ms.
|
54 |
// If a greater value is set (several seconds),
|
55 |
// please update _pulse_timeout_start accordingly */
|
56 |
// PULSE_TIMEOUT_IN_USEC = 100000,
|
57 |
|
58 |
/* return value */
|
59 |
PULSE_OK = 0,
|
60 |
PULSE_ERROR = -1,
|
61 |
PULSE_NO_CONNECTION = -2
|
62 |
};
|
63 |
|
64 |
#ifdef USE_PULSEAUDIO
|
65 |
|
66 |
static t_wave_callback* my_callback_is_output_enabled=NULL;
|
67 |
|
68 |
#define SAMPLE_RATE 22050
|
69 |
#define ESPEAK_FORMAT PA_SAMPLE_S16LE
|
70 |
#define ESPEAK_CHANNEL 1
|
71 |
|
72 |
#define MAXLENGTH 132300
|
73 |
#define TLENGTH 4410
|
74 |
#define PREBUF 2200
|
75 |
#define MINREQ 880
|
76 |
#define FRAGSIZE 0
|
77 |
|
78 |
static pthread_mutex_t pulse_mutex;
|
79 |
|
80 |
static pa_context *context = NULL;
|
81 |
static pa_stream *stream = NULL;
|
82 |
static pa_threaded_mainloop *mainloop = NULL;
|
83 |
|
84 |
static pa_cvolume volume;
|
85 |
static int volume_valid = 0;
|
86 |
|
87 |
static int do_trigger = 0;
|
88 |
static uint64_t written = 0;
|
89 |
static int time_offset_msec = 0;
|
90 |
static int just_flushed = 0;
|
91 |
|
92 |
static int connected = 0;
|
93 |
|
94 |
#define CHECK_DEAD_GOTO(label, warn) do { \
|
95 |
if (!mainloop || \
|
96 |
!context || pa_context_get_state(context) != PA_CONTEXT_READY || \
|
97 |
!stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \
|
98 |
if (warn) \
|
99 |
SHOW("Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \
|
100 |
goto label; \
|
101 |
} \
|
102 |
} while(0);
|
103 |
|
104 |
#define CHECK_CONNECTED(retval) \
|
105 |
do { \
|
106 |
if (!connected) return retval; \
|
107 |
} while (0);
|
108 |
|
109 |
#define CHECK_CONNECTED_NO_RETVAL(id) \
|
110 |
do { \
|
111 |
if (!connected){ SHOW("CHECK_CONNECTED_NO_RETVAL: !pulse_connected\n", ""); return; } \
|
112 |
} while (0);
|
113 |
|
114 |
//>
|
115 |
|
116 |
|
117 |
// static void display_timing_info(const pa_timing_info* the_time)
|
118 |
// {
|
119 |
// const struct timeval *tv=&(the_time->timestamp);
|
120 |
|
121 |
// SHOW_TIME("ti>");
|
122 |
// SHOW("ti> timestamp=%03d.%03dms\n",(int)(tv->tv_sec%1000), (int)(tv->tv_usec/1000));
|
123 |
// SHOW("ti> synchronized_clocks=%d\n",the_time->synchronized_clocks);
|
124 |
// SHOW("ti> sink_usec=%ld\n",the_time->sink_usec);
|
125 |
// SHOW("ti> source_usec=%ld\n",the_time->source_usec);
|
126 |
// SHOW("ti> transport=%ld\n",the_time->transport_usec);
|
127 |
// SHOW("ti> playing=%d\n",the_time->playing);
|
128 |
// SHOW("ti> write_index_corrupt=%d\n",the_time->write_index_corrupt);
|
129 |
// SHOW("ti> write_index=0x%lx\n",the_time->write_index);
|
130 |
// SHOW("ti> read_index_corrupt=%d\n",the_time->read_index_corrupt);
|
131 |
// SHOW("ti> read_index=0x%lx\n",the_time->read_index);
|
132 |
// }
|
133 |
|
134 |
|
135 |
static void info_cb(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
|
136 |
ENTER(__FUNCTION__);
|
137 |
assert(c);
|
138 |
|
139 |
if (!i)
|
140 |
return;
|
141 |
|
142 |
volume = i->volume;
|
143 |
volume_valid = 1;
|
144 |
}
|
145 |
|
146 |
static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
|
147 |
pa_operation *o;
|
148 |
ENTER(__FUNCTION__);
|
149 |
|
150 |
assert(c);
|
151 |
|
152 |
if (!stream ||
|
153 |
index != pa_stream_get_index(stream) ||
|
154 |
(t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
|
155 |
t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)))
|
156 |
return;
|
157 |
|
158 |
if (!(o = pa_context_get_sink_input_info(c, index, info_cb, NULL))) {
|
159 |
SHOW("pa_context_get_sink_input_info() failed: %s\n", pa_strerror(pa_context_errno(c)));
|
160 |
return;
|
161 |
}
|
162 |
|
163 |
pa_operation_unref(o);
|
164 |
}
|
165 |
|
166 |
static void context_state_cb(pa_context *c, void *userdata) {
|
167 |
ENTER(__FUNCTION__);
|
168 |
assert(c);
|
169 |
|
170 |
switch (pa_context_get_state(c)) {
|
171 |
case PA_CONTEXT_READY:
|
172 |
case PA_CONTEXT_TERMINATED:
|
173 |
case PA_CONTEXT_FAILED:
|
174 |
pa_threaded_mainloop_signal(mainloop, 0);
|
175 |
break;
|
176 |
|
177 |
case PA_CONTEXT_UNCONNECTED:
|
178 |
case PA_CONTEXT_CONNECTING:
|
179 |
case PA_CONTEXT_AUTHORIZING:
|
180 |
case PA_CONTEXT_SETTING_NAME:
|
181 |
break;
|
182 |
}
|
183 |
}
|
184 |
|
185 |
static void stream_state_cb(pa_stream *s, void * userdata) {
|
186 |
ENTER(__FUNCTION__);
|
187 |
assert(s);
|
188 |
|
189 |
switch (pa_stream_get_state(s)) {
|
190 |
|
191 |
case PA_STREAM_READY:
|
192 |
case PA_STREAM_FAILED:
|
193 |
case PA_STREAM_TERMINATED:
|
194 |
pa_threaded_mainloop_signal(mainloop, 0);
|
195 |
break;
|
196 |
|
197 |
case PA_STREAM_UNCONNECTED:
|
198 |
case PA_STREAM_CREATING:
|
199 |
break;
|
200 |
}
|
201 |
}
|
202 |
|
203 |
static void stream_success_cb(pa_stream *s, int success, void *userdata) {
|
204 |
ENTER(__FUNCTION__);
|
205 |
assert(s);
|
206 |
|
207 |
if (userdata)
|
208 |
*(int*) userdata = success;
|
209 |
|
210 |
pa_threaded_mainloop_signal(mainloop, 0);
|
211 |
}
|
212 |
|
213 |
static void context_success_cb(pa_context *c, int success, void *userdata) {
|
214 |
ENTER(__FUNCTION__);
|
215 |
assert(c);
|
216 |
|
217 |
if (userdata)
|
218 |
*(int*) userdata = success;
|
219 |
|
220 |
pa_threaded_mainloop_signal(mainloop, 0);
|
221 |
}
|
222 |
|
223 |
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
|
224 |
ENTER(__FUNCTION__);
|
225 |
assert(s);
|
226 |
|
227 |
pa_threaded_mainloop_signal(mainloop, 0);
|
228 |
}
|
229 |
|
230 |
static void stream_latency_update_cb(pa_stream *s, void *userdata) {
|
231 |
// ENTER(__FUNCTION__);
|
232 |
assert(s);
|
233 |
|
234 |
pa_threaded_mainloop_signal(mainloop, 0);
|
235 |
}
|
236 |
|
237 |
static int pulse_free(void) {
|
238 |
ENTER(__FUNCTION__);
|
239 |
size_t l = 0;
|
240 |
pa_operation *o = NULL;
|
241 |
|
242 |
CHECK_CONNECTED(0);
|
243 |
|
244 |
SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop_lock");
|
245 |
pa_threaded_mainloop_lock(mainloop);
|
246 |
CHECK_DEAD_GOTO(fail, 1);
|
247 |
|
248 |
if ((l = pa_stream_writable_size(stream)) == (size_t) -1) {
|
249 |
SHOW("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context)));
|
250 |
l = 0;
|
251 |
goto fail;
|
252 |
}
|
253 |
|
254 |
SHOW("pulse_free: %s (ret=%d)\n", "pa_stream_writable_size", l);
|
255 |
|
256 |
/* If this function is called twice with no pulse_write() call in
|
257 |
* between this means we should trigger the playback */
|
258 |
if (do_trigger) {
|
259 |
int success = 0;
|
260 |
|
261 |
SHOW("pulse_free: %s (call)\n", "pa_stream_trigger");
|
262 |
if (!(o = pa_stream_trigger(stream, stream_success_cb, &success))) {
|
263 |
SHOW("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
|
264 |
goto fail;
|
265 |
}
|
266 |
|
267 |
SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop");
|
268 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
|
269 |
CHECK_DEAD_GOTO(fail, 1);
|
270 |
pa_threaded_mainloop_wait(mainloop);
|
271 |
}
|
272 |
SHOW("pulse_free: %s (ret)\n", "pa_threaded_main_loop");
|
273 |
|
274 |
if (!success)
|
275 |
SHOW("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
|
276 |
}
|
277 |
|
278 |
fail:
|
279 |
SHOW("pulse_free: %s (call)\n", "pa_operation_unref");
|
280 |
if (o)
|
281 |
pa_operation_unref(o);
|
282 |
|
283 |
SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop_unlock");
|
284 |
pa_threaded_mainloop_unlock(mainloop);
|
285 |
|
286 |
do_trigger = !!l;
|
287 |
SHOW("pulse_free: %d (ret)\n", (int)l);
|
288 |
return (int) l;
|
289 |
}
|
290 |
|
291 |
static int pulse_playing(const pa_timing_info *the_timing_info) {
|
292 |
ENTER(__FUNCTION__);
|
293 |
int r = 0;
|
294 |
const pa_timing_info *i;
|
295 |
|
296 |
assert(the_timing_info);
|
297 |
|
298 |
CHECK_CONNECTED(0);
|
299 |
|
300 |
pa_threaded_mainloop_lock(mainloop);
|
301 |
|
302 |
for (;;) {
|
303 |
CHECK_DEAD_GOTO(fail, 1);
|
304 |
|
305 |
if ((i = pa_stream_get_timing_info(stream)))
|
306 |
{
|
307 |
break;
|
308 |
}
|
309 |
if (pa_context_errno(context) != PA_ERR_NODATA) {
|
310 |
SHOW("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context)));
|
311 |
goto fail;
|
312 |
}
|
313 |
|
314 |
pa_threaded_mainloop_wait(mainloop);
|
315 |
}
|
316 |
|
317 |
r = i->playing;
|
318 |
memcpy((void*)the_timing_info, (void*)i, sizeof(pa_timing_info));
|
319 |
|
320 |
// display_timing_info(i);
|
321 |
|
322 |
fail:
|
323 |
pa_threaded_mainloop_unlock(mainloop);
|
324 |
|
325 |
return r;
|
326 |
}
|
327 |
|
328 |
|
329 |
// static void pulse_flush(int time) {
|
330 |
// ENTER(__FUNCTION__);
|
331 |
// pa_operation *o = NULL;
|
332 |
// int success = 0;
|
333 |
|
334 |
// CHECK_CONNECTED();
|
335 |
|
336 |
// pa_threaded_mainloop_lock(mainloop);
|
337 |
// CHECK_DEAD_GOTO(fail, 1);
|
338 |
|
339 |
// if (!(o = pa_stream_flush(stream, stream_success_cb, &success))) {
|
340 |
// SHOW("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context)));
|
341 |
// goto fail;
|
342 |
// }
|
343 |
|
344 |
// while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
|
345 |
// CHECK_DEAD_GOTO(fail, 1);
|
346 |
// pa_threaded_mainloop_wait(mainloop);
|
347 |
// }
|
348 |
|
349 |
// if (!success)
|
350 |
// SHOW("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context)));
|
351 |
|
352 |
// written = (uint64_t) (((double) time * pa_bytes_per_second(pa_stream_get_sample_spec(stream))) / 1000);
|
353 |
// just_flushed = 1;
|
354 |
// time_offset_msec = time;
|
355 |
|
356 |
// fail:
|
357 |
// if (o)
|
358 |
// pa_operation_unref(o);
|
359 |
|
360 |
// pa_threaded_mainloop_unlock(mainloop);
|
361 |
// }
|
362 |
|
363 |
|
364 |
static void pulse_write(void* ptr, int length) {
|
365 |
ENTER(__FUNCTION__);
|
366 |
|
367 |
|
368 |
SHOW("pulse_write > length=%d\n", length);
|
369 |
|
370 |
CHECK_CONNECTED();
|
371 |
|
372 |
pa_threaded_mainloop_lock(mainloop);
|
373 |
CHECK_DEAD_GOTO(fail, 1);
|
374 |
|
375 |
if (pa_stream_write(stream, ptr, length, NULL, PA_SEEK_RELATIVE, (pa_seek_mode_t)0) < 0) {
|
376 |
SHOW("pa_stream_write() failed: %s", pa_strerror(pa_context_errno(context)));
|
377 |
goto fail;
|
378 |
}
|
379 |
|
380 |
do_trigger = 0;
|
381 |
written += length;
|
382 |
|
383 |
fail:
|
384 |
|
385 |
pa_threaded_mainloop_unlock(mainloop);
|
386 |
}
|
387 |
|
388 |
static int drain(void) {
|
389 |
pa_operation *o = NULL;
|
390 |
int success = 0;
|
391 |
int ret = PULSE_ERROR;
|
392 |
|
393 |
ENTER(__FUNCTION__);
|
394 |
|
395 |
CHECK_CONNECTED(ret);
|
396 |
|
397 |
pa_threaded_mainloop_lock(mainloop);
|
398 |
CHECK_DEAD_GOTO(fail, 0);
|
399 |
|
400 |
SHOW_TIME("pa_stream_drain (call)");
|
401 |
if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) {
|
402 |
SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
|
403 |
goto fail;
|
404 |
}
|
405 |
|
406 |
SHOW_TIME("pa_threaded_mainloop_wait (call)");
|
407 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
|
408 |
CHECK_DEAD_GOTO(fail, 1);
|
409 |
pa_threaded_mainloop_wait(mainloop);
|
410 |
}
|
411 |
SHOW_TIME("pa_threaded_mainloop_wait (ret)");
|
412 |
|
413 |
if (!success) {
|
414 |
SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
|
415 |
}
|
416 |
else {
|
417 |
ret = PULSE_OK;
|
418 |
}
|
419 |
|
420 |
fail:
|
421 |
SHOW_TIME("pa_operation_unref (call)");
|
422 |
if (o)
|
423 |
pa_operation_unref(o);
|
424 |
|
425 |
pa_threaded_mainloop_unlock(mainloop);
|
426 |
SHOW_TIME("drain (ret)");
|
427 |
|
428 |
return ret;
|
429 |
}
|
430 |
|
431 |
|
432 |
static void pulse_close(void) {
|
433 |
|
434 |
ENTER(__FUNCTION__);
|
435 |
|
436 |
drain();
|
437 |
|
438 |
connected = 0;
|
439 |
|
440 |
if (mainloop)
|
441 |
pa_threaded_mainloop_stop(mainloop);
|
442 |
|
443 |
connected = 0;
|
444 |
|
445 |
if (context) {
|
446 |
SHOW_TIME("pa_context_disconnect (call)");
|
447 |
pa_context_disconnect(context);
|
448 |
pa_context_unref(context);
|
449 |
context = NULL;
|
450 |
}
|
451 |
|
452 |
if (mainloop) {
|
453 |
SHOW_TIME("pa_threaded_mainloop_free (call)");
|
454 |
pa_threaded_mainloop_free(mainloop);
|
455 |
mainloop = NULL;
|
456 |
}
|
457 |
SHOW_TIME("pulse_close (ret)");
|
458 |
|
459 |
}
|
460 |
|
461 |
|
462 |
static int pulse_open()
|
463 |
{
|
464 |
ENTER(__FUNCTION__);
|
465 |
pa_sample_spec ss;
|
466 |
pa_operation *o = NULL;
|
467 |
int success;
|
468 |
int ret = PULSE_ERROR;
|
469 |
|
470 |
assert(!mainloop);
|
471 |
assert(!context);
|
472 |
assert(!stream);
|
473 |
assert(!connected);
|
474 |
|
475 |
pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL);
|
476 |
|
477 |
ss.format = ESPEAK_FORMAT;
|
478 |
ss.rate = SAMPLE_RATE;
|
479 |
ss.channels = ESPEAK_CHANNEL;
|
480 |
|
481 |
if (!pa_sample_spec_valid(&ss))
|
482 |
return false;
|
483 |
|
484 |
/* if (!volume_valid) { */
|
485 |
pa_cvolume_reset(&volume, ss.channels);
|
486 |
volume_valid = 1;
|
487 |
/* } else if (volume.channels != ss.channels) */
|
488 |
/* pa_cvolume_set(&volume, ss.channels, pa_cvolume_avg(&volume)); */
|
489 |
|
490 |
SHOW_TIME("pa_threaded_mainloop_new (call)");
|
491 |
if (!(mainloop = pa_threaded_mainloop_new())) {
|
492 |
SHOW("Failed to allocate main loop\n","");
|
493 |
goto fail;
|
494 |
}
|
495 |
|
496 |
pa_threaded_mainloop_lock(mainloop);
|
497 |
|
498 |
SHOW_TIME("pa_context_new (call)");
|
499 |
if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) {
|
500 |
SHOW("Failed to allocate context\n","");
|
501 |
goto unlock_and_fail;
|
502 |
}
|
503 |
|
504 |
pa_context_set_state_callback(context, context_state_cb, NULL);
|
505 |
pa_context_set_subscribe_callback(context, subscribe_cb, NULL);
|
506 |
|
507 |
SHOW_TIME("pa_context_connect (call)");
|
508 |
if (pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL) < 0) {
|
509 |
SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
|
510 |
ret = PULSE_NO_CONNECTION;
|
511 |
goto unlock_and_fail;
|
512 |
}
|
513 |
|
514 |
SHOW_TIME("pa_threaded_mainloop_start (call)");
|
515 |
if (pa_threaded_mainloop_start(mainloop) < 0) {
|
516 |
SHOW("Failed to start main loop","");
|
517 |
goto unlock_and_fail;
|
518 |
}
|
519 |
|
520 |
/* Wait until the context is ready */
|
521 |
SHOW_TIME("pa_threaded_mainloop_wait");
|
522 |
pa_threaded_mainloop_wait(mainloop);
|
523 |
|
524 |
if (pa_context_get_state(context) != PA_CONTEXT_READY) {
|
525 |
SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
|
526 |
ret = PULSE_NO_CONNECTION;
|
527 |
if (mainloop)
|
528 |
pa_threaded_mainloop_stop(mainloop);
|
529 |
goto unlock_and_fail;
|
530 |
}
|
531 |
|
532 |
SHOW_TIME("pa_stream_new");
|
533 |
if (!(stream = pa_stream_new(context, "unknown", &ss, NULL))) {
|
534 |
SHOW("Failed to create stream: %s", pa_strerror(pa_context_errno(context)));
|
535 |
goto unlock_and_fail;
|
536 |
}
|
537 |
|
538 |
pa_stream_set_state_callback(stream, stream_state_cb, NULL);
|
539 |
pa_stream_set_write_callback(stream, stream_request_cb, NULL);
|
540 |
pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);
|
541 |
|
542 |
|
543 |
|
544 |
pa_buffer_attr a_attr;
|
545 |
|
546 |
a_attr.maxlength = MAXLENGTH;
|
547 |
a_attr.tlength = TLENGTH;
|
548 |
a_attr.prebuf = PREBUF;
|
549 |
a_attr.minreq = MINREQ;
|
550 |
a_attr.fragsize = 0;
|
551 |
|
552 |
SHOW_TIME("pa_connect_playback");
|
553 |
if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), &volume, NULL) < 0) {
|
554 |
SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
|
555 |
goto unlock_and_fail;
|
556 |
}
|
557 |
|
558 |
/* Wait until the stream is ready */
|
559 |
SHOW_TIME("pa_threaded_mainloop_wait");
|
560 |
pa_threaded_mainloop_wait(mainloop);
|
561 |
|
562 |
if (pa_stream_get_state(stream) != PA_STREAM_READY) {
|
563 |
SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
|
564 |
goto unlock_and_fail;
|
565 |
}
|
566 |
|
567 |
/* Now subscribe to events */
|
568 |
SHOW_TIME("pa_context_subscribe");
|
569 |
if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) {
|
570 |
SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
|
571 |
goto unlock_and_fail;
|
572 |
}
|
573 |
|
574 |
success = 0;
|
575 |
SHOW_TIME("pa_threaded_mainloop_wait");
|
576 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
|
577 |
CHECK_DEAD_GOTO(fail, 1);
|
578 |
pa_threaded_mainloop_wait(mainloop);
|
579 |
}
|
580 |
|
581 |
if (!success) {
|
582 |
SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
|
583 |
goto unlock_and_fail;
|
584 |
}
|
585 |
|
586 |
pa_operation_unref(o);
|
587 |
|
588 |
/* Now request the initial stream info */
|
589 |
if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_cb, NULL))) {
|
590 |
SHOW("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context)));
|
591 |
goto unlock_and_fail;
|
592 |
}
|
593 |
|
594 |
SHOW_TIME("pa_threaded_mainloop_wait 2");
|
595 |
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
|
596 |
CHECK_DEAD_GOTO(fail, 1);
|
597 |
pa_threaded_mainloop_wait(mainloop);
|
598 |
}
|
599 |
|
600 |
/* if (!volume_valid) { */
|
601 |
/* SHOW("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); */
|
602 |
/* goto unlock_and_fail; */
|
603 |
/* } */
|
604 |
|
605 |
do_trigger = 0;
|
606 |
written = 0;
|
607 |
time_offset_msec = 0;
|
608 |
just_flushed = 0;
|
609 |
connected = 1;
|
610 |
// volume_time_event = NULL;
|
611 |
|
612 |
pa_threaded_mainloop_unlock(mainloop);
|
613 |
SHOW_TIME("pulse_open (ret true)");
|
614 |
|
615 |
// return true;
|
616 |
return PULSE_OK;
|
617 |
|
618 |
|
619 |
unlock_and_fail:
|
620 |
|
621 |
if (o)
|
622 |
pa_operation_unref(o);
|
623 |
|
624 |
pa_threaded_mainloop_unlock(mainloop);
|
625 |
|
626 |
fail:
|
627 |
|
628 |
// pulse_close();
|
629 |
|
630 |
if (ret == PULSE_NO_CONNECTION) {
|
631 |
if (context) {
|
632 |
SHOW_TIME("pa_context_disconnect (call)");
|
633 |
pa_context_disconnect(context);
|
634 |
pa_context_unref(context);
|
635 |
context = NULL;
|
636 |
}
|
637 |
|
638 |
if (mainloop) {
|
639 |
SHOW_TIME("pa_threaded_mainloop_free (call)");
|
640 |
pa_threaded_mainloop_free(mainloop);
|
641 |
mainloop = NULL;
|
642 |
}
|
643 |
}
|
644 |
else {
|
645 |
pulse_close();
|
646 |
}
|
647 |
|
648 |
SHOW_TIME("pulse_open (ret false)");
|
649 |
|
650 |
return ret;
|
651 |
|
652 |
}
|
653 |
|
654 |
void wave_flush(void* theHandler)
|
655 |
{
|
656 |
ENTER("wave_flush");
|
657 |
|
658 |
// if (my_stream_could_start)
|
659 |
// {
|
660 |
// // #define buf 1024
|
661 |
// // static char a_buffer[buf*2];
|
662 |
// // memset(a_buffer,0,buf*2);
|
663 |
// // wave_write(theHandler, a_buffer, buf*2);
|
664 |
// start_stream();
|
665 |
// }
|
666 |
}
|
667 |
|
668 |
|
669 |
|
670 |
//<wave_set_callback_is_output_enabled
|
671 |
|
672 |
void wave_set_callback_is_output_enabled(t_wave_callback* cb)
|
673 |
{
|
674 |
my_callback_is_output_enabled = cb;
|
675 |
}
|
676 |
|
677 |
//>
|
678 |
//<wave_init
|
679 |
|
680 |
void wave_init()
|
681 |
{
|
682 |
ENTER("wave_init");
|
683 |
|
684 |
stream = NULL;
|
685 |
|
686 |
pulse_open();
|
687 |
}
|
688 |
|
689 |
//>
|
690 |
//<wave_open
|
691 |
|
692 |
void* wave_open(const char* the_api)
|
693 |
{
|
694 |
ENTER("wave_open");
|
695 |
return((void*)1);
|
696 |
}
|
697 |
|
698 |
//>
|
699 |
//<wave_write
|
700 |
|
701 |
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize)
|
702 |
{
|
703 |
ENTER("wave_write");
|
704 |
size_t bytes_to_write = theSize;
|
705 |
char* aBuffer=theMono16BitsWaveBuffer;
|
706 |
|
707 |
assert(stream);
|
708 |
|
709 |
size_t aTotalFreeMem=0;
|
710 |
|
711 |
pthread_mutex_lock(&pulse_mutex);
|
712 |
|
713 |
while (1)
|
714 |
{
|
715 |
if (my_callback_is_output_enabled
|
716 |
&& (0==my_callback_is_output_enabled()))
|
717 |
{
|
718 |
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
|
719 |
theSize=0;
|
720 |
goto terminate;
|
721 |
}
|
722 |
|
723 |
aTotalFreeMem = pulse_free();
|
724 |
if (aTotalFreeMem >= bytes_to_write)
|
725 |
{
|
726 |
SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write);
|
727 |
break;
|
728 |
}
|
729 |
|
730 |
// TBD: check if really helpful
|
731 |
if (aTotalFreeMem >= MAXLENGTH*2)
|
732 |
{
|
733 |
aTotalFreeMem = MAXLENGTH*2;
|
734 |
}
|
735 |
|
736 |
SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write);
|
737 |
|
738 |
// 500: threshold for avoiding too many calls to pulse_write
|
739 |
if (aTotalFreeMem>500)
|
740 |
{
|
741 |
pulse_write(aBuffer, aTotalFreeMem);
|
742 |
bytes_to_write -= aTotalFreeMem;
|
743 |
aBuffer += aTotalFreeMem;
|
744 |
}
|
745 |
|
746 |
usleep(10000);
|
747 |
}
|
748 |
|
749 |
pulse_write(aBuffer, bytes_to_write);
|
750 |
|
751 |
terminate:
|
752 |
pthread_mutex_unlock(&pulse_mutex);
|
753 |
SHOW("wave_write: theSize=%d", theSize);
|
754 |
SHOW_TIME("wave_write > LEAVE");
|
755 |
return theSize;
|
756 |
}
|
757 |
|
758 |
//>
|
759 |
//<wave_close
|
760 |
|
761 |
int wave_close(void* theHandler)
|
762 |
{
|
763 |
SHOW_TIME("wave_close > ENTER");
|
764 |
|
765 |
int a_status = pthread_mutex_lock(&pulse_mutex);
|
766 |
if (a_status) {
|
767 |
SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__);
|
768 |
return PULSE_ERROR;
|
769 |
}
|
770 |
|
771 |
drain();
|
772 |
|
773 |
pthread_mutex_unlock(&pulse_mutex);
|
774 |
SHOW_TIME("wave_close (ret)");
|
775 |
|
776 |
return PULSE_OK;
|
777 |
}
|
778 |
|
779 |
//>
|
780 |
//<wave_is_busy
|
781 |
|
782 |
int wave_is_busy(void* theHandler)
|
783 |
{
|
784 |
SHOW_TIME("wave_is_busy");
|
785 |
|
786 |
pa_timing_info a_timing_info;
|
787 |
int active = pulse_playing(&a_timing_info);
|
788 |
SHOW("wave_is_busy: %d\n",active);
|
789 |
return active;
|
790 |
}
|
791 |
|
792 |
//>
|
793 |
//<wave_terminate
|
794 |
|
795 |
void wave_terminate()
|
796 |
{
|
797 |
ENTER("wave_terminate");
|
798 |
|
799 |
// Pa_Terminate();
|
800 |
|
801 |
int a_status;
|
802 |
pthread_mutex_t* a_mutex = NULL;
|
803 |
a_mutex = &pulse_mutex;
|
804 |
a_status = pthread_mutex_lock(a_mutex);
|
805 |
|
806 |
pulse_close();
|
807 |
|
808 |
SHOW_TIME("unlock mutex");
|
809 |
a_status = pthread_mutex_unlock(a_mutex);
|
810 |
pthread_mutex_destroy(a_mutex);
|
811 |
}
|
812 |
|
813 |
//>
|
814 |
//<wave_get_read_position, wave_get_write_position, wave_get_remaining_time
|
815 |
|
816 |
uint32_t wave_get_read_position(void* theHandler)
|
817 |
{
|
818 |
pa_timing_info a_timing_info;
|
819 |
pulse_playing(&a_timing_info);
|
820 |
SHOW("wave_get_read_position > %lx\n", a_timing_info.read_index);
|
821 |
return a_timing_info.read_index;
|
822 |
}
|
823 |
|
824 |
uint32_t wave_get_write_position(void* theHandler)
|
825 |
{
|
826 |
pa_timing_info a_timing_info;
|
827 |
pulse_playing(&a_timing_info);
|
828 |
SHOW("wave_get_read_position > %lx\n", a_timing_info.write_index);
|
829 |
return a_timing_info.write_index;
|
830 |
}
|
831 |
|
832 |
int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
833 |
{
|
834 |
double a_time=0;
|
835 |
|
836 |
if (!time || !stream)
|
837 |
{
|
838 |
SHOW("event get_remaining_time> %s\n","audio device not available");
|
839 |
return -1;
|
840 |
}
|
841 |
|
842 |
pa_timing_info a_timing_info;
|
843 |
pulse_playing(&a_timing_info);
|
844 |
|
845 |
if (sample > a_timing_info.read_index)
|
846 |
{
|
847 |
// TBD: take in account time suplied by portaudio V18 API
|
848 |
a_time = sample - a_timing_info.read_index;
|
849 |
a_time = 0.5 + (a_time * 1000.0) / SAMPLE_RATE;
|
850 |
}
|
851 |
else
|
852 |
{
|
853 |
a_time = 0;
|
854 |
}
|
855 |
|
856 |
SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time);
|
857 |
|
858 |
*time = (uint32_t)a_time;
|
859 |
|
860 |
return 0;
|
861 |
}
|
862 |
|
863 |
//>
|
864 |
//<wave_test_get_write_buffer
|
865 |
|
866 |
void *wave_test_get_write_buffer()
|
867 |
{
|
868 |
return NULL;
|
869 |
}
|
870 |
|
871 |
|
872 |
#else
|
873 |
// notdef USE_PULSEAUDIO
|
874 |
|
875 |
|
876 |
void wave_init() {}
|
877 |
void* wave_open(const char* the_api) {return (void *)1;}
|
878 |
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
|
879 |
int wave_close(void* theHandler) {return 0;}
|
880 |
int wave_is_busy(void* theHandler) {return 0;}
|
881 |
void wave_terminate() {}
|
882 |
uint32_t wave_get_read_position(void* theHandler) {return 0;}
|
883 |
uint32_t wave_get_write_position(void* theHandler) {return 0;}
|
884 |
void wave_flush(void* theHandler) {}
|
885 |
typedef int (t_wave_callback)(void);
|
886 |
void wave_set_callback_is_output_enabled(t_wave_callback* cb) {}
|
887 |
extern void* wave_test_get_write_buffer() {return NULL;}
|
888 |
|
889 |
int wave_get_remaining_time(uint32_t sample, uint32_t* time)
|
890 |
{
|
891 |
if (!time) return(-1);
|
892 |
*time = (uint32_t)0;
|
893 |
return 0;
|
894 |
}
|
895 |
|
896 |
#endif // of USE_PORTAUDIO
|
897 |
|
898 |
//>
|
899 |
//<clock_gettime2, add_time_in_ms
|
900 |
|
901 |
void clock_gettime2(struct timespec *ts)
|
902 |
{
|
903 |
struct timeval tv;
|
904 |
|
905 |
if (!ts)
|
906 |
{
|
907 |
return;
|
908 |
}
|
909 |
|
910 |
assert (gettimeofday(&tv, NULL) != -1);
|
911 |
ts->tv_sec = tv.tv_sec;
|
912 |
ts->tv_nsec = tv.tv_usec*1000;
|
913 |
}
|
914 |
|
915 |
void add_time_in_ms(struct timespec *ts, int time_in_ms)
|
916 |
{
|
917 |
if (!ts)
|
918 |
{
|
919 |
return;
|
920 |
}
|
921 |
|
922 |
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms;
|
923 |
while(t_ns >= ONE_BILLION)
|
924 |
{
|
925 |
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
|
926 |
ts->tv_sec += 1;
|
927 |
t_ns -= ONE_BILLION;
|
928 |
}
|
929 |
ts->tv_nsec = (long int)t_ns;
|
930 |
}
|
931 |
|
932 |
|
933 |
#endif // USE_ASYNC
|
934 |
|
935 |
//>
|