Pulseaudio, selecting Audio Device + multiple Audio

I just did some work with audio on SDL2. I had some troubles with selecting an audio device on pulseaudio. Somehow it always seemed to fall back to the default device.
A had a quick look over the code and fixed the problem (at least for me). Additionally it adds support for multi-device audio and device detection. If you are interested here is the patch for (SDL2-2.0.3) but I found no difference with the current repo.

Code:

diff -r 5bf21e9e191f src/audio/pulseaudio/SDL_pulseaudio.c
— a/src/audio/pulseaudio/SDL_pulseaudio.c Sun Sep 14 19:44:53 2014 +0200
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c Sun Sep 14 21:40:27 2014 +0200
@@ -106,6 +106,7 @@

static int load_pulseaudio_syms(void);

+pa_operation* (*PULSEAUDIO_pa_context_get_sink_info_list)(pa_context *c, pa_sink_info_cb_t cb, void *userdata);

#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC

@@ -202,11 +203,31 @@
SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
SDL_PULSEAUDIO_SYM(pa_stream_unref);
SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);

  • SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
    SDL_PULSEAUDIO_SYM(pa_strerror);
    return 0;
    }

+static void DeInit(struct SDL_PrivateAudioData *pulse_local_server)
+{

  • if (pulse_local_server != NULL)
  • {
  •   if (pulse_local_server->context != NULL)
    
  •   {
    
  •   	PULSEAUDIO_pa_context_disconnect(pulse_local_server->context);
    
  •   	PULSEAUDIO_pa_context_unref(pulse_local_server->context);
    
  •   	pulse_local_server->context = NULL;
    
  •   }
    
  •   if (pulse_local_server->mainloop != NULL) {
    
  •   	PULSEAUDIO_pa_mainloop_free(pulse_local_server->mainloop);
    
  •   	pulse_local_server->mainloop = NULL;
    
  •   }
    
  •   SDL_free(pulse_local_server);
    
  • }
    +}+

/* Check to see if we can connect to PulseAudio */
static SDL_bool
CheckPulseAudioAvailable()
@@ -307,16 +328,7 @@
PULSEAUDIO_pa_stream_unref(this->hidden->stream);
this->hidden->stream = NULL;
}

  •    if (this->hidden->context != NULL) {
    
  •        PULSEAUDIO_pa_context_disconnect(this->hidden->context);
    
  •        PULSEAUDIO_pa_context_unref(this->hidden->context);
    
  •        this->hidden->context = NULL;
    
  •    }
    
  •    if (this->hidden->mainloop != NULL) {
    
  •        PULSEAUDIO_pa_mainloop_free(this->hidden->mainloop);
    
  •        this->hidden->mainloop = NULL;
    
  •    }
    
  •    SDL_free(this->hidden);
    
  •   DeInit(this->hidden);
       this->hidden = NULL;
    

    }
    }
    @@ -344,6 +356,53 @@
    return “SDL Application”; /* oh well. */
    }

+static struct SDL_PrivateAudioData *ThreadInit()
+{

  • struct SDL_PrivateAudioData *h = NULL;
  • int state = 0;
  • struct SDL_PrivateAudioData *pulse_local_server = SDL_malloc((sizeof *pulse_local_server));
  • if (pulse_local_server == NULL) {
  •    return 0;
    
  • }
  • SDL_memset(pulse_local_server, 0, (sizeof *pulse_local_server));
  • h = pulse_local_server;
  • /* Set up a new main loop */
  • if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {
  •   DeInit(pulse_local_server);
    
  •   return 0;
    
  • }
  • h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);
  • h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName());
  • if (!h->context) {
  •   DeInit(pulse_local_server);
    
  •   return 0;
    
  • }
  • /* Connect to the PulseAudio server */
  • if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {
  •   DeInit(pulse_local_server);
    
  •   return 0;
    
  • }
  • do {
  •   if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
    
  •   	DeInit(pulse_local_server);
    
  •   	return 0;
    
  •   }
    
  •   state = PULSEAUDIO_pa_context_get_state(h->context);
    
  •   if (!PA_CONTEXT_IS_GOOD(state)) {
    
  •   	DeInit(pulse_local_server);
    
  •   	return 0;
    
  •   }
    
  • } while (state != PA_CONTEXT_READY);
  • return pulse_local_server;
    +}

static int
PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
{
@@ -356,12 +415,12 @@
int state = 0;

 /* Initialize all variables that we clean on shutdown */
  • this->hidden = (struct SDL_PrivateAudioData *)
  •    SDL_malloc((sizeof *this->hidden));
    
  • this->hidden = ThreadInit();
  • if (this->hidden == NULL) {
    return SDL_OutOfMemory();
    }
  • SDL_memset(this->hidden, 0, (sizeof *this->hidden));
  • h = this->hidden;

    paspec.format = PA_SAMPLE_INVALID;
    @@ -448,36 +507,6 @@
    PA_CHANNEL_MAP_WAVEEX);

    /* Set up a new main loop */

  • if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) {

  •    PULSEAUDIO_CloseDevice(this);
    
  •    return SDL_SetError("pa_mainloop_new() failed");
    
  • }

  • h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop);

  • h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName());

  • if (!h->context) {

  •    PULSEAUDIO_CloseDevice(this);
    
  •    return SDL_SetError("pa_context_new() failed");
    
  • }

  • /* Connect to the PulseAudio server */

  • if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) {

  •    PULSEAUDIO_CloseDevice(this);
    
  •    return SDL_SetError("Could not setup connection to PulseAudio");
    
  • }

  • do {

  •    if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
    
  •        PULSEAUDIO_CloseDevice(this);
    
  •        return SDL_SetError("pa_mainloop_iterate() failed");
    
  •    }
    
  •    state = PULSEAUDIO_pa_context_get_state(h->context);
    
  •    if (!PA_CONTEXT_IS_GOOD(state)) {
    
  •        PULSEAUDIO_CloseDevice(this);
    
  •        return SDL_SetError("Could not connect to PulseAudio");
    
  •    }
    
  • } while (state != PA_CONTEXT_READY);

  • h->stream = PULSEAUDIO_pa_stream_new(
    h->context,
    “Simple DirectMedia Layer”, /* stream description */
    @@ -490,7 +519,7 @@
    return SDL_SetError(“Could not set up PulseAudio stream”);
    }

  • if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags,

  • if (PULSEAUDIO_pa_stream_connect_playback(h->stream, devname, &paattr, flags,
    NULL, NULL) < 0) {
    PULSEAUDIO_CloseDevice(this);
    return SDL_SetError(“Could not connect PulseAudio stream”);
    @@ -519,6 +548,58 @@
    UnloadPulseAudioLibrary();
    }

+typedef struct
+{

  • uint8_t last;
  • SDL_AddAudioDevice addfn;
    +} sink_struct;

+static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata)
+{

  • sink_struct *a = (sink_struct *) userdata;
  • a->last = is_last;
  • if(i)
  • {
  •   a->addfn(i->name);
    
  • }
    +}

+void PULSEAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
+{

  • struct SDL_PrivateAudioData *h = NULL;
  • struct SDL_PrivateAudioData *pulse_local_server = ThreadInit();
  • if(pulse_local_server == NULL)
  • {
  •    //Out of Memory
    
  •    return;
    
  • }
  • sink_struct a;
  • h = pulse_local_server;
  • if(iscapture != 1)
  • {
  •   a.last = 0;
    
  •   a.addfn = addfn;
    
  •   pa_operation* o = PULSEAUDIO_pa_context_get_sink_info_list(h->context, get_sink_info_callback, &a);
    
  •   while(!a.last)
    
  •   {
    
  •   	if(PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_CANCELLED)
    
  •   	{
    
  •   		break;
    
  •   	}
    
  •   	if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
    
  •   		break;
    
  •   	}
    
  •   }
    
  • }
  • DeInit(pulse_local_server);
    +}

static int
PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
{
@@ -530,8 +611,9 @@
UnloadPulseAudioLibrary();
return 0;
}

  • /* Set the function pointers */
  • impl->DetectDevices = PULSEAUDIO_DetectDevices;
    impl->OpenDevice = PULSEAUDIO_OpenDevice;
    impl->PlayDevice = PULSEAUDIO_PlayDevice;
    impl->WaitDevice = PULSEAUDIO_WaitDevice;
    @@ -539,7 +621,7 @@
    impl->CloseDevice = PULSEAUDIO_CloseDevice;
    impl->WaitDone = PULSEAUDIO_WaitDone;
    impl->Deinitialize = PULSEAUDIO_Deinitialize;
  • impl->OnlyHasDefaultOutputDevice = 1;
  • impl->OnlyHasDefaultOutputDevice = 0;

    return 1; /* this audio target is available. /
    }
    diff -r 5bf21e9e191f src/audio/pulseaudio/SDL_pulseaudio.h
    — a/src/audio/pulseaudio/SDL_pulseaudio.h Sun Sep 14 19:44:53 2014 +0200
    +++ b/src/audio/pulseaudio/SDL_pulseaudio.h Sun Sep 14 21:40:27 2014 +0200
    @@ -30,6 +30,7 @@
    /
    Hidden “this” pointer for the audio functions */
    #define _THIS SDL_AudioDevice *this

struct SDL_PrivateAudioData
{
/* pulseaudio structures */

And I assume it is necessary to add: It is ok the add it to the SDL2 Codebase under the zlib license


void (*segfault)(void) = 0;
int main(int argc, char **argv){segfault(); return 0;}
//Should not work, but does what told :slight_smile:

Added it to bugzilla - have only found it today [Embarassed]------------------------
void (*segfault)(void) = 0;
int main(int argc, char **argv){segfault(); return 0;}
//Should not work, but does what told :slight_smile:

That’s great, I’ve found it very frustrating trying to use HDMI audio -
maybe this will fix it.On Tue, Sep 16, 2014 at 1:39 PM, dfrizel <dominik.frizel at gmx.at> wrote:

Added it to bugzilla - have only found it today [image: Embarassed]


void (*segfault)(void) = 0;
int main(int argc, char **argv){segfault(); return 0;}
//Should not work, but does what told [image: Smile]


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org