still adjusting asm for better as compatibility..
[pcsx_rearmed.git] / plugins / dfsound / pulseaudio.c
CommitLineData
ef79bbde
P
1/***************************************************************************
2 pulseaudio.c - description
3 -------------------
4begin : Thu Feb 04 2010
5copyright : (C) 2010 by Tristin Celestin
6email : cetris1@umbc.edu
7comment : Much of this was taken from simple.c, in the pulseaudio
8 library
9***************************************************************************/
10/***************************************************************************
11 * *
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. *
17 * *
18 ***************************************************************************/
19
b68bf5c2 20#include <stdio.h>
ef79bbde 21
ef79bbde 22#include <pulse/pulseaudio.h>
b68bf5c2 23#include "out.h"
ef79bbde
P
24
25////////////////////////////////////////////////////////////////////////
26// pulseaudio structs
27////////////////////////////////////////////////////////////////////////
28
29typedef struct {
30 pa_threaded_mainloop *mainloop;
31 pa_context *context;
32 pa_mainloop_api *api;
33 pa_stream *stream;
34 pa_sample_spec spec;
35 int first;
36} Device;
37
38typedef struct {
39 unsigned int frequency;
40 unsigned int latency_in_msec;
41} Settings;
42
43////////////////////////////////////////////////////////////////////////
44// pulseaudio globals
45////////////////////////////////////////////////////////////////////////
46
47static Device device = {
48 .mainloop = NULL,
49 .api = NULL,
50 .context = NULL,
51 .stream = NULL
52};
53
54static Settings settings = {
55 .frequency = 44100,
56 .latency_in_msec = 20,
57};
58
59// the number of bytes written in SoundFeedStreamData
60const int mixlen = 3240;
61
62// used to calculate how much space is used in the buffer, for debugging purposes
63//int maxlength = 0;
64
65////////////////////////////////////////////////////////////////////////
66// CALLBACKS FOR THREADED MAINLOOP
67////////////////////////////////////////////////////////////////////////
68static void context_state_cb (pa_context *context, void *userdata)
69{
70 Device *dev = userdata;
71
72 if ((context == NULL) || (dev == NULL))
73 return;
74
75 switch (pa_context_get_state (context))
76 {
77 case PA_CONTEXT_READY:
78 case PA_CONTEXT_TERMINATED:
79 case PA_CONTEXT_FAILED:
80 pa_threaded_mainloop_signal (dev->mainloop, 0);
81 break;
82
83 case PA_CONTEXT_UNCONNECTED:
84 case PA_CONTEXT_CONNECTING:
85 case PA_CONTEXT_AUTHORIZING:
86 case PA_CONTEXT_SETTING_NAME:
87 break;
88 }
89}
90
91static void stream_state_cb (pa_stream *stream, void * userdata)
92{
93 Device *dev = userdata;
94
95 if ((stream == NULL) || (dev == NULL))
96 return;
97
98 switch (pa_stream_get_state (stream))
99 {
100 case PA_STREAM_READY:
101 case PA_STREAM_FAILED:
102 case PA_STREAM_TERMINATED:
103 pa_threaded_mainloop_signal (dev->mainloop, 0);
104 break;
105
106 case PA_STREAM_UNCONNECTED:
107 case PA_STREAM_CREATING:
108 break;
109 }
110}
111
112static void stream_latency_update_cb (pa_stream *stream, void *userdata)
113{
114 Device *dev = userdata;
115
116 if ((stream == NULL) || (dev == NULL))
117 return;
118
119 pa_threaded_mainloop_signal (dev->mainloop, 0);
120}
121
122static void stream_request_cb (pa_stream *stream, size_t length, void *userdata)
123{
124 Device *dev = userdata;
125
126 if ((stream == NULL) || (dev == NULL))
127 return;
128 pa_threaded_mainloop_signal (dev->mainloop, 0);
129}
130
131////////////////////////////////////////////////////////////////////////
132// SETUP SOUND
133////////////////////////////////////////////////////////////////////////
134
b68bf5c2 135static int pulse_init(void)
ef79bbde
P
136{
137 int error_number;
138
139 // Acquire mainloop ///////////////////////////////////////////////////////
140 device.mainloop = pa_threaded_mainloop_new ();
141 if (device.mainloop == NULL)
142 {
143 fprintf (stderr, "Could not acquire PulseAudio main loop\n");
07c13dfd 144 return -1;
ef79bbde
P
145 }
146
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);
151
152 if (device.context == NULL)
153 {
154 fprintf (stderr, "Could not acquire PulseAudio device context\n");
07c13dfd 155 return -1;
ef79bbde
P
156 }
157
158 // Connect to PulseAudio server ///////////////////////////////////////////
159 if (pa_context_connect (device.context, NULL, 0, NULL) < 0)
160 {
161 error_number = pa_context_errno (device.context);
162 fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number));
07c13dfd 163 return -1;
ef79bbde
P
164 }
165
166 // Run mainloop until sever context is ready //////////////////////////////
167 pa_threaded_mainloop_lock (device.mainloop);
168 if (pa_threaded_mainloop_start (device.mainloop) < 0)
169 {
170 fprintf (stderr, "Could not start mainloop\n");
07c13dfd 171 return -1;
ef79bbde
P
172 }
173
174 pa_context_state_t context_state;
175 context_state = pa_context_get_state (device.context);
176 while (context_state != PA_CONTEXT_READY)
177 {
178 context_state = pa_context_get_state (device.context);
179 if (! PA_CONTEXT_IS_GOOD (context_state))
180 {
181 error_number = pa_context_errno (device.context);
182 fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number));
07c13dfd 183 return -1;
ef79bbde
P
184 }
185 else if (context_state == PA_CONTEXT_READY)
186 break;
187 else
188 fprintf (stderr, "PulseAudio context state is %d\n", context_state);
189 pa_threaded_mainloop_wait (device.mainloop);
190 }
191
192 // Set sample spec ////////////////////////////////////////////////////////
193 device.spec.format = PA_SAMPLE_S16NE;
97ea4077 194 device.spec.channels = 2;
ef79bbde
P
195 device.spec.rate = settings.frequency;
196
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;
202
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);
208
209 // Acquire new stream using spec //////////////////////////////////////////
210 device.stream = pa_stream_new (device.context, "PCSX", &device.spec, NULL);
211 if (device.stream == NULL)
212 {
213 error_number = pa_context_errno (device.context);
214 fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number));
07c13dfd 215 return -1;
ef79bbde
P
216 }
217
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);
222
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)
227 {
b68bf5c2 228 error_number = pa_context_errno (device.context);
ef79bbde 229 fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number));
07c13dfd 230 return -1;
ef79bbde
P
231 }
232
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)
237 {
238 stream_state = pa_stream_get_state (device.stream);
239
240 if (stream_state == PA_STREAM_READY)
241 break;
242
243 else if (! PA_STREAM_IS_GOOD (stream_state))
244 {
245 error_number = pa_context_errno (device.context);
246 fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number));
07c13dfd 247 return -1;
ef79bbde
P
248 }
249 else
250 fprintf (stderr, "PulseAudio stream state is %d\n", stream_state);
251 pa_threaded_mainloop_wait (device.mainloop);
252 }
253
254 pa_threaded_mainloop_unlock (device.mainloop);
255
256 fprintf (stderr, "PulseAudio should be connected\n");
07c13dfd 257 return 0;
ef79bbde
P
258}
259
260////////////////////////////////////////////////////////////////////////
261// REMOVE SOUND
262////////////////////////////////////////////////////////////////////////
07c13dfd 263static void pulse_finish(void)
ef79bbde
P
264{
265 if (device.mainloop != NULL)
266 pa_threaded_mainloop_stop (device.mainloop);
267
268 // Release in reverse order of acquisition
269 if (device.stream != NULL)
270 {
271 pa_stream_unref (device.stream);
272 device.stream = NULL;
273
274 }
275 if (device.context != NULL)
276 {
277 pa_context_disconnect (device.context);
278 pa_context_unref (device.context);
279 device.context = NULL;
280 }
281
282 if (device.mainloop != NULL)
283 {
284 pa_threaded_mainloop_free (device.mainloop);
285 device.mainloop = NULL;
286 }
287
288}
289
290////////////////////////////////////////////////////////////////////////
291// GET BYTES BUFFERED
292////////////////////////////////////////////////////////////////////////
293
07c13dfd 294static int pulse_busy(void)
ef79bbde
P
295{
296 int free_space;
ef79bbde
P
297
298 if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL))
f8edb5bc 299 return 1;
ef79bbde
P
300
301 pa_threaded_mainloop_lock (device.mainloop);
302 free_space = pa_stream_writable_size (device.stream);
303 pa_threaded_mainloop_unlock (device.mainloop);
304
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)
308 {
309 // Don't buffer anymore, just play
310 //fprintf (stderr, "Not buffering.\n");
f8edb5bc 311 return 1;
ef79bbde
P
312 }
313 else
314 {
315 // Buffer some sound
316 //fprintf (stderr, "Buffering.\n");
317 return 0;
318 }
319}
320
321////////////////////////////////////////////////////////////////////////
322// FEED SOUND DATA
323////////////////////////////////////////////////////////////////////////
324
07c13dfd 325static void pulse_feed(void *pSound, int lBytes)
ef79bbde 326{
ef79bbde
P
327 if (device.mainloop != NULL)
328 {
329 pa_threaded_mainloop_lock (device.mainloop);
330 if (pa_stream_write (device.stream, pSound, lBytes, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
331 {
332 fprintf (stderr, "Could not perform write\n");
333 }
334 else
335 {
336 //fprintf (stderr, "Wrote %d bytes\n", lBytes);
337 pa_threaded_mainloop_unlock (device.mainloop);
338 }
339 }
340}
07c13dfd 341
342void out_register_pulse(struct out_driver *drv)
343{
344 drv->name = "pulseaudio";
345 drv->init = pulse_init;
346 drv->finish = pulse_finish;
347 drv->busy = pulse_busy;
348 drv->feed = pulse_feed;
349}