Add mixer mode "none"

This commit is contained in:
badaix 2020-05-03 20:10:01 +02:00
parent 4a7b72773a
commit dd913acff9
8 changed files with 90 additions and 81 deletions

View file

@ -40,7 +40,8 @@ struct ClientSettings
{ {
hardware, hardware,
software, software,
script script,
none
}; };
Mode mode{Mode::software}; Mode mode{Mode::software};

View file

@ -64,20 +64,18 @@ void Controller::getNextMessage()
{ {
if (stream_ && decoder_) if (stream_ && decoder_)
{ {
// auto wireChunk = msg::message_cast<msg::WireChunk>(std::move(response)); // execute on the io_context to do the (costly) decoding on another thread (if more than one thread is used)
// boost::asio::post(io_context_, [this, response = std::move(response)]() mutable {
auto pcmChunk = msg::message_cast<msg::PcmChunk>(std::move(response)); auto pcmChunk = msg::message_cast<msg::PcmChunk>(std::move(response));
pcmChunk->format = sampleFormat_; pcmChunk->format = sampleFormat_;
// std::make_unique<msg::PcmChunk>(sampleFormat_, *wireChunk);
// pcmChunk->deserialize(baseMessage, buffer);
// LOG(TRACE, LOG_TAG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.getFormat() << "\n"; // LOG(TRACE, LOG_TAG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.getFormat() << "\n";
if (decoder_->decode(pcmChunk.get())) if (decoder_->decode(pcmChunk.get()))
{ {
// LOG(TRACE, LOG_TAG) << ", decoded: " << pcmChunk->payloadSize << ", Duration: " << pcmChunk->durationMs() // LOG(TRACE, LOG_TAG) << ", decoded: " << pcmChunk->payloadSize << ", Duration: " << pcmChunk->durationMs() << ", sec: " <<
// << ", sec: " << pcmChunk->timestamp.sec << ", usec: " << pcmChunk->timestamp.usec / 1000 << ", type: " << // pcmChunk->timestamp.sec << ", usec: " << pcmChunk->timestamp.usec / 1000 << ", type: " << pcmChunk->type << "\n";
// pcmChunk->type
// << "\n";
stream_->addChunk(std::move(pcmChunk)); stream_->addChunk(std::move(pcmChunk));
} }
// });
} }
} }
else if (response->type == message_type::kTime) else if (response->type == message_type::kTime)
@ -94,8 +92,7 @@ void Controller::getNextMessage()
<< ", volume: " << serverSettings_->getVolume() << ", muted: " << serverSettings_->isMuted() << "\n"; << ", volume: " << serverSettings_->getVolume() << ", muted: " << serverSettings_->isMuted() << "\n";
if (stream_ && player_) if (stream_ && player_)
{ {
player_->setVolume(serverSettings_->getVolume() / 100.); player_->setVolume(serverSettings_->getVolume() / 100., serverSettings_->isMuted());
player_->setMute(serverSettings_->isMuted());
stream_->setBufferLen(std::max(0, serverSettings_->getBufferMs() - serverSettings_->getLatency() - settings_.player.latency)); stream_->setBufferLen(std::max(0, serverSettings_->getBufferMs() - serverSettings_->getLatency() - settings_.player.latency));
} }
} }
@ -181,8 +178,7 @@ void Controller::getNextMessage()
// The player class will send the device's volume to the server instead // The player class will send the device's volume to the server instead
if (settings_.player.mixer.mode != ClientSettings::Mixer::Mode::hardware) if (settings_.player.mixer.mode != ClientSettings::Mixer::Mode::hardware)
{ {
player_->setVolume(serverSettings_->getVolume() / 100.); player_->setVolume(serverSettings_->getVolume() / 100., serverSettings_->isMuted());
player_->setMute(serverSettings_->isMuted());
} }
} }
else if (response->type == message_type::kStreamTags) else if (response->type == message_type::kStreamTags)

View file

@ -50,39 +50,17 @@ AlsaPlayer::AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings
} }
void AlsaPlayer::setMute(bool mute) void AlsaPlayer::setHardwareVolume(double volume, bool muted)
{ {
if (settings_.mixer.mode != ClientSettings::Mixer::Mode::hardware)
{
Player::setMute(mute);
return;
}
int val = mute ? 0 : 1;
int err = snd_mixer_selem_set_playback_switch(elem_, SND_MIXER_SCHN_MONO, val);
if (err < 0)
LOG(ERROR, LOG_TAG) << "Failed to mute, error: " << snd_strerror(err) << "\n";
}
void AlsaPlayer::setVolume(double volume)
{
if (settings_.mixer.mode != ClientSettings::Mixer::Mode::hardware)
{
Player::setVolume(volume);
return;
}
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// boost::system::error_code ec;
// sd_.cancel(ec);
// if (ctl_)
// snd_ctl_subscribe_events(ctl_, 0);
last_change_ = std::chrono::steady_clock::now(); last_change_ = std::chrono::steady_clock::now();
try try
{ {
int err = 0; int val = muted ? 0 : 1;
int err = snd_mixer_selem_set_playback_switch(elem_, SND_MIXER_SCHN_MONO, val);
if (err < 0)
LOG(ERROR, LOG_TAG) << "Failed to mute, error: " << snd_strerror(err) << "\n";
long minv, maxv; long minv, maxv;
if ((err = snd_mixer_selem_get_playback_dB_range(elem_, &minv, &maxv)) == 0) if ((err = snd_mixer_selem_get_playback_dB_range(elem_, &minv, &maxv)) == 0)
{ {
@ -90,7 +68,7 @@ void AlsaPlayer::setVolume(double volume)
volume = volume * (1 - min_norm) + min_norm; volume = volume * (1 - min_norm) + min_norm;
double mixer_volume = 6000.0 * log10(volume) + maxv; double mixer_volume = 6000.0 * log10(volume) + maxv;
LOG(DEBUG, LOG_TAG) << "Mixer volume range [" << minv << ", " << maxv << "], volume: " << volume << ", mixer volume: " << mixer_volume << "\n"; LOG(DEBUG, LOG_TAG) << "Mixer playback dB range [" << minv << ", " << maxv << "], volume: " << volume << ", mixer volume: " << mixer_volume << "\n";
if ((err = snd_mixer_selem_set_playback_dB_all(elem_, mixer_volume, 0)) < 0) if ((err = snd_mixer_selem_set_playback_dB_all(elem_, mixer_volume, 0)) < 0)
throw SnapException(std::string("Failed to set playback volume, error: ") + snd_strerror(err)); throw SnapException(std::string("Failed to set playback volume, error: ") + snd_strerror(err));
} }
@ -100,7 +78,8 @@ void AlsaPlayer::setVolume(double volume)
throw SnapException(std::string("Failed to get playback volume range, error: ") + snd_strerror(err)); throw SnapException(std::string("Failed to get playback volume range, error: ") + snd_strerror(err));
auto mixer_volume = volume * (maxv - minv) + minv; auto mixer_volume = volume * (maxv - minv) + minv;
LOG(DEBUG, LOG_TAG) << "Mixer volume range [" << minv << ", " << maxv << "], volume: " << volume << ", mixer volume: " << mixer_volume << "\n"; LOG(DEBUG, LOG_TAG) << "Mixer playback volume range [" << minv << ", " << maxv << "], volume: " << volume << ", mixer volume: " << mixer_volume
<< "\n";
if ((err = snd_mixer_selem_set_playback_volume_all(elem_, mixer_volume)) < 0) if ((err = snd_mixer_selem_set_playback_volume_all(elem_, mixer_volume)) < 0)
throw SnapException(std::string("Failed to set playback volume, error: ") + snd_strerror(err)); throw SnapException(std::string("Failed to set playback volume, error: ") + snd_strerror(err));
} }
@ -109,20 +88,15 @@ void AlsaPlayer::setVolume(double volume)
{ {
LOG(ERROR, LOG_TAG) << "Exception: " << e.what() << "\n"; LOG(ERROR, LOG_TAG) << "Exception: " << e.what() << "\n";
} }
// if (ctl_)
// {
// snd_ctl_subscribe_events(ctl_, 1);
// waitForEvent();
// }
} }
bool AlsaPlayer::getVolume(double& volume, bool& muted) bool AlsaPlayer::getHardwareVolume(double& volume, bool& muted)
{ {
long vol; long vol;
int err = 0; int err = 0;
std::lock_guard<std::mutex> lock(mutex_);
try try
{ {
while (snd_mixer_handle_events(mixer_) > 0) while (snd_mixer_handle_events(mixer_) > 0)
@ -153,7 +127,7 @@ bool AlsaPlayer::getVolume(double& volume, bool& muted)
} }
int val; int val;
if ((err = snd_mixer_selem_get_playback_switch(elem_, SND_MIXER_SCHN_MONO, &val)) < 0) if ((err = snd_mixer_selem_get_playback_switch(elem_, SND_MIXER_SCHN_MONO, &val)) < 0)
return false; throw SnapException(std::string("Failed to get mute state, error: ") + snd_strerror(err));
muted = (val == 0); muted = (val == 0);
LOG(DEBUG, LOG_TAG) << "Get volume, mixer volume range [" << minv << ", " << maxv << "], volume: " << volume << ", muted: " << muted << "\n"; LOG(DEBUG, LOG_TAG) << "Get volume, mixer volume range [" << minv << ", " << maxv << "], volume: " << volume << ", muted: " << muted << "\n";
snd_mixer_handle_events(mixer_); snd_mixer_handle_events(mixer_);
@ -203,7 +177,7 @@ void AlsaPlayer::waitForEvent()
{ {
double volume; double volume;
bool muted; bool muted;
if (getVolume(volume, muted)) if (getHardwareVolume(volume, muted))
{ {
LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n"; LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n";
notifyVolumeChange(volume, muted); notifyVolumeChange(volume, muted);

View file

@ -39,8 +39,6 @@ public:
/// List the system's audio output devices /// List the system's audio output devices
static std::vector<PcmDevice> pcm_list(void); static std::vector<PcmDevice> pcm_list(void);
void setVolume(double volume) override;
void setMute(bool mute) override;
protected: protected:
void worker() override; void worker() override;
@ -52,7 +50,9 @@ private:
void initMixer(); void initMixer();
void uninitMixer(); void uninitMixer();
bool getVolume(double& volume, bool& muted) override; bool getHardwareVolume(double& volume, bool& muted) override;
void setHardwareVolume(double volume, bool muted) override;
void waitForEvent(); void waitForEvent();
snd_pcm_t* handle_; snd_pcm_t* handle_;

View file

@ -19,11 +19,16 @@
#include <cmath> #include <cmath>
#include <iostream> #include <iostream>
#include <boost/process.hpp>
#include "common/aixlog.hpp" #include "common/aixlog.hpp"
#include "common/snap_exception.hpp"
#include "common/str_compat.hpp"
#include "player.hpp" #include "player.hpp"
using namespace std; using namespace std;
using namespace boost::process;
static constexpr auto LOG_TAG = "Player"; static constexpr auto LOG_TAG = "Player";
@ -51,7 +56,7 @@ void Player::start()
{ {
double volume; double volume;
bool muted; bool muted;
if (getVolume(volume, muted)) if (getHardwareVolume(volume, muted))
{ {
LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n"; LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n";
notifyVolumeChange(volume, muted); notifyVolumeChange(volume, muted);
@ -76,28 +81,33 @@ void Player::worker()
} }
bool Player::getVolume(double& volume, bool& muted) void Player::setHardwareVolume(double volume, bool muted)
{ {
std::ignore = volume; throw SnapException("Failed to set hardware mixer volume: not supported");
std::ignore = muted; }
bool Player::getHardwareVolume(double& volume, bool& muted)
{
throw SnapException("Failed to get hardware mixer volume: not supported");
return false; return false;
} }
void Player::adjustVolume(char* buffer, size_t frames) void Player::adjustVolume(char* buffer, size_t frames)
{ {
// if (settings_.mixer.mode != ClientSettings::Mixer::Mode::software) double volume = volCorrection_;
// return; // apply volume changes only for software mixer
// for any other mixer, we might still have to apply the volCorrection_
double volume = volume_; if (settings_.mixer.mode == ClientSettings::Mixer::Mode::software)
if (muted_)
volume = 0.;
const SampleFormat& sampleFormat = stream_->getFormat();
if ((volume < 1.0) || (volCorrection_ != 1.))
{ {
volume = muted_ ? 0. : volume_;
volume *= volCorrection_; volume *= volCorrection_;
}
if (volume != 1.0)
{
const SampleFormat& sampleFormat = stream_->getFormat();
if (sampleFormat.sampleSize() == 1) if (sampleFormat.sampleSize() == 1)
adjustVolume<int8_t>(buffer, frames * sampleFormat.channels(), volume); adjustVolume<int8_t>(buffer, frames * sampleFormat.channels(), volume);
else if (sampleFormat.sampleSize() == 2) else if (sampleFormat.sampleSize() == 2)
@ -128,13 +138,31 @@ void Player::setVolume_exp(double volume, double base)
} }
void Player::setVolume(double volume) void Player::setVolume(double volume, bool mute)
{ {
volume_ = volume;
muted_ = mute;
if (settings_.mixer.mode == ClientSettings::Mixer::Mode::hardware)
{
setHardwareVolume(volume, muted_);
}
else if (settings_.mixer.mode == ClientSettings::Mixer::Mode::software)
{
if (settings_.mixer.parameter == "poly")
setVolume_poly(volume, 3.);
else
setVolume_exp(volume, 10.); setVolume_exp(volume, 10.);
} }
else if (settings_.mixer.mode == ClientSettings::Mixer::Mode::script)
void Player::setMute(bool mute)
{ {
muted_ = mute; try
{
child c(exe = settings_.mixer.parameter, args = {"--volume", cpt::to_string(volume), "--mute", mute ? "true" : "false"});
c.detach();
}
catch (const std::exception& e)
{
LOG(ERROR, LOG_TAG) << "Failed to run script '" + settings_.mixer.parameter + "', error: " << e.what() << "\n";
}
}
} }

View file

@ -46,8 +46,10 @@ public:
virtual ~Player(); virtual ~Player();
/// Set audio volume in range [0..1] /// Set audio volume in range [0..1]
virtual void setVolume(double volume); /// @param volume the volume on range [0..1]
virtual void setMute(bool mute); /// @param muted muted or not
virtual void setVolume(double volume, bool mute);
/// Called on start, before the first audio sample is sent or any other function is called. /// Called on start, before the first audio sample is sent or any other function is called.
/// In case of hardware mixer, it will call getVolume and notify the server about the current volume /// In case of hardware mixer, it will call getVolume and notify the server about the current volume
virtual void start(); virtual void start();
@ -68,8 +70,13 @@ protected:
/// get the hardware mixer volume /// get the hardware mixer volume
/// @param[out] volume the volume on range [0..1] /// @param[out] volume the volume on range [0..1]
/// @param[out] muted muted or not /// @param[out] muted muted or not
/// @return true on success /// @return success or not
virtual bool getVolume(double& volume, bool& muted); virtual bool getHardwareVolume(double& volume, bool& muted);
/// set the hardware mixer volume
/// @param volume the volume on range [0..1]
/// @param muted muted or not
virtual void setHardwareVolume(double volume, bool muted);
void setVolume_poly(double volume, double exp); void setVolume_poly(double volume, double exp);
void setVolume_exp(double volume, double base); void setVolume_exp(double volume, double base);

View file

@ -150,9 +150,9 @@ int main(int argc, char** argv)
#endif #endif
std::shared_ptr<popl::Value<std::string>> mixer_mode; std::shared_ptr<popl::Value<std::string>> mixer_mode;
if (hw_mixer_supported) if (hw_mixer_supported)
mixer_mode = op.add<Value<string>>("", "mixer", "<software|hardware|script>[:<options>]", "software"); mixer_mode = op.add<Value<string>>("", "mixer", "<software|hardware|script|none>[:<options>]", "software");
else else
mixer_mode = op.add<Value<string>>("", "mixer", "<software|script>[:<options>]", "software"); mixer_mode = op.add<Value<string>>("", "mixer", "<software|script|none>[:<options>]", "software");
try try
{ {
@ -318,6 +318,8 @@ int main(int argc, char** argv)
settings.player.mixer.mode = ClientSettings::Mixer::Mode::hardware; settings.player.mixer.mode = ClientSettings::Mixer::Mode::hardware;
else if (mode == "script") else if (mode == "script")
settings.player.mixer.mode = ClientSettings::Mixer::Mode::script; settings.player.mixer.mode = ClientSettings::Mixer::Mode::script;
else if (mode == "none")
settings.player.mixer.mode = ClientSettings::Mixer::Mode::none;
else else
throw SnapException("Mixer mode not supported: " + mode); throw SnapException("Mixer mode not supported: " + mode);
} }

View file

@ -221,6 +221,7 @@ int main(int argc, char* argv[])
else else
throw SnapException("Invalid log sink: " + settings.logging.sink); throw SnapException("Invalid log sink: " + settings.logging.sink);
// TODO: op vs conf
for (const auto& opt : conf.unknown_options()) for (const auto& opt : conf.unknown_options())
LOG(WARNING) << "unknown configuration option: " << opt << "\n"; LOG(WARNING) << "unknown configuration option: " << opt << "\n";