dfxvideo: massive cleanup
[pcsx_rearmed.git] / plugins / dfsound / pulseaudio.c
1 /***************************************************************************
2                 pulseaudio.c  -  description
3                      -------------------
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
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
33 typedef 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
42 typedef struct {
43      unsigned int frequency;
44      unsigned int latency_in_msec;
45 } Settings;
46
47 ////////////////////////////////////////////////////////////////////////
48 // pulseaudio globals
49 ////////////////////////////////////////////////////////////////////////
50
51 static Device device = {
52      .mainloop = NULL,
53      .api = NULL,
54      .context = NULL,
55      .stream = NULL
56 };
57
58 static Settings settings = {
59      .frequency = 44100,
60      .latency_in_msec = 20,
61 };
62
63 // the number of bytes written in SoundFeedStreamData
64 const 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 ////////////////////////////////////////////////////////////////////////
72 static 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
95 static 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
116 static 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
126 static 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
139 void 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 ////////////////////////////////////////////////////////////////////////
270 void 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
301 unsigned 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
335 void 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