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