2 * Copyright (C) 2008-2009 Kim Woelders
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 #if defined(HAVE_SOUND) && defined(HAVE_SOUND_PA)
27 #include <pulse/pulseaudio.h>
31 #define Estrdup strdup
36 #define D2printf(fmt...) if(EDebug(EDBUG_TYPE_SOUND)>1)Eprintf(fmt)
37 #define D3printf(fmt...) if(EDebug(EDBUG_TYPE_SOUND)>2)Eprintf(fmt)
38 #define D4printf(fmt...) if(EDebug(EDBUG_TYPE_SOUND)>3)Eprintf(fmt)
40 #define D2printf(fmt...)
41 #define D3printf(fmt...)
42 #define D4printf(fmt...)
51 static pa_mainloop *pa_mloop = NULL;
52 static pa_mainloop_api *mainloop_api = NULL;
53 static pa_context *pa_ctx = NULL;
55 static pa_stream *sample_stream = NULL;
56 static size_t sample_length = 0;
57 static int pa_block = 0;
59 static void _sound_pa_Exit(void);
66 D3printf("%s: beg\n", __func__);
71 err = pa_mainloop_iterate(pa_mloop, pa_block, &rc);
72 D4printf("%s: run err=%d rc=%d\n", __func__, err, rc);
80 D3printf("%s: end\n", __func__);
85 context_op_callback(pa_context * pac __UNUSED__, int success __UNUSED__,
86 void *userdata __UNUSED__)
88 D2printf("%s: succ=%d %s\n", __func__, success,
89 (success) ? "" : pa_strerror(pa_context_errno(pac)));
94 context_drain_complete(pa_context * pac __UNUSED__, void *userdata __UNUSED__)
96 D2printf("%s\n", __func__);
100 context_drain(pa_context * pac)
104 D2printf("%s\n", __func__);
105 op = pa_context_drain(pac, context_drain_complete, NULL);
107 pa_operation_unref(op);
112 stream_state_callback(pa_stream * pas, void *userdata __UNUSED__)
114 D2printf("%s: state=%d\n", __func__, pa_stream_get_state(pas));
115 switch (pa_stream_get_state(pas))
117 case PA_STREAM_CREATING: /* 1 */
118 case PA_STREAM_READY: /* 2 */
121 case PA_STREAM_TERMINATED: /* 4 */
122 context_drain(pa_stream_get_context(pas));
125 case PA_STREAM_FAILED: /* 3 */
127 Eprintf("PA failure: %s\n",
128 pa_strerror(pa_context_errno(pa_stream_get_context(pas))));
135 stream_write_callback(pa_stream * pas, size_t length, void *userdata)
137 D2printf("%s: state=%d length=%d\n", __func__, pa_stream_get_state(pas),
139 Sample *s = userdata;
142 left = s->ssd.size - s->written;
143 length = (left > length) ? length : left;
144 pa_stream_write(pas, s->ssd.data, length, NULL, 0, PA_SEEK_RELATIVE);
145 s->written += length;
147 D2printf("%s: size=%d written=%d\n", __func__, s->ssd.size, s->written);
148 if (s->written >= s->ssd.size)
150 pa_stream_set_write_callback(pas, NULL, NULL);
151 pa_stream_finish_upload(pas);
156 context_state_callback(pa_context * pac, void *userdata __UNUSED__)
158 D2printf("%s: state=%d\n", __func__, pa_context_get_state(pac));
159 switch (pa_context_get_state(pac))
161 case PA_CONTEXT_CONNECTING: /* 1 */
162 case PA_CONTEXT_AUTHORIZING: /* 2 */
163 case PA_CONTEXT_SETTING_NAME: /* 3 */
166 case PA_CONTEXT_READY: /* 4 */
170 case PA_CONTEXT_TERMINATED: /* 6 */
173 case PA_CONTEXT_FAILED: /* 5 */
175 Eprintf("PA failure: %s\n", pa_strerror(pa_context_errno(pac)));
176 pa_mainloop_quit(pa_mloop, 1);
182 _sound_pa_Destroy(Sample * s)
186 D2printf("%s beg: %s\n", __func__, s ? s->name : "?");
190 if (pa_ctx && s->name)
193 pa_context_remove_sample(pa_ctx, s->name, context_op_callback, NULL);
195 pa_operation_unref(op);
198 D2printf("%s end\n", __func__);
206 _sound_pa_Load(const char *file)
209 pa_sample_spec sample_spec;
216 s = ECALLOC(Sample, 1);
220 err = SoundSampleGetData(file, &s->ssd);
226 s->name = Estrdup(file);
229 for (p = s->name; *p != '\0'; p++)
233 switch (s->ssd.bit_per_sample)
236 sample_spec.format = PA_SAMPLE_U8;
239 sample_spec.format = PA_SAMPLE_S16NE;
242 sample_spec.rate = s->ssd.rate;
243 sample_spec.channels = s->ssd.channels;
244 sample_length = s->ssd.size;
246 sample_stream = pa_stream_new(pa_ctx, s->name, &sample_spec, NULL);
249 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
250 pa_stream_set_write_callback(sample_stream, stream_write_callback, s);
251 pa_stream_connect_upload(sample_stream, sample_length);
262 _sound_pa_Destroy(s);
267 _sound_pa_Play(Sample * s)
271 D2printf("%s beg: %s\n", __func__, s ? s->name : "?");
275 op = pa_context_play_sample(pa_ctx, s->name, NULL, PA_VOLUME_NORM,
276 context_op_callback, NULL);
278 pa_operation_unref(op);
280 D2printf("%s end\n", __func__);
286 D2printf("%s\n", __func__);
289 pa_stream_unref(stream);
294 pa_context_disconnect(pa_ctx);
295 pa_context_unref(pa_ctx);
301 pa_mainloop_quit(pa_mloop, 0);
302 pa_mainloop_free(pa_mloop);
312 /* Set up a new main loop */
313 pa_mloop = pa_mainloop_new();
316 Eprintf("pa_mainloop_new() failed.\n");
320 mainloop_api = pa_mainloop_get_api(pa_mloop);
322 /* Create a new connection context */
323 pa_ctx = pa_context_new(mainloop_api, "e16");
326 Eprintf("pa_context_new() failed.\n");
330 pa_context_set_state_callback(pa_ctx, context_state_callback, NULL);
332 /* Connect the context */
333 err = pa_context_connect(pa_ctx, NULL, 0, NULL);
335 Eprintf("pa_context_connect(): %s\n", pa_strerror(err));
342 return pa_ctx == NULL;
348 __EXPORT__ extern const SoundOps SoundOps_pa;
349 const SoundOps SoundOps_pa = {
350 _sound_pa_Init, _sound_pa_Exit, _sound_pa_Load, _sound_pa_Destroy,
354 #endif /* HAVE_SOUND && HAVE_SOUND_PA */