mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-29 00:46:17 +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,
|
||||
software,
|
||||
script
|
||||
script,
|
||||
none
|
||||
};
|
||||
|
||||
Mode mode{Mode::software};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue