From 030f87c90c69a4cc171dc45aa44eb149c122372d Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Sat, 16 Sep 2017 08:21:07 +0200 Subject: [PATCH] Add sndio mixer plugin --- Makefile.am | 1 + doc/mpdconf.example | 2 +- src/mixer/MixerList.hxx | 1 + src/mixer/plugins/SndioMixerPlugin.cxx | 63 ++++++++++++++++++++++++ src/output/plugins/SndioOutputPlugin.cxx | 52 +++++++++++++++++-- src/output/plugins/SndioOutputPlugin.hxx | 11 ++++- 6 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/mixer/plugins/SndioMixerPlugin.cxx diff --git a/Makefile.am b/Makefile.am index 4abbd0947..ffafc50fe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1469,6 +1469,7 @@ if ENABLE_SNDIO liboutput_plugins_a_SOURCES += \ src/output/plugins/SndioOutputPlugin.cxx \ src/output/plugins/SndioOutputPlugin.hxx +libmixer_plugins_a_SOURCES += src/mixer/plugins/SndioMixerPlugin.cxx endif if ENABLE_HAIKU diff --git a/doc/mpdconf.example b/doc/mpdconf.example index 2609c38c1..504ecd825 100644 --- a/doc/mpdconf.example +++ b/doc/mpdconf.example @@ -310,7 +310,7 @@ input { #audio_output { # type "sndio" # name "sndio output" -# mixer_type "software" +# mixer_type "hardware" #} # # An example of an OS X output: diff --git a/src/mixer/MixerList.hxx b/src/mixer/MixerList.hxx index 7c5fe24b2..ae4a4e81e 100644 --- a/src/mixer/MixerList.hxx +++ b/src/mixer/MixerList.hxx @@ -36,5 +36,6 @@ extern const MixerPlugin osx_mixer_plugin; extern const MixerPlugin roar_mixer_plugin; extern const MixerPlugin pulse_mixer_plugin; extern const MixerPlugin winmm_mixer_plugin; +extern const MixerPlugin sndio_mixer_plugin; #endif diff --git a/src/mixer/plugins/SndioMixerPlugin.cxx b/src/mixer/plugins/SndioMixerPlugin.cxx new file mode 100644 index 000000000..f063e8892 --- /dev/null +++ b/src/mixer/plugins/SndioMixerPlugin.cxx @@ -0,0 +1,63 @@ +/* + * Copyright 2017 Christopher Zimmermann + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "mixer/MixerInternal.hxx" +#include "mixer/Listener.hxx" +#include "output/plugins/SndioOutputPlugin.hxx" + +#include + +class SndioMixer final : public Mixer { + SndioOutput &output; + +public: + SndioMixer(SndioOutput &_output, MixerListener &_listener) + :Mixer(sndio_mixer_plugin, _listener), output(_output) + { + output.RegisterMixerListener(this, &_listener); + } + + /* virtual methods from class Mixer */ + void Open() override {} + + void Close() noexcept override {} + + int GetVolume() override { + return output.GetVolume(); + } + + void SetVolume(unsigned volume) override { + output.SetVolume(volume); + } + +}; + +static Mixer * +sndio_mixer_init(gcc_unused EventLoop &event_loop, + AudioOutput &ao, + MixerListener &listener, + gcc_unused const ConfigBlock &block) +{ + return new SndioMixer((SndioOutput &)ao, listener); +} + +const MixerPlugin sndio_mixer_plugin = { + sndio_mixer_init, + false, +}; diff --git a/src/output/plugins/SndioOutputPlugin.cxx b/src/output/plugins/SndioOutputPlugin.cxx index bcda3126a..1ae92f1bf 100644 --- a/src/output/plugins/SndioOutputPlugin.cxx +++ b/src/output/plugins/SndioOutputPlugin.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "SndioOutputPlugin.hxx" +#include "mixer/MixerList.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -47,10 +48,16 @@ SndioOutput::SndioOutput(const ConfigBlock &block) :AudioOutput(0), device(block.GetBlockValue("device", SIO_DEVANY)), buffer_time(block.GetBlockValue("buffer_time", - MPD_SNDIO_BUFFER_TIME_MS)) + MPD_SNDIO_BUFFER_TIME_MS)), + raw_volume(SIO_MAXVOL) { } +static void +VolumeCallback(void *arg, unsigned int volume) { + ((SndioOutput *)arg)->VolumeChanged(volume); +} + AudioOutput * SndioOutput::Create(EventLoop &, const ConfigBlock &block) { return new SndioOutput(block); @@ -125,6 +132,15 @@ SndioOutput::Open(AudioFormat &audio_format) throw std::runtime_error("Requested audio params cannot be satisfied"); } + // Set volume after opening fresh audio stream which does + // know nothing about previous audio streams. + sio_setvol(sio_hdl, raw_volume); + // sio_onvol returns 0 if no volume knob is available. + // This is the case on raw audio devices rather than + // the sndiod audio server. + if (sio_onvol(sio_hdl, VolumeCallback, this) == 0) + raw_volume = -1; + if (!sio_start(sio_hdl)) { sio_close(sio_hdl); throw std::runtime_error("Failed to start audio device"); @@ -148,9 +164,39 @@ SndioOutput::Play(const void *chunk, size_t size) return n; } +void +SndioOutput::SetVolume(unsigned int volume) { + sio_setvol(sio_hdl, volume * SIO_MAXVOL / 100); +} + +static inline unsigned int +RawToPercent(int raw_volume) { + return raw_volume < 0 ? 100 : raw_volume * 100 / SIO_MAXVOL; +} + +void +SndioOutput::VolumeChanged(int _raw_volume) { + if (raw_volume >= 0 && listener != nullptr && mixer != nullptr) { + raw_volume = _raw_volume; + listener->OnMixerVolumeChanged(*mixer, + RawToPercent(raw_volume)); + } +} + +unsigned int +SndioOutput::GetVolume() { + return RawToPercent(raw_volume); +} + +void +SndioOutput::RegisterMixerListener(Mixer *_mixer, MixerListener *_listener) { + mixer = _mixer; + listener = _listener; +} + const struct AudioOutputPlugin sndio_output_plugin = { "sndio", sndio_test_default_device, - &SndioOutput::Create, - nullptr, + SndioOutput::Create, + &sndio_mixer_plugin, }; diff --git a/src/output/plugins/SndioOutputPlugin.hxx b/src/output/plugins/SndioOutputPlugin.hxx index 288042d40..f98c07489 100644 --- a/src/output/plugins/SndioOutputPlugin.hxx +++ b/src/output/plugins/SndioOutputPlugin.hxx @@ -18,6 +18,7 @@ */ #include "../OutputAPI.hxx" +#include "mixer/Listener.hxx" #ifndef MPD_SNDIO_OUTPUT_PLUGIN_HXX #define MPD_SNDIO_OUTPUT_PLUGIN_HXX @@ -25,15 +26,23 @@ extern const struct AudioOutputPlugin sndio_output_plugin; class SndioOutput final : AudioOutput { + Mixer *mixer = nullptr; + MixerListener *listener = nullptr; const char *const device; const unsigned buffer_time; /* in ms */ struct sio_hdl *sio_hdl; + int raw_volume; public: SndioOutput(const ConfigBlock &block); static AudioOutput *Create(EventLoop &, - const ConfigBlock &block); + const ConfigBlock &block); + + void SetVolume(unsigned int _volume); + unsigned int GetVolume(); + void VolumeChanged(int _volume); + void RegisterMixerListener(Mixer *_mixer, MixerListener *_listener); private: void Open(AudioFormat &audio_format) override;