From f41c227199c4705b0836321a561274583be4d9d1 Mon Sep 17 00:00:00 2001 From: Maciej Gibowicz Date: Wed, 17 Jul 2024 13:10:25 +0200 Subject: [PATCH] [BH-2029] Add volume control during fade in and fade out During the fade in or fade out phase, when the user turns the encoder to set the volume, the fade in or fade out phase continues and the target volume is set to the one requested by the user --- .../services/audio/ServiceAudio.cpp | 5 +- .../BellHybrid/services/audio/VolumeFade.cpp | 64 +++++++++++++++---- .../audio/include/audio/VolumeFade.hpp | 11 +++- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/products/BellHybrid/services/audio/ServiceAudio.cpp b/products/BellHybrid/services/audio/ServiceAudio.cpp index 0c404c739ea456d6d41b4b6ab9b70de3dbf97cf0..108090f5bc40993b45e4bf54bdadc4871d340096 100644 --- a/products/BellHybrid/services/audio/ServiceAudio.cpp +++ b/products/BellHybrid/services/audio/ServiceAudio.cpp @@ -331,7 +331,10 @@ namespace service const auto clampedValue = std::clamp(utils::getNumericValue(value), minVolumeToSet, maxVolumeToSet); auto retCode = audio::RetCode::Success; - if (const auto activeInput = audioMux.GetActiveInput(); activeInput.has_value()) { + if (volumeFade->IsActive()) { + volumeFade->SetVolume(clampedValue); + } + else if (const auto activeInput = audioMux.GetActiveInput(); activeInput.has_value()) { if (activeInput.value() != nullptr) { retCode = activeInput.value()->audio->SetOutputVolume(clampedValue); } diff --git a/products/BellHybrid/services/audio/VolumeFade.cpp b/products/BellHybrid/services/audio/VolumeFade.cpp index d7c5cd288828262b8d3a16bbe4c30e874d6d5bdf..0fe1e1e6e9f06cf6386d46bbb679abd9357d76c0 100644 --- a/products/BellHybrid/services/audio/VolumeFade.cpp +++ b/products/BellHybrid/services/audio/VolumeFade.cpp @@ -28,7 +28,7 @@ namespace audio VolumeFade::VolumeFade(sys::Service *parent, SetCallback callback) : setVolumeCallback(std::move(callback)) { - timerHandle = sys::TimerFactory::createPeriodicTimer( + timerHandle = sys::TimerFactory::createSingleShotTimer( parent, timerName, fadeInterval, [this]([[maybe_unused]] sys::Timer &timer) { PerformNextFadeStep(); }); } @@ -51,7 +51,7 @@ namespace audio this->minVolume = minVolume; this->maxVolume = maxVolume; currentVolume = minVolume + fadeStep; - state = State::FadeIn; + phase = Phase::FadeIn; timestamp = std::chrono::system_clock::now(); Restart(); timerHandle.restart(fadeInterval); @@ -66,8 +66,35 @@ namespace audio void VolumeFade::Stop() { - timerHandle.stop(); - state = State::Disable; + if (timerHandle.isActive()) { + timerHandle.stop(); + } + phase = Phase::Idle; + } + + void VolumeFade::SetVolume(float volume) + { + if (IsFadePhaseActive()) { + SetTargetVolume(volume); + } + else { + currentVolume = std::clamp(volume, minVolume, maxVolume); + if (setVolumeCallback != nullptr) { + setVolumeCallback(currentVolume); + } + RestartTimer(); + } + } + + void VolumeFade::SetTargetVolume(float volume) + { + targetVolume = std::clamp(volume, minVolume, maxVolume); + if (currentVolume > targetVolume) { + currentVolume = std::clamp(currentVolume, minVolume, targetVolume); + if (setVolumeCallback != nullptr) { + setVolumeCallback(currentVolume); + } + } } bool VolumeFade::IsActive() @@ -75,25 +102,31 @@ namespace audio return timerHandle.isActive(); } + bool VolumeFade::IsFadePhaseActive() + { + return (phase == Phase::FadeIn) || (phase == Phase::FadeOut); + } + void VolumeFade::PerformNextFadeStep() { - switch (state) { - case State::FadeIn: + switch (phase) { + case Phase::FadeIn: if (currentVolume < targetVolume) { TurnUpVolume(); + timerHandle.restart(fadeInterval); } else if (fadeParams.mode == audio::Fade::InOut && fadeParams.playbackDuration.has_value()) { - state = State::FadeOut; - const auto now = std::chrono::system_clock::now(); - const auto timeElapsed = std::chrono::duration_cast(now - timestamp); - timerHandle.restart( - calculateTimerPeriod(timeElapsed, fadeParams.playbackDuration.value(), currentVolume)); + phase = Phase::Wait; + RestartTimer(); } else { Stop(); } break; - case State::FadeOut: + case Phase::Wait: + phase = Phase::FadeOut; + [[fallthrough]]; + case Phase::FadeOut: if (currentVolume > 0.0f) { TurnDownVolume(); timerHandle.restart(fadeInterval); @@ -123,4 +156,11 @@ namespace audio } } + void VolumeFade::RestartTimer() + { + const auto now = std::chrono::system_clock::now(); + const auto timeElapsed = std::chrono::duration_cast(now - timestamp); + timerHandle.restart(calculateTimerPeriod(timeElapsed, fadeParams.playbackDuration.value(), currentVolume)); + } + } // namespace audio diff --git a/products/BellHybrid/services/audio/include/audio/VolumeFade.hpp b/products/BellHybrid/services/audio/include/audio/VolumeFade.hpp index a2e7619a0a609ef97755051b3c1ff177f44b8cab..e20c074bbe1e476f670b936052bd4e284b005cdf 100644 --- a/products/BellHybrid/services/audio/include/audio/VolumeFade.hpp +++ b/products/BellHybrid/services/audio/include/audio/VolumeFade.hpp @@ -19,13 +19,15 @@ namespace audio void Start(float targetVolume, float minVolume, float maxVolume, audio::FadeParams fadeParams); void Restart(); void Stop(); + void SetVolume(float volume); bool IsActive(); private: - enum class State + enum class Phase { - Disable, + Idle, FadeIn, + Wait, FadeOut }; @@ -36,11 +38,14 @@ namespace audio float maxVolume; float currentVolume; SetCallback setVolumeCallback; - State state{State::Disable}; + Phase phase{Phase::Idle}; std::chrono::time_point timestamp; void PerformNextFadeStep(); + void RestartTimer(); void TurnUpVolume(); void TurnDownVolume(); + void SetTargetVolume(float volume); + bool IsFadePhaseActive(); }; } // namespace audio