mirror of
https://gitlab.freedesktop.org/pulseaudio/pavucontrol.git
synced 2025-12-20 05:20:06 +01:00
Decay volume meters on monitor stream suspend
If the stream is suspended mid peak, the volume meter just hangs at the last level. This can happen with a idle suspend timeout of 0 on PulseAudio, or just by default on PipeWire. When that happens, we attach to the frame clock and decay to zero in a second. Fixes: https://gitlab.freedesktop.org/pulseaudio/pavucontrol/-/issues/174
This commit is contained in:
parent
cd7bd578ae
commit
ce48aec45f
3 changed files with 88 additions and 23 deletions
|
|
@ -626,7 +626,7 @@ static void suspended_callback(pa_stream *s, void *userdata) {
|
|||
MainWindow *w = static_cast<MainWindow*>(userdata);
|
||||
|
||||
if (pa_stream_is_suspended(s))
|
||||
w->updateVolumeMeter(pa_stream_get_device_index(s), PA_INVALID_INDEX, -1);
|
||||
w->updateVolumeMeter(pa_stream_get_device_index(s), pa_stream_get_monitor_stream(s), -1);
|
||||
}
|
||||
|
||||
static void read_callback(pa_stream *s, size_t length, void *userdata) {
|
||||
|
|
@ -1091,13 +1091,15 @@ void MainWindow::updateDeviceInfo(const pa_ext_device_restore_info &info) {
|
|||
|
||||
|
||||
void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_idx, double v) {
|
||||
MinimalStreamWidget *sw = NULL;
|
||||
|
||||
if (sink_input_idx != PA_INVALID_INDEX) {
|
||||
SinkInputWidget *w;
|
||||
|
||||
if (sinkInputWidgets.count(sink_input_idx)) {
|
||||
w = sinkInputWidgets[sink_input_idx];
|
||||
w->updatePeak(v);
|
||||
sw = static_cast<MinimalStreamWidget*>(w);
|
||||
goto done;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -1105,22 +1107,38 @@ void MainWindow::updateVolumeMeter(uint32_t source_index, uint32_t sink_input_id
|
|||
for (std::map<uint32_t, SinkWidget*>::iterator i = sinkWidgets.begin(); i != sinkWidgets.end(); ++i) {
|
||||
SinkWidget* w = i->second;
|
||||
|
||||
if (w->monitor_index == source_index)
|
||||
w->updatePeak(v);
|
||||
if (w->monitor_index == source_index) {
|
||||
sw = static_cast<MinimalStreamWidget*>(w);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<uint32_t, SourceWidget*>::iterator i = sourceWidgets.begin(); i != sourceWidgets.end(); ++i) {
|
||||
SourceWidget* w = i->second;
|
||||
|
||||
if (w->index == source_index)
|
||||
w->updatePeak(v);
|
||||
if (w->index == source_index) {
|
||||
sw = static_cast<MinimalStreamWidget*>(w);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<uint32_t, SourceOutputWidget*>::iterator i = sourceOutputWidgets.begin(); i != sourceOutputWidgets.end(); ++i) {
|
||||
SourceOutputWidget* w = i->second;
|
||||
|
||||
if (w->sourceIndex() == source_index)
|
||||
w->updatePeak(v);
|
||||
if (w->sourceIndex() == source_index) {
|
||||
sw = static_cast<MinimalStreamWidget*>(w);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (sw) {
|
||||
if (v == -1) {
|
||||
sw->decayToZero();
|
||||
} else {
|
||||
sw->stopDecay();
|
||||
sw->updatePeak(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject) :
|
|||
peak(NULL),
|
||||
updating(false),
|
||||
volumeMeterEnabled(false),
|
||||
volumeMeterVisible(true) {
|
||||
volumeMeterVisible(true),
|
||||
decayTickId(0),
|
||||
decayLastFrameTime(-1) {
|
||||
}
|
||||
|
||||
MinimalStreamWidget::~MinimalStreamWidget() {
|
||||
|
|
@ -61,22 +63,27 @@ void MinimalStreamWidget::init() {
|
|||
peakProgressBar.hide();
|
||||
}
|
||||
|
||||
#define DECAY_STEP (1.0 / PEAKS_RATE)
|
||||
void MinimalStreamWidget::stopDecay() {
|
||||
if (decayTickId) {
|
||||
remove_tick_callback(decayTickId);
|
||||
decayTickId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MinimalStreamWidget::updatePeak(double v) {
|
||||
if (lastPeak >= DECAY_STEP)
|
||||
if (v < lastPeak - DECAY_STEP)
|
||||
v = lastPeak - DECAY_STEP;
|
||||
void MinimalStreamWidget::updatePeak(double v, double decayStep) {
|
||||
if (lastPeak >= decayStep)
|
||||
if (v < lastPeak - decayStep)
|
||||
v = lastPeak - decayStep;
|
||||
|
||||
lastPeak = v;
|
||||
|
||||
if (v >= 0) {
|
||||
peakProgressBar.set_sensitive(TRUE);
|
||||
peakProgressBar.set_fraction(v);
|
||||
} else {
|
||||
peakProgressBar.set_sensitive(FALSE);
|
||||
peakProgressBar.set_fraction(0);
|
||||
}
|
||||
if (v >= 0) {
|
||||
peakProgressBar.set_sensitive(TRUE);
|
||||
peakProgressBar.set_fraction(v);
|
||||
} else {
|
||||
peakProgressBar.set_sensitive(FALSE);
|
||||
peakProgressBar.set_fraction(0);
|
||||
}
|
||||
|
||||
enableVolumeMeter();
|
||||
}
|
||||
|
|
@ -98,6 +105,40 @@ void MinimalStreamWidget::setVolumeMeterVisible(bool v) {
|
|||
peakProgressBar.show();
|
||||
}
|
||||
} else {
|
||||
stopDecay();
|
||||
peakProgressBar.hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool MinimalStreamWidget::decayOnTick(const Glib::RefPtr<Gdk::FrameClock>& frameClock) {
|
||||
auto frameTime = frameClock->get_frame_time();
|
||||
|
||||
if (lastPeak == 0) {
|
||||
decayTickId = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scale elapsed time (µs) so we decay in at most 1 second
|
||||
if (frameTime != decayLastFrameTime)
|
||||
updatePeak(0, (frameTime - decayLastFrameTime) / 1000000.0);
|
||||
|
||||
decayLastFrameTime = frameTime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MinimalStreamWidget::decayToZero() {
|
||||
if (decayTickId)
|
||||
stopDecay();
|
||||
|
||||
auto frameClock = get_frame_clock();
|
||||
|
||||
if (!frameClock) {
|
||||
/* Widget isn't visible, set all the way to 0 */
|
||||
updatePeak(0, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
decayLastFrameTime = frameClock->get_frame_time();
|
||||
decayTickId = add_tick_callback(sigc::mem_fun(*this, &MinimalStreamWidget::decayOnTick));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#define PEAKS_RATE 144
|
||||
#define DECAY_STEP (1.0 / PEAKS_RATE)
|
||||
|
||||
class MinimalStreamWidget : public Gtk::Box {
|
||||
public:
|
||||
|
|
@ -50,17 +51,22 @@ public:
|
|||
|
||||
bool volumeMeterEnabled;
|
||||
void enableVolumeMeter();
|
||||
void updatePeak(double v);
|
||||
void updatePeak(double v, double decayStep = DECAY_STEP);
|
||||
void setVolumeMeterVisible(bool v);
|
||||
|
||||
void decayToZero();
|
||||
void stopDecay();
|
||||
|
||||
protected:
|
||||
/* Subclasses must call this after the constructor to finalize the initial
|
||||
* layout. */
|
||||
void init();
|
||||
bool decayOnTick(const Glib::RefPtr<Gdk::FrameClock>& frame_clock);
|
||||
|
||||
private :
|
||||
bool volumeMeterVisible;
|
||||
|
||||
guint decayTickId;
|
||||
gint64 decayLastFrameTime;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue