PageRenderTime 56ms CodeModel.GetById 13ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/native/external/espeak/src/wave_pulse.cpp

http://eyes-free.googlecode.com/
C++ | 935 lines | 586 code | 224 blank | 125 comment | 83 complexity | dc1eddd858e0db1caceb7bef36c35ce8 MD5 | raw file
  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
 50enum {ONE_BILLION=1000000000};
 51
 52enum {
 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
 66static 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
 78static pthread_mutex_t pulse_mutex;
 79
 80static pa_context *context = NULL;
 81static pa_stream *stream = NULL;
 82static pa_threaded_mainloop *mainloop = NULL;
 83
 84static pa_cvolume volume;
 85static int volume_valid = 0;
 86
 87static int do_trigger = 0;
 88static uint64_t written = 0;
 89static int time_offset_msec = 0;
 90static int just_flushed = 0;
 91
 92static int connected = 0;
 93
 94#define CHECK_DEAD_GOTO(label, warn) do { \
 95if (!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) \
105do { \
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
135static 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
146static 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
166static 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
185static 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
203static 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
213static 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
223static 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
230static 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
237static 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    
278fail:
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
291static 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
322fail:
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
364static 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
383fail:
384    
385    pa_threaded_mainloop_unlock(mainloop);
386}
387
388static 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    
420fail:
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
432static 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
462static 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
619unlock_and_fail:
620
621    if (o)
622        pa_operation_unref(o);
623    
624    pa_threaded_mainloop_unlock(mainloop);
625    
626fail:
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
654void 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
672void 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
680void wave_init()
681{
682  ENTER("wave_init");
683
684  stream = NULL;
685
686  pulse_open();
687}
688
689//>
690//<wave_open
691
692void* wave_open(const char* the_api)
693{
694  ENTER("wave_open");
695  return((void*)1);
696}
697
698//>
699//<wave_write
700
701size_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
761int 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
782int 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
795void 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
816uint32_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
824uint32_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
832int 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
866void *wave_test_get_write_buffer()
867{
868  return NULL;
869}
870
871
872#else
873// notdef USE_PULSEAUDIO
874
875
876void wave_init() {}
877void* wave_open(const char* the_api) {return (void *)1;}
878size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
879int wave_close(void* theHandler) {return 0;}
880int wave_is_busy(void* theHandler) {return 0;}
881void wave_terminate() {}
882uint32_t wave_get_read_position(void* theHandler) {return 0;}
883uint32_t wave_get_write_position(void* theHandler) {return 0;}
884void wave_flush(void* theHandler) {}
885typedef int (t_wave_callback)(void);
886void wave_set_callback_is_output_enabled(t_wave_callback* cb) {}
887extern void* wave_test_get_write_buffer() {return NULL;}
888
889int 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
901void 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
915void 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//>