diff options
Diffstat (limited to 'plugins/dfsound/pulseaudio.c')
| -rwxr-xr-x[-rw-r--r--] | plugins/dfsound/pulseaudio.c | 708 |
1 files changed, 354 insertions, 354 deletions
diff --git a/plugins/dfsound/pulseaudio.c b/plugins/dfsound/pulseaudio.c index bd43bb5a..bd5e2b8d 100644..100755 --- a/plugins/dfsound/pulseaudio.c +++ b/plugins/dfsound/pulseaudio.c @@ -1,354 +1,354 @@ -/***************************************************************************
- pulseaudio.c - description
- -------------------
-begin : Thu Feb 04 2010
-copyright : (C) 2010 by Tristin Celestin
-email : cetris1@umbc.edu
-comment : Much of this was taken from simple.c, in the pulseaudio
- library
-***************************************************************************/
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. See also the license.txt file for *
- * additional informations. *
- * *
- ***************************************************************************/
-
-#include "stdafx.h"
-
-#ifdef USEPULSEAUDIO
-
-#define _IN_OSS
-
-#include "externals.h"
-#include <pulse/pulseaudio.h>
-
-////////////////////////////////////////////////////////////////////////
-// pulseaudio structs
-////////////////////////////////////////////////////////////////////////
-
-typedef struct {
- pa_threaded_mainloop *mainloop;
- pa_context *context;
- pa_mainloop_api *api;
- pa_stream *stream;
- pa_sample_spec spec;
- int first;
-} Device;
-
-typedef struct {
- unsigned int frequency;
- unsigned int latency_in_msec;
-} Settings;
-
-////////////////////////////////////////////////////////////////////////
-// pulseaudio globals
-////////////////////////////////////////////////////////////////////////
-
-static Device device = {
- .mainloop = NULL,
- .api = NULL,
- .context = NULL,
- .stream = NULL
-};
-
-static Settings settings = {
- .frequency = 44100,
- .latency_in_msec = 20,
-};
-
-// the number of bytes written in SoundFeedStreamData
-const int mixlen = 3240;
-
-// used to calculate how much space is used in the buffer, for debugging purposes
-//int maxlength = 0;
-
-////////////////////////////////////////////////////////////////////////
-// CALLBACKS FOR THREADED MAINLOOP
-////////////////////////////////////////////////////////////////////////
-static void context_state_cb (pa_context *context, void *userdata)
-{
- Device *dev = userdata;
-
- if ((context == NULL) || (dev == NULL))
- return;
-
- switch (pa_context_get_state (context))
- {
- case PA_CONTEXT_READY:
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal (dev->mainloop, 0);
- break;
-
- case PA_CONTEXT_UNCONNECTED:
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
- }
-}
-
-static void stream_state_cb (pa_stream *stream, void * userdata)
-{
- Device *dev = userdata;
-
- if ((stream == NULL) || (dev == NULL))
- return;
-
- switch (pa_stream_get_state (stream))
- {
- case PA_STREAM_READY:
- case PA_STREAM_FAILED:
- case PA_STREAM_TERMINATED:
- pa_threaded_mainloop_signal (dev->mainloop, 0);
- break;
-
- case PA_STREAM_UNCONNECTED:
- case PA_STREAM_CREATING:
- break;
- }
-}
-
-static void stream_latency_update_cb (pa_stream *stream, void *userdata)
-{
- Device *dev = userdata;
-
- if ((stream == NULL) || (dev == NULL))
- return;
-
- pa_threaded_mainloop_signal (dev->mainloop, 0);
-}
-
-static void stream_request_cb (pa_stream *stream, size_t length, void *userdata)
-{
- Device *dev = userdata;
-
- if ((stream == NULL) || (dev == NULL))
- return;
- pa_threaded_mainloop_signal (dev->mainloop, 0);
-}
-
-////////////////////////////////////////////////////////////////////////
-// SETUP SOUND
-////////////////////////////////////////////////////////////////////////
-
-void SetupSound (void)
-{
- int error_number;
-
- // Acquire mainloop ///////////////////////////////////////////////////////
- device.mainloop = pa_threaded_mainloop_new ();
- if (device.mainloop == NULL)
- {
- fprintf (stderr, "Could not acquire PulseAudio main loop\n");
- return;
- }
-
- // Acquire context ////////////////////////////////////////////////////////
- device.api = pa_threaded_mainloop_get_api (device.mainloop);
- device.context = pa_context_new (device.api, "PCSXR");
- pa_context_set_state_callback (device.context, context_state_cb, &device);
-
- if (device.context == NULL)
- {
- fprintf (stderr, "Could not acquire PulseAudio device context\n");
- return;
- }
-
- // Connect to PulseAudio server ///////////////////////////////////////////
- if (pa_context_connect (device.context, NULL, 0, NULL) < 0)
- {
- error_number = pa_context_errno (device.context);
- fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number));
- return;
- }
-
- // Run mainloop until sever context is ready //////////////////////////////
- pa_threaded_mainloop_lock (device.mainloop);
- if (pa_threaded_mainloop_start (device.mainloop) < 0)
- {
- fprintf (stderr, "Could not start mainloop\n");
- return;
- }
-
- pa_context_state_t context_state;
- context_state = pa_context_get_state (device.context);
- while (context_state != PA_CONTEXT_READY)
- {
- context_state = pa_context_get_state (device.context);
- if (! PA_CONTEXT_IS_GOOD (context_state))
- {
- error_number = pa_context_errno (device.context);
- fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number));
- return;
- }
- else if (context_state == PA_CONTEXT_READY)
- break;
- else
- fprintf (stderr, "PulseAudio context state is %d\n", context_state);
- pa_threaded_mainloop_wait (device.mainloop);
- }
-
- // Set sample spec ////////////////////////////////////////////////////////
- device.spec.format = PA_SAMPLE_S16NE;
- if (iDisStereo)
- device.spec.channels = 1;
- else
- device.spec.channels = 2;
- device.spec.rate = settings.frequency;
-
- pa_buffer_attr buffer_attributes;
- buffer_attributes.tlength = pa_bytes_per_second (& device.spec) / 5;
- buffer_attributes.maxlength = buffer_attributes.tlength * 3;
- buffer_attributes.minreq = buffer_attributes.tlength / 3;
- buffer_attributes.prebuf = buffer_attributes.tlength;
-
- //maxlength = buffer_attributes.maxlength;
- //fprintf (stderr, "Total space: %u\n", buffer_attributes.maxlength);
- //fprintf (stderr, "Minimum request size: %u\n", buffer_attributes.minreq);
- //fprintf (stderr, "Bytes needed before playback: %u\n", buffer_attributes.prebuf);
- //fprintf (stderr, "Target buffer size: %lu\n", buffer_attributes.tlength);
-
- // Acquire new stream using spec //////////////////////////////////////////
- device.stream = pa_stream_new (device.context, "PCSXR", &device.spec, NULL);
- if (device.stream == NULL)
- {
- error_number = pa_context_errno (device.context);
- fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number));
- return;
- }
-
- // Set callbacks for server events ////////////////////////////////////////
- pa_stream_set_state_callback (device.stream, stream_state_cb, &device);
- pa_stream_set_write_callback (device.stream, stream_request_cb, &device);
- pa_stream_set_latency_update_callback (device.stream, stream_latency_update_cb, &device);
-
- // Ready stream for playback //////////////////////////////////////////////
- pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
- //pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS);
- if (pa_stream_connect_playback (device.stream, NULL, &buffer_attributes, flags, NULL, NULL) < 0)
- {
- pa_context_errno (device.context);
- fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number));
- return;
- }
-
- // Run mainloop until stream is ready /////////////////////////////////////
- pa_stream_state_t stream_state;
- stream_state = pa_stream_get_state (device.stream);
- while (stream_state != PA_STREAM_READY)
- {
- stream_state = pa_stream_get_state (device.stream);
-
- if (stream_state == PA_STREAM_READY)
- break;
-
- else if (! PA_STREAM_IS_GOOD (stream_state))
- {
- error_number = pa_context_errno (device.context);
- fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number));
- return;
- }
- else
- fprintf (stderr, "PulseAudio stream state is %d\n", stream_state);
- pa_threaded_mainloop_wait (device.mainloop);
- }
-
- pa_threaded_mainloop_unlock (device.mainloop);
-
- fprintf (stderr, "PulseAudio should be connected\n");
- return;
-}
-
-////////////////////////////////////////////////////////////////////////
-// REMOVE SOUND
-////////////////////////////////////////////////////////////////////////
-void RemoveSound (void)
-{
- if (device.mainloop != NULL)
- pa_threaded_mainloop_stop (device.mainloop);
-
- // Release in reverse order of acquisition
- if (device.stream != NULL)
- {
- pa_stream_unref (device.stream);
- device.stream = NULL;
-
- }
- if (device.context != NULL)
- {
- pa_context_disconnect (device.context);
- pa_context_unref (device.context);
- device.context = NULL;
- }
-
- if (device.mainloop != NULL)
- {
- pa_threaded_mainloop_free (device.mainloop);
- device.mainloop = NULL;
- }
-
-}
-
-////////////////////////////////////////////////////////////////////////
-// GET BYTES BUFFERED
-////////////////////////////////////////////////////////////////////////
-
-unsigned long SoundGetBytesBuffered (void)
-{
- int free_space;
- int error_code;
- long latency;
- int playing = 0;
-
- if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL))
- return SOUNDSIZE;
-
- pa_threaded_mainloop_lock (device.mainloop);
- free_space = pa_stream_writable_size (device.stream);
- pa_threaded_mainloop_unlock (device.mainloop);
-
- //fprintf (stderr, "Free space: %d\n", free_space);
- //fprintf (stderr, "Used space: %d\n", maxlength - free_space);
- if (free_space < mixlen * 3)
- {
- // Don't buffer anymore, just play
- //fprintf (stderr, "Not buffering.\n");
- return SOUNDSIZE;
- }
- else
- {
- // Buffer some sound
- //fprintf (stderr, "Buffering.\n");
- return 0;
- }
-}
-
-////////////////////////////////////////////////////////////////////////
-// FEED SOUND DATA
-////////////////////////////////////////////////////////////////////////
-
-void SoundFeedStreamData (unsigned char *pSound, long lBytes)
-{
- int error_code;
- int size;
-
- if (device.mainloop != NULL)
- {
- pa_threaded_mainloop_lock (device.mainloop);
- if (pa_stream_write (device.stream, pSound, lBytes, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
- {
- fprintf (stderr, "Could not perform write\n");
- }
- else
- {
- //fprintf (stderr, "Wrote %d bytes\n", lBytes);
- pa_threaded_mainloop_unlock (device.mainloop);
- }
- }
-}
-#endif
+/*************************************************************************** + pulseaudio.c - description + ------------------- +begin : Thu Feb 04 2010 +copyright : (C) 2010 by Tristin Celestin +email : cetris1@umbc.edu +comment : Much of this was taken from simple.c, in the pulseaudio + library +***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. See also the license.txt file for * + * additional informations. * + * * + ***************************************************************************/ + +#include "stdafx.h" + +#ifdef USEPULSEAUDIO + +#define _IN_OSS + +#include "externals.h" +#include <pulse/pulseaudio.h> + +//////////////////////////////////////////////////////////////////////// +// pulseaudio structs +//////////////////////////////////////////////////////////////////////// + +typedef struct { + pa_threaded_mainloop *mainloop; + pa_context *context; + pa_mainloop_api *api; + pa_stream *stream; + pa_sample_spec spec; + int first; +} Device; + +typedef struct { + unsigned int frequency; + unsigned int latency_in_msec; +} Settings; + +//////////////////////////////////////////////////////////////////////// +// pulseaudio globals +//////////////////////////////////////////////////////////////////////// + +static Device device = { + .mainloop = NULL, + .api = NULL, + .context = NULL, + .stream = NULL +}; + +static Settings settings = { + .frequency = 44100, + .latency_in_msec = 20, +}; + +// the number of bytes written in SoundFeedStreamData +const int mixlen = 3240; + +// used to calculate how much space is used in the buffer, for debugging purposes +//int maxlength = 0; + +//////////////////////////////////////////////////////////////////////// +// CALLBACKS FOR THREADED MAINLOOP +//////////////////////////////////////////////////////////////////////// +static void context_state_cb (pa_context *context, void *userdata) +{ + Device *dev = userdata; + + if ((context == NULL) || (dev == NULL)) + return; + + switch (pa_context_get_state (context)) + { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal (dev->mainloop, 0); + break; + + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + } +} + +static void stream_state_cb (pa_stream *stream, void * userdata) +{ + Device *dev = userdata; + + if ((stream == NULL) || (dev == NULL)) + return; + + switch (pa_stream_get_state (stream)) + { + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + pa_threaded_mainloop_signal (dev->mainloop, 0); + break; + + case PA_STREAM_UNCONNECTED: + case PA_STREAM_CREATING: + break; + } +} + +static void stream_latency_update_cb (pa_stream *stream, void *userdata) +{ + Device *dev = userdata; + + if ((stream == NULL) || (dev == NULL)) + return; + + pa_threaded_mainloop_signal (dev->mainloop, 0); +} + +static void stream_request_cb (pa_stream *stream, size_t length, void *userdata) +{ + Device *dev = userdata; + + if ((stream == NULL) || (dev == NULL)) + return; + pa_threaded_mainloop_signal (dev->mainloop, 0); +} + +//////////////////////////////////////////////////////////////////////// +// SETUP SOUND +//////////////////////////////////////////////////////////////////////// + +void SetupSound (void) +{ + int error_number; + + // Acquire mainloop /////////////////////////////////////////////////////// + device.mainloop = pa_threaded_mainloop_new (); + if (device.mainloop == NULL) + { + fprintf (stderr, "Could not acquire PulseAudio main loop\n"); + return; + } + + // Acquire context //////////////////////////////////////////////////////// + device.api = pa_threaded_mainloop_get_api (device.mainloop); + device.context = pa_context_new (device.api, "PCSXR"); + pa_context_set_state_callback (device.context, context_state_cb, &device); + + if (device.context == NULL) + { + fprintf (stderr, "Could not acquire PulseAudio device context\n"); + return; + } + + // Connect to PulseAudio server /////////////////////////////////////////// + if (pa_context_connect (device.context, NULL, 0, NULL) < 0) + { + error_number = pa_context_errno (device.context); + fprintf (stderr, "Could not connect to PulseAudio server: %s\n", pa_strerror(error_number)); + return; + } + + // Run mainloop until sever context is ready ////////////////////////////// + pa_threaded_mainloop_lock (device.mainloop); + if (pa_threaded_mainloop_start (device.mainloop) < 0) + { + fprintf (stderr, "Could not start mainloop\n"); + return; + } + + pa_context_state_t context_state; + context_state = pa_context_get_state (device.context); + while (context_state != PA_CONTEXT_READY) + { + context_state = pa_context_get_state (device.context); + if (! PA_CONTEXT_IS_GOOD (context_state)) + { + error_number = pa_context_errno (device.context); + fprintf (stderr, "Context state is not good: %s\n", pa_strerror (error_number)); + return; + } + else if (context_state == PA_CONTEXT_READY) + break; + else + fprintf (stderr, "PulseAudio context state is %d\n", context_state); + pa_threaded_mainloop_wait (device.mainloop); + } + + // Set sample spec //////////////////////////////////////////////////////// + device.spec.format = PA_SAMPLE_S16NE; + if (iDisStereo) + device.spec.channels = 1; + else + device.spec.channels = 2; + device.spec.rate = settings.frequency; + + pa_buffer_attr buffer_attributes; + buffer_attributes.tlength = pa_bytes_per_second (& device.spec) / 5; + buffer_attributes.maxlength = buffer_attributes.tlength * 3; + buffer_attributes.minreq = buffer_attributes.tlength / 3; + buffer_attributes.prebuf = buffer_attributes.tlength; + + //maxlength = buffer_attributes.maxlength; + //fprintf (stderr, "Total space: %u\n", buffer_attributes.maxlength); + //fprintf (stderr, "Minimum request size: %u\n", buffer_attributes.minreq); + //fprintf (stderr, "Bytes needed before playback: %u\n", buffer_attributes.prebuf); + //fprintf (stderr, "Target buffer size: %lu\n", buffer_attributes.tlength); + + // Acquire new stream using spec ////////////////////////////////////////// + device.stream = pa_stream_new (device.context, "PCSXR", &device.spec, NULL); + if (device.stream == NULL) + { + error_number = pa_context_errno (device.context); + fprintf (stderr, "Could not acquire new PulseAudio stream: %s\n", pa_strerror (error_number)); + return; + } + + // Set callbacks for server events //////////////////////////////////////// + pa_stream_set_state_callback (device.stream, stream_state_cb, &device); + pa_stream_set_write_callback (device.stream, stream_request_cb, &device); + pa_stream_set_latency_update_callback (device.stream, stream_latency_update_cb, &device); + + // Ready stream for playback ////////////////////////////////////////////// + pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE); + //pa_stream_flags_t flags = (pa_stream_flags_t) (PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS); + if (pa_stream_connect_playback (device.stream, NULL, &buffer_attributes, flags, NULL, NULL) < 0) + { + pa_context_errno (device.context); + fprintf (stderr, "Could not connect for playback: %s\n", pa_strerror (error_number)); + return; + } + + // Run mainloop until stream is ready ///////////////////////////////////// + pa_stream_state_t stream_state; + stream_state = pa_stream_get_state (device.stream); + while (stream_state != PA_STREAM_READY) + { + stream_state = pa_stream_get_state (device.stream); + + if (stream_state == PA_STREAM_READY) + break; + + else if (! PA_STREAM_IS_GOOD (stream_state)) + { + error_number = pa_context_errno (device.context); + fprintf (stderr, "Stream state is not good: %s\n", pa_strerror (error_number)); + return; + } + else + fprintf (stderr, "PulseAudio stream state is %d\n", stream_state); + pa_threaded_mainloop_wait (device.mainloop); + } + + pa_threaded_mainloop_unlock (device.mainloop); + + fprintf (stderr, "PulseAudio should be connected\n"); + return; +} + +//////////////////////////////////////////////////////////////////////// +// REMOVE SOUND +//////////////////////////////////////////////////////////////////////// +void RemoveSound (void) +{ + if (device.mainloop != NULL) + pa_threaded_mainloop_stop (device.mainloop); + + // Release in reverse order of acquisition + if (device.stream != NULL) + { + pa_stream_unref (device.stream); + device.stream = NULL; + + } + if (device.context != NULL) + { + pa_context_disconnect (device.context); + pa_context_unref (device.context); + device.context = NULL; + } + + if (device.mainloop != NULL) + { + pa_threaded_mainloop_free (device.mainloop); + device.mainloop = NULL; + } + +} + +//////////////////////////////////////////////////////////////////////// +// GET BYTES BUFFERED +//////////////////////////////////////////////////////////////////////// + +unsigned long SoundGetBytesBuffered (void) +{ + int free_space; + int error_code; + long latency; + int playing = 0; + + if ((device.mainloop == NULL) || (device.api == NULL) || ( device.context == NULL) || (device.stream == NULL)) + return SOUNDSIZE; + + pa_threaded_mainloop_lock (device.mainloop); + free_space = pa_stream_writable_size (device.stream); + pa_threaded_mainloop_unlock (device.mainloop); + + //fprintf (stderr, "Free space: %d\n", free_space); + //fprintf (stderr, "Used space: %d\n", maxlength - free_space); + if (free_space < mixlen * 3) + { + // Don't buffer anymore, just play + //fprintf (stderr, "Not buffering.\n"); + return SOUNDSIZE; + } + else + { + // Buffer some sound + //fprintf (stderr, "Buffering.\n"); + return 0; + } +} + +//////////////////////////////////////////////////////////////////////// +// FEED SOUND DATA +//////////////////////////////////////////////////////////////////////// + +void SoundFeedStreamData (unsigned char *pSound, long lBytes) +{ + int error_code; + int size; + + if (device.mainloop != NULL) + { + pa_threaded_mainloop_lock (device.mainloop); + if (pa_stream_write (device.stream, pSound, lBytes, NULL, 0LL, PA_SEEK_RELATIVE) < 0) + { + fprintf (stderr, "Could not perform write\n"); + } + else + { + //fprintf (stderr, "Wrote %d bytes\n", lBytes); + pa_threaded_mainloop_unlock (device.mainloop); + } + } +} +#endif |
