1 /***
2 This file is part of xmms-pulse.
3
4 Updates for Audacious are:
5 Copyright 2016 John Lindgren
6
7 xmms-pulse is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 xmms-pulse is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with xmms-pulse; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 USA.
21 ***/
22
23 #include <pulse/pulseaudio.h>
24
25 #include <libaudcore/i18n.h>
26 #include <libaudcore/plugin.h>
27 #include <libaudcore/runtime.h>
28 #include <libaudcore/threads.h>
29 #include <libaudcore/preferences.h>
30
31 class PulseOutput : public OutputPlugin
32 {
33 public:
34 static const char about[];
35 static const PreferencesWidget widgets[];
36 static const PluginPreferences prefs;
37 static const char * const prefs_defaults[];
38 static const String default_context_name;
39 static const String default_stream_name;
40
41 static constexpr PluginInfo info = {
42 N_("PulseAudio Output"),
43 PACKAGE,
44 about,
45 & prefs
46 };
47
PulseOutput()48 constexpr PulseOutput () : OutputPlugin (info, 8) {}
49
50 bool init ();
51 void cleanup ();
52
53 StereoVolume get_volume ();
54 void set_volume (StereoVolume v);
55
56 bool open_audio (int fmt, int rate, int nch, String & error);
57 void close_audio ();
58
59 void period_wait ();
60 int write_audio (const void * ptr, int length);
61 void drain ();
62
63 int get_delay ();
64
65 void pause (bool pause);
66 void flush ();
67 };
68
69 EXPORT PulseOutput aud_plugin_instance;
70
71 const PreferencesWidget PulseOutput::widgets[] = {
72 WidgetEntry (N_("Context name:"),
73 WidgetString ("pulse", "context_name")),
74 WidgetEntry (N_("Stream name:"),
75 WidgetString ("pulse", "stream_name")),
76 };
77
78 const PluginPreferences PulseOutput::prefs = {{widgets}};
79
80 const String PulseOutput::default_context_name = String ("Audacious");
81 const String PulseOutput::default_stream_name = String ("Audacious");
82
83 const char * const PulseOutput::prefs_defaults[] = {
84 "context_name", PulseOutput::default_context_name,
85 "stream_name", PulseOutput::default_stream_name,
86 nullptr
87 };
88
89 static aud::mutex pulse_mutex;
90 static aud::condvar pulse_cond;
91
92 static pa_context * context = nullptr;
93 static pa_stream * stream = nullptr;
94 static pa_mainloop * mainloop = nullptr;
95
96 static bool connected, flushed, polling;
97
98 static pa_cvolume volume;
99
100 static StereoVolume saved_volume = {0, 0};
101 static bool saved_volume_changed = false;
102
103 /* Check whether the connection is still alive. */
alive()104 static bool alive ()
105 {
106 return pa_context_get_state (context) == PA_CONTEXT_READY &&
107 pa_stream_get_state (stream) == PA_STREAM_READY;
108 }
109
110 /* Cooperative polling method. Only one thread calls the actual poll function,
111 * and dispatches the events received. Any other threads simply wait for the
112 * first thread to finish. */
poll_events(aud::mutex::holder & lock)113 static void poll_events (aud::mutex::holder & lock)
114 {
115 if (polling)
116 pulse_cond.wait (lock);
117 else
118 {
119 pa_mainloop_prepare (mainloop, -1);
120
121 polling = true;
122 lock.unlock ();
123
124 pa_mainloop_poll (mainloop);
125
126 lock.lock ();
127 polling = false;
128
129 pa_mainloop_dispatch (mainloop);
130
131 pulse_cond.notify_all ();
132 }
133 }
134
135 /* Wait for an asynchronous operation to complete. Return immediately if the
136 * connection dies. */
finish(pa_operation * op,aud::mutex::holder & lock)137 static bool finish (pa_operation * op, aud::mutex::holder & lock)
138 {
139 pa_operation_state_t state;
140 while ((state = pa_operation_get_state (op)) != PA_OPERATION_DONE && alive ())
141 poll_events (lock);
142
143 pa_operation_unref (op);
144 return (state == PA_OPERATION_DONE);
145 }
146
147 #define REPORT(function) do { \
148 AUDERR ("%s() failed: %s\n", function, pa_strerror (pa_context_errno (context))); \
149 } while (0)
150
151 #define CHECK(function, ...) do { \
152 auto op = function (__VA_ARGS__, & success); \
153 if (! op || ! finish (op, lock) || ! success) \
154 REPORT (#function); \
155 } while (0)
156
info_cb(pa_context *,const pa_sink_input_info * i,int,void * userdata)157 static void info_cb (pa_context *, const pa_sink_input_info * i, int, void * userdata)
158 {
159 if (! i)
160 return;
161
162 volume = i->volume;
163
164 if (userdata)
165 * (int *) userdata = 1;
166 }
167
subscribe_cb(pa_context * c,pa_subscription_event_type t,uint32_t index,void *)168 static void subscribe_cb (pa_context * c, pa_subscription_event_type t, uint32_t index, void *)
169 {
170 pa_operation * o;
171
172 if (! stream || index != pa_stream_get_index (stream) ||
173 (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE) &&
174 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_NEW)))
175 return;
176
177 if (! (o = pa_context_get_sink_input_info (c, index, info_cb, nullptr)))
178 {
179 REPORT ("pa_context_get_sink_input_info");
180 return;
181 }
182
183 pa_operation_unref (o);
184 }
185
stream_success_cb(pa_stream *,int success,void * userdata)186 static void stream_success_cb (pa_stream *, int success, void * userdata)
187 {
188 if (userdata)
189 * (int * ) userdata = success;
190 }
191
context_success_cb(pa_context *,int success,void * userdata)192 static void context_success_cb (pa_context *, int success, void * userdata)
193 {
194 if (userdata)
195 * (int * ) userdata = success;
196 }
197
get_volume_locked(aud::mutex::holder & lock)198 static void get_volume_locked (aud::mutex::holder & lock)
199 {
200 if (! polling)
201 {
202 /* read any pending events to get the lastest volume */
203 while (pa_mainloop_iterate (mainloop, 0, nullptr) > 0)
204 continue;
205 }
206
207 if (volume.channels == 2)
208 {
209 saved_volume.left = aud::rescale<int> (volume.values[0], PA_VOLUME_NORM, 100);
210 saved_volume.right = aud::rescale<int> (volume.values[1], PA_VOLUME_NORM, 100);
211 }
212 else
213 {
214 saved_volume.left = aud::rescale<int> (pa_cvolume_avg (& volume), PA_VOLUME_NORM, 100);
215 saved_volume.right = saved_volume.left;
216 }
217
218 saved_volume_changed = false;
219 }
220
get_volume()221 StereoVolume PulseOutput::get_volume ()
222 {
223 auto lock = pulse_mutex.take ();
224
225 if (connected)
226 get_volume_locked (lock);
227
228 return saved_volume;
229 }
230
set_volume_locked(aud::mutex::holder & lock)231 static void set_volume_locked (aud::mutex::holder & lock)
232 {
233 if (volume.channels != 1)
234 {
235 volume.values[0] = aud::rescale<int> (saved_volume.left, 100, PA_VOLUME_NORM);
236 volume.values[1] = aud::rescale<int> (saved_volume.right, 100, PA_VOLUME_NORM);
237 volume.channels = 2;
238 }
239 else
240 {
241 int mono = aud::max (saved_volume.left, saved_volume.right);
242 volume.values[0] = aud::rescale<int> (mono, 100, PA_VOLUME_NORM);
243 volume.channels = 1;
244 }
245
246 int success = 0;
247 CHECK (pa_context_set_sink_input_volume, context,
248 pa_stream_get_index (stream), & volume, context_success_cb);
249
250 saved_volume_changed = false;
251 }
252
set_volume(StereoVolume v)253 void PulseOutput::set_volume (StereoVolume v)
254 {
255 auto lock = pulse_mutex.take ();
256
257 saved_volume = v;
258 saved_volume_changed = true;
259
260 if (connected)
261 set_volume_locked (lock);
262 }
263
pause(bool pause)264 void PulseOutput::pause (bool pause)
265 {
266 auto lock = pulse_mutex.take ();
267
268 int success = 0;
269 CHECK (pa_stream_cork, stream, pause, stream_success_cb);
270 }
271
get_delay()272 int PulseOutput::get_delay ()
273 {
274 auto lock = pulse_mutex.take ();
275
276 pa_usec_t usec;
277 int neg;
278
279 if (pa_stream_get_latency (stream, & usec, & neg) == PA_OK)
280 return usec / 1000;
281 else
282 return 0;
283 }
284
drain()285 void PulseOutput::drain ()
286 {
287 auto lock = pulse_mutex.take ();
288
289 int success = 0;
290 CHECK (pa_stream_drain, stream, stream_success_cb);
291 }
292
flush()293 void PulseOutput::flush ()
294 {
295 auto lock = pulse_mutex.take ();
296
297 int success = 0;
298 CHECK (pa_stream_flush, stream, stream_success_cb);
299
300 /* wake up period_wait() */
301 flushed = true;
302 if (polling)
303 pa_mainloop_wakeup (mainloop);
304 }
305
period_wait()306 void PulseOutput::period_wait ()
307 {
308 auto lock = pulse_mutex.take ();
309
310 int success = 0;
311 CHECK (pa_stream_trigger, stream, stream_success_cb);
312
313 /* if the connection dies, wait until flush() is called */
314 while ((! pa_stream_writable_size (stream) || ! alive ()) && ! flushed)
315 poll_events (lock);
316 }
317
write_audio(const void * ptr,int length)318 int PulseOutput::write_audio (const void * ptr, int length)
319 {
320 auto lock = pulse_mutex.take ();
321 int ret = 0;
322
323 length = aud::min ((size_t) length, pa_stream_writable_size (stream));
324
325 if (pa_stream_write (stream, ptr, length, nullptr, 0, PA_SEEK_RELATIVE) < 0)
326 REPORT ("pa_stream_write");
327 else
328 ret = length;
329
330 flushed = false;
331 return ret;
332 }
333
close_audio_locked(aud::mutex::holder & lock)334 static void close_audio_locked (aud::mutex::holder & lock)
335 {
336 /* wait for any parallel tasks (e.g. set_volume()) to complete */
337 while (polling)
338 pulse_cond.wait (lock);
339
340 connected = false;
341
342 if (stream)
343 {
344 pa_stream_disconnect (stream);
345 pa_stream_unref (stream);
346 stream = nullptr;
347 }
348
349 if (context)
350 {
351 pa_context_disconnect (context);
352 pa_context_unref (context);
353 context = nullptr;
354 }
355
356 if (mainloop)
357 {
358 pa_mainloop_free (mainloop);
359 mainloop = nullptr;
360 }
361 }
362
close_audio()363 void PulseOutput::close_audio ()
364 {
365 auto lock = pulse_mutex.take ();
366 close_audio_locked (lock);
367 }
368
to_pulse_format(int aformat)369 static pa_sample_format_t to_pulse_format (int aformat)
370 {
371 switch (aformat)
372 {
373 case FMT_U8: return PA_SAMPLE_U8;
374 case FMT_S16_LE: return PA_SAMPLE_S16LE;
375 case FMT_S16_BE: return PA_SAMPLE_S16BE;
376 #ifdef PA_SAMPLE_S24_32LE
377 case FMT_S24_LE: return PA_SAMPLE_S24_32LE;
378 case FMT_S24_BE: return PA_SAMPLE_S24_32BE;
379 #endif
380 #ifdef PA_SAMPLE_S32LE
381 case FMT_S32_LE: return PA_SAMPLE_S32LE;
382 case FMT_S32_BE: return PA_SAMPLE_S32BE;
383 #endif
384 case FMT_FLOAT: return PA_SAMPLE_FLOAT32NE;
385 default: return PA_SAMPLE_INVALID;
386 }
387 }
388
set_sample_spec(pa_sample_spec & ss,int fmt,int rate,int nch)389 static bool set_sample_spec (pa_sample_spec & ss, int fmt, int rate, int nch)
390 {
391 ss.format = to_pulse_format (fmt);
392 if (ss.format == PA_SAMPLE_INVALID)
393 return false;
394
395 ss.rate = rate;
396 ss.channels = nch;
397
398 return pa_sample_spec_valid (& ss);
399 }
400
set_buffer_attr(pa_buffer_attr & buffer,const pa_sample_spec & ss)401 static void set_buffer_attr (pa_buffer_attr & buffer, const pa_sample_spec & ss)
402 {
403 int buffer_ms = aud_get_int ("output_buffer_size");
404 size_t buffer_size = pa_usec_to_bytes ((pa_usec_t) 1000 * buffer_ms, & ss);
405
406 buffer.maxlength = (uint32_t) -1;
407 buffer.tlength = buffer_size;
408 buffer.prebuf = (uint32_t) -1;
409 buffer.minreq = (uint32_t) -1;
410 buffer.fragsize = buffer_size;
411 }
412
get_context_name()413 static String get_context_name ()
414 {
415 String context_name = aud_get_str ("pulse", "context_name");
416 if (context_name == String (""))
417 {
418 return PulseOutput::default_context_name;
419 }
420 return context_name;
421 }
422
create_context(aud::mutex::holder & lock)423 static bool create_context (aud::mutex::holder & lock)
424 {
425 if (! (mainloop = pa_mainloop_new ()))
426 {
427 AUDERR ("Failed to allocate main loop\n");
428 return false;
429 }
430
431 if (! (context = pa_context_new (pa_mainloop_get_api (mainloop), get_context_name ())))
432 {
433 AUDERR ("Failed to allocate context\n");
434 return false;
435 }
436
437 if (pa_context_connect (context, nullptr, (pa_context_flags_t) 0, nullptr) < 0)
438 {
439 REPORT ("pa_context_connect");
440 return false;
441 }
442
443 /* Wait until the context is ready */
444 pa_context_state_t cstate;
445 while ((cstate = pa_context_get_state (context)) != PA_CONTEXT_READY)
446 {
447 if (cstate == PA_CONTEXT_TERMINATED || cstate == PA_CONTEXT_FAILED)
448 {
449 REPORT ("pa_context_connect");
450 return false;
451 }
452
453 poll_events (lock);
454 }
455
456 return true;
457 }
458
get_stream_name()459 static String get_stream_name ()
460 {
461 String stream_name = aud_get_str ("pulse", "stream_name");
462 if (stream_name == String (""))
463 {
464 return PulseOutput::default_stream_name;
465 }
466 return stream_name;
467 }
468
create_stream(aud::mutex::holder & lock,const pa_sample_spec & ss)469 static bool create_stream (aud::mutex::holder & lock, const pa_sample_spec & ss)
470 {
471 if (! (stream = pa_stream_new (context, get_stream_name (), & ss, nullptr)))
472 {
473 REPORT ("pa_stream_new");
474 return false;
475 }
476
477 /* Connect stream with sink and default volume */
478 pa_buffer_attr buffer;
479 set_buffer_attr (buffer, ss);
480
481 auto flags = pa_stream_flags_t (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
482 if (pa_stream_connect_playback (stream, nullptr, & buffer, flags, nullptr, nullptr) < 0)
483 {
484 REPORT ("pa_stream_connect_playback");
485 return false;
486 }
487
488 /* Wait until the stream is ready */
489 pa_stream_state_t sstate;
490 while ((sstate = pa_stream_get_state (stream)) != PA_STREAM_READY)
491 {
492 if (sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED)
493 {
494 REPORT ("pa_stream_connect_playback");
495 return false;
496 }
497
498 poll_events (lock);
499 }
500
501 return true;
502 }
503
subscribe_events(aud::mutex::holder & lock)504 static bool subscribe_events (aud::mutex::holder & lock)
505 {
506 pa_context_set_subscribe_callback (context, subscribe_cb, nullptr);
507
508 /* Now subscribe to events */
509 int success = 0;
510 CHECK (pa_context_subscribe, context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb);
511 if (! success)
512 return false;
513
514 /* Now request the initial stream info */
515 success = 0;
516 CHECK (pa_context_get_sink_input_info, context, pa_stream_get_index (stream), info_cb);
517 if (! success)
518 return false;
519
520 return true;
521 }
522
open_audio(int fmt,int rate,int nch,String & error)523 bool PulseOutput::open_audio (int fmt, int rate, int nch, String & error)
524 {
525 auto lock = pulse_mutex.take ();
526
527 pa_sample_spec ss;
528 if (! set_sample_spec (ss, fmt, rate, nch))
529 return false;
530
531 if (! create_context (lock) ||
532 ! create_stream (lock, ss) ||
533 ! subscribe_events (lock))
534 {
535 close_audio_locked (lock);
536 return false;
537 }
538
539 connected = true;
540 flushed = true;
541
542 if (saved_volume_changed)
543 set_volume_locked (lock);
544 else
545 get_volume_locked (lock);
546
547 return true;
548 }
549
init()550 bool PulseOutput::init ()
551 {
552 aud_config_set_defaults ("pulse", prefs_defaults);
553
554 /* check for a running server and get initial volume */
555 String error;
556 if (! open_audio (FMT_S16_NE, 44100, 2, error))
557 return false;
558
559 close_audio ();
560 return true;
561 }
562
cleanup()563 void PulseOutput::cleanup ()
564 {
565 if (saved_volume_changed)
566 {
567 /* save final volume */
568 String error;
569 if (open_audio (FMT_S16_NE, 44100, 2, error))
570 close_audio ();
571 }
572 }
573
574 const char PulseOutput::about[] =
575 N_("Audacious PulseAudio Output Plugin\n\n"
576 "This program is free software; you can redistribute it and/or modify "
577 "it under the terms of the GNU General Public License as published by "
578 "the Free Software Foundation; either version 2 of the License, or "
579 "(at your option) any later version.\n\n"
580 "This program is distributed in the hope that it will be useful, "
581 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
582 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
583 "GNU General Public License for more details.\n\n"
584 "You should have received a copy of the GNU General Public License "
585 "along with this program; if not, write to the Free Software "
586 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, "
587 "USA.");
588