1 /*
2 * Copyright (C) 2008 2009 2011 2012, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit 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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * mhWaveEdit 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 mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21
22 #include <poll.h>
23 #include <pulse/pulseaudio.h>
24
25 #include "int_box.h"
26
27 #ifndef PA_CHECK_VERSION
28 #define PA_CHECK_VERSION(a,b,c) (0)
29 #endif
30
31 #undef MLDEBUG
32
33 /* ----------------------- */
34 /* Mainloop API */
35
36 struct pulse_api_io_event {
37 gpointer iosource;
38 int fd;
39 pa_io_event_flags_t events;
40 gpointer userdata;
41 pa_io_event_cb_t cb;
42 pa_io_event_destroy_cb_t destroy_cb;
43 };
44
45 struct pulse_api_time_event {
46 gpointer timesource;
47 pa_time_event_cb_t cb;
48 pa_time_event_destroy_cb_t destroy_cb;
49 void *userdata;
50 };
51
52 struct pulse_api_defer_event {
53 gpointer constsource;
54 pa_defer_event_cb_t cb;
55 pa_defer_event_destroy_cb_t destroy_cb;
56 void *userdata;
57 };
58
59 struct pulse_api_userdata {
60 gpointer *sources;
61 int sources_cap;
62 int sources_len;
63 };
64
65 static pa_mainloop_api *pulse_api(void);
66
67 #ifdef MLDEBUG
pulse_api_list_sources(struct pulse_api_userdata * ud)68 static void pulse_api_list_sources(struct pulse_api_userdata *ud)
69 {
70 int i;
71 printf("sources: ");
72 for (i=0; i<ud->sources_len; i++)
73 printf("%p ",ud->sources[i]);
74 puts("");
75 }
76 #endif
77
pulse_api_source_added(gpointer src,struct pa_mainloop_api * api)78 static void pulse_api_source_added(gpointer src, struct pa_mainloop_api *api)
79 {
80 struct pulse_api_userdata *ud = (struct pulse_api_userdata *)api->userdata;
81
82 #ifdef MLDEBUG
83 printf("pulse_api_source_added: %p\n",src);
84 #endif
85 if (ud->sources_cap == ud->sources_len) {
86 if (ud->sources_cap == 0)
87 ud->sources_cap = 1;
88 else
89 ud->sources_cap *= 2;
90 ud->sources = g_realloc(ud->sources,
91 ud->sources_cap*sizeof(ud->sources[0]));
92 }
93 ud->sources[ud->sources_len++] = src;
94 #ifdef MLDEBUG
95 pulse_api_list_sources(ud);
96 #endif
97 }
98
pulse_api_source_removed(gpointer src,struct pa_mainloop_api * api)99 static void pulse_api_source_removed(gpointer src, struct pa_mainloop_api *api)
100 {
101 struct pulse_api_userdata *ud = (struct pulse_api_userdata *)api->userdata;
102 int i;
103 #ifdef MLDEBUG
104 printf("pulse_api_source_removed: %p\n",src);
105 #endif
106 for (i=0; i<ud->sources_len; i++)
107 if (ud->sources[i] == src)
108 break;
109 for (; i+1<ud->sources_len; i++)
110 ud->sources[i] = ud->sources[i+1];
111 ud->sources_len--;
112
113 #ifdef MLDEBUG
114 pulse_api_list_sources(ud);
115 #endif
116 }
117
pa_to_poll(pa_io_event_flags_t events)118 static int pa_to_poll(pa_io_event_flags_t events)
119 {
120 int i = 0;
121 if (events & PA_IO_EVENT_INPUT) i |= POLLIN;
122 if (events & PA_IO_EVENT_OUTPUT) i |= POLLOUT;
123 if (events & PA_IO_EVENT_HANGUP) i |= POLLHUP;
124 if (events & PA_IO_EVENT_ERROR) i |= POLLERR;
125 return i;
126 }
127
poll_to_pa(int events)128 static pa_io_event_flags_t poll_to_pa(int events)
129 {
130 pa_io_event_flags_t e = PA_IO_EVENT_NULL;
131 if (events & POLLIN) e |= PA_IO_EVENT_INPUT;
132 if (events & POLLOUT) e |= PA_IO_EVENT_OUTPUT;
133 if (events & POLLHUP) e |= PA_IO_EVENT_HANGUP;
134 if (events & PA_IO_EVENT_ERROR) e |= POLLERR;
135 return e;
136 }
137
pulse_api_io_cb(gpointer iosource,int fd,gushort revents,gpointer user_data)138 static void pulse_api_io_cb(gpointer iosource, int fd, gushort revents,
139 gpointer user_data)
140 {
141 struct pulse_api_io_event *e = user_data;
142 /* puts("I/O event triggered"); */
143 e->cb(pulse_api(),(pa_io_event *)e,fd,poll_to_pa(revents),e->userdata);
144 }
145
pulse_api_io_new(pa_mainloop_api * a,int fd,pa_io_event_flags_t events,pa_io_event_cb_t cb,void * userdata)146 static pa_io_event *pulse_api_io_new(pa_mainloop_api *a, int fd,
147 pa_io_event_flags_t events,
148 pa_io_event_cb_t cb,
149 void *userdata)
150 {
151 struct pulse_api_io_event *e;
152
153 #ifdef MLDEBUG
154 printf("Adding I/O event, fd=%d, events=%d\n",fd,events);
155 #endif
156 e = g_malloc(sizeof(*e));
157 e->userdata = userdata;
158 e->fd = fd;
159 e->events = events;
160 e->cb = cb;
161 e->destroy_cb = NULL;
162 e->iosource = mainloop_io_source_add(fd, pa_to_poll(events),
163 pulse_api_io_cb, e);
164 pulse_api_source_added(e->iosource,a);
165 return (pa_io_event *)e;
166 }
167
pulse_api_io_enable(pa_io_event * e,pa_io_event_flags_t events)168 static void pulse_api_io_enable(pa_io_event *e, pa_io_event_flags_t events)
169 {
170 struct pulse_api_io_event *es = (struct pulse_api_io_event *)e;
171
172 #ifdef MLDEBUG
173 printf("IO event set enable, fd=%d ,events=%d, es->events=%d\n",
174 es->fd,events,es->events);
175 #endif
176 mainloop_io_source_set_events(es->iosource,pa_to_poll(events));
177 }
178
pulse_api_io_free(pa_io_event * e)179 static void pulse_api_io_free(pa_io_event *e)
180 {
181 struct pulse_api_io_event *es = (struct pulse_api_io_event *)e;
182 #ifdef MLDEBUG
183 printf("Removing IO event (fd %d)\n",es->fd);
184 #endif
185 mainloop_io_source_free(es->iosource);
186 pulse_api_source_removed(es->iosource, pulse_api());
187 if (es->destroy_cb) es->destroy_cb(pulse_api(),e,es->userdata);
188
189 g_free(es);
190 }
191
pulse_api_io_set_destroy(pa_io_event * e,pa_io_event_destroy_cb_t cb)192 static void pulse_api_io_set_destroy(pa_io_event *e,
193 pa_io_event_destroy_cb_t cb)
194 {
195 struct pulse_api_io_event *es = (struct pulse_api_io_event *)e;
196 es->destroy_cb = cb;
197 }
198
pa_api_time_new_cb(gpointer timesource,GTimeVal * current_time,gpointer user_data)199 static gint pa_api_time_new_cb(gpointer timesource, GTimeVal *current_time,
200 gpointer user_data)
201 {
202 struct pulse_api_time_event *es =
203 (struct pulse_api_time_event *)user_data;
204 struct timeval tv;
205 /* puts("Time event triggered"); */
206 tv.tv_sec = current_time->tv_sec;
207 tv.tv_usec = current_time->tv_usec;
208 es->cb(pulse_api(),(pa_time_event *)es,&tv,es->userdata);
209 return 0;
210 }
211
pulse_api_time_new(pa_mainloop_api * a,const struct timeval * tv,pa_time_event_cb_t cb,void * userdata)212 static pa_time_event *pulse_api_time_new(pa_mainloop_api *a,
213 const struct timeval *tv,
214 pa_time_event_cb_t cb,
215 void *userdata)
216 {
217 struct pulse_api_time_event *es;
218 GTimeVal gtv;
219
220 #ifdef MLDEBUG
221 GTimeVal temp_tv;
222
223 g_get_current_time(&temp_tv);
224 printf("Adding time event, triggers in %d s %d us\n",
225 tv->tv_sec-temp_tv.tv_sec, tv->tv_usec-temp_tv.tv_usec);
226 #endif
227
228 es = g_malloc(sizeof(*es));
229 es->cb = cb;
230 es->destroy_cb = NULL;
231 es->userdata = userdata;
232 gtv.tv_sec = tv->tv_sec;
233 gtv.tv_usec = tv->tv_usec;
234 es->timesource = mainloop_time_source_add(>v,pa_api_time_new_cb,es);
235 pulse_api_source_added(es->timesource, a);
236 return (pa_time_event *)es;
237 }
238
pulse_api_time_restart(pa_time_event * e,const struct timeval * tv)239 static void pulse_api_time_restart(pa_time_event *e, const struct timeval *tv)
240 {
241 struct pulse_api_time_event *es = (struct pulse_api_time_event *)e;
242 GTimeVal gtv;
243
244 /*
245 GTimeVal temp_tv;
246 g_get_current_time(&temp_tv);
247 printf("Restarting time event, triggers in %d s %d us\n",
248 tv->tv_sec-temp_tv.tv_sec, tv->tv_usec-temp_tv.tv_usec); */
249
250 gtv.tv_sec = tv->tv_sec;
251 gtv.tv_usec = tv->tv_usec;
252 mainloop_time_source_restart(es->timesource,>v);
253 }
254
pulse_api_time_free(pa_time_event * e)255 static void pulse_api_time_free(pa_time_event *e)
256 {
257 struct pulse_api_time_event *es = (struct pulse_api_time_event *)e;
258 #ifdef MLDEBUG
259 puts("Removing time event");
260 #endif
261 mainloop_time_source_free(es->timesource);
262 if (es->destroy_cb) es->destroy_cb(pulse_api(),e,es->userdata);
263 pulse_api_source_removed(es->timesource, pulse_api());
264 g_free(es);
265 }
266
pulse_api_time_set_destroy(pa_time_event * e,pa_time_event_destroy_cb_t cb)267 static void pulse_api_time_set_destroy(pa_time_event *e,
268 pa_time_event_destroy_cb_t cb)
269 {
270 struct pulse_api_time_event *es = (struct pulse_api_time_event *)e;
271 es->destroy_cb = cb;
272 }
273
pulse_api_defer_new_cb(gpointer csource,gpointer user_data)274 static int pulse_api_defer_new_cb(gpointer csource, gpointer user_data)
275 {
276 struct pulse_api_defer_event *es = user_data;
277 #ifdef MLDEBUG
278 puts("Defer event triggered");
279 #endif
280 es->cb(pulse_api(),(pa_defer_event *)es,es->userdata);
281 return 0;
282 }
283
pulse_api_defer_new(pa_mainloop_api * a,pa_defer_event_cb_t cb,void * userdata)284 static pa_defer_event *pulse_api_defer_new(pa_mainloop_api *a,
285 pa_defer_event_cb_t cb,
286 void *userdata)
287 {
288 struct pulse_api_defer_event *es;
289
290 #ifdef MLDEBUG
291 puts("Adding defer event");
292 #endif
293 es = g_malloc(sizeof(*es));
294 es->cb = cb;
295 es->destroy_cb = NULL;
296 es->userdata = userdata;
297 es->constsource = mainloop_constant_source_add(pulse_api_defer_new_cb,
298 es, FALSE);
299 pulse_api_source_added(es->constsource,a);
300 return (pa_defer_event *)es;
301 }
302
pulse_api_defer_enable(pa_defer_event * e,int b)303 static void pulse_api_defer_enable(pa_defer_event *e, int b)
304 {
305 struct pulse_api_defer_event *es = (struct pulse_api_defer_event *)e;
306 #ifdef MLDEBUG
307 printf("Defer event set enabled=%d\n",b);
308 #endif
309 mainloop_constant_source_enable(es->constsource, b);
310 }
311
pulse_api_defer_free(pa_defer_event * e)312 static void pulse_api_defer_free(pa_defer_event *e)
313 {
314 struct pulse_api_defer_event *es = (struct pulse_api_defer_event *)e;
315 #ifdef MLDEBUG
316 puts("Removing defer event");
317 #endif
318 mainloop_constant_source_free(es->constsource);
319 if (es->destroy_cb) es->destroy_cb(pulse_api(),e,es->userdata);
320 pulse_api_source_removed(es->constsource,pulse_api());
321 g_free(es);
322 }
323
pulse_api_defer_set_destroy(pa_defer_event * e,pa_defer_event_destroy_cb_t cb)324 static void pulse_api_defer_set_destroy(pa_defer_event *e,
325 pa_defer_event_destroy_cb_t cb)
326 {
327 struct pulse_api_defer_event *es = (struct pulse_api_defer_event *)e;
328 es->destroy_cb = cb;
329 }
330
pulse_api_quit(pa_mainloop_api * a,int retval)331 static void pulse_api_quit(pa_mainloop_api *a, int retval)
332 {
333 }
334
pulse_api(void)335 static pa_mainloop_api *pulse_api(void)
336 {
337 static gboolean api_setup = FALSE;
338 static struct pa_mainloop_api api;
339 struct pulse_api_userdata *ud;
340
341 if (!api_setup) {
342 api.io_new = pulse_api_io_new;
343 api.io_enable = pulse_api_io_enable;
344 api.io_free = pulse_api_io_free;
345 api.io_set_destroy = pulse_api_io_set_destroy;
346 api.time_new = pulse_api_time_new;
347 api.time_restart = pulse_api_time_restart;
348 api.time_free = pulse_api_time_free;
349 api.time_set_destroy = pulse_api_time_set_destroy;
350 api.defer_new = pulse_api_defer_new;
351 api.defer_enable = pulse_api_defer_enable;
352 api.defer_free = pulse_api_defer_free;
353 api.defer_set_destroy = pulse_api_defer_set_destroy;
354 api.quit = pulse_api_quit;
355
356 ud = g_malloc0(sizeof(*ud));
357 api.userdata = ud;
358
359 api_setup = TRUE;
360 }
361
362 return &api;
363 }
364
pulse_api_block(void)365 static void pulse_api_block(void)
366 {
367 gpointer *srcp;
368 struct pulse_api_userdata *ud =
369 (struct pulse_api_userdata *)(pulse_api()->userdata);
370 #ifdef MLDEBUG
371 puts("pulse_api_block");
372 pulse_api_list_sources(ud);
373 #endif
374
375 srcp = g_malloc(ud->sources_len * sizeof(ud->sources[0]));
376 memcpy(srcp,ud->sources,ud->sources_len*sizeof(ud->sources[0]));
377 mainloop_recurse_on(srcp, ud->sources_len);
378 g_free(srcp);
379 }
380
381
382 /* --------------------------------
383 * Driver core
384 */
385
386 static struct {
387 pa_context *ctx;
388 pa_context_state_t ctx_state;
389 int ctx_errno;
390 pa_stream *stream;
391 pa_stream_state_t stream_state;
392 pa_sample_spec stream_sspec;
393 pa_buffer_attr stream_attr;
394 pa_stream_flags_t stream_flags;
395 gboolean record_flag;
396 const char *record_data;
397 size_t record_bytes,record_pos;
398 gint overflow_count,overflow_report_count;
399 gboolean flush_state; /* 0 = no flush requested yet, 1 = waiting, 2 = done */
400 gboolean recursing_mainloop;
401 gpointer ready_constsource;
402 GVoidFunc ready_func;
403 gboolean stopping;
404 } pulse_data = { 0 };
405
pulse_context_state_cb(pa_context * c,void * userdata)406 static void pulse_context_state_cb(pa_context *c, void *userdata)
407 {
408 g_assert(pulse_data.ctx == c);
409
410 pulse_data.ctx_state = pa_context_get_state(c);
411 /* printf("Context state change to: %d\n",pulse_data.ctx_state); */
412
413 if (pulse_data.ctx_state == PA_CONTEXT_FAILED ||
414 pulse_data.ctx_state == PA_CONTEXT_TERMINATED) {
415 pulse_data.ctx_errno = pa_context_errno(pulse_data.ctx);
416 pa_context_unref(pulse_data.ctx);
417 pulse_data.ctx = NULL;
418 }
419
420 }
421
pulse_connect(gboolean autospawn,gboolean silent)422 static gboolean pulse_connect(gboolean autospawn, gboolean silent)
423 {
424 gchar *c;
425 int i;
426 if (pulse_data.ctx != NULL) return TRUE;
427 pulse_data.ctx = pa_context_new(pulse_api(),"mhwaveedit");
428 g_assert(pulse_data.ctx != NULL);
429 pulse_data.ctx_state = PA_CONTEXT_UNCONNECTED;
430 pa_context_set_state_callback(pulse_data.ctx,pulse_context_state_cb,
431 NULL);
432 i = pa_context_connect(pulse_data.ctx, NULL,
433 autospawn?0:PA_CONTEXT_NOAUTOSPAWN, NULL);
434 g_assert(i == 0 || pulse_data.ctx == NULL);
435
436 while (pulse_data.ctx_state != PA_CONTEXT_READY &&
437 pulse_data.ctx_state != PA_CONTEXT_FAILED &&
438 pulse_data.ctx_state != PA_CONTEXT_TERMINATED)
439 pulse_api_block();
440
441 if (!silent && pulse_data.ctx_state != PA_CONTEXT_READY) {
442 c = g_strdup_printf(_("Connection to PulseAudio server failed: %s"),
443 pa_strerror(pulse_data.ctx_errno));
444 user_error(c);
445 g_free(c);
446 }
447
448 return (pulse_data.ctx != NULL);
449 }
450
pulse_init(gboolean silent)451 static gboolean pulse_init(gboolean silent)
452 {
453 return pulse_connect(FALSE,silent);
454 }
455
pulse_quit(void)456 static void pulse_quit(void)
457 {
458 if (pulse_data.ctx != NULL) {
459 pa_context_disconnect(pulse_data.ctx);
460 /* Should be unref:d and set to NULL by the state callback */
461 g_assert(pulse_data.ctx == NULL);
462 }
463 }
464
465 #if PA_CHECK_VERSION(0,9,15)
466 #define HAS24
467 #endif
468
format_to_pulse(Dataformat * format,pa_sample_spec * ss_out)469 static gboolean format_to_pulse(Dataformat *format, pa_sample_spec *ss_out)
470 {
471 pa_sample_format_t sf;
472
473 if (format->type == DATAFORMAT_PCM) {
474 if (format->samplesize == 1 && !format->sign)
475 sf = PA_SAMPLE_U8;
476 else if (format->samplesize == 2 && format->sign)
477 sf = (format->bigendian)?PA_SAMPLE_S16BE:PA_SAMPLE_S16LE;
478 #ifdef HAS24
479 else if (format->samplesize == 3 && format->sign)
480 sf = (format->bigendian)?PA_SAMPLE_S24BE:PA_SAMPLE_S24LE;
481 #endif
482 else if (format->samplesize == 4 && format->sign) {
483 if (format->packing == 0 || format->packing == 1)
484 sf = (format->bigendian)?PA_SAMPLE_S32BE:PA_SAMPLE_S32LE;
485 #ifdef HAS24
486 else
487 sf = (format->bigendian)?PA_SAMPLE_S24_32LE:PA_SAMPLE_S24_32BE;
488 #endif
489 } else
490 return TRUE;
491 } else if (format->type == DATAFORMAT_FLOAT && format->samplesize == 4) {
492 if (!ieee_le_compatible && !ieee_be_compatible)
493 return TRUE;
494 else if (format->bigendian)
495 sf = PA_SAMPLE_FLOAT32BE;
496 else
497 sf = PA_SAMPLE_FLOAT32LE;
498 } else
499 return TRUE;
500
501 ss_out->format = sf;
502 ss_out->rate = format->samplerate;
503 ss_out->channels = format->channels;
504 return FALSE;
505 }
506
pa_format_from_pulse(pa_sample_spec * ss,Dataformat * format_out)507 static gboolean pa_format_from_pulse(pa_sample_spec *ss, Dataformat *format_out)
508 {
509 Dataformat f;
510 int i = ss->format;
511
512 switch (i) {
513 case PA_SAMPLE_U8:
514 case PA_SAMPLE_S16LE:
515 case PA_SAMPLE_S16BE:
516 #ifdef HAS24
517 case PA_SAMPLE_S24LE:
518 case PA_SAMPLE_S24BE:
519 case PA_SAMPLE_S24_32LE:
520 case PA_SAMPLE_S24_32BE:
521 #endif
522 case PA_SAMPLE_S32LE:
523 case PA_SAMPLE_S32BE:
524 f.type = DATAFORMAT_PCM;
525 f.packing = 0;
526 if (i == PA_SAMPLE_U8)
527 f.samplesize = 1;
528 else if (i == PA_SAMPLE_S16LE || i == PA_SAMPLE_S16BE) {
529 f.samplesize = 2;
530 #ifdef HAS24
531 } else if (i == PA_SAMPLE_S24LE || i == PA_SAMPLE_S24BE)
532 f.samplesize = 3;
533 else if (i == PA_SAMPLE_S24_32LE || i == PA_SAMPLE_S24_32LE) {
534 f.samplesize = 4;
535 f.packing = 2;
536 #endif
537 } else
538 f.samplesize = 4;
539 f.sign = !(i == PA_SAMPLE_U8);
540 if (i == PA_SAMPLE_S16BE || i == PA_SAMPLE_S32BE
541 #ifdef HAS24
542 || i == PA_SAMPLE_S24BE || i == PA_SAMPLE_S24_32BE
543 #endif
544 )
545 f.bigendian = TRUE;
546 else
547 f.bigendian = FALSE;
548 break;
549 case PA_SAMPLE_FLOAT32LE:
550 if (!ieee_le_compatible && !ieee_be_compatible) return TRUE;
551 f.type = DATAFORMAT_FLOAT;
552 f.samplesize = 4;
553 f.bigendian = FALSE;
554 break;
555 case PA_SAMPLE_FLOAT32BE:
556 if (!ieee_le_compatible && !ieee_be_compatible) return TRUE;
557 f.type = DATAFORMAT_FLOAT;
558 f.samplesize = 4;
559 f.bigendian = TRUE;
560 break;
561 default:
562 return TRUE;
563 }
564 f.channels = ss->channels;
565 f.samplebytes = f.samplesize * f.channels;
566 f.samplerate = ss->rate;
567
568 memcpy(format_out,&f,sizeof(Dataformat));
569 return FALSE;
570 }
571
572
pulse_stream_state_cb(pa_stream * p,void * userdata)573 static void pulse_stream_state_cb(pa_stream *p, void *userdata)
574 {
575 g_assert(pulse_data.stream == p);
576 pulse_data.stream_state = pa_stream_get_state(p);
577 /* printf("Stream state change to: %d\n",pulse_data.stream_state); */
578 if (pulse_data.stream_state == PA_STREAM_FAILED ||
579 pulse_data.stream_state == PA_STREAM_TERMINATED) {
580 pa_stream_unref(pulse_data.stream);
581 pulse_data.stream = NULL;
582 }
583 }
584
pulse_overflow_func(pa_stream * p,void * userdata)585 static void pulse_overflow_func(pa_stream *p, void *userdata)
586 {
587 pulse_data.overflow_count ++;
588 }
589
pulse_wait_connect(int silent)590 static int pulse_wait_connect(int silent)
591 {
592 gchar *c;
593
594 pulse_data.recursing_mainloop = TRUE;
595 while (pulse_data.stream_state != PA_STREAM_READY &&
596 pulse_data.stream_state != PA_STREAM_FAILED &&
597 pulse_data.stream_state != PA_STREAM_TERMINATED)
598 pulse_api_block();
599 pulse_data.recursing_mainloop = FALSE;
600
601 if (!silent && pulse_data.stream_state != PA_STREAM_READY) {
602 c = g_strdup_printf(_("Connection to PulseAudio server failed: %s"),
603 pa_strerror(pa_context_errno(pulse_data.ctx)));
604 user_error(c);
605 g_free(c);
606 return 1;
607 }
608
609 return 0;
610 }
611
pulse_select_format_main(Dataformat * format,gboolean record,gboolean silent,pa_stream_request_cb_t ready_func,gpointer rf_userdata)612 static gint pulse_select_format_main(Dataformat *format, gboolean record,
613 gboolean silent,
614 pa_stream_request_cb_t ready_func,
615 gpointer rf_userdata)
616 {
617 pa_sample_spec ss;
618 int i;
619 guint32 u;
620
621 /* printf("pulse_output_select_format, silent==%d\n",silent); */
622 if (format_to_pulse(format,&ss)) return -1;
623
624 if (!pulse_connect(TRUE,silent)) return silent?-1:+1;
625
626 g_assert(pulse_data.stream == NULL);
627 pulse_data.record_flag = record;
628 pulse_data.record_data = NULL;
629 pulse_data.stream_state = PA_STREAM_UNCONNECTED;
630 pulse_data.stream = pa_stream_new(pulse_data.ctx, "p", &ss, NULL);
631 g_assert(pulse_data.stream != NULL);
632 memcpy(&pulse_data.stream_sspec, &ss, sizeof(pa_sample_spec));
633 pa_stream_set_state_callback(pulse_data.stream, pulse_stream_state_cb,
634 NULL);
635
636 if (record) {
637 pa_stream_set_read_callback(pulse_data.stream,ready_func,rf_userdata);
638 pulse_data.overflow_count = pulse_data.overflow_report_count = 0;
639 pa_stream_set_overflow_callback(pulse_data.stream,
640 pulse_overflow_func,NULL);
641 } else
642 pa_stream_set_write_callback(pulse_data.stream,ready_func,
643 rf_userdata);
644
645 memset(&pulse_data.stream_attr, 0xFF, sizeof(pulse_data.stream_attr));
646 pulse_data.stream_flags = 0;
647
648 if (record) {
649 pulse_data.stream_flags |= PA_STREAM_ADJUST_LATENCY;
650 pulse_data.stream_attr.fragsize = (format->samplerate *
651 format->samplebytes) / 10;
652 } else {
653 u = inifile_get_guint32("pulseLatency", 0);
654 if (u != 0) {
655 pulse_data.stream_flags |= PA_STREAM_ADJUST_LATENCY;
656 pulse_data.stream_attr.tlength =
657 (u * format->samplerate * format->samplebytes) / 1000;
658 }
659 }
660
661 if (record)
662 i = pa_stream_connect_record(pulse_data.stream,NULL,
663 &pulse_data.stream_attr,
664 pulse_data.stream_flags);
665 else
666 i = pa_stream_connect_playback(pulse_data.stream,NULL,
667 &pulse_data.stream_attr,
668 pulse_data.stream_flags,NULL,
669 NULL);
670
671 g_assert(i == 0 || pulse_data.stream == NULL);
672
673 if (pulse_wait_connect(silent)) return 1;
674
675 if (pulse_data.stream_state != PA_STREAM_READY)
676 return -1;
677
678 return 0;
679 }
680
ready_constsource_cb(gpointer csource,gpointer user_data)681 static int ready_constsource_cb(gpointer csource, gpointer user_data)
682 {
683 g_assert(pulse_data.stream != NULL);
684 mainloop_constant_source_enable(csource, FALSE);
685 pulse_data.ready_func();
686 return 0;
687 }
688
pulse_ready_func(pa_stream * p,size_t bytes,void * userdata)689 static void pulse_ready_func(pa_stream *p, size_t bytes, void *userdata)
690 {
691 if (!pulse_data.stopping)
692 mainloop_constant_source_enable(pulse_data.ready_constsource, TRUE);
693 }
694
ready_constsource_setup(void)695 static void ready_constsource_setup(void)
696 {
697 if (pulse_data.ready_constsource == NULL)
698 pulse_data.ready_constsource =
699 mainloop_constant_source_add(ready_constsource_cb,NULL,FALSE);
700 mainloop_constant_source_enable(pulse_data.ready_constsource, FALSE);
701 }
702
pulse_output_select_format(Dataformat * format,gboolean silent,GVoidFunc ready_func)703 static gint pulse_output_select_format(Dataformat *format, gboolean silent,
704 GVoidFunc ready_func)
705 {
706 ready_constsource_setup();
707 pulse_data.ready_func = ready_func;
708 return pulse_select_format_main(format,FALSE,silent,
709 pulse_ready_func,NULL);
710 }
711
pulse_input_select_format(Dataformat * format,gboolean silent,GVoidFunc ready_func)712 static gint pulse_input_select_format(Dataformat *format, gboolean silent,
713 GVoidFunc ready_func)
714 {
715 ready_constsource_setup();
716 pulse_data.ready_func = ready_func;
717 return pulse_select_format_main(format,TRUE,silent,
718 pulse_ready_func,NULL);
719 }
720
pulse_output_want_data(void)721 static gboolean pulse_output_want_data(void)
722 {
723 size_t s;
724 if (pulse_data.stream == NULL) return FALSE;
725 s = pa_stream_writable_size(pulse_data.stream);
726 return (s > 0);
727 }
728
pulse_flush_success_cb(pa_stream * s,int success,void * userdata)729 static void pulse_flush_success_cb(pa_stream *s, int success, void *userdata)
730 {
731 gchar *c;
732 if (!success) {
733 c = g_strdup_printf(_("Failed to drain stream: %s"),
734 pa_strerror(pa_context_errno(pulse_data.ctx)));
735 user_error(c);
736 g_free(c);
737 }
738 /* puts("pulse_flush_success_cb"); */
739 pulse_data.flush_state = 2;
740 }
741
pulse_flush_start(void)742 static void pulse_flush_start(void)
743 {
744 pa_operation *o;
745
746 /* puts("pulse_flush_start"); */
747 if (pulse_data.flush_state != 0) return;
748 pulse_data.flush_state = 1;
749 o = pa_stream_drain(pulse_data.stream, pulse_flush_success_cb,
750 NULL);
751 g_assert(o != NULL);
752 pa_operation_unref(o);
753 }
754
pulse_flush_done(void)755 static gboolean pulse_flush_done(void)
756 {
757 return (pulse_data.flush_state == 2);
758 }
759
pulse_flush_in_progress(void)760 static gboolean pulse_flush_in_progress(void)
761 {
762 return (pulse_data.flush_state == 1);
763 }
764
pulse_output_play(gchar * buffer,guint bufsize)765 static guint pulse_output_play(gchar *buffer, guint bufsize)
766 {
767 int i;
768 size_t s;
769 if (pulse_data.stream == NULL) return 0;
770 if (buffer == NULL) {
771 g_assert(bufsize == 0);
772 pulse_flush_start();
773 return pulse_flush_done()?0:1;
774 }
775 s = pa_stream_writable_size(pulse_data.stream);
776 if (bufsize > s) bufsize = s;
777 i = pa_stream_write(pulse_data.stream, buffer, bufsize, NULL, 0,
778 PA_SEEK_RELATIVE);
779 g_assert(i == 0);
780 if (pulse_data.flush_state == 2)
781 pulse_data.flush_state = 0;
782 if (bufsize < s)
783 mainloop_constant_source_enable(pulse_data.ready_constsource,TRUE);
784 return bufsize;
785 }
786
pulse_output_clear_buffers(void)787 static void pulse_output_clear_buffers(void)
788 {
789 pa_operation *p;
790 int i;
791 if (pulse_data.stream == NULL) return;
792 if (inifile_get_gboolean("pulseReconnect",TRUE)) {
793 pa_stream_set_write_callback(pulse_data.stream, NULL, NULL);
794 pa_stream_set_state_callback(pulse_data.stream, NULL, NULL);
795 pa_stream_disconnect(pulse_data.stream);
796 pa_stream_unref(pulse_data.stream);
797 pulse_data.stream_state = PA_STREAM_UNCONNECTED;
798 pulse_data.stream = pa_stream_new(pulse_data.ctx, "p", &pulse_data.stream_sspec, NULL);
799 pa_stream_set_state_callback(pulse_data.stream, pulse_stream_state_cb, NULL);
800 pa_stream_set_write_callback(pulse_data.stream,pulse_ready_func, NULL);
801 i = pa_stream_connect_playback(pulse_data.stream,NULL,
802 &pulse_data.stream_attr,
803 pulse_data.stream_flags,NULL,
804 NULL);
805 g_assert(i==0 && pulse_data.stream!=NULL);
806 i = pulse_wait_connect(0);
807 g_assert(pulse_data.stream_state == PA_STREAM_READY ||
808 (i != 0 && pulse_data.stream==NULL) );
809 } else {
810 p = pa_stream_flush(pulse_data.stream, NULL, NULL);
811 pa_operation_unref(p);
812 }
813 }
814
pulse_output_stop(gboolean must_flush)815 static gboolean pulse_output_stop(gboolean must_flush)
816 {
817 /* puts("pulse_output_stop"); */
818 if (pulse_data.ready_constsource != NULL)
819 mainloop_constant_source_enable(pulse_data.ready_constsource,FALSE);
820 pulse_data.stopping = 1;
821
822 if (pulse_data.stream != NULL) {
823 if (must_flush || pulse_flush_in_progress()) {
824
825 pulse_flush_start();
826
827 pulse_data.recursing_mainloop = TRUE;
828 while (!pulse_flush_done())
829 pulse_api_block();
830 pulse_data.recursing_mainloop = FALSE;
831 }
832 pulse_data.flush_state = 0;
833
834 pa_stream_disconnect(pulse_data.stream);
835 pulse_data.recursing_mainloop = TRUE;
836 while (pulse_data.stream != NULL)
837 pulse_api_block();
838 pulse_data.recursing_mainloop = FALSE;
839
840 }
841 pulse_data.stopping = 0;
842 return FALSE;
843 }
844
pulse_needs_polling(void)845 static gboolean pulse_needs_polling(void)
846 {
847 return FALSE;
848 }
849
850 struct pulse_scb_data {
851 pa_sample_spec ss;
852 gboolean is_set;
853 };
854
pulse_source_info_cb(pa_context * c,const pa_source_info * i,int eol,gpointer userdata)855 static void pulse_source_info_cb(pa_context *c, const pa_source_info *i,
856 int eol, gpointer userdata)
857 {
858 struct pulse_scb_data *dp = (struct pulse_scb_data *)userdata;
859 if (eol) return;
860 memcpy(&(dp->ss),&(i->sample_spec),sizeof(pa_sample_spec));
861 dp->is_set = TRUE;
862 }
863
864
865 /* PA 0.9.12-? has a bug where passing @DEFAULT_SOURCE@ doesn't work
866 * Perform a quite messy workaround, ifdef:d with the
867 * DEFUALT_SOURCE_BROKEN macro */
868
869 /* Becuase PA didn't introduce PA_MAJOR/MINOR/MICRO macros until 0.9.15,
870 * this will have to be done for the older versions as well... */
871 #define DEFAULT_SOURCE_BROKEN
872
873
874 #ifdef DEFAULT_SOURCE_BROKEN
875
pulse_output_info_cb(pa_context * c,const pa_source_output_info * i,int eol,void * userdata)876 static void pulse_output_info_cb(pa_context *c, const pa_source_output_info *i,
877 int eol, void *userdata)
878 {
879 int *idxp = (int *)userdata;
880 if (eol || *idxp >= 0) return;
881 *idxp = i->source;
882 }
883
pulse_get_default_source_index(pa_context * c)884 static int pulse_get_default_source_index(pa_context *c)
885 {
886 pa_stream *str;
887 pa_sample_spec ss;
888 int i;
889 pa_stream_state_t state;
890 pa_operation *oper;
891 pa_operation_state_t oper_state;
892 int idx;
893 int srcidx;
894
895 ss.format = PA_SAMPLE_S16LE;
896 ss.rate = 44100;
897 ss.channels = 2;
898 str = pa_stream_new(c,"in_fmt_probe",&ss,NULL);
899 g_assert(str != NULL);
900 i = pa_stream_connect_record(str,NULL,NULL,0);
901 g_assert(i == 0);
902 while (1) {
903 state = pa_stream_get_state(str);
904 if (state != PA_STREAM_CREATING) break;
905 pulse_api_block();
906 }
907 if (state != PA_STREAM_READY) {
908 printf("pa_stream_connect_record: %s\n",
909 pa_strerror(pa_context_errno(c)));
910 pa_stream_unref(str);
911 return -1;
912 }
913 idx = pa_stream_get_index(str);
914 /*printf("Calling get_source_output_info_by_index with index %d\n",idx);*/
915 srcidx = -1;
916 oper = pa_context_get_source_output_info(c,idx,
917 pulse_output_info_cb,
918 &srcidx);
919 while (1) {
920 oper_state = pa_operation_get_state(oper);
921 if (oper_state != PA_OPERATION_RUNNING) break;
922 pulse_api_block();
923 }
924 if (srcidx < 0)
925 printf("pa_context_get_output_info_by_name: %s\n",
926 pa_strerror(pa_context_errno(c)));
927 pa_operation_unref(oper);
928 pa_stream_disconnect(str);
929 pa_stream_unref(str);
930 return srcidx;
931 }
932
933 #endif
934
pulse_get_default_source_info(pa_context * c,pa_source_info_cb_t cb,void * userdata)935 static pa_operation *pulse_get_default_source_info(pa_context *c,
936 pa_source_info_cb_t cb,
937 void *userdata)
938 {
939 #ifdef DEFAULT_SOURCE_BROKEN
940 int i;
941 i = pulse_get_default_source_index(c);
942 if (i < 0) return NULL;
943 return pa_context_get_source_info_by_index(c,i,cb,userdata);
944 #else
945 return pa_context_get_source_info_by_name(c,"@DEFAULT_SOURCE@",cb,
946 userdata);
947 #endif
948 }
949
pulse_input_supported_formats(gboolean * complete)950 static GList *pulse_input_supported_formats(gboolean *complete)
951 {
952 pa_operation *p;
953 struct pulse_scb_data d;
954 Dataformat f,*pf;
955
956 if (!pulse_connect(TRUE,TRUE)) {
957 *complete = TRUE;
958 return NULL;
959 }
960 d.is_set = FALSE;
961 p = pulse_get_default_source_info(pulse_data.ctx,
962 pulse_source_info_cb,&d);
963 while (p != NULL && pa_operation_get_state(p) == PA_OPERATION_RUNNING)
964 pulse_api_block();
965 if (p != NULL) pa_operation_unref(p);
966
967 if (!d.is_set) {
968 /* if (p != NULL) puts("Unexpected p!=NULL"); */
969
970 printf("get_default_source_info: %s\n",
971 pa_strerror(pa_context_errno(pulse_data.ctx)));
972 *complete = TRUE;
973 return NULL;
974 }
975
976 if (pa_format_from_pulse(&d.ss,&f)) {
977 printf("Unsupported input format: %s\n",
978 pa_sample_format_to_string(d.ss.format));
979 *complete = TRUE;
980 return NULL;
981 }
982
983 pf = g_malloc(sizeof(*pf));
984 memcpy(pf,&f,sizeof(Dataformat));
985 *complete = TRUE;
986 return g_list_append(NULL,pf);
987 }
988
pulse_input_store(Ringbuf * buf)989 static void pulse_input_store(Ringbuf *buf)
990 {
991 int i;
992 size_t b;
993
994 if (pulse_data.overflow_report_count < pulse_data.overflow_count) {
995 console_message("Overrun occured!");
996 pulse_data.overflow_report_count = pulse_data.overflow_count;
997 }
998
999 if (pulse_data.record_data == NULL) {
1000 i = pa_stream_peek(pulse_data.stream,
1001 (const void **)&pulse_data.record_data,
1002 &pulse_data.record_bytes);
1003 if (i != 0) {
1004 fprintf(stderr,"mhWaveEdit: pa_stream_peek: %s\n",
1005 pa_strerror(pa_context_errno(pulse_data.ctx)));
1006 return;
1007 }
1008 /*
1009 printf("pa_stream_peek: i=%d,data=%p,bytes=%d\n",i,
1010 pulse_data.record_data,pulse_data.record_bytes);
1011 */
1012 pulse_data.record_pos = 0;
1013 }
1014
1015 if (pulse_data.record_data == NULL) return;
1016
1017 b = ringbuf_enqueue(buf,
1018 (gpointer)pulse_data.record_data+pulse_data.record_pos,
1019 pulse_data.record_bytes-pulse_data.record_pos);
1020 pulse_data.record_pos += b;
1021
1022 if (pulse_data.record_pos >= pulse_data.record_bytes) {
1023 g_assert(pulse_data.record_pos == pulse_data.record_bytes);
1024 pa_stream_drop(pulse_data.stream);
1025 pulse_data.record_data = NULL;
1026 } else {
1027 mainloop_constant_source_enable(pulse_data.ready_constsource,TRUE);
1028 }
1029
1030 }
1031
pulse_input_stop(void)1032 static void pulse_input_stop(void)
1033 {
1034 pulse_data.stopping = TRUE;
1035 if (pulse_data.ready_constsource != NULL)
1036 mainloop_constant_source_enable(pulse_data.ready_constsource,FALSE);
1037
1038 if (pulse_data.stream != NULL) {
1039
1040 g_assert(pulse_data.record_flag);
1041
1042 pa_stream_disconnect(pulse_data.stream);
1043 pulse_data.recursing_mainloop = TRUE;
1044 while (pulse_data.stream != NULL)
1045 pulse_api_block();
1046 pulse_data.recursing_mainloop = FALSE;
1047 }
1048 pulse_data.stopping = FALSE;
1049 }
1050
pulse_input_overrun_count(void)1051 static int pulse_input_overrun_count(void)
1052 {
1053 /* Pulse does not report internal overruns in the source,
1054 * therefore just using the overflow_count could give a false
1055 * impression */
1056 return -1;
1057 }
1058
1059
1060
1061 /* --------------------------------
1062 * Preferences UI
1063 */
1064
1065 struct pulse_prefs_ui {
1066 GtkWidget *wnd;
1067 GtkToggleButton *reconn;
1068 Intbox *pblat;
1069 };
1070
pulse_prefs_ok(GtkButton * button,gpointer user_data)1071 static void pulse_prefs_ok(GtkButton *button, gpointer user_data)
1072 {
1073 struct pulse_prefs_ui *up = (struct pulse_prefs_ui *)user_data;
1074 if (intbox_check_limit(up->pblat,0,10000,_("playback latency")))
1075 return;
1076 inifile_set_guint32("pulseLatency",up->pblat->val);
1077 inifile_set_gboolean("pulseReconnect",
1078 gtk_toggle_button_get_active(up->reconn));
1079 gtk_widget_destroy(up->wnd);
1080 }
1081
pulse_preferences(void)1082 static void pulse_preferences(void)
1083 {
1084 GtkWidget *a,*b,*c,*d;
1085 struct pulse_prefs_ui *up;
1086 up = g_malloc(sizeof(*up));
1087 a = gtk_window_new(GTK_WINDOW_DIALOG);
1088 up->wnd = a;
1089 gtk_window_set_title(GTK_WINDOW(a),_("PulseAudio Preferences"));
1090 gtk_window_set_modal(GTK_WINDOW(a),TRUE);
1091 gtk_window_set_position(GTK_WINDOW(a),GTK_WIN_POS_MOUSE);
1092 gtk_container_set_border_width(GTK_CONTAINER(a),5);
1093 gtk_signal_connect_object(GTK_OBJECT(a),"destroy",
1094 GTK_SIGNAL_FUNC(g_free),(GtkObject *)up);
1095 b = gtk_vbox_new(FALSE,5);
1096 gtk_container_add(GTK_CONTAINER(a),b);
1097 c = gtk_hbox_new(FALSE,3);
1098 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
1099 d = gtk_label_new(_("Playback latency: "));
1100 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
1101 d = intbox_new(inifile_get_guint32("pulseLatency",0));
1102 up->pblat = INTBOX(d);
1103 gtk_box_pack_start(GTK_BOX(c),d,TRUE,TRUE,0);
1104 d = gtk_label_new(_("ms (0 for maximum)"));
1105 gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
1106 c = gtk_check_button_new_with_label(_("Reconnect when moving playback"));
1107 up->reconn = GTK_TOGGLE_BUTTON(c);
1108 gtk_toggle_button_set_active(up->reconn,inifile_get_gboolean("pulseReconnect",TRUE));
1109 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
1110 c = gtk_hseparator_new();
1111 gtk_box_pack_end(GTK_BOX(b),c,FALSE,FALSE,0);
1112 c = gtk_hbutton_box_new();
1113 gtk_box_pack_end(GTK_BOX(b),c,FALSE,FALSE,0);
1114 d = gtk_button_new_with_label(_("OK"));
1115 gtk_signal_connect(GTK_OBJECT(d),"clicked",GTK_SIGNAL_FUNC(pulse_prefs_ok),
1116 up);
1117 gtk_container_add(GTK_CONTAINER(c),d);
1118 d = gtk_button_new_with_label(_("Cancel"));
1119 gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
1120 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1121 GTK_OBJECT(a));
1122 gtk_container_add(GTK_CONTAINER(c),d);
1123 gtk_widget_show_all(a);
1124 }
1125