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,
software,
script
script,
none
};
Mode mode{Mode::software};

View file

@ -64,20 +64,18 @@ void Controller::getNextMessage()
{
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));
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";
if (decoder_->decode(pcmChunk.get()))
{
// LOG(TRACE, LOG_TAG) << ", decoded: " << pcmChunk->payloadSize << ", Duration: " << pcmChunk->durationMs()
// << ", sec: " << pcmChunk->timestamp.sec << ", usec: " << pcmChunk->timestamp.usec / 1000 << ", type: " <<
// pcmChunk->type
// << "\n";
// LOG(TRACE, LOG_TAG) << ", decoded: " << pcmChunk->payloadSize << ", Duration: " << pcmChunk->durationMs() << ", sec: " <<
// pcmChunk->timestamp.sec << ", usec: " << pcmChunk->timestamp.usec / 1000 << ", type: " << pcmChunk->type << "\n";
stream_->addChunk(std::move(pcmChunk));
}
// });
}
}
else if (response->type == message_type::kTime)
@ -94,8 +92,7 @@ void Controller::getNextMessage()
<< ", volume: " << serverSettings_->getVolume() << ", muted: " << serverSettings_->isMuted() << "\n";
if (stream_ && player_)
{
player_->setVolume(serverSettings_->getVolume() / 100.);
player_->setMute(serverSettings_->isMuted());
player_->setVolume(serverSettings_->getVolume() / 100., serverSettings_->isMuted());
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
if (settings_.player.mixer.mode != ClientSettings::Mixer::Mode::hardware)
{
player_->setVolume(serverSettings_->getVolume() / 100.);
player_->setMute(serverSettings_->isMuted());
player_->setVolume(serverSettings_->getVolume() / 100., serverSettings_->isMuted());
}
}
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_);
// boost::system::error_code ec;
// sd_.cancel(ec);
// if (ctl_)
// snd_ctl_subscribe_events(ctl_, 0);
last_change_ = std::chrono::steady_clock::now();
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;
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;
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)
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));
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)
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";
}
// 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;
int err = 0;
std::lock_guard<std::mutex> lock(mutex_);
try
{
while (snd_mixer_handle_events(mixer_) > 0)
@ -153,7 +127,7 @@ bool AlsaPlayer::getVolume(double& volume, bool& muted)
}
int val;
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);
LOG(DEBUG, LOG_TAG) << "Get volume, mixer volume range [" << minv << ", " << maxv << "], volume: " << volume << ", muted: " << muted << "\n";
snd_mixer_handle_events(mixer_);
@ -203,7 +177,7 @@ void AlsaPlayer::waitForEvent()
{
double volume;
bool muted;
if (getVolume(volume, muted))
if (getHardwareVolume(volume, muted))
{
LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n";
notifyVolumeChange(volume, muted);

View file

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

View file

@ -19,11 +19,16 @@
#include <cmath>
#include <iostream>
#include <boost/process.hpp>
#include "common/aixlog.hpp"
#include "common/snap_exception.hpp"
#include "common/str_compat.hpp"
#include "player.hpp"
using namespace std;
using namespace boost::process;
static constexpr auto LOG_TAG = "Player";
@ -51,7 +56,7 @@ void Player::start()
{
double volume;
bool muted;
if (getVolume(volume, muted))
if (getHardwareVolume(volume, muted))
{
LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n";
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;
std::ignore = muted;
throw SnapException("Failed to set hardware mixer volume: not supported");
}
bool Player::getHardwareVolume(double& volume, bool& muted)
{
throw SnapException("Failed to get hardware mixer volume: not supported");
return false;
}
void Player::adjustVolume(char* buffer, size_t frames)
{
// if (settings_.mixer.mode != ClientSettings::Mixer::Mode::software)
// return;
double volume = volume_;
if (muted_)
volume = 0.;
const SampleFormat& sampleFormat = stream_->getFormat();
if ((volume < 1.0) || (volCorrection_ != 1.))
double volume = volCorrection_;
// apply volume changes only for software mixer
// for any other mixer, we might still have to apply the volCorrection_
if (settings_.mixer.mode == ClientSettings::Mixer::Mode::software)
{
volume = muted_ ? 0. : volume_;
volume *= volCorrection_;
}
if (volume != 1.0)
{
const SampleFormat& sampleFormat = stream_->getFormat();
if (sampleFormat.sampleSize() == 1)
adjustVolume<int8_t>(buffer, frames * sampleFormat.channels(), volume);
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.);
}
void Player::setMute(bool mute)
else if (settings_.mixer.mode == ClientSettings::Mixer::Mode::script)
{
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();
/// Set audio volume in range [0..1]
virtual void setVolume(double volume);
virtual void setMute(bool mute);
/// @param volume the volume on range [0..1]
/// @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.
/// In case of hardware mixer, it will call getVolume and notify the server about the current volume
virtual void start();
@ -68,8 +70,13 @@ protected:
/// get the hardware mixer volume
/// @param[out] volume the volume on range [0..1]
/// @param[out] muted muted or not
/// @return true on success
virtual bool getVolume(double& volume, bool& muted);
/// @return success or not
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_exp(double volume, double base);

View file

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

View file

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