1 /***************************************************************************
2 pulseaudio.c - description
4 begin : Thu Feb 04 2010
5 copyright : (C) 2010 by Tristin Celestin
6 email : cetris1@umbc.edu
7 comment : Much of this was taken from simple.c, in the pulseaudio
9 ***************************************************************************/
10 /***************************************************************************
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. See also the license.txt file for *
16 * additional informations. *
18 ***************************************************************************/
22 #include <pulse/pulseaudio.h>
25 ////////////////////////////////////////////////////////////////////////
27 ////////////////////////////////////////////////////////////////////////
30 pa_threaded_mainloop *mainloop;
39 unsigned int frequency;
40 unsigned int latency_in_msec;
43 ////////////////////////////////////////////////////////////////////////
45 ////////////////////////////////////////////////////////////////////////
47 static Device device = {
54 static Settings settings = {
56 .latency_in_msec = 20,
59 // the number of bytes written in SoundFeedStreamData
60 const int mixlen = 3240;
62 // used to calculate how much space is used in the buffer, for debugging purposes
65 ////////////////////////////////////////////////////////////////////////
66 // CALLBACKS FOR THREADED MAINLOOP
67 ////////////////////////////////////////////////////////////////////////
68 static void context_state_cb (pa_context *context, void *userdata)
70 Device *dev = userdata;
72 if ((context == NULL) || (dev == NULL))
75 switch (pa_context_get_state (context))
77 case PA_CONTEXT_READY:
78 case PA_CONTEXT_TERMINATED:
79 case PA_CONTEXT_FAILED:
80 pa_threaded_mainloop_signal (dev->mainloop, 0);
83 case PA_CONTEXT_UNCONNECTED:
84 case PA_CONTEXT_CONNECTING:
85 case PA_CONTEXT_AUTHORIZING:
86 case PA_CONTEXT_SETTING_NAME:
91 static void stream_state_cb (pa_stream *stream, void * userdata)
93 Device *dev = userdata;
95 if ((stream == NULL) || (dev == NULL))
98 switch (pa_stream_get_state (stream))
100 case PA_STREAM_READY:
101 case PA_STREAM_FAILED:
102 case PA_STREAM_TERMINATED:
103 pa_threaded_mainloop_signal (dev->mainloop, 0);
106 case PA_STREAM_UNCONNECTED:
107 case PA_STREAM_CREATING:
112 static void stream_latency_update_cb (pa_stream *stream, void *userdata)
114 Device *dev = userdata;
116 if ((stream == NULL) || (dev == NULL))
119 pa_threaded_mainloop_signal (dev->mainloop, 0);
122 static void stream_request_cb (pa_stream *stream, size_t length, void *userdata)
124 Device *dev = userdata;
126 if ((stream == NULL) || (dev == NULL))
128 pa_threaded_mainloop_signal (dev->mainloop, 0);
131 ////////////////////////////////////////////////////////////////////////
133 ////////////////////////////////////////////////////////////////////////
135 static int pulse_init(void)
139 // Acquire mainloop ///////////////////////////////////////////////////////
140 device.mainloop = pa_threaded_mainloop_new ();
141 if (device.mainloop == NULL)
143 fprintf (stderr, "Could not acquire PulseAudio main loop\n");
147 // Acquire context ////////////////////////////////////////////////////////
148 device.api = pa_threaded_mainloop_get_api (device.mainloop);
149 device.context = pa_context_new (device.api, "PCSX");
150 pa_context_set_state_callback (device.context, context_state_cb, &device);
152 if (device.context == NULL)
154 fprintf (stderr, "Could not acquire PulseAudio device context\n");
158 // Connect to PulseAudio server ///////////////////////////////////////////
159 if (pa_context_connect (device.context, NULL, 0, NULL) < 0)
161 error_number = pa_context_errno (device.context);
162 fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number));
166 // Run mainloop until sever context is ready //////////////////////////////
167 pa_threaded_mainloop_lock (device.mainloop);
168 if (pa_threaded_mainloop_start (device.mainloop) < 0)
170 fprintf (stderr, "Could not start mainloop\n");
174 pa_context_state_t context_state;
175 context_state = pa_context_get_state (device.context);
176 while (context_state != PA_CONTEXT_READY)
178 context_state = pa_context_get_state (device.context);
179 if (! PA_CONTEXT_IS_GOOD (context_state))
181 error_number = pa_context_errno (device.context);
182 fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number));
185 else if (context_state == PA_CONTEXT_READY)
188 fprintf (stderr, "PulseAudio context state is %d\n", context_state);
189 pa_threaded_mainloop_wait (device.mainloop);
192 // Set sample spec ////////////////////////////////////////////////////////
193 device.spec.format = PA_SAMPLE_S16NE;
194 device.spec.channels = 2;
195 device.spec.rate = settings.frequency;
197 pa_buffer_attr buffer_attributes;
198 buffer_attributes.tlength = pa_bytes_per_second (& device.spec) / 5;
199 buffer_attributes.maxlength = buffer_attributes.tlength * 3;
200 buffer_attributes.minreq = buffer_attributes.tlength / 3;
201 buffer_attributes.prebuf = buffer_attributes.tlength;
203 //maxlength = buffer_attributes.maxlength;
204 //fprintf (stderr, "Total space: %u\n", buffer_attributes.maxlength);
205 //fprintf (stderr, "Minimum request size: %u\n", buffer_attributes.minreq);
206 //fprintf (stderr, "Bytes needed before playback: %u\n", buffer_attributes.prebuf);
207 //fprintf (stderr, "Target buffer size: %lu\n", buffer_attributes.tlength);
209 // Acquire new stream using spec //////////////////////////////////////////
210 device.stream = pa_stream_new (device.context, "PCSX", &device.spec, NULL);
211 if (device.stream == NULL)
213 error_number = pa_context_errno (device.context);
214 fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number));
218 // Set callbacks for server events ////////////////////////////////////////
219 pa_stream_set_state_callback (device.stream, stream_state_cb, &device);
220 pa_stream_set_write_callback (device.stream, stream_request_cb, &device);
221 pa_stream_set_latency_update_callback (device.stream, stream_latency_update_cb, &device);
223 // Ready stream for playback //////////////////////////////////////////////
224 pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
225 //pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS);
226 if (pa_stream_connect_playback (device.stream, NULL, &buffer_attributes, flags, NULL, NULL) < 0)
228 error_number = pa_context_errno (device.context);
229 fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number));
233 // Run mainloop until stream is ready /////////////////////////////////////
234 pa_stream_state_t stream_state;
235 stream_state = pa_stream_get_state (device.stream);
236 while (stream_state != PA_STREAM_READY)
238 stream_state = pa_stream_get_state (device.stream);
240 if (stream_state == PA_STREAM_READY)
243 else if (! PA_STREAM_IS_GOOD (stream_state))
245 error_number = pa_context_errno (device.context);
246 fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number));
250 fprintf (stderr, "PulseAudio stream state is %d\n", stream_state);
251 pa_threaded_mainloop_wait (device.mainloop);
254 pa_threaded_mainloop_unlock (device.mainloop);
256 fprintf (stderr, "PulseAudio should be connected\n");
260 ////////////////////////////////////////////////////////////////////////
262 ////////////////////////////////////////////////////////////////////////
263 static void pulse_finish(void)
265 if (device.mainloop != NULL)
266 pa_threaded_mainloop_stop (device.mainloop);
268 // Release in reverse order of acquisition
269 if (device.stream != NULL)
271 pa_stream_unref (device.stream);
272 device.stream = NULL;
275 if (device.context != NULL)
277 pa_context_disconnect (device.context);
278 pa_context_unref (device.context);
279 device.context = NULL;
282 if (device.mainloop != NULL)
284 pa_threaded_mainloop_free (device.mainloop);
285 device.mainloop = NULL;
290 ////////////////////////////////////////////////////////////////////////
291 // GET BYTES BUFFERED
292 ////////////////////////////////////////////////////////////////////////
294 static int pulse_busy(void)
298 if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL))
301 pa_threaded_mainloop_lock (device.mainloop);
302 free_space = pa_stream_writable_size (device.stream);
303 pa_threaded_mainloop_unlock (device.mainloop);
305 //fprintf (stderr, "Free space: %d\n", free_space);
306 //fprintf (stderr, "Used space: %d\n", maxlength - free_space);
307 if (free_space < mixlen * 3)
309 // Don't buffer anymore, just play
310 //fprintf (stderr, "Not buffering.\n");
316 //fprintf (stderr, "Buffering.\n");
321 ////////////////////////////////////////////////////////////////////////
323 ////////////////////////////////////////////////////////////////////////
325 static void pulse_feed(void *pSound, int lBytes)
327 if (device.mainloop != NULL)
329 pa_threaded_mainloop_lock (device.mainloop);
330 if (pa_stream_write (device.stream, pSound, lBytes, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
332 fprintf (stderr, "Could not perform write\n");
336 //fprintf (stderr, "Wrote %d bytes\n", lBytes);
337 pa_threaded_mainloop_unlock (device.mainloop);
342 void out_register_pulse(struct out_driver *drv)
344 drv->name = "pulseaudio";
345 drv->init = pulse_init;
346 drv->finish = pulse_finish;
347 drv->busy = pulse_busy;
348 drv->feed = pulse_feed;