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