From 5e58e989a84c7ab24149a9648da607d81303468b Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 18 Aug 2018 12:08:58 -1000 Subject: [PATCH] Remove ALSA audio backend --- CMakeLists.txt | 13 +- include/boo/audiodev/IAudioVoiceEngine.hpp | 13 +- lib/audiodev/ALSA.cpp | 404 --------------------- lib/audiodev/AudioVoiceEngine.cpp | 10 + lib/audiodev/AudioVoiceEngine.hpp | 2 + lib/audiodev/LinuxMidi.hpp | 11 +- lib/audiodev/PulseAudio.cpp | 156 ++++++-- lib/audiodev/WAVOut.cpp | 25 +- 8 files changed, 171 insertions(+), 463 deletions(-) delete mode 100644 lib/audiodev/ALSA.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec169db..146acf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,16 +191,13 @@ else(NOT GEKKO) find_path(PULSEAUDIO_INCLUDE_DIR NAMES pulse/pulseaudio.h) - - if (PULSEAUDIO_INCLUDE_DIR) - list(APPEND PLAT_SRCS lib/audiodev/PulseAudio.cpp) - list(APPEND _BOO_SYS_LIBS pulse) - message(STATUS "Using PulseAudio backend") - else() - list(APPEND PLAT_SRCS lib/audiodev/ALSA.cpp) - message(STATUS "Using ALSA backend") + if(PULSEAUDIO_INCLUDE_DIR-NOTFOUND) + message(FATAL_ERROR "Unix build of boo requires pulseaudio") endif() + list(APPEND PLAT_SRCS lib/audiodev/PulseAudio.cpp) + list(APPEND _BOO_SYS_LIBS pulse) + if(DBUS_INCLUDE_DIR-NOTFOUND) message(FATAL_ERROR "Unix build of boo requires dbus") endif() diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index e139339..43dda61 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -64,8 +64,17 @@ struct IAudioVoiceEngine /** Enable or disable Lt/Rt surround encoding. If successful, getAvailableSet() will return Surround51 */ virtual bool enableLtRt(bool enable)=0; - /** Get list of MIDI devices found on system */ - virtual std::vector> enumerateMIDIDevices() const=0; + /** Get current Audio output in use */ + virtual std::string getCurrentAudioOutput() const=0; + + /** Set current Audio output to use */ + virtual bool setCurrentAudioOutput(const char* name)=0; + + /** Get list of Audio output devices found on system */ + virtual std::vector> enumerateAudioOutputs() const=0; + + /** Get list of MIDI input devices found on system */ + virtual std::vector> enumerateMIDIInputs() const=0; /** Create ad-hoc MIDI in port and register with system */ virtual std::unique_ptr newVirtualMIDIIn(ReceiveFunctor&& receiver)=0; diff --git a/lib/audiodev/ALSA.cpp b/lib/audiodev/ALSA.cpp deleted file mode 100644 index b22be5d..0000000 --- a/lib/audiodev/ALSA.cpp +++ /dev/null @@ -1,404 +0,0 @@ -#include -#include -#include "AudioVoiceEngine.hpp" -#include "logvisor/logvisor.hpp" - -#include "LinuxMidi.hpp" - -namespace boo -{ -logvisor::Module ALSALog("boo::ALSA"); - -static const uint64_t StereoChans = (1 << SND_CHMAP_FL) | - (1 << SND_CHMAP_FR); - -static const uint64_t QuadChans = (1 << SND_CHMAP_FL) | - (1 << SND_CHMAP_FR) | - (1 << SND_CHMAP_RL) | - (1 << SND_CHMAP_RR); - -static const uint64_t S51Chans = (1 << SND_CHMAP_FL) | - (1 << SND_CHMAP_FR) | - (1 << SND_CHMAP_RL) | - (1 << SND_CHMAP_RR) | - (1 << SND_CHMAP_FC) | - (1 << SND_CHMAP_LFE); - -static const uint64_t S71Chans = (1 << SND_CHMAP_FL) | - (1 << SND_CHMAP_FR) | - (1 << SND_CHMAP_RL) | - (1 << SND_CHMAP_RR) | - (1 << SND_CHMAP_FC) | - (1 << SND_CHMAP_LFE) | - (1 << SND_CHMAP_SL) | - (1 << SND_CHMAP_SR); - -struct ALSAAudioVoiceEngine : LinuxMidi -{ - snd_pcm_t* m_pcm; - snd_pcm_uframes_t m_bufSize; - snd_pcm_uframes_t m_periodSize; - - std::vector m_final16; - std::vector m_final32; - std::vector m_finalFlt; - - ~ALSAAudioVoiceEngine() - { - if (m_pcm) - { - snd_pcm_drain(m_pcm); - snd_pcm_close(m_pcm); - } - } - - AudioChannelSet _getAvailableSet() - { - snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(m_pcm); - if (!chmaps) - return AudioChannelSet::Stereo; - - static const std::array testSets = - {{AudioChannelSet::Surround71, AudioChannelSet::Surround51, - AudioChannelSet::Quad, AudioChannelSet::Stereo}}; - for (AudioChannelSet set : testSets) - { - for (snd_pcm_chmap_query_t** chmap = chmaps ; *chmap != nullptr ; ++chmap) - { - snd_pcm_chmap_t* chm = &(*chmap)->map; - uint64_t chBits = 0; - for (unsigned c=0 ; cchannels ; ++c) - chBits |= 1 << chm->pos[c]; - - switch (set) - { - case AudioChannelSet::Stereo: - { - if ((chBits & StereoChans) == StereoChans) - { - snd_pcm_free_chmaps(chmaps); - return AudioChannelSet::Stereo; - } - break; - } - case AudioChannelSet::Quad: - { - if ((chBits & QuadChans) == QuadChans) - { - snd_pcm_free_chmaps(chmaps); - return AudioChannelSet::Quad; - } - break; - } - case AudioChannelSet::Surround51: - { - if ((chBits & S51Chans) == S51Chans) - { - snd_pcm_free_chmaps(chmaps); - return AudioChannelSet::Surround51; - } - break; - } - case AudioChannelSet::Surround71: - { - if ((chBits & S71Chans) == S71Chans) - { - snd_pcm_free_chmaps(chmaps); - return AudioChannelSet::Surround71; - } - break; - } - default: break; - } - } - } - - snd_pcm_free_chmaps(chmaps); - return AudioChannelSet::Unknown; - } - - ALSAAudioVoiceEngine() - { - /* Open device */ - if (snd_pcm_open(&m_pcm, "default", SND_PCM_STREAM_PLAYBACK, 0)) - { - ALSALog.report(logvisor::Error, "unable to allocate ALSA voice"); - return; - } - - /* Query audio card for best supported format amd sample-rate */ - snd_pcm_hw_params_t* hwParams; - snd_pcm_hw_params_malloc(&hwParams); - snd_pcm_hw_params_any(m_pcm, hwParams); - - int errr; - if ((errr = snd_pcm_hw_params_set_access(m_pcm, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set interleaved mode. %s\n", snd_strerror(errr)); - return; - } - - snd_pcm_format_t bestFmt; - if (!snd_pcm_hw_params_test_format(m_pcm, hwParams, SND_PCM_FORMAT_FLOAT)) - { - bestFmt = SND_PCM_FORMAT_FLOAT; - m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I; - m_mixInfo.m_bitsPerSample = 32; - } - else if (!snd_pcm_hw_params_test_format(m_pcm, hwParams, SND_PCM_FORMAT_S32)) - { - bestFmt = SND_PCM_FORMAT_S32; - m_mixInfo.m_sampleFormat = SOXR_INT32_I; - m_mixInfo.m_bitsPerSample = 32; - } - else if (!snd_pcm_hw_params_test_format(m_pcm, hwParams, SND_PCM_FORMAT_S16)) - { - bestFmt = SND_PCM_FORMAT_S16; - m_mixInfo.m_sampleFormat = SOXR_INT16_I; - m_mixInfo.m_bitsPerSample = 16; - } - else - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "unsupported audio formats on default ALSA device"); - return; - } - - if ((errr = snd_pcm_hw_params_set_format(m_pcm, hwParams, bestFmt))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set format. %s\n", snd_strerror(errr)); - return; - } - - /* Query audio card for channel map */ - m_mixInfo.m_channels = _getAvailableSet(); - unsigned int chCount = ChannelCount(m_mixInfo.m_channels); - if ((errr = snd_pcm_hw_params_set_channels_near(m_pcm, hwParams, &chCount))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set channels number. %s\n", snd_strerror(errr)); - return; - } - - unsigned int bestRate; - if (!snd_pcm_hw_params_test_rate(m_pcm, hwParams, 96000, 0)) - { - bestRate = 96000; - } - else if (!snd_pcm_hw_params_test_rate(m_pcm, hwParams, 48000, 0)) - { - bestRate = 48000; - } - else - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "unsupported audio sample rates on default ALSA device"); - return; - } - - if ((errr = snd_pcm_hw_params_set_rate_near(m_pcm, hwParams, &bestRate, 0))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set rate. %s\n", snd_strerror(errr)); - return; - } - m_mixInfo.m_sampleRate = bestRate; - m_5msFrames = bestRate * 5 / 1000; - - snd_pcm_uframes_t periodSz = m_5msFrames * 4; - if ((errr = snd_pcm_hw_params_set_period_size_near(m_pcm, hwParams, &periodSz, 0))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set period size. %s\n", snd_strerror(errr)); - return; - } - - snd_pcm_uframes_t bufSz = periodSz * 2; - if ((errr = snd_pcm_hw_params_set_buffer_size_near(m_pcm, hwParams, &bufSz))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set buffer size. %s\n", snd_strerror(errr)); - return; - } - - /* Write parameters */ - if ((errr = snd_pcm_hw_params(m_pcm, hwParams))) - { - snd_pcm_hw_params_free(hwParams); - snd_pcm_close(m_pcm); - m_pcm = nullptr; - ALSALog.report(logvisor::Error, "Can't set harware parameters. %s\n", snd_strerror(errr)); - return; - } - - snd_pcm_hw_params_free(hwParams); - - - snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(m_pcm); - ChannelMap& chmapOut = m_mixInfo.m_channelMap; - if (chmaps) - { - snd_pcm_chmap_t* foundChmap = nullptr; - for (snd_pcm_chmap_query_t** chmap = chmaps ; *chmap != nullptr ; ++chmap) - { - if ((*chmap)->map.channels == chCount) - { - snd_pcm_chmap_t* chm = &(*chmap)->map; - uint64_t chBits = 0; - for (unsigned c=0 ; cchannels ; ++c) - chBits |= 1 << chm->pos[c]; - - bool good = false; - switch (m_mixInfo.m_channels) - { - case AudioChannelSet::Stereo: - if ((chBits & StereoChans) == StereoChans) - good = true; - break; - case AudioChannelSet::Quad: - if ((chBits & QuadChans) == QuadChans) - good = true; - break; - case AudioChannelSet::Surround51: - if ((chBits & S51Chans) == S51Chans) - good = true; - break; - case AudioChannelSet::Surround71: - if ((chBits & S71Chans) == S71Chans) - good = true; - break; - default: break; - } - - if (good) - { - foundChmap = chm; - break; - } - } - } - - if (!foundChmap) - { - snd_pcm_close(m_pcm); - m_pcm = nullptr; - snd_pcm_free_chmaps(chmaps); - ALSALog.report(logvisor::Error, "unable to find matching ALSA voice chmap"); - return; - } - chmapOut.m_channelCount = chCount; - for (unsigned c=0 ; cchannels ; ++c) - chmapOut.m_channels[c] = AudioChannel(foundChmap->pos[c] - 3); - snd_pcm_set_chmap(m_pcm, foundChmap); - snd_pcm_free_chmaps(chmaps); - } - else - { - chmapOut.m_channelCount = 2; - chmapOut.m_channels[0] = AudioChannel::FrontLeft; - chmapOut.m_channels[1] = AudioChannel::FrontRight; - } - - snd_pcm_get_params(m_pcm, &m_bufSize, &m_periodSize); - snd_pcm_prepare(m_pcm); - m_mixInfo.m_periodFrames = m_periodSize; - - /* Allocate master mix space */ - switch (m_mixInfo.m_sampleFormat) - { - case SOXR_INT16_I: - m_final16.resize(m_periodSize * m_mixInfo.m_channelMap.m_channelCount); - break; - case SOXR_INT32_I: - m_final32.resize(m_periodSize * m_mixInfo.m_channelMap.m_channelCount); - break; - case SOXR_FLOAT32_I: - m_finalFlt.resize(m_periodSize * m_mixInfo.m_channelMap.m_channelCount); - break; - default: - break; - } - } - - void pumpAndMixVoices() - { - if (!m_pcm) - { - /* Dummy pump mode - use failsafe defaults for 1/60sec of samples */ - m_mixInfo.m_sampleRate = 32000.0; - m_mixInfo.m_sampleFormat = SOXR_FLOAT32_I; - m_mixInfo.m_bitsPerSample = 32; - m_5msFrames = 32000 / 60; - m_mixInfo.m_periodFrames = m_5msFrames; - m_mixInfo.m_channels = AudioChannelSet::Stereo; - m_mixInfo.m_channelMap.m_channelCount = 2; - m_mixInfo.m_channelMap.m_channels[0] = AudioChannel::FrontLeft; - m_mixInfo.m_channelMap.m_channels[1] = AudioChannel::FrontRight; - _pumpAndMixVoices(m_5msFrames, (float*)nullptr); - return; - } - - snd_pcm_sframes_t frames = snd_pcm_avail_update(m_pcm); - if (frames < 0) - { - snd_pcm_state_t st = snd_pcm_state(m_pcm); - if (st == SND_PCM_STATE_XRUN) - { - snd_pcm_prepare(m_pcm); - frames = snd_pcm_avail_update(m_pcm); - ALSALog.report(logvisor::Warning, "ALSA underrun %ld frames", frames); - } - else - return; - } - if (frames < 0) - return; - - snd_pcm_sframes_t buffers = frames / m_periodSize; - for (snd_pcm_sframes_t b=0 ; b NewAudioVoiceEngine() -{ - return std::make_unique(); -} - -} diff --git a/lib/audiodev/AudioVoiceEngine.cpp b/lib/audiodev/AudioVoiceEngine.cpp index 47e7923..0733a5d 100644 --- a/lib/audiodev/AudioVoiceEngine.cpp +++ b/lib/audiodev/AudioVoiceEngine.cpp @@ -91,6 +91,16 @@ template void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, in template void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut); template void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut); +void BaseAudioVoiceEngine::_resetSampleRate() +{ + if (m_voiceHead) + for (boo::AudioVoice& vox : *m_voiceHead) + vox._resetSampleRate(vox.m_sampleRateIn); + if (m_submixHead) + for (boo::AudioSubmix& smx : *m_submixHead) + smx._resetOutputSampleRate(); +} + ObjToken BaseAudioVoiceEngine::allocateNewMonoVoice(double sampleRate, IAudioVoiceCallback* cb, diff --git a/lib/audiodev/AudioVoiceEngine.hpp b/lib/audiodev/AudioVoiceEngine.hpp index 2db3589..07b72df 100644 --- a/lib/audiodev/AudioVoiceEngine.hpp +++ b/lib/audiodev/AudioVoiceEngine.hpp @@ -53,6 +53,8 @@ protected: template void _pumpAndMixVoices(size_t frames, T* dataOut); + void _resetSampleRate(); + public: BaseAudioVoiceEngine() : m_mainSubmix(std::make_unique(*this, nullptr, -1, false)) {} ~BaseAudioVoiceEngine(); diff --git a/lib/audiodev/LinuxMidi.hpp b/lib/audiodev/LinuxMidi.hpp index b1d5674..8faa4b2 100644 --- a/lib/audiodev/LinuxMidi.hpp +++ b/lib/audiodev/LinuxMidi.hpp @@ -19,7 +19,7 @@ static inline double TimespecToDouble(struct timespec& ts) struct LinuxMidi : BaseAudioVoiceEngine { - std::vector> enumerateMIDIDevices() const + std::vector> enumerateMIDIInputs() const { std::vector> ret; int status; @@ -30,6 +30,9 @@ struct LinuxMidi : BaseAudioVoiceEngine if (card < 0) return {}; + snd_rawmidi_info_t* info; + snd_rawmidi_info_malloc(&info); + while (card >= 0) { snd_ctl_t *ctl; @@ -46,9 +49,9 @@ struct LinuxMidi : BaseAudioVoiceEngine break; if (device >= 0) { - snd_rawmidi_info_t *info; - snd_rawmidi_info_alloca(&info); snd_rawmidi_info_set_device(info, device); + if (snd_rawmidi_info_get_stream(info) != SND_RAWMIDI_STREAM_INPUT) + continue; sprintf(name + strlen(name), ",%d", device); ret.push_back(std::make_pair(name, snd_rawmidi_info_get_name(info))); } @@ -60,6 +63,8 @@ struct LinuxMidi : BaseAudioVoiceEngine break; } + snd_rawmidi_info_free(info); + return ret; } diff --git a/lib/audiodev/PulseAudio.cpp b/lib/audiodev/PulseAudio.cpp index 80a3780..68262ea 100644 --- a/lib/audiodev/PulseAudio.cpp +++ b/lib/audiodev/PulseAudio.cpp @@ -41,6 +41,7 @@ struct PulseAudioVoiceEngine : LinuxMidi pa_context* m_ctx = nullptr; pa_stream* m_stream = nullptr; std::string m_sinkName; + bool m_handleMove = false; pa_sample_spec m_sampleSpec = {}; pa_channel_map m_chanMap = {}; @@ -60,7 +61,7 @@ struct PulseAudioVoiceEngine : LinuxMidi return retval; } - int _paIterate(pa_operation* op) + int _paIterate(pa_operation* op) const { int retval = 0; while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) @@ -68,42 +69,16 @@ struct PulseAudioVoiceEngine : LinuxMidi return retval; } - PulseAudioVoiceEngine() + bool _setupSink() { - if (!(m_mainloop = pa_mainloop_new())) + if (m_stream) { - Log.report(logvisor::Error, "Unable to pa_mainloop_new()"); - return; - } - - pa_mainloop_api* mlApi = pa_mainloop_get_api(m_mainloop); - pa_proplist* propList = pa_proplist_new(); - pa_proplist_sets(propList, PA_PROP_APPLICATION_ICON_NAME, APP->getUniqueName().data()); - char pidStr[16]; - snprintf(pidStr, 16, "%d", int(getpid())); - pa_proplist_sets(propList, PA_PROP_APPLICATION_PROCESS_ID, pidStr); - if (!(m_ctx = pa_context_new_with_proplist(mlApi, APP->getFriendlyName().data(), propList))) - { - Log.report(logvisor::Error, "Unable to pa_context_new_with_proplist()"); - pa_mainloop_free(m_mainloop); - m_mainloop = nullptr; - return; + pa_stream_disconnect(m_stream); + pa_stream_unref(m_stream); + m_stream = nullptr; } pa_operation* op; - - if (pa_context_connect(m_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr)) - { - Log.report(logvisor::Error, "Unable to pa_context_connect()"); - goto err; - } - - _paWaitReady(); - - op = pa_context_get_server_info(m_ctx, pa_server_info_cb_t(_getServerInfoReply), this); - _paIterate(op); - pa_operation_unref(op); - m_sampleSpec.format = PA_SAMPLE_INVALID; op = pa_context_get_sink_info_by_name(m_ctx, m_sinkName.c_str(), pa_sink_info_cb_t(_getSinkInfoReply), this); _paIterate(op); @@ -142,9 +117,12 @@ struct PulseAudioVoiceEngine : LinuxMidi goto err; } + pa_stream_set_moved_callback(m_stream, pa_stream_notify_cb_t(_streamMoved), this); + _paStreamWaitReady(); - return; + _resetSampleRate(); + return true; err: if (m_stream) { @@ -152,6 +130,50 @@ struct PulseAudioVoiceEngine : LinuxMidi pa_stream_unref(m_stream); m_stream = nullptr; } + return false; + } + + PulseAudioVoiceEngine() + { + if (!(m_mainloop = pa_mainloop_new())) + { + Log.report(logvisor::Error, "Unable to pa_mainloop_new()"); + return; + } + + pa_mainloop_api* mlApi = pa_mainloop_get_api(m_mainloop); + pa_proplist* propList = pa_proplist_new(); + pa_proplist_sets(propList, PA_PROP_APPLICATION_ICON_NAME, APP->getUniqueName().data()); + char pidStr[16]; + snprintf(pidStr, 16, "%d", int(getpid())); + pa_proplist_sets(propList, PA_PROP_APPLICATION_PROCESS_ID, pidStr); + if (!(m_ctx = pa_context_new_with_proplist(mlApi, APP->getFriendlyName().data(), propList))) + { + Log.report(logvisor::Error, "Unable to pa_context_new_with_proplist()"); + pa_mainloop_free(m_mainloop); + m_mainloop = nullptr; + return; + } + + pa_operation* op; + + if (pa_context_connect(m_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr)) + { + Log.report(logvisor::Error, "Unable to pa_context_connect()"); + goto err; + } + + _paWaitReady(); + + op = pa_context_get_server_info(m_ctx, pa_server_info_cb_t(_getServerInfoReply), this); + _paIterate(op); + pa_operation_unref(op); + + if (!_setupSink()) + goto err; + + return; + err: pa_context_disconnect(m_ctx); pa_context_unref(m_ctx); m_ctx = nullptr; @@ -177,6 +199,12 @@ struct PulseAudioVoiceEngine : LinuxMidi } } + static void _streamMoved(pa_stream* p, PulseAudioVoiceEngine* userdata) + { + userdata->m_sinkName = pa_stream_get_device_name(p); + userdata->m_handleMove = true; + } + static void _getServerInfoReply(pa_context* c, const pa_server_info* i, PulseAudioVoiceEngine* userdata) { userdata->m_sinkName = i->default_sink_name; @@ -274,7 +302,7 @@ struct PulseAudioVoiceEngine : LinuxMidi } } - static void _getSinkInfoReply(pa_context *c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) + static void _getSinkInfoReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) { if (!i) return; @@ -284,6 +312,59 @@ struct PulseAudioVoiceEngine : LinuxMidi userdata->_parseAudioChannelSet(&i->channel_map); } + mutable std::vector> m_sinks; + static void _getSinkInfoListReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) + { + if (i) + userdata->m_sinks.push_back(std::make_pair(i->name, i->description)); + } + std::vector> enumerateAudioOutputs() const + { + pa_operation* op = pa_context_get_sink_info_list(m_ctx, pa_sink_info_cb_t(_getSinkInfoListReply), (void*)this); + _paIterate(op); + pa_operation_unref(op); + std::vector> ret; + ret.swap(m_sinks); + return ret; + } + + std::string getCurrentAudioOutput() const + { + return m_sinkName; + } + + bool m_sinkOk = false; + static void _checkAudioSinkReply(pa_context* c, const pa_sink_info* i, int eol, PulseAudioVoiceEngine* userdata) + { + if (i) + userdata->m_sinkOk = true; + } + bool setCurrentAudioOutput(const char* name) + { + m_sinkOk = false; + pa_operation* op; + op = pa_context_get_sink_info_by_name(m_ctx, name, pa_sink_info_cb_t(_checkAudioSinkReply), this); + _paIterate(op); + pa_operation_unref(op); + if (m_sinkOk) + { + m_sinkName = name; + return _setupSink(); + } + return false; + } + + void _doIterate() + { + int retval; + pa_mainloop_iterate(m_mainloop, 1, &retval); + if (m_handleMove) + { + m_handleMove = false; + _setupSink(); + } + } + void pumpAndMixVoices() { if (!m_stream) @@ -307,10 +388,9 @@ struct PulseAudioVoiceEngine : LinuxMidi size_t writableFrames = writableSz / frameSz; size_t writablePeriods = writableFrames / m_mixInfo.m_periodFrames; - int retval; if (!writablePeriods) { - pa_mainloop_iterate(m_mainloop, 1, &retval); + _doIterate(); return; } @@ -322,7 +402,7 @@ struct PulseAudioVoiceEngine : LinuxMidi pa_stream_state_t st = pa_stream_get_state(m_stream); Log.report(logvisor::Error, "Unable to pa_stream_begin_write(): %s %d", pa_strerror(pa_context_errno(m_ctx)), st); - pa_mainloop_iterate(m_mainloop, 1, &retval); + _doIterate(); return; } @@ -333,7 +413,7 @@ struct PulseAudioVoiceEngine : LinuxMidi if (pa_stream_write(m_stream, data, nbytes, nullptr, 0, PA_SEEK_RELATIVE)) Log.report(logvisor::Error, "Unable to pa_stream_write()"); - pa_mainloop_iterate(m_mainloop, 1, &retval); + _doIterate(); } }; diff --git a/lib/audiodev/WAVOut.cpp b/lib/audiodev/WAVOut.cpp index a6a6742..7d5fadd 100644 --- a/lib/audiodev/WAVOut.cpp +++ b/lib/audiodev/WAVOut.cpp @@ -13,7 +13,22 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine return boo::AudioChannelSet::Stereo; } - std::vector> enumerateMIDIDevices() const + std::string getCurrentAudioOutput() const + { + return "wavout"; + } + + bool setCurrentAudioOutput(const char* name) + { + return false; + } + + std::vector> enumerateAudioOutputs() const + { + return {{"wavout", "WAVOut"}}; + } + + std::vector> enumerateMIDIInputs() const { return {}; } @@ -161,13 +176,7 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine m_mixInfo.m_periodFrames = periodFrames; m_mixInfo.m_sampleRate = sampleRate; _buildAudioRenderClient(); - - if (m_voiceHead) - for (boo::AudioVoice& vox : *m_voiceHead) - vox._resetSampleRate(vox.m_sampleRateIn); - if (m_submixHead) - for (boo::AudioSubmix& smx : *m_submixHead) - smx._resetOutputSampleRate(); + _resetSampleRate(); } void pumpAndMixVoices()