spu: put globals into a structure
[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 <stdio.h>
21
22 #include <pulse/pulseaudio.h>
23 #include "out.h"
24
25 ////////////////////////////////////////////////////////////////////////
26 // pulseaudio structs
27 ////////////////////////////////////////////////////////////////////////
28
29 typedef 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
38 typedef struct {
39      unsigned int frequency;
40      unsigned int latency_in_msec;
41 } Settings;
42
43 ////////////////////////////////////////////////////////////////////////
44 // pulseaudio globals
45 ////////////////////////////////////////////////////////////////////////
46
47 static Device device = {
48      .mainloop = NULL,
49      .api = NULL,
50      .context = NULL,
51      .stream = NULL
52 };
53
54 static Settings settings = {
55      .frequency = 44100,
56      .latency_in_msec = 20,
57 };
58
59 // the number of bytes written in SoundFeedStreamData
60 const 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 ////////////////////////////////////////////////////////////////////////
68 static 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
91 static 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
112 static 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
122 static 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
135 static int pulse_init(void)
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");
144           return -1;
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");
155           return -1;
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));
163           return -1;
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");
171           return -1;
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));
183                return -1;
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;
194      device.spec.channels = 2;
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));
215           return -1;
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      {
228           error_number = pa_context_errno (device.context);
229           fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number));
230           return -1;
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));
247                return -1;
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");
257      return 0;
258 }
259
260 ////////////////////////////////////////////////////////////////////////
261 // REMOVE SOUND
262 ////////////////////////////////////////////////////////////////////////
263 static void pulse_finish(void)
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
294 static int pulse_busy(void)
295 {
296      int free_space;
297
298      if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL))
299           return 1;
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");
311           return 1;
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
325 static void pulse_feed(void *pSound, int lBytes)
326 {
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 }
341
342 void 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 }