mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-21 04:56:13 +02:00
Send initial hardware mixer volume to the server
This commit is contained in:
parent
2217595c6b
commit
e8bb8ecdba
7 changed files with 116 additions and 96 deletions
|
@ -157,6 +157,12 @@ void Controller::getNextMessage()
|
|||
throw SnapException("No audio player support");
|
||||
|
||||
player_->setVolumeCallback([this](double volume, bool muted) {
|
||||
static double last_volume(-1);
|
||||
static bool last_muted(true);
|
||||
if ((volume != last_volume) || (last_muted != muted))
|
||||
{
|
||||
last_volume = volume;
|
||||
last_muted = muted;
|
||||
auto settings = std::make_shared<msg::ClientSettings>();
|
||||
settings->setVolume(static_cast<uint16_t>(volume * 100.));
|
||||
settings->setMuted(muted);
|
||||
|
@ -168,10 +174,16 @@ void Controller::getNextMessage()
|
|||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
player_->start();
|
||||
// Don't change the initial hardware mixer volume on the user's device.
|
||||
// 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_->start();
|
||||
}
|
||||
}
|
||||
else if (response->type == message_type::kStreamTags)
|
||||
{
|
||||
|
@ -194,8 +206,8 @@ void Controller::getNextMessage()
|
|||
void Controller::sendTimeSyncMessage(int quick_syncs)
|
||||
{
|
||||
auto timeReq = std::make_shared<msg::Time>();
|
||||
clientConnection_->sendRequest<msg::Time>(
|
||||
timeReq, 2s, [this, quick_syncs](const boost::system::error_code& ec, const std::unique_ptr<msg::Time>& response) mutable {
|
||||
clientConnection_->sendRequest<msg::Time>(timeReq, 2s, [this, quick_syncs](const boost::system::error_code& ec,
|
||||
const std::unique_ptr<msg::Time>& response) mutable {
|
||||
if (ec)
|
||||
{
|
||||
LOG(ERROR, LOG_TAG) << "Time sync request failed: " << ec.message() << "\n";
|
||||
|
@ -211,8 +223,7 @@ void Controller::sendTimeSyncMessage(int quick_syncs)
|
|||
if (quick_syncs > 0)
|
||||
{
|
||||
if (--quick_syncs == 0)
|
||||
LOG(INFO, LOG_TAG) << "diff to server [ms]: " << (float)TimeProvider::getInstance().getDiffToServer<chronos::usec>().count() / 1000.f
|
||||
<< "\n";
|
||||
LOG(INFO, LOG_TAG) << "diff to server [ms]: " << (float)TimeProvider::getInstance().getDiffToServer<chronos::usec>().count() / 1000.f << "\n";
|
||||
next = 100us;
|
||||
}
|
||||
timer_.expires_after(next);
|
||||
|
|
|
@ -29,7 +29,7 @@ using namespace std;
|
|||
static constexpr auto LOG_TAG = "Alsa";
|
||||
|
||||
AlsaPlayer::AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream)
|
||||
: Player(io_context, settings, stream), handle_(nullptr), ctl_(nullptr), sd_(io_context)
|
||||
: Player(io_context, settings, stream), handle_(nullptr), ctl_(nullptr), mixer_(nullptr), elem_(nullptr), sd_(io_context)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -37,9 +37,6 @@ AlsaPlayer::AlsaPlayer(boost::asio::io_context& io_context, const ClientSettings
|
|||
void AlsaPlayer::setVolume(double volume)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
int err = 0;
|
||||
snd_mixer_elem_t* elem(nullptr);
|
||||
snd_mixer_t* mixer(nullptr);
|
||||
// boost::system::error_code ec;
|
||||
// sd_.cancel(ec);
|
||||
// if (ctl_)
|
||||
|
@ -48,23 +45,21 @@ void AlsaPlayer::setVolume(double volume)
|
|||
last_change_ = std::chrono::steady_clock::now();
|
||||
try
|
||||
{
|
||||
openMixer(&elem, &mixer);
|
||||
|
||||
int err = 0;
|
||||
long minv, maxv;
|
||||
if ((err = snd_mixer_selem_get_playback_volume_range(elem, &minv, &maxv)) < 0)
|
||||
if ((err = snd_mixer_selem_get_playback_volume_range(elem_, &minv, &maxv)) < 0)
|
||||
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";
|
||||
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));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(ERROR, LOG_TAG) << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
if (mixer != nullptr)
|
||||
snd_mixer_close(mixer);
|
||||
|
||||
// if (ctl_)
|
||||
// {
|
||||
// snd_ctl_subscribe_events(ctl_, 1);
|
||||
|
@ -78,68 +73,35 @@ bool AlsaPlayer::getVolume(double& volume, bool& muted)
|
|||
long vol;
|
||||
int err = 0;
|
||||
|
||||
snd_mixer_elem_t* elem(nullptr);
|
||||
snd_mixer_t* mixer(nullptr);
|
||||
|
||||
try
|
||||
{
|
||||
openMixer(&elem, &mixer);
|
||||
|
||||
if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &vol)) < 0)
|
||||
snd_mixer_handle_events(mixer_);
|
||||
if ((err = snd_mixer_selem_get_playback_volume(elem_, SND_MIXER_SCHN_MONO, &vol)) < 0)
|
||||
throw SnapException(std::string("Failed to get playback volume, error: ") + snd_strerror(err));
|
||||
|
||||
// make the value bound to 1
|
||||
long minv, maxv;
|
||||
if ((err = snd_mixer_selem_get_playback_volume_range(elem, &minv, &maxv)) < 0)
|
||||
if ((err = snd_mixer_selem_get_playback_volume_range(elem_, &minv, &maxv)) < 0)
|
||||
throw SnapException(std::string("Failed to get playback volume range, error: ") + snd_strerror(err));
|
||||
vol -= minv;
|
||||
maxv = maxv - minv;
|
||||
volume = static_cast<double>(vol) / static_cast<double>(maxv);
|
||||
|
||||
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;
|
||||
muted = (val == 0);
|
||||
LOG(DEBUG, LOG_TAG) << "Get volume, mixer volume range [" << minv << ", " << maxv << "], volume: " << volume << ", muted: " << muted << "\n";
|
||||
if (mixer != nullptr)
|
||||
snd_mixer_close(mixer);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (mixer != nullptr)
|
||||
snd_mixer_close(mixer);
|
||||
LOG(ERROR, LOG_TAG) << "Exception: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AlsaPlayer::openMixer(snd_mixer_elem_t** elem, snd_mixer_t** mixer)
|
||||
{
|
||||
snd_mixer_selem_id_t* sid;
|
||||
|
||||
snd_mixer_selem_id_alloca(&sid);
|
||||
std::string mix_name = "Master";
|
||||
int mix_index = 0;
|
||||
// sets simple-mixer index and name
|
||||
snd_mixer_selem_id_set_index(sid, mix_index);
|
||||
snd_mixer_selem_id_set_name(sid, mix_name.c_str());
|
||||
|
||||
int err;
|
||||
if ((err = snd_mixer_open(mixer, 0)) < 0)
|
||||
throw SnapException(std::string("Failed to open mixer, error: ") + snd_strerror(err));
|
||||
if ((err = snd_mixer_attach(*mixer, settings_.pcm_device.name.c_str())) < 0)
|
||||
throw SnapException("Failed to attach mixer to " + settings_.pcm_device.name + ", error: " + snd_strerror(err));
|
||||
if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0)
|
||||
throw SnapException(std::string("Failed to register selem, error: ") + snd_strerror(err));
|
||||
if ((err = snd_mixer_load(*mixer)) < 0)
|
||||
throw SnapException(std::string("Failed to load mixer, error: ") + snd_strerror(err));
|
||||
*elem = snd_mixer_find_selem(*mixer, sid);
|
||||
if (!elem)
|
||||
throw SnapException(std::string("Failed to find selem, error: ") + snd_strerror(err));
|
||||
}
|
||||
|
||||
void AlsaPlayer::waitForEvent()
|
||||
{
|
||||
sd_.async_wait(boost::asio::posix::stream_descriptor::wait_read, [this](const boost::system::error_code& ec) {
|
||||
|
@ -192,6 +154,26 @@ void AlsaPlayer::initMixer()
|
|||
fd_ = std::make_unique<pollfd>();
|
||||
snd_ctl_poll_descriptors(ctl_, fd_.get(), 1);
|
||||
|
||||
snd_mixer_selem_id_t* sid;
|
||||
snd_mixer_selem_id_alloca(&sid);
|
||||
std::string mix_name = "Master";
|
||||
int mix_index = 0;
|
||||
// sets simple-mixer index and name
|
||||
snd_mixer_selem_id_set_index(sid, mix_index);
|
||||
snd_mixer_selem_id_set_name(sid, mix_name.c_str());
|
||||
|
||||
if ((err = snd_mixer_open(&mixer_, 0)) < 0)
|
||||
throw SnapException(std::string("Failed to open mixer, error: ") + snd_strerror(err));
|
||||
if ((err = snd_mixer_attach(mixer_, settings_.pcm_device.name.c_str())) < 0)
|
||||
throw SnapException("Failed to attach mixer to " + settings_.pcm_device.name + ", error: " + snd_strerror(err));
|
||||
if ((err = snd_mixer_selem_register(mixer_, NULL, NULL)) < 0)
|
||||
throw SnapException(std::string("Failed to register selem, error: ") + snd_strerror(err));
|
||||
if ((err = snd_mixer_load(mixer_)) < 0)
|
||||
throw SnapException(std::string("Failed to load mixer, error: ") + snd_strerror(err));
|
||||
elem_ = snd_mixer_find_selem(mixer_, sid);
|
||||
if (!elem_)
|
||||
throw SnapException(std::string("Failed to find selem, error: ") + snd_strerror(err));
|
||||
|
||||
sd_ = boost::asio::posix::stream_descriptor(io_context_, fd_->fd);
|
||||
waitForEvent();
|
||||
}
|
||||
|
@ -315,7 +297,7 @@ void AlsaPlayer::initAlsa()
|
|||
// snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, frames_);
|
||||
snd_pcm_sw_params(handle_, swparams);
|
||||
|
||||
// initMixer();
|
||||
initMixer();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,13 +49,16 @@ private:
|
|||
void initAlsa();
|
||||
void uninitAlsa();
|
||||
void initMixer();
|
||||
bool getVolume(double& volume, bool& muted);
|
||||
void openMixer(snd_mixer_elem_t** elem, snd_mixer_t** mixer);
|
||||
|
||||
bool getVolume(double& volume, bool& muted) override;
|
||||
void waitForEvent();
|
||||
|
||||
snd_pcm_t* handle_;
|
||||
snd_ctl_t* ctl_;
|
||||
|
||||
snd_mixer_t* mixer_;
|
||||
snd_mixer_elem_t* elem_;
|
||||
|
||||
std::unique_ptr<pollfd> fd_;
|
||||
std::vector<char> buffer_;
|
||||
snd_pcm_uframes_t frames_;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static constexpr auto LOG_TAG = "Player";
|
||||
|
||||
Player::Player(boost::asio::io_context& io_context, const ClientSettings::Player& settings, std::shared_ptr<Stream> stream)
|
||||
: io_context_(io_context), active_(false), stream_(stream), settings_(settings), volume_(1.0), muted_(false), volCorrection_(1.0)
|
||||
|
@ -43,6 +44,19 @@ void Player::start()
|
|||
active_ = true;
|
||||
if (needsThread())
|
||||
playerThread_ = thread(&Player::worker, this);
|
||||
|
||||
// If hardware mixer is used, send the initial volume to the server, because this is
|
||||
// the volume that is configured by the user on his local device
|
||||
if (settings_.mixer.mode == ClientSettings::Mixer::Mode::hardware)
|
||||
{
|
||||
double volume;
|
||||
bool muted;
|
||||
if (getVolume(volume, muted))
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Volume: " << volume << ", muted: " << muted << "\n";
|
||||
notifyVolumeChange(volume, muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,6 +76,12 @@ void Player::worker()
|
|||
}
|
||||
|
||||
|
||||
bool Player::getVolume(double& volume, bool& muted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Player::adjustVolume(char* buffer, size_t frames)
|
||||
{
|
||||
// if (settings_.mixer.mode != ClientSettings::Mixer::Mode::software)
|
||||
|
@ -92,7 +112,7 @@ void Player::adjustVolume(char* buffer, size_t frames)
|
|||
void Player::setVolume_poly(double volume, double exp)
|
||||
{
|
||||
volume_ = std::pow(volume, exp);
|
||||
LOG(DEBUG) << "setVolume poly: " << volume << " => " << volume_ << "\n";
|
||||
LOG(DEBUG, LOG_TAG) << "setVolume poly: " << volume << " => " << volume_ << "\n";
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,7 +122,7 @@ void Player::setVolume_exp(double volume, double base)
|
|||
// double base = M_E;
|
||||
// double base = 10.;
|
||||
volume_ = (pow(base, volume) - 1) / (base - 1);
|
||||
LOG(DEBUG) << "setVolume exp: " << volume << " => " << volume_ << "\n";
|
||||
LOG(DEBUG, LOG_TAG) << "setVolume exp: " << volume << " => " << volume_ << "\n";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ protected:
|
|||
void setVolume_poly(double volume, double exp);
|
||||
void setVolume_exp(double volume, double base);
|
||||
|
||||
virtual bool getVolume(double& volume, bool& muted);
|
||||
void adjustVolume(char* buffer, size_t frames);
|
||||
void notifyVolumeChange(double volume, bool muted) const
|
||||
{
|
||||
|
|
|
@ -120,7 +120,7 @@ int main(int argc, char** argv)
|
|||
"", "logfilter", "log filter <tag>:<level>[,<tag>:<level>]* with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]",
|
||||
settings.logging.filter);
|
||||
auto versionSwitch = op.add<Switch>("v", "version", "show version number");
|
||||
#if defined(HAS_ALSA) || defined(WINDOWS)
|
||||
#if defined(HAS_ALSA) || defined(HAS_WASAPI)
|
||||
auto listSwitch = op.add<Switch>("l", "list", "list PCM devices");
|
||||
/*auto soundcardValue =*/op.add<Value<string>>("s", "soundcard", "index or name of the pcm device", "default", &pcm_device);
|
||||
#endif
|
||||
|
@ -135,16 +135,19 @@ int main(int argc, char** argv)
|
|||
/*auto latencyValue =*/op.add<Value<int>>("", "latency", "latency of the PCM device", 0, &settings.player.latency);
|
||||
/*auto instanceValue =*/op.add<Value<size_t>>("i", "instance", "instance id", 1, &settings.instance);
|
||||
/*auto hostIdValue =*/op.add<Value<string>>("", "hostID", "unique host id", "", &settings.host_id);
|
||||
#ifdef ANDROID
|
||||
#if defined(HAS_OBOE) && defined(HAS_OPENSL)
|
||||
op.add<Value<string>>("", "player", "audio backend", "", &settings.player.player_name);
|
||||
#endif
|
||||
#ifdef HAS_SOXR
|
||||
auto sample_format = op.add<Value<string>>("", "sampleformat", "resample audio stream to <rate>:<bits>:<channels>", "");
|
||||
#endif
|
||||
#ifdef HAS_WASAPI
|
||||
auto sharing_mode = op.add<Value<string>>("", "sharingmode", "audio mode to use [shared/exclusive]", "shared");
|
||||
auto sharing_mode = op.add<Value<string>>("", "sharingmode", "audio mode to use [shared|exclusive]", "shared");
|
||||
#endif
|
||||
|
||||
// TODO: hardcoded
|
||||
settings.player.mixer.mode = ClientSettings::Mixer::Mode::hardware;
|
||||
|
||||
try
|
||||
{
|
||||
op.parse(argc, argv);
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
#include "client_settings.hpp"
|
||||
#include "codec_header.hpp"
|
||||
#include "hello.hpp"
|
||||
#include "pcm_chunk.hpp"
|
||||
#include "server_settings.hpp"
|
||||
#include "stream_tags.hpp"
|
||||
#include "time.hpp"
|
||||
#include "pcm_chunk.hpp"
|
||||
|
||||
#include "common/str_compat.hpp"
|
||||
#include "common/utils.hpp"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue