diff options
| author | Carl Hetherington <cth@carlh.net> | 2019-04-15 12:42:29 +0100 |
|---|---|---|
| committer | cah <cah@ableton.com> | 2019-12-03 11:16:07 +0100 |
| commit | 8b9951729c312893e88dcc80f546fb531fc9928c (patch) | |
| tree | 18557b919769b9314dd37ac064a5917a7ee783ec | |
| parent | 66fd3929981693fb335737a28f47678f168f749b (diff) | |
Try to support > 2 output channels with PulseAudio.
| -rw-r--r-- | RtAudio.cpp | 96 | ||||
| -rw-r--r-- | RtAudio.h | 10 |
2 files changed, 102 insertions, 4 deletions
diff --git a/RtAudio.cpp b/RtAudio.cpp index 3e78f75..bea68a8 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -8436,6 +8436,7 @@ static void *alsaCallbackHandler( void *ptr ) #include <pulse/error.h> #include <pulse/simple.h> +#include <pulse/pulseaudio.h> #include <cstdio> static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000, @@ -8472,8 +8473,33 @@ unsigned int RtApiPulse::getDeviceCount( void ) return 1; } +void RtApiPulse::sinkInfoCallback(pa_context*, const pa_sink_info* info, int, void* arg) +{ + RtApiPulse* api = (RtApiPulse *) arg; + if (info) { + api->channels_ = info->sample_spec.channels; + } + pa_threaded_mainloop_signal(api->mainloop_, 0); +} + +void RtApiPulse::contextStateCallback(pa_context* c, void* arg) +{ + pa_threaded_mainloop* mainloop = (pa_threaded_mainloop*) arg; + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + pa_threaded_mainloop_signal(mainloop, 0); + break; + default: + break; + } +} + RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ ) { + /* Set up some defaults in case we crash and burn */ RtAudio::DeviceInfo info; info.probed = true; info.name = "PulseAudio"; @@ -8489,6 +8515,72 @@ RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ ) info.preferredSampleRate = 48000; info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; + /* Get the number of output channels from pulseaudio. A simple task, you say? + "What is your mainloop?" */ + mainloop_ = pa_threaded_mainloop_new(); + if (!mainloop_) { + return info; + } + + pa_threaded_mainloop_start(mainloop_); + pa_threaded_mainloop_lock(mainloop_); + + /* "And what is your context?" */ + pa_context* context = pa_context_new(pa_threaded_mainloop_get_api(mainloop_), "RtAudio"); + if (!context) { + pa_threaded_mainloop_unlock(mainloop_); + pa_threaded_mainloop_stop(mainloop_); + pa_threaded_mainloop_free(mainloop_); + mainloop_ = 0; + return info; + } + + pa_context_set_state_callback(context, contextStateCallback, mainloop_); + + pa_context_connect(context, 0, (pa_context_flags_t) 0, 0); + + /* "And what is your favourite colour?" */ + int connected = 0; + pa_context_state_t state = pa_context_get_state(context); + for (; !connected; state = pa_context_get_state(context)) { + switch (state) { + case PA_CONTEXT_READY: + connected = 1; + continue; + case PA_CONTEXT_FAILED: + case PA_CONTEXT_TERMINATED: + /* Blue! No, I mean red! */ + pa_threaded_mainloop_unlock(mainloop_); + pa_context_disconnect(context); + pa_context_unref(context); + pa_threaded_mainloop_stop(mainloop_); + pa_threaded_mainloop_free(mainloop_); + mainloop_ = 0; + return info; + default: + pa_threaded_mainloop_wait(mainloop_); + break; + } + } + + pa_operation* op = pa_context_get_sink_info_by_index(context, 0, sinkInfoCallback, this); + + if (op) { + pa_operation_unref(op); + } + + pa_threaded_mainloop_wait(mainloop_); + pa_threaded_mainloop_unlock(mainloop_); + + pa_context_disconnect(context); + pa_context_unref(context); + + pa_threaded_mainloop_stop(mainloop_); + pa_threaded_mainloop_free(mainloop_); + mainloop_ = 0; + + info.outputChannels = channels_; + return info; } @@ -8763,10 +8855,6 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, if ( device != 0 ) return false; if ( mode != INPUT && mode != OUTPUT ) return false; - if ( channels != 1 && channels != 2 ) { - errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels."; - return false; - } ss.channels = channels; if ( firstChannel != 0 ) return false; @@ -1099,15 +1099,25 @@ public: unsigned int firstChannel, unsigned int sampleRate, RtAudioFormat format, unsigned int *bufferSize, RtAudio::StreamOptions *options ); + + static void sinkInfoCallback(pa_context* c, const pa_sink_info* info, int eol, void* arg); + static void contextStateCallback(pa_context* c, void* arg); + pa_threaded_mainloop* mainloop_; + int channels_; }; #endif #if defined(__LINUX_PULSE__) +struct pa_context; +struct pa_sink_info; +struct pa_threaded_mainloop; + class RtApiPulse: public RtApi { public: + RtApiPulse() : mainloop_(0), channels_(2) {} ~RtApiPulse(); RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; } unsigned int getDeviceCount( void ); |
