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