M harmony_changelog.md => harmony_changelog.md +1 -0
@@ 9,6 9,7 @@
* Added What's New section shown after update
* Added versioning for private assets
* Added greetings in all languages
+* Added fade in and fade out to relaxation songs
### Changed / Improved
M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +9 -2
@@ 77,10 77,17 @@ namespace audio
Other
};
- enum class FadeIn
+ enum class Fade
{
Disable,
- Enable
+ In,
+ InOut
+ };
+
+ struct FadeParams
+ {
+ Fade mode;
+ std::optional<std::chrono::seconds> playbackDuration = std::nullopt;
};
enum class VolumeUpdateType
M products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp => products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp +6 -5
@@ 23,13 23,14 @@ namespace alarms
if (duration.has_value()) {
spawnTimer(duration.value());
}
- const auto fadeInSettings = utils::getNumericValue<bool>(
+ const auto fadeEnabledInSettings = utils::getNumericValue<bool>(
settings.getValue(bell::settings::Alarm::fadeActive, settings::SettingsScope::Global));
- const auto fadeInEnabled = (fadeInSettings && (playbackType == audio::PlaybackType::Alarm))
- ? audio::FadeIn::Enable
- : audio::FadeIn::Disable;
+ const auto fadeInEnabled = (fadeEnabledInSettings && (playbackType == audio::PlaybackType::Alarm))
+ ? audio::Fade::In
+ : audio::Fade::Disable;
- auto msg = std::make_shared<service::AudioStartPlaybackRequest>(path, playbackType, fadeInEnabled);
+ auto msg =
+ std::make_shared<service::AudioStartPlaybackRequest>(path, playbackType, audio::FadeParams{fadeInEnabled});
return service.bus.sendUnicast(std::move(msg), service::audioServiceName);
}
M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp => products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp +4 -3
@@ 89,9 89,10 @@ namespace app::powernap
const auto &filePath = settings->getValue(bell::settings::Alarm::tonePath, settings::SettingsScope::Global);
const auto fadeInActive = utils::getNumericValue<bool>(settings->getValue(bell::settings::Alarm::fadeActive,
settings::SettingsScope::Global))
- ? audio::FadeIn::Enable
- : audio::FadeIn::Disable;
- audioModel.play(filePath, AbstractAudioModel::PlaybackType::Alarm, {}, fadeInActive);
+ ? audio::Fade::In
+ : audio::Fade::Disable;
+
+ audioModel.play(filePath, AbstractAudioModel::PlaybackType::Alarm, {}, audio::FadeParams{fadeInActive});
napAlarmTimer.start();
napFinished = true;
}
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.cpp +11 -7
@@ 41,12 41,15 @@ namespace app::relaxation
Expects(timer != nullptr);
AbstractRelaxationPlayer::PlaybackMode mode;
- const auto value = settings->getValue(timerValueDBRecordName, settings::SettingsScope::AppLocal);
- const auto songLength = std::chrono::seconds{song.audioProperties.songLength};
- if (utils::is_number(value) && (utils::getNumericValue<int>(value) != 0) &&
- !isSongLengthEqualToPeriod(songLength, std::chrono::minutes{utils::getNumericValue<int>(value)})) {
- const auto playbackTimeInMinutes = std::chrono::minutes{utils::getNumericValue<int>(value)};
- timer->reset(playbackTimeInMinutes);
+ const auto settingsValue = settings->getValue(timerValueDBRecordName, settings::SettingsScope::AppLocal);
+ const auto presetDuration = utils::getNumericValue<int>(settingsValue);
+ const auto songLength = std::chrono::seconds{song.audioProperties.songLength};
+ auto playbackDuration = songLength;
+
+ if (utils::is_number(settingsValue) && (presetDuration != 0) &&
+ !isSongLengthEqualToPeriod(songLength, std::chrono::minutes{presetDuration})) {
+ playbackDuration = std::chrono::minutes{presetDuration};
+ timer->reset(playbackDuration);
mode = AbstractRelaxationPlayer::PlaybackMode::Looped;
}
else {
@@ 76,7 79,8 @@ namespace app::relaxation
}
};
- player.start(song.fileInfo.path, mode, std::move(onStartCallback), std::move(onFinishedCallback));
+ player.start(
+ song.fileInfo.path, mode, std::move(onStartCallback), std::move(onFinishedCallback), playbackDuration);
}
void RelaxationRunningProgressPresenter::stop()
M products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.cpp => products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.cpp +4 -2
@@ 17,7 17,8 @@ namespace app::relaxation
void RelaxationPlayer::start(const std::string &filePath,
AbstractRelaxationPlayer::PlaybackMode mode,
AbstractAudioModel::OnStateChangeCallback &&stateChangeCallback,
- AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback)
+ AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback,
+ std::optional<std::chrono::seconds> playbackDuration)
{
using Status = AbstractAudioModel::PlaybackFinishStatus;
using Type = AbstractAudioModel::PlaybackType;
@@ 41,8 42,9 @@ namespace app::relaxation
}
};
+ auto fadeParams = audio::FadeParams{.mode = audio::Fade::InOut, .playbackDuration = playbackDuration};
audioModel.setPlaybackFinishedCb(std::move(onPlayerFinished));
- audioModel.play(filePath, Type::Multimedia, std::move(stateChangeCallback));
+ audioModel.play(filePath, Type::Multimedia, std::move(stateChangeCallback), std::move(fadeParams));
}
void RelaxationPlayer::stop(AbstractAudioModel::OnStateChangeCallback &&callback)
M products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.hpp => products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.hpp +10 -8
@@ 26,16 26,17 @@ namespace app::relaxation
SingleShot
};
- virtual ~AbstractRelaxationPlayer() = default;
+ virtual ~AbstractRelaxationPlayer() = default;
virtual void start(const std::string &filePath,
PlaybackMode mode,
AbstractAudioModel::OnStateChangeCallback &&callback,
- AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback) = 0;
- virtual void stop(AbstractAudioModel::OnStateChangeCallback &&callback) = 0;
- virtual void pause(AbstractAudioModel::OnStateChangeCallback &&callback) = 0;
- virtual void resume(AbstractAudioModel::OnStateChangeCallback &&callback) = 0;
- virtual PlaybackMode getCurrentMode() const noexcept = 0;
- virtual bool isPaused() = 0;
+ AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback,
+ std::optional<std::chrono::seconds> playbackDuration = std::nullopt) = 0;
+ virtual void stop(AbstractAudioModel::OnStateChangeCallback &&callback) = 0;
+ virtual void pause(AbstractAudioModel::OnStateChangeCallback &&callback) = 0;
+ virtual void resume(AbstractAudioModel::OnStateChangeCallback &&callback) = 0;
+ virtual PlaybackMode getCurrentMode() const noexcept = 0;
+ virtual bool isPaused() = 0;
};
class RelaxationPlayer : public AbstractRelaxationPlayer
@@ 47,7 48,8 @@ namespace app::relaxation
void start(const std::string &filePath,
PlaybackMode mode,
AbstractAudioModel::OnStateChangeCallback &&callback,
- AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback) override;
+ AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback,
+ std::optional<std::chrono::seconds> playbackDuration = std::nullopt) override;
void stop(AbstractAudioModel::OnStateChangeCallback &&callback) override;
void pause(AbstractAudioModel::OnStateChangeCallback &&callback) override;
void resume(AbstractAudioModel::OnStateChangeCallback &&callback) override;
M products/BellHybrid/apps/common/include/common/models/AbstractAudioModel.hpp => products/BellHybrid/apps/common/include/common/models/AbstractAudioModel.hpp +11 -11
@@ 39,22 39,22 @@ namespace app
using OnGetValueCallback = std::function<void(const audio::RetCode, Volume)>;
using OnPlaybackFinishedCallback = std::function<void(PlaybackFinishStatus)>;
- virtual ~AbstractAudioModel() noexcept = default;
+ virtual ~AbstractAudioModel() noexcept = default;
virtual void setVolume(Volume volume,
PlaybackType playbackType,
audio::VolumeUpdateType updateType = audio::VolumeUpdateType::UpdateDB,
- OnStateChangeCallback &&callback = {}) = 0;
- virtual std::optional<Volume> getVolume(PlaybackType playbackType) = 0;
- virtual void getVolume(PlaybackType playbackType, OnGetValueCallback &&callback) = 0;
+ OnStateChangeCallback &&callback = {}) = 0;
+ virtual std::optional<Volume> getVolume(PlaybackType playbackType) = 0;
+ virtual void getVolume(PlaybackType playbackType, OnGetValueCallback &&callback) = 0;
virtual void play(const std::string &filePath,
PlaybackType type,
OnStateChangeCallback &&callback,
- audio::FadeIn fadeIn = audio::FadeIn::Disable) = 0;
- virtual void stopAny(OnStateChangeCallback &&callback) = 0;
- virtual void stopPlayedByThis(OnStateChangeCallback &&callback) = 0;
- virtual void pause(OnStateChangeCallback &&callback) = 0;
- virtual void resume(OnStateChangeCallback &&callback) = 0;
- virtual void setPlaybackFinishedCb(OnPlaybackFinishedCallback &&callback) = 0;
- virtual bool hasPlaybackFinished() = 0;
+ std::optional<audio::FadeParams> fadeParams = std::nullopt) = 0;
+ virtual void stopAny(OnStateChangeCallback &&callback) = 0;
+ virtual void stopPlayedByThis(OnStateChangeCallback &&callback) = 0;
+ virtual void pause(OnStateChangeCallback &&callback) = 0;
+ virtual void resume(OnStateChangeCallback &&callback) = 0;
+ virtual void setPlaybackFinishedCb(OnPlaybackFinishedCallback &&callback) = 0;
+ virtual bool hasPlaybackFinished() = 0;
};
} // namespace app
M products/BellHybrid/apps/common/include/common/models/AudioModel.hpp => products/BellHybrid/apps/common/include/common/models/AudioModel.hpp +1 -1
@@ 23,7 23,7 @@ namespace app
void play(const std::string &filePath,
PlaybackType type,
OnStateChangeCallback &&callback,
- audio::FadeIn fadeIn = audio::FadeIn::Disable) override;
+ std::optional<audio::FadeParams> fadeParams = std::nullopt) override;
void stopAny(OnStateChangeCallback &&callback) override;
void stopPlayedByThis(OnStateChangeCallback &&callback) override;
void pause(OnStateChangeCallback &&callback) override;
M products/BellHybrid/apps/common/src/AudioModel.cpp => products/BellHybrid/apps/common/src/AudioModel.cpp +3 -2
@@ 87,10 87,11 @@ namespace app
void AudioModel::play(const std::string &filePath,
PlaybackType type,
OnStateChangeCallback &&callback,
- audio::FadeIn fadeIn)
+ std::optional<audio::FadeParams> fadeParams)
{
playbackFinishedFlag = false;
- auto msg = std::make_unique<service::AudioStartPlaybackRequest>(filePath, convertPlaybackType(type), fadeIn);
+ auto msg =
+ std::make_unique<service::AudioStartPlaybackRequest>(filePath, convertPlaybackType(type), fadeParams);
auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::audioServiceName);
auto cb = [_callback = callback, this](auto response) {
M products/BellHybrid/services/audio/CMakeLists.txt => products/BellHybrid/services/audio/CMakeLists.txt +2 -2
@@ 13,12 13,12 @@ target_include_directories(bell-audio
target_sources(bell-audio
PRIVATE
ServiceAudio.cpp
- VolumeFadeIn.cpp
+ VolumeFade.cpp
PUBLIC
include/audio/AudioMessage.hpp
include/audio/ServiceAudio.hpp
- include/audio/VolumeFadeIn.hpp
+ include/audio/VolumeFade.hpp
)
M products/BellHybrid/services/audio/ServiceAudio.cpp => products/BellHybrid/services/audio/ServiceAudio.cpp +8 -8
@@ 92,11 92,11 @@ namespace service
}
}
};
- volumeFadeIn = std::make_unique<audio::VolumeFadeIn>(this, std::move(callback));
+ volumeFade = std::make_unique<audio::VolumeFade>(this, std::move(callback));
connect(typeid(AudioStartPlaybackRequest), [this](sys::Message *msg) -> sys::MessagePointer {
auto *msgl = static_cast<AudioStartPlaybackRequest *>(msg);
- return handleStart(audio::Operation::Type::Playback, msgl->fadeIn, msgl->fileName, msgl->playbackType);
+ return handleStart(audio::Operation::Type::Playback, msgl->fadeParams, msgl->fileName, msgl->playbackType);
});
connect(typeid(internal::AudioEOFNotificationMessage), [this](sys::Message *msg) -> sys::MessagePointer {
@@ 113,7 113,7 @@ namespace service
});
connect(typeid(AudioStopRequest), [this](sys::Message *msg) -> sys::MessagePointer {
- volumeFadeIn->Stop();
+ volumeFade->Stop();
auto *msgl = static_cast<AudioStopRequest *>(msg);
return handleStop(msgl->stopVec, msgl->token);
});
@@ 168,7 168,7 @@ namespace service
}
auto Audio::handleStart(audio::Operation::Type opType,
- audio::FadeIn fadeIn,
+ std::optional<audio::FadeParams> fadeParams,
const std::string &fileName,
const audio::PlaybackType &playbackType) -> std::unique_ptr<AudioResponseMessage>
{
@@ 193,8 193,8 @@ namespace service
auto input = audioMux.GetPlaybackInput(playbackType);
AudioStart(input);
manageCpuSentinel();
- if (fadeIn == audio::FadeIn::Enable) {
- volumeFadeIn->Start(getVolume(playbackType), minVolumeToSet, maxVolumeToSet);
+ if (fadeParams.has_value()) {
+ volumeFade->Start(getVolume(playbackType), minVolumeToSet, maxVolumeToSet, fadeParams.value());
}
return std::make_unique<AudioStartPlaybackResponse>(retCode, retToken);
@@ 275,8 275,8 @@ namespace service
if (const auto input = audioMux.GetInput(token); input) {
if (shouldLoop((*input)->audio->GetCurrentOperationPlaybackType())) {
(*input)->audio->Start();
- if (volumeFadeIn->IsActive()) {
- volumeFadeIn->Restart();
+ if (volumeFade->IsActive()) {
+ volumeFade->Restart();
}
if ((*input)->audio->IsMuted()) {
R products/BellHybrid/services/audio/VolumeFadeIn.cpp => products/BellHybrid/services/audio/VolumeFade.cpp +73 -17
@@ 1,70 1,126 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-#include "VolumeFadeIn.hpp"
+#include "VolumeFade.hpp"
#include <log/log.hpp>
namespace audio
{
namespace
{
+ using namespace std::chrono_literals;
constexpr auto timerName{"volumeFadeTimer"};
- constexpr std::chrono::milliseconds fadeInterval{300};
+ constexpr auto fadeInterval{33ms};
constexpr auto fadeStep{0.1f};
+
+ std::chrono::milliseconds calculateTimerPeriod(std::chrono::seconds elapsed,
+ std::chrono::seconds duration,
+ float currentVolume)
+ {
+ using floatToMs = std::chrono::duration<float, std::chrono::milliseconds::period>;
+ const auto timeRequired = floatToMs(currentVolume / fadeStep) * fadeInterval.count();
+ const auto calculatedTime =
+ std::chrono::duration_cast<std::chrono::milliseconds>(duration - elapsed - timeRequired);
+ return calculatedTime > std::chrono::milliseconds::zero() ? calculatedTime
+ : std::chrono::milliseconds::zero();
+ }
} // namespace
- VolumeFadeIn::VolumeFadeIn(sys::Service *parent, SetCallback callback) : setVolumeCallback(std::move(callback))
+ VolumeFade::VolumeFade(sys::Service *parent, SetCallback callback) : setVolumeCallback(std::move(callback))
{
timerHandle = sys::TimerFactory::createPeriodicTimer(
parent, timerName, fadeInterval, [this]([[maybe_unused]] sys::Timer &timer) { PerformNextFadeStep(); });
}
- VolumeFadeIn::~VolumeFadeIn()
+ VolumeFade::~VolumeFade()
{
Stop();
}
- void VolumeFadeIn::Start(float targetVolume, float minVolume, float maxVolume)
+ void VolumeFade::Start(float targetVolume, float minVolume, float maxVolume, audio::FadeParams fadeParams)
{
+ if (fadeParams.mode == audio::Fade::Disable) {
+ return;
+ }
if (targetVolume > maxVolume || minVolume > maxVolume) {
- LOG_ERROR("Incorrect parameters for audio fade in!");
+ LOG_ERROR("Incorrect parameters for audio fade!");
return;
}
+ this->fadeParams = fadeParams;
this->targetVolume = targetVolume;
this->minVolume = minVolume;
this->maxVolume = maxVolume;
currentVolume = minVolume + fadeStep;
+ state = State::FadeIn;
+ timestamp = std::chrono::system_clock::now();
Restart();
- timerHandle.start();
+ timerHandle.restart(fadeInterval);
}
- void VolumeFadeIn::Restart()
+ void VolumeFade::Restart()
{
if (setVolumeCallback != nullptr) {
setVolumeCallback(currentVolume);
}
}
- void VolumeFadeIn::Stop()
+ void VolumeFade::Stop()
{
timerHandle.stop();
+ state = State::Disable;
}
- bool VolumeFadeIn::IsActive()
+ bool VolumeFade::IsActive()
{
return timerHandle.isActive();
}
- void VolumeFadeIn::PerformNextFadeStep()
+ void VolumeFade::PerformNextFadeStep()
{
- if (currentVolume < targetVolume) {
- currentVolume = std::clamp(std::min(currentVolume + fadeStep, targetVolume), minVolume, maxVolume);
- if (setVolumeCallback != nullptr) {
- setVolumeCallback(currentVolume);
+ switch (state) {
+ case State::FadeIn:
+ if (currentVolume < targetVolume) {
+ TurnUpVolume();
+ }
+ 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));
}
+ else {
+ Stop();
+ }
+ break;
+ case State::FadeOut:
+ if (currentVolume > 0.0f) {
+ TurnDownVolume();
+ timerHandle.restart(fadeInterval);
+ }
+ else {
+ Stop();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ void VolumeFade::TurnUpVolume()
+ {
+ currentVolume = std::clamp(std::min(currentVolume + fadeStep, targetVolume), minVolume, maxVolume);
+ if (setVolumeCallback != nullptr) {
+ setVolumeCallback(currentVolume);
}
- else {
- Stop();
+ }
+ void VolumeFade::TurnDownVolume()
+ {
+ currentVolume = std::clamp(std::max(currentVolume - fadeStep, 0.0f), minVolume, maxVolume);
+ if (setVolumeCallback != nullptr) {
+ setVolumeCallback(currentVolume);
}
}
+
} // namespace audio
M products/BellHybrid/services/audio/include/audio/AudioMessage.hpp => products/BellHybrid/services/audio/include/audio/AudioMessage.hpp +3 -3
@@ 120,13 120,13 @@ namespace service
public:
AudioStartPlaybackRequest(const std::string &fileName,
const audio::PlaybackType &playbackType,
- const audio::FadeIn fadeIn = audio::FadeIn::Disable)
- : AudioMessage(), fileName(fileName), playbackType(playbackType), fadeIn(fadeIn)
+ std::optional<audio::FadeParams> fadeParams = std::nullopt)
+ : AudioMessage(), fileName(fileName), playbackType(playbackType), fadeParams(fadeParams)
{}
const std::string fileName;
const audio::PlaybackType playbackType;
- const audio::FadeIn fadeIn;
+ const std::optional<audio::FadeParams> fadeParams;
};
class AudioStartPlaybackResponse : public AudioResponseMessage
M products/BellHybrid/services/audio/include/audio/ServiceAudio.hpp => products/BellHybrid/services/audio/include/audio/ServiceAudio.hpp +3 -3
@@ 4,7 4,7 @@
#pragma once
#include "AudioMessage.hpp"
-#include "VolumeFadeIn.hpp"
+#include "VolumeFade.hpp"
#include <Audio/Audio.hpp>
#include <Audio/AudioMux.hpp>
#include <MessageType.hpp>
@@ 42,7 42,7 @@ namespace service
};
auto handleStart(audio::Operation::Type opType,
- audio::FadeIn fadeIn,
+ std::optional<audio::FadeParams> fadeParams,
const std::string &fileName = {},
const audio::PlaybackType &playbackType = audio::PlaybackType::None)
-> std::unique_ptr<AudioResponseMessage>;
@@ 75,7 75,7 @@ namespace service
mutable audio::AudioMux audioMux;
std::shared_ptr<sys::CpuSentinel> cpuSentinel;
std::unique_ptr<settings::Settings> settingsProvider;
- std::unique_ptr<audio::VolumeFadeIn> volumeFadeIn;
+ std::unique_ptr<audio::VolumeFade> volumeFade;
};
} // namespace service
R products/BellHybrid/services/audio/include/audio/VolumeFadeIn.hpp => products/BellHybrid/services/audio/include/audio/VolumeFade.hpp +16 -4
@@ 8,27 8,39 @@
namespace audio
{
- class VolumeFadeIn
+ class VolumeFade
{
public:
using SetCallback = std::function<void(float)>;
- VolumeFadeIn(sys::Service *parent, SetCallback callback);
- ~VolumeFadeIn();
+ VolumeFade(sys::Service *parent, SetCallback callback);
+ ~VolumeFade();
- void Start(float targetVolume, float minVolume, float maxVolume);
+ void Start(float targetVolume, float minVolume, float maxVolume, audio::FadeParams fadeParams);
void Restart();
void Stop();
bool IsActive();
private:
+ enum class State
+ {
+ Disable,
+ FadeIn,
+ FadeOut
+ };
+
+ audio::FadeParams fadeParams;
sys::TimerHandle timerHandle;
float targetVolume;
float minVolume;
float maxVolume;
float currentVolume;
SetCallback setVolumeCallback;
+ State state{State::Disable};
+ std::chrono::time_point<std::chrono::system_clock> timestamp;
void PerformNextFadeStep();
+ void TurnUpVolume();
+ void TurnDownVolume();
};
} // namespace audio