46
67
/* Default to 20 ms of latency (1764 = 44100 * 0.020 * 2) */
47
68
#define DEFAULT_PA_MIN_AUDIO_LENgTH 1764
49
static FILE *pulseDebugFile = NULL;
70
static int pulse_log_level;
71
static char const *pulse_play_cmd = "paplay";
51
73
/* Write to /tmp/speech-dispatcher-pulse.log */
53
static void MSG(char *message, ...)
57
if(pulseDebugFile == NULL) {
58
pulseDebugFile = fopen ("/tmp/speech-dispatcher-pulse.log", "w");
60
va_start(ap, message);
61
vfprintf(pulseDebugFile, message, ap);
63
fflush(pulseDebugFile);
66
static void MSG(char *message, ...)
71
int pulse_stop (AudioID * id);
73
int pulse_open (AudioID * id, void **pars);
75
int pulse_close (AudioID * id);
77
int pulse_play (AudioID * id, AudioTrack track);
79
int pulse_set_volume (AudioID * id, int volume);
82
static int _pulse_open(AudioID * id, int sample_rate, int num_channels,
85
pa_buffer_attr buffAttr;
89
ss.rate = sample_rate;
90
ss.channels = num_channels;
91
if(bytes_per_sample == 2) {
92
switch (spd_audio_endian) {
94
ss.format = PA_SAMPLE_S16LE;
97
ss.format = PA_SAMPLE_S16BE;
101
ss.format = PA_SAMPLE_U8;
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));
120
int pulse_open (AudioID * id, void **pars)
122
id->pa_simple = NULL;
123
id->pa_server = (char *)pars[0];
125
id->pa_current_rate = -1;
126
id->pa_current_bps = -1;
127
id->pa_current_channels = -1;
129
if(! strcmp(id->pa_server, "default")) {
130
id->pa_server = NULL;
133
id->pa_min_audio_length = pars[1]?(int)pars[1] : DEFAULT_PA_MIN_AUDIO_LENgTH;
134
id->pa_stop_playback = 0;
136
return _pulse_open(id, 44100, 1, 2);
139
int pulse_play (AudioID * id, AudioTrack track)
141
int bytes_per_sample;
144
signed short *output_samples;
151
if(track.samples == NULL || track.num_samples <= 0) {
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;
161
MSG("ERROR: Unsupported sound data format, track.bits = %d\n", track.bits);
164
output_samples = track.samples;
165
num_bytes = track.num_samples * bytes_per_sample;
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 */
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;
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;
185
while((outcnt < num_bytes) && !id->pa_stop_playback) {
186
if((num_bytes - outcnt) > PULSE_SEND_BYTES) {
187
i = PULSE_SEND_BYTES;
189
i = (num_bytes - outcnt);
191
if(pa_simple_write(id->pa_simple, ((char *)output_samples) + outcnt, i, &error) < 0) {
192
pa_simple_drain(id->pa_simple, NULL);
194
MSG("ERROR: Audio: pulse_play(): %s - closing device - re-open it in next run\n", pa_strerror(error));
197
MSG("Pulse: wrote %u bytes\n", i);
75
static FILE *pulseDebugFile = NULL;
76
static void MSG(char *message, ...)
80
if (pulseDebugFile == NULL) {
81
pulseDebugFile = fopen("/tmp/speech-dispatcher-pulse.log", "w");
83
va_start(ap, message);
84
vfprintf(pulseDebugFile, message, ap);
86
fflush(pulseDebugFile);
89
static void MSG(char *message, ...)
94
static int _pulse_open(spd_pulse_id_t * id, int sample_rate,
95
int num_channels, int bytes_per_sample)
97
pa_buffer_attr buffAttr;
101
ss.rate = sample_rate;
102
ss.channels = num_channels;
103
if (bytes_per_sample == 2) {
104
switch (id->id.format) {
106
ss.format = PA_SAMPLE_S16LE;
109
ss.format = PA_SAMPLE_S16BE;
113
ss.format = PA_SAMPLE_U8;
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 */
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",
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)
141
if (pulse_id->pa_simple != NULL) {
142
pa_simple_free(pulse_id->pa_simple);
143
pulse_id->pa_simple = NULL;
147
static AudioID *pulse_open(void **pars)
149
spd_pulse_id_t *pulse_id;
152
pulse_id = (spd_pulse_id_t *) g_malloc(sizeof(spd_pulse_id_t));
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;
158
pulse_id->id.format = SPD_AUDIO_LE;
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;
164
pulse_id->pa_current_rate = -1;
165
pulse_id->pa_current_bps = -1;
166
pulse_id->pa_current_channels = -1;
168
if (!strcmp(pulse_id->pa_server, "default")) {
169
pulse_id->pa_server = NULL;
172
if (pars[4] != NULL && atoi(pars[4]) != 0)
173
pulse_id->pa_min_audio_length = atoi(pars[4]);
175
pulse_id->pa_stop_playback = 0;
177
ret = _pulse_open(pulse_id, 44100, 1, 2);
183
return (AudioID *) pulse_id;
186
static int pulse_play(AudioID * id, AudioTrack track)
188
int bytes_per_sample;
191
signed short *output_samples;
194
spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
199
if (track.samples == NULL || track.num_samples <= 0) {
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;
209
MSG("ERROR: Unsupported sound data format, track.bits = %d\n",
213
output_samples = track.samples;
214
num_bytes = track.num_samples * bytes_per_sample;
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,
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;
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;
236
while ((outcnt < num_bytes) && !pulse_id->pa_stop_playback) {
237
if ((num_bytes - outcnt) > PULSE_SEND_BYTES) {
238
i = PULSE_SEND_BYTES;
240
i = (num_bytes - outcnt);
243
(pulse_id->pa_simple, ((char *)output_samples) + outcnt, i,
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));
250
MSG("Pulse: wrote %u bytes\n", i);
204
257
/* stop the pulse_play() loop */
205
int pulse_stop (AudioID * id)
207
id->pa_stop_playback = 1;
211
int pulse_close (AudioID * id)
213
if(id->pa_simple != NULL) {
214
pa_simple_free(id->pa_simple);
215
id->pa_simple = NULL;
220
int pulse_set_volume (AudioID * id, int volume)
258
static int pulse_stop(AudioID * id)
260
spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
262
pulse_id->pa_stop_playback = 1;
266
static int pulse_close(AudioID * id)
268
spd_pulse_id_t *pulse_id = (spd_pulse_id_t *) id;
269
pulse_connection_close(pulse_id);
276
static int pulse_set_volume(AudioID * id, int volume)
281
static void pulse_set_loglevel(int level)
284
pulse_log_level = level;
288
static char const *pulse_get_playcmd(void)
290
return pulse_play_cmd;
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 = {
305
spd_audio_plugin_t *pulse_plugin_get(void)
307
return &pulse_functions;
310
spd_audio_plugin_t *SPD_AUDIO_PLUGIN_ENTRY(void)
311
__attribute__ ((weak, alias("pulse_plugin_get")));