mirror of
https://github.com/badaix/snapcast.git
synced 2025-06-02 19:01:47 +02:00
Add mixer mode "none"
This commit is contained in:
parent
4a7b72773a
commit
dd913acff9
8 changed files with 90 additions and 81 deletions
|
@ -40,7 +40,8 @@ struct ClientSettings
|
||||||
{
|
{
|
||||||
hardware,
|
hardware,
|
||||||
software,
|
software,
|
||||||
script
|
script,
|
||||||
|
none
|
||||||
};
|
};
|
||||||
|
|
||||||
Mode mode{Mode::software};
|
Mode mode{Mode::software};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue