~aleteoryx/muditaos

f41c227199c4705b0836321a561274583be4d9d1 — Maciej Gibowicz 1 year, 4 months ago 18e9fc0
[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
M products/BellHybrid/services/audio/ServiceAudio.cpp => products/BellHybrid/services/audio/ServiceAudio.cpp +4 -1
@@ 331,7 331,10 @@ namespace service
        const auto clampedValue = std::clamp(utils::getNumericValue<float>(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);
            }

M products/BellHybrid/services/audio/VolumeFade.cpp => products/BellHybrid/services/audio/VolumeFade.cpp +52 -12
@@ 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<std::chrono::seconds>(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<std::chrono::seconds>(now - timestamp);
        timerHandle.restart(calculateTimerPeriod(timeElapsed, fadeParams.playbackDuration.value(), currentVolume));
    }

} // namespace audio

M products/BellHybrid/services/audio/include/audio/VolumeFade.hpp => products/BellHybrid/services/audio/include/audio/VolumeFade.hpp +8 -3
@@ 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<std::chrono::system_clock> timestamp;

        void PerformNextFadeStep();
        void RestartTimer();
        void TurnUpVolume();
        void TurnDownVolume();
        void SetTargetVolume(float volume);
        bool IsFadePhaseActive();
    };
} // namespace audio