mave plugin and core shared includes to single location
[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 #define _IN_OSS
23
24 #include "externals.h"
25 #include <pulse/pulseaudio.h>
26
27 ////////////////////////////////////////////////////////////////////////
28 // pulseaudio structs
29 ////////////////////////////////////////////////////////////////////////
30
31 typedef 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
40 typedef struct {
41      unsigned int frequency;
42      unsigned int latency_in_msec;
43 } Settings;
44
45 ////////////////////////////////////////////////////////////////////////
46 // pulseaudio globals
47 ////////////////////////////////////////////////////////////////////////
48
49 static Device device = {
50      .mainloop = NULL,
51      .api = NULL,
52      .context = NULL,
53      .stream = NULL
54 };
55
56 static Settings settings = {
57      .frequency = 44100,
58      .latency_in_msec = 20,
59 };
60
61 // the number of bytes written in SoundFeedStreamData
62 const 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 ////////////////////////////////////////////////////////////////////////
70 static 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
93 static 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
114 static 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
124 static 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
137 static 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 ////////////////////////////////////////////////////////////////////////
265 static 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
296 static 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
330 static 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
350 void 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 }