2 * This file is part of DisOrder.
3 * Copyright (C) 2013 Richard Kettlewell, 2018 Mark Wooding
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /** @file lib/uaudio-pulseaudio.c
19 * @brief Support for PulseAudio backend */
24 #include <pulse/context.h>
25 #include <pulse/error.h>
26 #include <pulse/thread-mainloop.h>
27 #include <pulse/stream.h>
32 #include "configuration.h"
34 static const char *const pulseaudio_options
[] = {
39 static pa_threaded_mainloop
*loop
;
40 static pa_context
*ctx
;
41 static pa_stream
*str
;
43 #define PAERRSTR pa_strerror(pa_context_errno(ctx))
45 /** @brief Callback: wake up main loop when the context is ready. */
46 static void cb_ctxstate(attribute((unused
)) pa_context
*xctx
,
47 attribute((unused
)) void *p
) {
48 pa_context_state_t st
= pa_context_get_state(ctx
);
50 case PA_CONTEXT_READY
:
51 pa_threaded_mainloop_signal(loop
, 0);
54 if(!PA_CONTEXT_IS_GOOD(st
))
55 disorder_fatal(0, "pulseaudio failed: %s", PAERRSTR
);
59 /** @brief Callback: wake up main loop when the stream is ready. */
60 static void cb_strstate(attribute((unused
)) pa_stream
*xstr
,
61 attribute((unused
)) void *p
) {
62 pa_stream_state_t st
= pa_stream_get_state(str
);
65 pa_threaded_mainloop_signal(loop
, 0);
68 if(!PA_STREAM_IS_GOOD(st
))
69 disorder_fatal(0, "pulseaudio failed: %s", PAERRSTR
);
73 /** @brief Callback: wake up main loop when there's output buffer space. */
74 static void cb_wakeup(attribute((unused
)) pa_stream
*xstr
,
75 attribute((unused
)) size_t sz
,
76 attribute((unused
)) void *p
)
77 { pa_threaded_mainloop_signal(loop
, 0); }
79 /** @brief Open the PulseAudio sound device */
80 static void pulseaudio_open() {
81 /* Much of the following is cribbed from the PulseAudio `simple' source. */
85 /* Set up the sample format. */
88 case 8: if(!uaudio_signed
) ss
.format
= PA_SAMPLE_U8
; break;
89 case 16: if(uaudio_signed
) ss
.format
= PA_SAMPLE_S16NE
; break;
90 case 32: if(uaudio_signed
) ss
.format
= PA_SAMPLE_S32NE
; break;
93 disorder_fatal(0, "unsupported uaudio format (%d, %d)",
94 uaudio_bits
, uaudio_signed
);
95 ss
.channels
= uaudio_channels
;
96 ss
.rate
= uaudio_rate
;
98 /* Create the random PulseAudio pieces. */
99 loop
= pa_threaded_mainloop_new();
100 if(!loop
) disorder_fatal(0, "failed to create pulseaudio main loop");
101 pa_threaded_mainloop_lock(loop
);
102 ctx
= pa_context_new(pa_threaded_mainloop_get_api(loop
),
103 uaudio_get("application", "DisOrder"));
104 if(!ctx
) disorder_fatal(0, "failed to create pulseaudio context");
105 pa_context_set_state_callback(ctx
, cb_ctxstate
, 0);
106 if(pa_context_connect(ctx
, 0, 0, 0) < 0)
107 disorder_fatal(0, "failed to connect to pulseaudio server: %s",
110 /* Set the main loop going. */
111 if(pa_threaded_mainloop_start(loop
) < 0)
112 disorder_fatal(0, "failed to start pulseaudio main loop");
113 while(pa_context_get_state(ctx
) != PA_CONTEXT_READY
)
114 pa_threaded_mainloop_wait(loop
);
116 /* Set up my stream. */
117 str
= pa_stream_new(ctx
, "DisOrder", &ss
, 0);
119 disorder_fatal(0, "failed to create pulseaudio stream: %s", PAERRSTR
);
120 pa_stream_set_write_callback(str
, cb_wakeup
, 0);
121 if(pa_stream_connect_playback(str
, 0, 0,
122 PA_STREAM_ADJUST_LATENCY
,
124 disorder_fatal(0, "failed to connect pulseaudio stream for playback: %s",
126 pa_stream_set_state_callback(str
, cb_strstate
, 0);
128 /* Wait until the stream is ready. */
129 while(pa_stream_get_state(str
) != PA_STREAM_READY
)
130 pa_threaded_mainloop_wait(loop
);
133 pa_threaded_mainloop_unlock(loop
);
136 /** @brief Close the PulseAudio sound device */
137 static void pulseaudio_close(void) {
138 if(loop
) pa_threaded_mainloop_stop(loop
);
139 if(str
) { pa_stream_unref(str
); str
= 0; }
140 if(ctx
) { pa_context_disconnect(ctx
); pa_context_unref(ctx
); ctx
= 0; }
141 if(loop
) { pa_threaded_mainloop_free(loop
); loop
= 0; }
144 /** @brief Actually play sound via PulseAudio */
145 static size_t pulseaudio_play(void *buffer
, size_t samples
,
146 attribute((unused
)) unsigned flags
) {
147 unsigned char *p
= buffer
;
148 size_t n
, sz
= samples
*uaudio_sample_size
;
150 pa_threaded_mainloop_lock(loop
);
153 /* Wait until some output space becomes available. */
154 while(!(n
= pa_stream_writable_size(str
)))
155 pa_threaded_mainloop_wait(loop
);
157 if(pa_stream_write(str
, p
, n
, 0, 0, PA_SEEK_RELATIVE
) < 0)
158 disorder_fatal(0, "failed to write pulseaudio data: %s", PAERRSTR
);
161 pa_threaded_mainloop_unlock(loop
);
165 static void pulseaudio_start(uaudio_callback
*callback
,
168 uaudio_thread_start(callback
, userdata
, pulseaudio_play
,
169 32 / uaudio_sample_size
,
170 4096 / uaudio_sample_size
,
174 static void pulseaudio_stop(void) {
175 uaudio_thread_stop();
179 static void pulseaudio_open_mixer(void) {
180 disorder_error(0, "no pulseaudio mixer support yet");
183 static void pulseaudio_close_mixer(void) {
186 static void pulseaudio_get_volume(int *left
, int *right
) {
190 static void pulseaudio_set_volume(int *left
, int *right
) {
194 static void pulseaudio_configure(void) {
197 const struct uaudio uaudio_pulseaudio
= {
198 .name
= "pulseaudio",
199 .options
= pulseaudio_options
,
200 .start
= pulseaudio_start
,
201 .stop
= pulseaudio_stop
,
202 .activate
= uaudio_thread_activate
,
203 .deactivate
= uaudio_thread_deactivate
,
204 .open_mixer
= pulseaudio_open_mixer
,
205 .close_mixer
= pulseaudio_close_mixer
,
206 .get_volume
= pulseaudio_get_volume
,
207 .set_volume
= pulseaudio_set_volume
,
208 .configure
= pulseaudio_configure
,
209 .flags
= UAUDIO_API_CLIENT
,