static void WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o) { /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */ if (mainloop && o) { SDL_bool okay = SDL_TRUE; while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) { okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0); } PULSEAUDIO_pa_operation_unref(o); } }
static int PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen) { struct SDL_PrivateAudioData *h = this->hidden; const void *data = NULL; size_t nbytes = 0; while (SDL_AtomicGet(&this->enabled)) { if (h->capturebuf != NULL) { const int cpy = SDL_min(buflen, h->capturelen); SDL_memcpy(buffer, h->capturebuf, cpy); /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/ h->capturebuf += cpy; h->capturelen -= cpy; if (h->capturelen == 0) { h->capturebuf = NULL; PULSEAUDIO_pa_stream_drop(h->stream); /* done with this fragment. */ } return cpy; /* new data, return it. */ } if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY || PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY || PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { SDL_OpenedAudioDeviceDisconnected(this); return -1; /* uhoh, pulse failed! */ } if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) { continue; /* no data available yet. */ } /* a new fragment is available! */ PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes); SDL_assert(nbytes > 0); if (data == NULL) { /* NULL==buffer had a hole. Ignore that. */ PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */ } else { /* store this fragment's data, start feeding it to SDL. */ /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/ h->capturebuf = (const Uint8 *) data; h->capturelen = nbytes; } } return -1; /* not enabled? */ }
/* This function waits until it is possible to write a full sound buffer */ static void PULSEAUDIO_WaitDevice(_THIS) { struct SDL_PrivateAudioData *h = this->hidden; while(1) { if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY || PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY || PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { this->enabled = 0; return; } if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) { return; } } }
static int ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context) { pa_mainloop *mainloop = NULL; pa_context *context = NULL; pa_mainloop_api *mainloop_api = NULL; int state = 0; *_mainloop = NULL; *_context = NULL; /* Set up a new main loop */ if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) { return SDL_SetError("pa_mainloop_new() failed"); } *_mainloop = mainloop; mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop); SDL_assert(mainloop_api); /* this never fails, right? */ context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName()); if (!context) { return SDL_SetError("pa_context_new() failed"); } *_context = context; /* Connect to the PulseAudio server */ if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) { return SDL_SetError("Could not setup connection to PulseAudio"); } do { if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) { return SDL_SetError("pa_mainloop_iterate() failed"); } state = PULSEAUDIO_pa_context_get_state(context); if (!PA_CONTEXT_IS_GOOD(state)) { return SDL_SetError("Could not connect to PulseAudio"); } } while (state != PA_CONTEXT_READY); return 0; /* connected and ready! */ }
static void PULSEAUDIO_WaitDone(_THIS) { if (this->enabled) { struct SDL_PrivateAudioData *h = this->hidden; pa_operation *o = PULSEAUDIO_pa_stream_drain(h->stream, stream_drain_complete, NULL); if (o) { while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) { if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY || PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY || PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { PULSEAUDIO_pa_operation_cancel(o); break; } } PULSEAUDIO_pa_operation_unref(o); } } }
static int PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) { struct SDL_PrivateAudioData *h = NULL; Uint16 test_format = 0; pa_sample_spec paspec; pa_buffer_attr paattr; pa_channel_map pacmap; pa_stream_flags_t flags = 0; int state = 0; /* Initialize all variables that we clean on shutdown */ this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc((sizeof *this->hidden)); if (this->hidden == NULL) { return SDL_OutOfMemory(); } SDL_memset(this->hidden, 0, (sizeof *this->hidden)); h = this->hidden; paspec.format = PA_SAMPLE_INVALID; /* Try for a closest match on audio format */ for (test_format = SDL_FirstAudioFormat(this->spec.format); (paspec.format == PA_SAMPLE_INVALID) && test_format;) { #ifdef DEBUG_AUDIO fprintf(stderr, "Trying format 0x%4.4x\n", test_format); #endif switch (test_format) { case AUDIO_U8: paspec.format = PA_SAMPLE_U8; break; case AUDIO_S16LSB: paspec.format = PA_SAMPLE_S16LE; break; case AUDIO_S16MSB: paspec.format = PA_SAMPLE_S16BE; break; case AUDIO_S32LSB: paspec.format = PA_SAMPLE_S32LE; break; case AUDIO_S32MSB: paspec.format = PA_SAMPLE_S32BE; break; case AUDIO_F32LSB: paspec.format = PA_SAMPLE_FLOAT32LE; break; case AUDIO_F32MSB: paspec.format = PA_SAMPLE_FLOAT32BE; break; default: paspec.format = PA_SAMPLE_INVALID; break; } if (paspec.format == PA_SAMPLE_INVALID) { test_format = SDL_NextAudioFormat(); } } if (paspec.format == PA_SAMPLE_INVALID) { PULSEAUDIO_CloseDevice(this); return SDL_SetError("Couldn't find any hardware audio formats"); } this->spec.format = test_format; /* Calculate the final parameters for this audio specification */ #ifdef PA_STREAM_ADJUST_LATENCY this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */ #endif SDL_CalculateAudioSpec(&this->spec); /* Allocate mixing buffer */ h->mixlen = this->spec.size; h->mixbuf = (Uint8 *) SDL_AllocAudioMem(h->mixlen); if (h->mixbuf == NULL) { PULSEAUDIO_CloseDevice(this); return SDL_OutOfMemory(); } SDL_memset(h->mixbuf, this->spec.silence, this->spec.size); paspec.channels = this->spec.channels; paspec.rate = this->spec.freq; /* Reduced prebuffering compared to the defaults. */ #ifdef PA_STREAM_ADJUST_LATENCY /* 2x original requested bufsize */ paattr.tlength = h->mixlen * 4; paattr.prebuf = -1; paattr.maxlength = -1; /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */ paattr.minreq = h->mixlen; flags = PA_STREAM_ADJUST_LATENCY; #else paattr.tlength = h->mixlen*2; paattr.prebuf = h->mixlen*2; paattr.maxlength = h->mixlen*2; paattr.minreq = h->mixlen; #endif /* The SDL ALSA output hints us that we use Windows' channel mapping */ /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */ PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels, 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 */ &paspec, /* sample format spec */ &pacmap /* channel map */ ); if (h->stream == NULL) { PULSEAUDIO_CloseDevice(this); return SDL_SetError("Could not set up PulseAudio stream"); } if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags, NULL, NULL) < 0) { PULSEAUDIO_CloseDevice(this); return SDL_SetError("Could not connect PulseAudio stream"); } do { if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { PULSEAUDIO_CloseDevice(this); return SDL_SetError("pa_mainloop_iterate() failed"); } state = PULSEAUDIO_pa_stream_get_state(h->stream); if (!PA_STREAM_IS_GOOD(state)) { PULSEAUDIO_CloseDevice(this); return SDL_SetError("Could not create to PulseAudio stream"); } } while (state != PA_STREAM_READY); /* We're ready to rock and roll. :-) */ return 0; }