Implement volume updates for pipewire output

This commit is contained in:
Nicolai Syvertsen 2021-10-18 23:51:52 +02:00
parent 31151cec3c
commit b941a7df83
3 changed files with 109 additions and 13 deletions

View File

@ -37,6 +37,8 @@ public:
{ {
} }
~PipeWireMixer() override;
PipeWireMixer(const PipeWireMixer &) = delete; PipeWireMixer(const PipeWireMixer &) = delete;
PipeWireMixer &operator=(const PipeWireMixer &) = delete; PipeWireMixer &operator=(const PipeWireMixer &) = delete;
@ -82,7 +84,14 @@ pipewire_mixer_init([[maybe_unused]] EventLoop &event_loop, AudioOutput &ao,
const ConfigBlock &) const ConfigBlock &)
{ {
auto &po = (PipeWireOutput &)ao; auto &po = (PipeWireOutput &)ao;
return new PipeWireMixer(po, listener); auto *pm = new PipeWireMixer(po, listener);
pipewire_output_set_mixer(po, *pm);
return pm;
}
PipeWireMixer::~PipeWireMixer()
{
pipewire_output_clear_mixer(output, *this);
} }
const MixerPlugin pipewire_mixer_plugin = { const MixerPlugin pipewire_mixer_plugin = {

View File

@ -41,6 +41,9 @@
#include <spa/param/audio/format-utils.h> #include <spa/param/audio/format-utils.h>
#include <spa/param/props.h> #include <spa/param/props.h>
#include <cmath>
#include <iostream>
#ifdef __GNUC__ #ifdef __GNUC__
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
@ -75,6 +78,9 @@ class PipeWireOutput final : AudioOutput {
float volume = 1.0; float volume = 1.0;
PipeWireMixer *mixer = nullptr;
int channels;
/** /**
* The active sample format, needed for PcmSilence(). * The active sample format, needed for PcmSilence().
*/ */
@ -124,11 +130,21 @@ public:
events.state_changed = StateChanged; events.state_changed = StateChanged;
events.process = Process; events.process = Process;
events.drained = Drained; events.drained = Drained;
events.control_info = ControlInfo;
events.param_changed = ParamChanged;
return events; return events;
} }
void SetVolume(float volume); void SetVolume(float volume);
void SetMixer(PipeWireMixer &_mixer);
void ClearMixer([[maybe_unused]] PipeWireMixer &old_mixer) {
assert(mixer == &old_mixer);
mixer = nullptr;
}
private: private:
void CheckThrowError() { void CheckThrowError() {
if (disconnected) if (disconnected)
@ -163,6 +179,46 @@ private:
o.Drained(); o.Drained();
} }
void ControlInfo(const struct pw_stream_control *control) {
float sum = 0;
unsigned c;
for (c = 0; c < control->n_values; c++)
sum += control->values[c];
sum /= control->n_values;
if (mixer != nullptr)
pipewire_mixer_on_change(*mixer, std::cbrt(sum));
pw_thread_loop_signal(thread_loop, false);
}
static void ControlInfo(void *data,
[[maybe_unused]] uint32_t id,
const struct pw_stream_control *control) noexcept {
auto &o = *(PipeWireOutput *)data;
if (StringIsEqual(control->name, "Channel Volumes"))
o.ControlInfo(control);
}
void ParamChanged() {
if (restore_volume) {
SetVolume(volume);
restore_volume = false;
}
}
static void ParamChanged(void *data,
uint32_t id,
const struct spa_pod *param)
{
if (id != SPA_PARAM_Format || param == NULL)
return;
auto &o = *(PipeWireOutput *)data;
o.ParamChanged();
}
/* virtual methods from class AudioOutput */ /* virtual methods from class AudioOutput */
void Enable() override; void Enable() override;
void Disable() noexcept override; void Disable() noexcept override;
@ -214,11 +270,20 @@ PipeWireOutput::SetVolume(float _volume)
{ {
const PipeWire::ThreadLoopLock lock(thread_loop); const PipeWire::ThreadLoopLock lock(thread_loop);
if (stream != nullptr && !restore_volume && float newvol = _volume*_volume*_volume;
pw_stream_set_control(stream,
SPA_PROP_volume, 1, &_volume, if (stream != nullptr && !restore_volume) {
float vol[SPA_AUDIO_MAX_CHANNELS];
int i;
for (i = 0; i < channels; i++) {
vol[i] = newvol;
}
if (pw_stream_set_control(stream,
SPA_PROP_channelVolumes, channels, vol,
0) != 0) 0) != 0)
throw std::runtime_error("pw_stream_set_control() failed"); throw std::runtime_error("pw_stream_set_control() failed");
}
volume = _volume; volume = _volume;
} }
@ -404,6 +469,7 @@ PipeWireOutput::Open(AudioFormat &audio_format)
frame_size = audio_format.GetFrameSize(); frame_size = audio_format.GetFrameSize();
sample_format = audio_format.format; sample_format = audio_format.format;
channels = audio_format.channels;
interrupted = false; interrupted = false;
/* allocate a ring buffer of 0.5 seconds */ /* allocate a ring buffer of 0.5 seconds */
@ -451,14 +517,6 @@ PipeWireOutput::StateChanged(enum pw_stream_state state,
if (!was_disconnected && disconnected) if (!was_disconnected && disconnected)
pw_thread_loop_signal(thread_loop, false); pw_thread_loop_signal(thread_loop, false);
if (state == PW_STREAM_STATE_STREAMING && restore_volume) {
/* restore the last known volume after creating a new
pw_stream */
restore_volume = false;
pw_stream_set_control(stream,
SPA_PROP_volume, 1, &volume,
0);
}
} }
inline void inline void
@ -585,6 +643,28 @@ PipeWireOutput::Pause() noexcept
return true; return true;
} }
inline void
PipeWireOutput::SetMixer(PipeWireMixer &_mixer)
{
assert(mixer == nullptr);
mixer = &_mixer;
// TODO: Check if context and stream is ready and trigger a volume update...
}
void
pipewire_output_set_mixer(PipeWireOutput &po, PipeWireMixer &pm)
{
po.SetMixer(pm);
}
void
pipewire_output_clear_mixer(PipeWireOutput &po, PipeWireMixer &pm)
{
po.ClearMixer(pm);
}
const struct AudioOutputPlugin pipewire_output_plugin = { const struct AudioOutputPlugin pipewire_output_plugin = {
"pipewire", "pipewire",
nullptr, nullptr,

View File

@ -21,9 +21,16 @@
#define MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX #define MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX
class PipeWireOutput; class PipeWireOutput;
class PipeWireMixer;
extern const struct AudioOutputPlugin pipewire_output_plugin; extern const struct AudioOutputPlugin pipewire_output_plugin;
void
pipewire_output_set_mixer(PipeWireOutput &po, PipeWireMixer &pm);
void
pipewire_output_clear_mixer(PipeWireOutput &po, PipeWireMixer &pm);
void void
pipewire_output_set_volume(PipeWireOutput &output, float volume); pipewire_output_set_volume(PipeWireOutput &output, float volume);