~ubuntu-branches/ubuntu/trusty/speech-dispatcher/trusty-proposed

« back to all changes in this revision

Viewing changes to src/audio/pulse.c

  • Committer: Package Import Robot
  • Author(s): Luke Yelavich, Samuel Thibault, Luke Yelavich, Jason White, David Henningsson
  • Date: 2013-11-11 16:38:46 UTC
  • mfrom: (1.1.19)
  • Revision ID: package-import@ubuntu.com-20131111163846-lvu37ypp5sy9z5so
Tags: 0.8-0ubuntu1
[ Samuel Thibault ]
* debian/control: Set libspeechd2 multi-arch: same.
* debian/rules: Set multiarch libdir.
* debian/libspeechd-dev.install,libspeechd2.install,
  speech-dispatcher.install: Use multiarch libdir.
* Do not depend on dpkg | install-info, now that we use the install-info
  trigger.
* Bump Standards-Version to 3.9.5.
* Bump dotconf dependency to >= 1.3.

[ Luke Yelavich ]
* New upstream release
* debian/patches/infinite-loop.patch: Refreshed
* Dropped patches:
  - debian/patches/build-doc.patch
  - debian/patches/procname.patch
  - debian/patches/paths+files.patch
  - debian/patches/pthread.patch
* Add libltdl-dev and intltool to build-depends
* Update packaging for speech-dispatcher python 3 bindings.
* Move speech-dispatcher modules to an architecture independant dir, since
  modules can be written in any language, and i386 only modules can be
  used on amd64 systems
* Create separate audio plugins package
* Convert to debhelper 7+ packaging.
* Use dh-autoreconf to handle autotools file rebuilds.
* Update standards version to 3.9.3.
* Add X-Python-Version related fields to debian/control.
* Patch in the speech-dispatcher-cs.texi file since it was forgotten in the
  0.8 tarball
* Add translations to speech-dispatcher
* Merge from debian unreleased git.  Remaining changes:
  - Moved the flite output module to a separate package, and added
    it to suggests, we don't want flite on the Ubuntu CD image
  - Don't build depend on libaudio-dev or libao-dev, Ubuntu CD size is an
    issue, every little bit helps
  - debian/gbp.conf: Adjust for the Ubuntu git branch
  - Python3-speechd needs to conflict against python-speechd

[ Jason White ]
* Raise level of subsection in fdl.texi to correct document structure.

[ David Henningsson ]
* debian/patches/pulse-default-latency.patch:
  Default to 20 ms latency instead of 1 ms latency (LP: #1208826)

[ Luke Yelavich ]
* spd_audio: Expose dlopened library's symbols to libs it loads. Thanks to
  Christopher Brannon <chris@the-brannons.com> for the patch, taken from
  the speech-dispatcher mailing list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 *
29
29
 */
30
30
 
 
31
#ifdef HAVE_CONFIG_H
 
32
#include <config.h>
 
33
#endif
 
34
 
 
35
#include <stdio.h>
 
36
#include <stdlib.h>
31
37
#include <sys/time.h>
32
38
#include <time.h>
33
39
#include <string.h>
 
40
#include <stdarg.h>
 
41
#include <glib.h>
 
42
 
34
43
#include <pulse/simple.h>
35
44
#include <pulse/error.h>
36
 
#include <stdarg.h>
 
45
 
 
46
#define SPD_AUDIO_PLUGIN_ENTRY spd_pulse_LTX_spd_audio_plugin_get
 
47
#include <spd_audio_plugin.h>
37
48
 
38
49
/* Switch this on to debug, see output log location in MSG() */
39
50
//#define DEBUG_PULSE
 
51
typedef struct {
 
52
        AudioID id;
 
53
        pa_simple *pa_simple;
 
54
        char *pa_server;
 
55
        int pa_min_audio_length;
 
56
        volatile int pa_stop_playback;
 
57
        int pa_current_rate;    // Sample rate for currently PA connection
 
58
        int pa_current_bps;     // Bits per sample rate for currently PA connection
 
59
        int pa_current_channels;        // Number of channels for currently PA connection
 
60
} spd_pulse_id_t;
40
61
 
41
62
/* send a packet of XXX bytes to the sound device */
42
63
#define PULSE_SEND_BYTES 256
46
67
/* Default to 20 ms of latency (1764 = 44100 * 0.020 * 2) */
47
68
#define DEFAULT_PA_MIN_AUDIO_LENgTH 1764
48
69
 
49
 
static FILE *pulseDebugFile = NULL;
 
70
static int pulse_log_level;
 
71
static char const *pulse_play_cmd = "paplay";
50
72
 
51
73
/* Write to /tmp/speech-dispatcher-pulse.log */
52
74
#ifdef DEBUG_PULSE
53
 
static void MSG(char *message, ...)
54
 
{
55
 
    va_list ap;
56
 
 
57
 
    if(pulseDebugFile == NULL) {
58
 
        pulseDebugFile = fopen ("/tmp/speech-dispatcher-pulse.log", "w");
59
 
    }
60
 
    va_start(ap, message);
61
 
    vfprintf(pulseDebugFile, message, ap);
62
 
    va_end(ap);
63
 
    fflush(pulseDebugFile);
64
 
}
65
 
#else
66
 
static void MSG(char *message, ...)
67
 
{
68
 
}
69
 
#endif
70
 
 
71
 
int pulse_stop (AudioID * id);
72
 
 
73
 
int pulse_open (AudioID * id, void **pars);
74
 
 
75
 
int pulse_close (AudioID * id);
76
 
 
77
 
int pulse_play (AudioID * id, AudioTrack track);
78
 
 
79
 
int pulse_set_volume (AudioID * id, int volume);
80
 
 
81
 
 
82
 
static int _pulse_open(AudioID * id, int sample_rate, int num_channels,
83
 
                       int bytes_per_sample)
84
 
{
85
 
  pa_buffer_attr buffAttr;
86
 
  pa_sample_spec ss;  
87
 
  int error;
88
 
 
89
 
  ss.rate = sample_rate;
90
 
  ss.channels = num_channels;
91
 
  if(bytes_per_sample == 2) {
92
 
      switch (spd_audio_endian) {
93
 
        case SPD_AUDIO_LE:
94
 
          ss.format = PA_SAMPLE_S16LE;
95
 
          break;
96
 
        case SPD_AUDIO_BE:
97
 
          ss.format = PA_SAMPLE_S16BE;
98
 
          break;
99
 
      }
100
 
  } else {
101
 
    ss.format = PA_SAMPLE_U8;
102
 
  }
103
 
  
104
 
  /* Set prebuf to one sample so that keys are spoken as soon as typed rather than delayed until the next key pressed */
105
 
  buffAttr.maxlength = (uint32_t)-1;
106
 
  //buffAttr.tlength = (uint32_t)-1; - this is the default, which causes key echo to not work properly.
107
 
  buffAttr.tlength = id->pa_min_audio_length;
108
 
  buffAttr.prebuf = (uint32_t)-1;
109
 
  buffAttr.minreq = (uint32_t)-1;
110
 
  buffAttr.fragsize = (uint32_t)-1;
111
 
  /* Open new connection */
112
 
  if(!(id->pa_simple = pa_simple_new(id->pa_server, "speech-dispatcher", PA_STREAM_PLAYBACK,
113
 
                                     NULL, "playback", &ss, NULL, &buffAttr, &error))) {
114
 
    fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
115
 
    return 1;
116
 
  }
117
 
  return 0;
118
 
}
119
 
 
120
 
int pulse_open (AudioID * id, void **pars)
121
 
{
122
 
    id->pa_simple = NULL;
123
 
    id->pa_server = (char *)pars[0];
124
 
 
125
 
    id->pa_current_rate = -1;
126
 
    id->pa_current_bps = -1;
127
 
    id->pa_current_channels = -1;
128
 
    
129
 
    if(! strcmp(id->pa_server, "default")) {
130
 
    id->pa_server = NULL;
131
 
    }
132
 
 
133
 
    id->pa_min_audio_length = pars[1]?(int)pars[1] : DEFAULT_PA_MIN_AUDIO_LENgTH;
134
 
    id->pa_stop_playback = 0;
135
 
 
136
 
    return _pulse_open(id, 44100, 1, 2);
137
 
}
138
 
 
139
 
int pulse_play (AudioID * id, AudioTrack track)
140
 
{
141
 
    int bytes_per_sample;
142
 
    int num_bytes;
143
 
    int outcnt = 0;
144
 
    signed short *output_samples;
145
 
    int i;
146
 
    int error;
147
 
 
148
 
    if(id == NULL) {
149
 
        return -1;
150
 
    }
151
 
    if(track.samples == NULL || track.num_samples <= 0) {
152
 
        return 0;
153
 
    }
154
 
    MSG("Starting playback\n");
155
 
    /* Choose the correct format */
156
 
    if(track.bits == 16){       
157
 
        bytes_per_sample = 2;
158
 
    } else if(track.bits == 8){
159
 
        bytes_per_sample = 1;
160
 
    } else {
161
 
        MSG("ERROR: Unsupported sound data format, track.bits = %d\n", track.bits);
162
 
        return -1;
163
 
    }
164
 
    output_samples = track.samples;
165
 
    num_bytes = track.num_samples * bytes_per_sample;
166
 
 
167
 
    /* Check if the current connection has suitable parameters for this track */
168
 
    if(id->pa_current_rate != track.sample_rate || id->pa_current_bps != track.bits
169
 
       || id->pa_current_channels != track.num_channels) {
170
 
        MSG("Reopenning connection due to change in track parameters sample_rate:%d bps:%d channels:%d\n",
171
 
            track.sample_rate, track.bits, track.num_channels);
172
 
        /* Close old connection if any */
173
 
        pulse_close(id);
174
 
        /* Open a new connection */
175
 
        _pulse_open(id, track.sample_rate, track.num_channels, bytes_per_sample);
176
 
        /* Keep track of current connection parameters */
177
 
        id->pa_current_rate = track.sample_rate;
178
 
        id->pa_current_bps = track.bits;
179
 
        id->pa_current_channels = track.num_channels;
180
 
    }
181
 
    MSG("bytes to play: %d, (%f secs)\n", num_bytes, (((float) (num_bytes) / 2) / (float) track.sample_rate));
182
 
    id->pa_stop_playback = 0;
183
 
    outcnt = 0;
184
 
    i = 0;
185
 
    while((outcnt < num_bytes) && !id->pa_stop_playback) {
186
 
       if((num_bytes - outcnt) > PULSE_SEND_BYTES) {
187
 
           i = PULSE_SEND_BYTES;
188
 
       } else {
189
 
           i = (num_bytes - outcnt);
190
 
       }
191
 
       if(pa_simple_write(id->pa_simple, ((char *)output_samples) + outcnt, i, &error) < 0) {
192
 
           pa_simple_drain(id->pa_simple, NULL);
193
 
           pulse_close(id);
194
 
           MSG("ERROR: Audio: pulse_play(): %s - closing device - re-open it in next run\n", pa_strerror(error));
195
 
           break;
196
 
       } else {
197
 
           MSG("Pulse: wrote %u bytes\n", i);
198
 
       }
199
 
       outcnt += i;
200
 
    }
201
 
    return 0;
 
75
static FILE *pulseDebugFile = NULL;
 
76
static void MSG(char *message, ...)
 
77
{
 
78
        va_list ap;
 
79
 
 
80
        if (pulseDebugFile == NULL) {
 
81
                pulseDebugFile = fopen("/tmp/speech-dispatcher-pulse.log", "w");
 
82
        }
 
83
        va_start(ap, message);
 
84
        vfprintf(pulseDebugFile, message, ap);
 
85
        va_end(ap);
 
86
        fflush(pulseDebugFile);
 
87
}
 
88
#else
 
89
static void MSG(char *message, ...)
 
90
{
 
91
}
 
92
#endif
 
93
 
 
94
static int _pulse_open(spd_pulse_id_t * id, int sample_rate,
 
95
                       int num_channels, int bytes_per_sample)
 
96
{
 
97
        pa_buffer_attr buffAttr;
 
98
        pa_sample_spec ss;
 
99
        int error;
 
100
 
 
101
        ss.rate = sample_rate;
 
102
        ss.channels = num_channels;
 
103
        if (bytes_per_sample == 2) {
 
104
                switch (id->id.format) {
 
105
                case SPD_AUDIO_LE:
 
106
                        ss.format = PA_SAMPLE_S16LE;
 
107
                        break;
 
108
                case SPD_AUDIO_BE:
 
109
                        ss.format = PA_SAMPLE_S16BE;
 
110
                        break;
 
111
                }
 
112
        } else {
 
113
                ss.format = PA_SAMPLE_U8;
 
114
        }
 
115
 
 
116
        /* Set prebuf to one sample so that keys are spoken as soon as typed rather than delayed until the next key pressed */
 
117
        buffAttr.maxlength = (uint32_t) - 1;
 
118
        //buffAttr.tlength = (uint32_t)-1; - this is the default, which causes key echo to not work properly.
 
119
        buffAttr.tlength = id->pa_min_audio_length;
 
120
        buffAttr.prebuf = (uint32_t) - 1;
 
121
        buffAttr.minreq = (uint32_t) - 1;
 
122
        buffAttr.fragsize = (uint32_t) - 1;
 
123
        /* Open new connection */
 
124
        if (!
 
125
            (id->pa_simple =
 
126
             pa_simple_new(id->pa_server, "speech-dispatcher",
 
127
                           PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL,
 
128
                           &buffAttr, &error))) {
 
129
                fprintf(stderr, __FILE__ ": pa_simple_new() failed: %s\n",
 
130
                        pa_strerror(error));
 
131
                return 1;
 
132
        }
 
133
        return 0;
 
134
}
 
135
 
 
136
/* Close the connection to the server.  Does not free the AudioID struct. */
 
137
/* Usable in pulse_play, which closes connections on failure or */
 
138
/* changes in audio parameters. */
 
139
static void pulse_connection_close(spd_pulse_id_t * pulse_id)
 
140
{
 
141
        if (pulse_id->pa_simple != NULL) {
 
142
                pa_simple_free(pulse_id->pa_simple);
 
143
                pulse_id->pa_simple = NULL;
 
144
        }
 
145
}
 
146
 
 
147
static AudioID *pulse_open(void **pars)
 
148
{
 
149
        spd_pulse_id_t *pulse_id;
 
150
        int ret;
 
151
 
 
152
        pulse_id = (spd_pulse_id_t *) g_malloc(sizeof(spd_pulse_id_t));
 
153
 
 
154
        /* Select an Endianness for the initial connection. */
 
155
#if defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
 
156
        pulse_id->id.format = SPD_AUDIO_BE;
 
157
#else
 
158
        pulse_id->id.format = SPD_AUDIO_LE;
 
159
#endif
 
160
        pulse_id->pa_simple = NULL;
 
161
        pulse_id->pa_server = (char *)pars[3];
 
162
        pulse_id->pa_min_audio_length = DEFAULT_PA_MIN_AUDIO_LENgTH;
 
163
 
 
164
        pulse_id->pa_current_rate = -1;
 
165
        pulse_id->pa_current_bps = -1;
 
166
        pulse_id->pa_current_channels = -1;
 
167
 
 
168
        if (!strcmp(pulse_id->pa_server, "default")) {
 
169
                pulse_id->pa_server = NULL;
 
170
        }
 
171
 
 
172
        if (pars[4] != NULL && atoi(pars[4]) != 0)
 
173
                pulse_id->pa_min_audio_length = atoi(pars[4]);
 
174
 
 
175
        pulse_id->pa_stop_playback = 0;
 
176
 
 
177
        ret = _pulse_open(pulse_id, 44100, 1, 2);
 
178
        if (ret) {
 
179
                g_free(pulse_id);
 
180
                pulse_id = NULL;
 
181
        }
 
182
 
 
183
        return (AudioID *) pulse_id;
 
184
}
 
185
 
 
186
static int pulse_play(AudioID * id, AudioTrack track)
 
187
{
 
188
        int bytes_per_sample;
 
189
        int num_bytes;
 
190
        int outcnt = 0;
 
191
        signed short *output_samples;
 
192
        int i;
 
193
        int error;
 
194
        spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
 
195
 
 
196
        if (id == NULL) {
 
197
                return -1;
 
198
        }
 
199
        if (track.samples == NULL || track.num_samples <= 0) {
 
200
                return 0;
 
201
        }
 
202
        MSG("Starting playback\n");
 
203
        /* Choose the correct format */
 
204
        if (track.bits == 16) {
 
205
                bytes_per_sample = 2;
 
206
        } else if (track.bits == 8) {
 
207
                bytes_per_sample = 1;
 
208
        } else {
 
209
                MSG("ERROR: Unsupported sound data format, track.bits = %d\n",
 
210
                    track.bits);
 
211
                return -1;
 
212
        }
 
213
        output_samples = track.samples;
 
214
        num_bytes = track.num_samples * bytes_per_sample;
 
215
 
 
216
        /* Check if the current connection has suitable parameters for this track */
 
217
        if (pulse_id->pa_current_rate != track.sample_rate
 
218
            || pulse_id->pa_current_bps != track.bits
 
219
            || pulse_id->pa_current_channels != track.num_channels) {
 
220
                MSG("Reopenning connection due to change in track parameters sample_rate:%d bps:%d channels:%d\n", track.sample_rate, track.bits, track.num_channels);
 
221
                /* Close old connection if any */
 
222
                pulse_connection_close(pulse_id);
 
223
                /* Open a new connection */
 
224
                _pulse_open(pulse_id, track.sample_rate, track.num_channels,
 
225
                            bytes_per_sample);
 
226
                /* Keep track of current connection parameters */
 
227
                pulse_id->pa_current_rate = track.sample_rate;
 
228
                pulse_id->pa_current_bps = track.bits;
 
229
                pulse_id->pa_current_channels = track.num_channels;
 
230
        }
 
231
        MSG("bytes to play: %d, (%f secs)\n", num_bytes,
 
232
            (((float)(num_bytes) / 2) / (float)track.sample_rate));
 
233
        pulse_id->pa_stop_playback = 0;
 
234
        outcnt = 0;
 
235
        i = 0;
 
236
        while ((outcnt < num_bytes) && !pulse_id->pa_stop_playback) {
 
237
                if ((num_bytes - outcnt) > PULSE_SEND_BYTES) {
 
238
                        i = PULSE_SEND_BYTES;
 
239
                } else {
 
240
                        i = (num_bytes - outcnt);
 
241
                }
 
242
                if (pa_simple_write
 
243
                    (pulse_id->pa_simple, ((char *)output_samples) + outcnt, i,
 
244
                     &error) < 0) {
 
245
                        pa_simple_drain(pulse_id->pa_simple, NULL);
 
246
                        pulse_connection_close(pulse_id);
 
247
                        MSG("ERROR: Audio: pulse_play(): %s - closing device - re-open it in next run\n", pa_strerror(error));
 
248
                        break;
 
249
                } else {
 
250
                        MSG("Pulse: wrote %u bytes\n", i);
 
251
                }
 
252
                outcnt += i;
 
253
        }
 
254
        return 0;
202
255
}
203
256
 
204
257
/* stop the pulse_play() loop */
205
 
int pulse_stop (AudioID * id)
206
 
{
207
 
    id->pa_stop_playback = 1;
208
 
    return 0;
209
 
}
210
 
 
211
 
int pulse_close (AudioID * id)
212
 
{
213
 
    if(id->pa_simple != NULL) {
214
 
        pa_simple_free(id->pa_simple);
215
 
        id->pa_simple = NULL;
216
 
    }
217
 
    return 0;
218
 
}
219
 
 
220
 
int pulse_set_volume (AudioID * id, int volume)
221
 
{
222
 
    return 0;
 
258
static int pulse_stop(AudioID * id)
 
259
{
 
260
        spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
 
261
 
 
262
        pulse_id->pa_stop_playback = 1;
 
263
        return 0;
 
264
}
 
265
 
 
266
static int pulse_close(AudioID * id)
 
267
{
 
268
        spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
 
269
        pulse_connection_close(pulse_id);
 
270
        g_free(pulse_id);
 
271
        id = NULL;
 
272
 
 
273
        return 0;
 
274
}
 
275
 
 
276
static int pulse_set_volume(AudioID * id, int volume)
 
277
{
 
278
        return 0;
 
279
}
 
280
 
 
281
static void pulse_set_loglevel(int level)
 
282
{
 
283
        if (level) {
 
284
                pulse_log_level = level;
 
285
        }
 
286
}
 
287
 
 
288
static char const *pulse_get_playcmd(void)
 
289
{
 
290
        return pulse_play_cmd;
223
291
}
224
292
 
225
293
/* Provide the pulse backend. */
226
 
spd_audio_plugin_t pulse_functions = {pulse_open, pulse_play, pulse_stop, pulse_close, pulse_set_volume};
 
294
static spd_audio_plugin_t pulse_functions = {
 
295
        "pulse",
 
296
        pulse_open,
 
297
        pulse_play,
 
298
        pulse_stop,
 
299
        pulse_close,
 
300
        pulse_set_volume,
 
301
        pulse_set_loglevel,
 
302
        pulse_get_playcmd
 
303
};
 
304
 
 
305
spd_audio_plugin_t *pulse_plugin_get(void)
 
306
{
 
307
        return &pulse_functions;
 
308
}
 
309
 
 
310
spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
 
311
    __attribute__ ((weak, alias("pulse_plugin_get")));