M .gitmodules => .gitmodules +4 -4
@@ 59,10 59,6 @@
[submodule "CrashDebug"]
path = third-party/CrashDebug/src
url = https://github.com/adamgreen/CrashDebug.git
-[submodule "minimp3"]
- path = third-party/minimp3/minimp3
- url = ../minimp3.git
- branch = RT1051
[submodule "parallel-hashmap"]
path = third-party/parallel-hashmap/src
url = https://github.com/greg7mdp/parallel-hashmap.git
@@ 116,3 112,7 @@
[submodule "third-party/fakeit/FakeIt"]
path = third-party/fakeit/FakeIt
url = https://github.com/eranpeer/FakeIt.git
+[submodule "third-party/minimp3/minimp3"]
+ path = third-party/minimp3/minimp3
+ url = https://github.com/mudita/minimp3.git
+ branch = mudita
M harmony_changelog.md => harmony_changelog.md +2 -0
@@ 12,6 12,8 @@
* Added fade in and fade out to relaxation songs
* Added time sync endpoint to be used by Mudita Center
* Added bedside lamp settings
+* Added gapless audio transition in relaxation
+
### Changed / Improved
M module-audio/Audio/Audio.cpp => module-audio/Audio/Audio.cpp +5 -3
@@ 11,7 11,8 @@ namespace audio
{
Audio::Audio(AudioServiceMessage::Callback callback) : currentOperation(), serviceCallback(std::move(callback))
{
- auto ret = Operation::Create(Operation::Type::Idle, "", audio::PlaybackType::None, serviceCallback);
+ auto ret = Operation::Create(
+ Operation::Type::Idle, "", audio::PlaybackType::None, audio::PlaybackMode::Single, serviceCallback);
if (ret) {
currentOperation = std::move(ret);
}
@@ 50,11 51,12 @@ namespace audio
audio::RetCode Audio::Start(Operation::Type op,
audio::Token token,
const std::string &filePath,
- const audio::PlaybackType &playbackType)
+ const audio::PlaybackType &playbackType,
+ const audio::PlaybackMode &playbackMode)
{
try {
- auto ret = Operation::Create(op, filePath, playbackType, serviceCallback);
+ auto ret = Operation::Create(op, filePath, playbackType, playbackMode, serviceCallback);
switch (op) {
case Operation::Type::Playback:
currentState = State::Playback;
M module-audio/Audio/Audio.hpp => module-audio/Audio/Audio.hpp +2 -1
@@ 97,7 97,8 @@ namespace audio
virtual audio::RetCode Start(Operation::Type op,
audio::Token token = audio::Token::MakeBadToken(),
const std::string &filePath = "",
- const audio::PlaybackType &playbackType = audio::PlaybackType::None);
+ const audio::PlaybackType &playbackType = audio::PlaybackType::None,
+ const audio::PlaybackMode &playbackMode = audio::PlaybackMode::Single);
virtual audio::RetCode Start();
virtual audio::RetCode Stop();
M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +6 -0
@@ 52,6 52,12 @@ namespace audio
Disabled
};
+ enum class PlaybackMode
+ {
+ Single,
+ Loop
+ };
+
enum class PlaybackType
{
None,
M module-audio/Audio/Operation/Operation.cpp => module-audio/Audio/Operation/Operation.cpp +3 -2
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "Operation.hpp"
@@ 18,6 18,7 @@ namespace audio
std::unique_ptr<Operation> Operation::Create(Operation::Type t,
const std::string &filePath,
const audio::PlaybackType &playbackType,
+ const PlaybackMode &playbackMode,
AudioServiceMessage::Callback callback)
{
std::unique_ptr<Operation> inst;
@@ 27,7 28,7 @@ namespace audio
inst = std::make_unique<IdleOperation>(filePath);
break;
case Type::Playback:
- inst = std::make_unique<PlaybackOperation>(filePath, playbackType, callback);
+ inst = std::make_unique<PlaybackOperation>(filePath, playbackType, playbackMode, callback);
break;
case Type::Router:
inst = std::make_unique<RouterOperation>(filePath, callback);
M module-audio/Audio/Operation/Operation.hpp => module-audio/Audio/Operation/Operation.hpp +6 -4
@@ 19,7 19,8 @@ namespace audio
{
public:
explicit Operation(AudioServiceMessage::Callback callback,
- const PlaybackType &playbackType = PlaybackType::None)
+ const PlaybackType &playbackType = PlaybackType::None,
+ const PlaybackMode &playbackMode = PlaybackMode::Single)
: playbackType(playbackType), serviceCallback(std::move(callback)), observer(serviceCallback)
{
factory = AudioPlatform::GetDeviceFactory();
@@ 59,9 60,10 @@ namespace audio
virtual ~Operation() = default;
static std::unique_ptr<Operation> Create(Type t,
- const std::string &filePath = "",
- const audio::PlaybackType &operations = audio::PlaybackType::None,
- AudioServiceMessage::Callback callback = nullptr);
+ const std::string &filePath = "",
+ const audio::PlaybackType &operations = audio::PlaybackType::None,
+ const audio::PlaybackMode &playbackMode = audio::PlaybackMode::Single,
+ AudioServiceMessage::Callback callback = nullptr);
virtual audio::RetCode Start(audio::Token token) = 0;
virtual audio::RetCode Stop() = 0;
M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +10 -4
@@ 17,8 17,9 @@ namespace audio
PlaybackOperation::PlaybackOperation(const std::string &filePath,
const audio::PlaybackType &playbackType,
+ const audio::PlaybackMode &playbackMode,
Callback callback)
- : Operation(std::move(callback), playbackType), dec(nullptr)
+ : Operation(std::move(callback), playbackType), playbackMode(playbackMode), dec(nullptr)
{
// order defines priority
AddProfile(Profile::Type::PlaybackHeadphones, playbackType, false);
@@ 26,9 27,14 @@ namespace audio
AddProfile(Profile::Type::PlaybackLoudspeaker, playbackType, true);
endOfFileCallback = [this]() {
- state = State::Idle;
- const auto msg = AudioServiceMessage::EndOfFile(operationToken);
- serviceCallback(&msg);
+ if (this->playbackMode == audio::PlaybackMode::Single) {
+ state = State::Idle;
+ const auto msg = AudioServiceMessage::EndOfFile(operationToken);
+ serviceCallback(&msg);
+ }
+ else {
+ dec->setPosition(playbackStartPosition);
+ }
};
fileDeletedCallback = [this]() {
M module-audio/Audio/Operation/PlaybackOperation.hpp => module-audio/Audio/Operation/PlaybackOperation.hpp +3 -0
@@ 20,6 20,7 @@ namespace audio
public:
PlaybackOperation(const std::string &filePath,
const audio::PlaybackType &playbackType,
+ const audio::PlaybackMode &playbackMode,
AudioServiceMessage::Callback callback = nullptr);
virtual ~PlaybackOperation();
@@ 38,6 39,8 @@ namespace audio
private:
static constexpr auto playbackTimeConstraint = 10ms;
+ static constexpr auto playbackStartPosition = 0U;
+ audio::PlaybackMode playbackMode = audio::PlaybackMode::Single;
std::unique_ptr<Stream> dataStreamOut;
std::unique_ptr<Decoder> dec;
M module-audio/Audio/decoder/DecoderMP3.cpp => module-audio/Audio/decoder/DecoderMP3.cpp +22 -66
@@ 1,76 1,36 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-#define DR_MP3_IMPLEMENTATION
-#define DR_MP3_NO_STDIO
+#define MINIMP3_IMPLEMENTATION
+#define MINIMP3_NO_STDIO
#include "DecoderCommon.hpp"
#include "DecoderMP3.hpp"
#include <cstdio>
-namespace
-{
- signed skipID3V2TagIfPresent(std::FILE *fd)
- {
- constexpr auto ID3V2FrameOffset = 0;
- constexpr auto ID3V2FrameHeaderSize = 10;
- constexpr auto ID3V2FrameMagicString = "ID3";
- constexpr auto ID3V2FrameMagicStringLength = 3;
- std::uint8_t frameBuffer[ID3V2FrameHeaderSize];
-
- /* Seek to the beginning of the frame and read frame's header */
- if (std::fseek(fd, ID3V2FrameOffset, SEEK_SET) != 0) {
- return -EIO;
- }
- if (std::fread(frameBuffer, sizeof(*frameBuffer), ID3V2FrameHeaderSize, fd) != ID3V2FrameHeaderSize) {
- return -EIO;
- }
-
- /* Check magic */
- if (strncmp(reinterpret_cast<const char *>(frameBuffer), ID3V2FrameMagicString, ID3V2FrameMagicStringLength) !=
- 0) {
- return 0;
- }
-
- /* The tag size (minus the 10-byte header) is encoded into four bytes,
- * but the most significant bit needs to be masked in each byte.
- * Those frame indices are just copied from the ID3V2 docs. */
- const auto ID3V2TagTotalSize = (((frameBuffer[6] & 0x7F) << 21) | ((frameBuffer[7] & 0x7F) << 14) |
- ((frameBuffer[8] & 0x7F) << 7) | ((frameBuffer[9] & 0x7F) << 0)) +
- ID3V2FrameHeaderSize;
-
- /* Skip the tag */
- if (std::fseek(fd, ID3V2FrameOffset + ID3V2TagTotalSize, SEEK_SET) != 0) {
- return -EIO;
- }
- return ID3V2TagTotalSize;
- }
-} // namespace
-
namespace audio
{
- DecoderMP3::DecoderMP3(const std::string &filePath) : Decoder(filePath), mp3(std::make_unique<drmp3>())
+ DecoderMP3::DecoderMP3(const std::string &filePath)
+ : Decoder(filePath), dec(std::make_unique<mp3dec_ex_t>()), io(std::make_unique<mp3dec_io_t>())
{
if (fileSize == 0) {
return;
}
- const auto tagSkipStatus = skipID3V2TagIfPresent(fd);
- if (tagSkipStatus < 0) {
- LOG_ERROR("Failed to skip ID3V2 tag, error %d", tagSkipStatus);
- }
- else if (tagSkipStatus == 0) {
- LOG_INFO("No ID3V2 tag to skip");
- }
+ io->read = mp3Read;
+ io->read_data = this;
+ io->seek = mp3Seek;
+ io->seek_data = this;
+ dec->io = io.get();
- if (drmp3_init(mp3.get(), drmp3Read, drmp3Seek, this, nullptr) == DRMP3_FALSE) {
- LOG_ERROR("Unable to initialize MP3 decoder");
+ if (mp3dec_ex_open_cb(dec.get(), dec->io, MP3D_SEEK_TO_SAMPLE)) {
+ LOG_ERROR("Failed to open minimp3");
return;
}
/* NOTE: Always convert to S16LE as an internal format */
- channelCount = mp3->channels;
- sampleRate = mp3->sampleRate;
+ channelCount = dec->info.channels;
+ sampleRate = dec->info.hz;
bitsPerSample = 16;
isInitialized = true;
}
@@ 80,7 40,7 @@ namespace audio
if (!isInitialized) {
return;
}
- drmp3_uninit(mp3.get());
+ mp3dec_ex_close(dec.get());
isInitialized = false;
}
@@ 90,18 50,16 @@ namespace audio
LOG_ERROR("MP3 decoder not initialized");
return;
}
- const auto totalFramesCount = drmp3_get_pcm_frame_count(mp3.get());
- drmp3_seek_to_pcm_frame(mp3.get(), totalFramesCount * pos);
- position = static_cast<float>(totalFramesCount) * pos / static_cast<float>(sampleRate);
+ mp3dec_ex_seek(dec.get(), pos);
}
std::int32_t DecoderMP3::decode(std::uint32_t samplesToRead, std::int16_t *pcmData)
{
- const auto samplesRead = drmp3_read_pcm_frames_s16(
- mp3.get(), samplesToRead / channelCount, reinterpret_cast<drmp3_int16 *>(pcmData));
+ const auto samplesRead = mp3dec_ex_read(dec.get(), reinterpret_cast<mp3d_sample_t *>(pcmData), samplesToRead);
if (samplesRead > 0) {
/* Calculate frame duration in seconds */
- position += static_cast<float>(samplesRead) / static_cast<float>(sampleRate);
+ const auto samplesPerChannel = static_cast<float>(samplesRead) / static_cast<float>(channelCount);
+ position += samplesPerChannel / static_cast<float>(sampleRate);
}
else if (!fileExists(fd)) {
/* Unfortunately this second check of file existence is needed
@@ 110,10 68,10 @@ namespace audio
LOG_WARN("File '%s' was deleted during playback!", filePath.c_str());
return fileDeletedRetCode;
}
- return samplesRead * channelCount;
+ return samplesRead;
}
- std::size_t DecoderMP3::drmp3Read(void *pUserData, void *pBufferOut, std::size_t bytesToRead)
+ std::size_t DecoderMP3::mp3Read(void *pBufferOut, std::size_t bytesToRead, void *pUserData)
{
const auto decoderContext = reinterpret_cast<DecoderMP3 *>(pUserData);
@@ 126,11 84,9 @@ namespace audio
return std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd);
}
- drmp3_bool32 DecoderMP3::drmp3Seek(void *pUserData, int offset, drmp3_seek_origin origin)
+ int DecoderMP3::mp3Seek(std::uint64_t offset, void *pUserData)
{
const auto decoderContext = reinterpret_cast<DecoderMP3 *>(pUserData);
- const auto seekError =
- std::fseek(decoderContext->fd, offset, origin == drmp3_seek_origin_start ? SEEK_SET : SEEK_CUR);
- return (seekError == 0) ? DRMP3_TRUE : DRMP3_FALSE;
+ return std::fseek(decoderContext->fd, offset, SEEK_SET);
}
} // namespace audio
M module-audio/Audio/decoder/DecoderMP3.hpp => module-audio/Audio/decoder/DecoderMP3.hpp +8 -10
@@ 4,7 4,7 @@
#pragma once
#include "Decoder.hpp"
-#include <src/dr_mp3.h>
+#include <minimp3_ex.h>
namespace audio
{
@@ 19,11 19,12 @@ namespace audio
void setPosition(float pos) override;
private:
- std::unique_ptr<drmp3> mp3;
+ std::unique_ptr<mp3dec_ex_t> dec;
+ std::unique_ptr<mp3dec_io_t> io;
// Callback for when data needs to be read from the client.
//
- // pUserData [in] The user data that was passed to drmp3_init() and family.
+ // pUserData [in] The user data that was assigned to mp3dec_io_t and family.
// pBufferOut [out] The output buffer.
// bytesToRead [in] The number of bytes to read.
//
@@ 31,19 32,16 @@ namespace audio
//
// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback
// until either the entire bytesToRead is filled or you have reached the end of the stream.
- static std::size_t drmp3Read(void *pUserData, void *pBufferOut, std::size_t bytesToRead);
+ static std::size_t mp3Read(void *pBufferOut, std::size_t bytesToRead, void *pUserData);
// Callback for when data needs to be seeked.
//
- // pUserData [in] The user data that was passed to drmp3_init() and family.
+ // pUserData [in] The user data that was assigned to mp3dec_io_t and family.
// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
- // origin [in] The origin of the seek - the current position or the start of the stream.
//
// Returns whether the seek was successful.
//
- // The offset will never be negative. Whether it is relative to the beginning or current position is
- // determined by the "origin" parameter which will be either drmp3_seek_origin_start or
- // drmp3_seek_origin_current.
- static drmp3_bool32 drmp3Seek(void *pUserData, int offset, drmp3_seek_origin origin);
+ // The offset will never be negative. It relates to the beginning position.
+ static int mp3Seek(std::uint64_t offset, void *pUserData);
};
} // namespace audio
M module-audio/Audio/decoder/DecoderWorker.cpp => module-audio/Audio/decoder/DecoderWorker.cpp +3 -2
@@ 93,13 93,14 @@ void audio::DecoderWorker::pushAudioData()
while (!audioStreamOut->isFull() && playbackEnabled) {
auto buffer = decoderBuffer.get();
- samplesRead = decoder->decode(bufferSize / readScale, buffer);
+ const auto totalBufferSize = bufferSize / readScale;
+ samplesRead = decoder->decode(totalBufferSize, buffer);
if (samplesRead == Decoder::fileDeletedRetCode) {
fileDeletedCallback();
break;
}
- if (samplesRead == 0) {
+ if (samplesRead < totalBufferSize) {
endOfFileCallback();
break;
}
M module-audio/Audio/decoder/DecoderWorker.hpp => module-audio/Audio/decoder/DecoderWorker.hpp +2 -2
@@ 37,7 37,7 @@ namespace audio
ChannelMode mode);
~DecoderWorker() override;
- virtual auto init(std::list<sys::WorkerQueueInfo> queues = std::list<sys::WorkerQueueInfo>()) -> bool override;
+ auto init(std::list<sys::WorkerQueueInfo> queues = std::list<sys::WorkerQueueInfo>()) -> bool override;
auto enablePlayback() -> bool;
auto disablePlayback() -> bool;
@@ 45,7 45,7 @@ namespace audio
private:
static constexpr std::size_t stackDepth = 12 * 1024;
- virtual auto handleMessage(std::uint32_t queueID) -> bool override;
+ auto handleMessage(std::uint32_t queueID) -> bool override;
void pushAudioData();
bool stateChangeWait();
M products/BellHybrid/CMakeLists.txt => products/BellHybrid/CMakeLists.txt +2 -2
@@ 144,14 144,14 @@ download_asset_release_json(json-common-target
${CMAKE_CURRENT_SOURCE_DIR}/assets/assets_common.json
${SYSROOT_PATH}/system_a/
MuditaOSPublicAssets
- 0.0.26
+ 0.0.27
${MUDITA_CACHE_DIR}
)
download_asset_release_json(json-community-target
${CMAKE_CURRENT_SOURCE_DIR}/assets/assets_community.json
${SYSROOT_PATH}/system_a/
MuditaOSPublicAssets
- 0.0.26
+ 0.0.27
${MUDITA_CACHE_DIR}
)
download_asset_json(json-rt1051-target
M products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp => products/BellHybrid/alarms/src/actions/PlayAudioActions.cpp +5 -4
@@ 12,10 12,11 @@ namespace alarms
{
PlayAudioAction::PlayAudioAction(sys::Service &service,
std::string_view toneSetting,
- audio::PlaybackType playbackType,
+ const audio::PlaybackType &playbackType,
+ const audio::PlaybackMode &playbackMode,
std::optional<std::string_view> durationSetting)
- : service{service}, toneSetting{toneSetting}, durationSetting{durationSetting},
- playbackType{playbackType}, settings{service::ServiceProxy{service.weak_from_this()}}
+ : service{service}, toneSetting{toneSetting}, durationSetting{durationSetting}, playbackType{playbackType},
+ playbackMode{playbackMode}, settings{service::ServiceProxy{service.weak_from_this()}}
{}
auto PlayAudioAction::play(const std::filesystem::path &path, std::optional<std::chrono::minutes> duration) -> bool
@@ 30,7 31,7 @@ namespace alarms
: audio::Fade::Disable;
auto msg = std::make_shared<service::AudioStartPlaybackRequest>(
- path, playbackType, audio::FadeParams{fadeInEnabled, audio::alarmMaxFadeDuration});
+ path, playbackType, playbackMode, audio::FadeParams{fadeInEnabled, audio::alarmMaxFadeDuration});
return service.bus.sendUnicast(std::move(msg), service::audioServiceName);
}
M products/BellHybrid/alarms/src/actions/PlayAudioActions.hpp => products/BellHybrid/alarms/src/actions/PlayAudioActions.hpp +3 -1
@@ 19,7 19,8 @@ namespace alarms
public:
PlayAudioAction(sys::Service &service,
std::string_view toneSetting,
- audio::PlaybackType = audio::PlaybackType::Alarm,
+ const audio::PlaybackType &playbackType = audio::PlaybackType::Alarm,
+ const audio::PlaybackMode &playbackMode = audio::PlaybackMode::Single,
std::optional<std::string_view> durationSetting = {});
auto turnOff() -> bool override;
@@ 37,6 38,7 @@ namespace alarms
const std::string toneSetting;
const std::optional<std::string> durationSetting;
const audio::PlaybackType playbackType;
+ const audio::PlaybackMode playbackMode;
settings::Settings settings;
};
M products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.cpp => products/BellHybrid/apps/application-bell-focus-timer/presenter/FocusSettingsPresenter.cpp +4 -1
@@ 146,7 146,10 @@ namespace app::focus
auto playSound = [this, notificationVolume]() {
this->audioModel.setVolume(notificationVolume->value(), AbstractAudioModel::PlaybackType::FocusTimer);
- this->audioModel.play(getFocusTimerAudioPath(), AbstractAudioModel::PlaybackType::FocusTimer, {});
+ this->audioModel.play(getFocusTimerAudioPath(),
+ AbstractAudioModel::PlaybackType::FocusTimer,
+ AbstractAudioModel::PlaybackMode::Single,
+ {});
};
notificationVolume->onEnter = playSound;
M products/BellHybrid/apps/application-bell-meditation-timer/presenter/SettingsPresenter.cpp => products/BellHybrid/apps/application-bell-meditation-timer/presenter/SettingsPresenter.cpp +4 -1
@@ 86,7 86,10 @@ namespace app::meditation
auto playSound = [this, chimeVolume]() {
this->audioModel.setVolume(chimeVolume->value(), AbstractAudioModel::PlaybackType::Meditation);
- this->audioModel.play(getMeditationGongSoundPath(), AbstractAudioModel::PlaybackType::Meditation, {});
+ this->audioModel.play(getMeditationGongSoundPath(),
+ AbstractAudioModel::PlaybackType::Meditation,
+ AbstractAudioModel::PlaybackMode::Single,
+ {});
};
chimeVolume->onEnter = playSound;
M products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp => products/BellHybrid/apps/application-bell-powernap/presenter/PowerNapProgressPresenter.cpp +5 -1
@@ 92,7 92,11 @@ namespace app::powernap
? audio::Fade::In
: audio::Fade::Disable;
- audioModel.play(filePath, AbstractAudioModel::PlaybackType::Alarm, {}, audio::FadeParams{fadeInActive});
+ audioModel.play(filePath,
+ AbstractAudioModel::PlaybackType::Alarm,
+ AbstractAudioModel::PlaybackMode::Single,
+ {},
+ audio::FadeParams{fadeInActive});
napAlarmTimer.start();
napFinished = true;
}
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp +3 -3
@@ 30,15 30,15 @@ namespace app::relaxation
{
Expects(timer != nullptr);
- AbstractRelaxationPlayer::PlaybackMode mode;
+ AbstractAudioModel::PlaybackMode mode;
const auto value = settings->getValue(timerValueDBRecordName, settings::SettingsScope::AppLocal);
if (utils::is_number(value) && utils::getNumericValue<int>(value) != 0) {
timer->reset(std::chrono::minutes{utils::getNumericValue<int>(value)});
- mode = AbstractRelaxationPlayer::PlaybackMode::Looped;
+ mode = AbstractAudioModel::PlaybackMode::Loop;
}
else {
const auto songLength = std::chrono::seconds{song.audioProperties.songLength};
- mode = AbstractRelaxationPlayer::PlaybackMode::SingleShot;
+ mode = AbstractAudioModel::PlaybackMode::Single;
if (songLength > std::chrono::seconds::zero()) {
timer->reset(songLength);
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.cpp +3 -3
@@ 40,7 40,7 @@ namespace app::relaxation
{
Expects(timer != nullptr);
- AbstractRelaxationPlayer::PlaybackMode mode;
+ AbstractAudioModel::PlaybackMode mode;
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};
@@ 50,10 50,10 @@ namespace app::relaxation
!isSongLengthEqualToPeriod(songLength, std::chrono::minutes{presetDuration})) {
playbackDuration = std::chrono::minutes{presetDuration};
timer->reset(playbackDuration);
- mode = AbstractRelaxationPlayer::PlaybackMode::Looped;
+ mode = AbstractAudioModel::PlaybackMode::Loop;
}
else {
- mode = AbstractRelaxationPlayer::PlaybackMode::SingleShot;
+ mode = AbstractAudioModel::PlaybackMode::Single;
if (songLength > std::chrono::seconds::zero()) {
timer->reset(songLength);
}
M products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.cpp => products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.cpp +5 -11
@@ 6,7 6,7 @@
namespace app::relaxation
{
- AbstractRelaxationPlayer::PlaybackMode RelaxationPlayer::getCurrentMode() const noexcept
+ AbstractAudioModel::PlaybackMode RelaxationPlayer::getCurrentMode() const noexcept
{
return playbackMode;
}
@@ 16,7 16,7 @@ namespace app::relaxation
{}
void RelaxationPlayer::start(const std::string &filePath,
- AbstractRelaxationPlayer::PlaybackMode mode,
+ const AbstractAudioModel::PlaybackMode &mode,
AbstractAudioModel::OnStateChangeCallback &&stateChangeCallback,
AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback,
std::optional<std::chrono::seconds> playbackDuration)
@@ 29,14 29,7 @@ namespace app::relaxation
auto onPlayerFinished = [callback = finishedCallback, this](Status status) {
if (status == Status::Error) {
- callback(status); // First playback finished with error
- }
- else if (playbackMode == PlaybackMode::Looped) {
- audioModel.play(recentFilePath, Type::Multimedia, [&](audio::RetCode retCode) { // Replay in loop mode
- if (retCode != audio::RetCode::Success) {
- callback(Status::Error); // Replay fail in looped mode
- }
- });
+ callback(status); // Playback finished with error
}
else {
callback(Status::Normal); // Normal finish in single shot mode
@@ 45,7 38,8 @@ namespace app::relaxation
auto fadeParams = audio::FadeParams{.mode = getFadeMode(), .playbackDuration = playbackDuration};
audioModel.setPlaybackFinishedCb(std::move(onPlayerFinished));
- audioModel.play(filePath, Type::Multimedia, std::move(stateChangeCallback), std::move(fadeParams));
+ audioModel.play(
+ filePath, Type::Multimedia, playbackMode, std::move(stateChangeCallback), std::move(fadeParams));
}
audio::Fade RelaxationPlayer::getFadeMode() const
M products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.hpp => products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.hpp +5 -11
@@ 21,22 21,16 @@ namespace app::relaxation
class AbstractRelaxationPlayer
{
public:
- enum class PlaybackMode
- {
- Looped,
- SingleShot
- };
-
virtual ~AbstractRelaxationPlayer() = default;
virtual void start(const std::string &filePath,
- PlaybackMode mode,
+ const AbstractAudioModel::PlaybackMode &mode,
AbstractAudioModel::OnStateChangeCallback &&callback,
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 AbstractAudioModel::PlaybackMode getCurrentMode() const noexcept = 0;
virtual audio::Fade getFadeMode() const = 0;
virtual bool isPaused() = 0;
};
@@ 48,21 42,21 @@ namespace app::relaxation
private:
void start(const std::string &filePath,
- PlaybackMode mode,
+ const AbstractAudioModel::PlaybackMode &mode,
AbstractAudioModel::OnStateChangeCallback &&callback,
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;
- PlaybackMode getCurrentMode() const noexcept override;
+ AbstractAudioModel::PlaybackMode getCurrentMode() const noexcept override;
audio::Fade getFadeMode() const override;
bool isPaused() override;
AbstractRelaxationFadeModel &fadeModel;
AbstractAudioModel &audioModel;
std::string recentFilePath;
- PlaybackMode playbackMode = PlaybackMode::SingleShot;
+ AbstractAudioModel::PlaybackMode playbackMode{AbstractAudioModel::PlaybackMode::Single};
bool paused{false};
};
} // namespace app::relaxation
M products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/BedtimeSettingsPresenter.cpp +4 -2
@@ 29,8 29,10 @@ namespace app::bell_settings
currentSoundPath = val;
this->audioModel.setVolume(this->provider->getCurrentVolume(), AbstractAudioModel::PlaybackType::Bedtime);
this->audioModel.setPlaybackFinishedCb(std::move(onFinishedCallback));
- this->audioModel.play(
- currentSoundPath, AbstractAudioModel::PlaybackType::Bedtime, std::move(onStartCallback));
+ this->audioModel.play(currentSoundPath,
+ AbstractAudioModel::PlaybackType::Bedtime,
+ AbstractAudioModel::PlaybackMode::Single,
+ std::move(onStartCallback));
};
this->provider->onExit = [this]() { getView()->exit(); };
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/AlarmSettingsPresenter.cpp +4 -2
@@ 32,8 32,10 @@ namespace app::bell_settings
currentSoundPath = val;
this->audioModel.setVolume(this->provider->getCurrentVolume(), AbstractAudioModel::PlaybackType::Alarm);
this->audioModel.setPlaybackFinishedCb(std::move(onFinishedCallback));
- this->audioModel.play(
- currentSoundPath, AbstractAudioModel::PlaybackType::Alarm, std::move(onStartCallback));
+ this->audioModel.play(currentSoundPath,
+ AbstractAudioModel::PlaybackType::Alarm,
+ AbstractAudioModel::PlaybackMode::Single,
+ std::move(onStartCallback));
};
this->provider->onExit = [this]() { getView()->exit(); };
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/PrewakeUpPresenter.cpp +4 -2
@@ 31,8 31,10 @@ namespace app::bell_settings
currentSoundPath = val;
this->audioModel.setVolume(this->provider->getCurrentVolume(), AbstractAudioModel::PlaybackType::PreWakeup);
this->audioModel.setPlaybackFinishedCb(std::move(onFinishedCallback));
- this->audioModel.play(
- currentSoundPath, AbstractAudioModel::PlaybackType::PreWakeup, std::move(onStartCallback));
+ this->audioModel.play(currentSoundPath,
+ AbstractAudioModel::PlaybackType::PreWakeup,
+ AbstractAudioModel::PlaybackMode::Single,
+ std::move(onStartCallback));
};
this->provider->onExit = [this]() { getView()->exit(); };
M products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/alarm_settings/SnoozePresenter.cpp +4 -2
@@ 30,8 30,10 @@ namespace app::bell_settings
currentSoundPath = val;
this->audioModel.setVolume(this->provider->getCurrentVolume(), AbstractAudioModel::PlaybackType::Snooze);
this->audioModel.setPlaybackFinishedCb(std::move(onFinishedCallback));
- this->audioModel.play(
- currentSoundPath, AbstractAudioModel::PlaybackType::Snooze, std::move(onStartCallback));
+ this->audioModel.play(currentSoundPath,
+ AbstractAudioModel::PlaybackType::Snooze,
+ AbstractAudioModel::PlaybackMode::Single,
+ std::move(onStartCallback));
};
this->provider->onExit = [this]() { getView()->exit(); };
M products/BellHybrid/apps/common/include/common/models/AbstractAudioModel.hpp => products/BellHybrid/apps/common/include/common/models/AbstractAudioModel.hpp +13 -5
@@ 4,7 4,6 @@
#pragma once
#include <module-audio/Audio/AudioCommon.hpp>
-
#include <string>
#include <functional>
#include <optional>
@@ 14,6 13,12 @@ namespace app
class AbstractAudioModel
{
public:
+ enum class PlaybackMode
+ {
+ Single,
+ Loop
+ };
+
enum class PlaybackType
{
Multimedia,
@@ 39,15 44,17 @@ 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;
+ 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,
+ const PlaybackType &type,
+ const PlaybackMode &mode,
OnStateChangeCallback &&callback,
std::optional<audio::FadeParams> fadeParams = std::nullopt) = 0;
virtual void stopAny(OnStateChangeCallback &&callback) = 0;
@@ 57,4 64,5 @@ namespace app
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 +2 -1
@@ 21,7 21,8 @@ namespace app
std::optional<Volume> getVolume(PlaybackType playbackType) override;
void getVolume(PlaybackType playbackType, OnGetValueCallback &&callback) override;
void play(const std::string &filePath,
- PlaybackType type,
+ const PlaybackType &type,
+ const PlaybackMode &mode,
OnStateChangeCallback &&callback,
std::optional<audio::FadeParams> fadeParams = std::nullopt) override;
void stopAny(OnStateChangeCallback &&callback) override;
M products/BellHybrid/apps/common/src/AudioModel.cpp => products/BellHybrid/apps/common/src/AudioModel.cpp +16 -3
@@ 31,6 31,18 @@ namespace
}
}
+ constexpr audio::PlaybackMode convertPlaybackMode(app::AbstractAudioModel::PlaybackMode mode)
+ {
+ using Mode = app::AbstractAudioModel::PlaybackMode;
+ switch (mode) {
+ case Mode::Loop:
+ return audio::PlaybackMode::Loop;
+ case Mode::Single:
+ default:
+ return audio::PlaybackMode::Single;
+ }
+ }
+
void reportError(const char *prefix, audio::RetCode code)
{
if (code != audio::RetCode::Success) {
@@ 85,13 97,14 @@ namespace app
}
void AudioModel::play(const std::string &filePath,
- PlaybackType type,
+ const PlaybackType &type,
+ const PlaybackMode &mode,
OnStateChangeCallback &&callback,
std::optional<audio::FadeParams> fadeParams)
{
playbackFinishedFlag = false;
- auto msg =
- std::make_unique<service::AudioStartPlaybackRequest>(filePath, convertPlaybackType(type), fadeParams);
+ auto msg = std::make_unique<service::AudioStartPlaybackRequest>(
+ filePath, convertPlaybackType(type), convertPlaybackMode(mode), fadeParams);
auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::audioServiceName);
auto cb = [_callback = callback, this](auto response) {
M products/BellHybrid/services/audio/ServiceAudio.cpp => products/BellHybrid/services/audio/ServiceAudio.cpp +8 -3
@@ 96,7 96,11 @@ namespace service
connect(typeid(AudioStartPlaybackRequest), [this](sys::Message *msg) -> sys::MessagePointer {
auto *msgl = static_cast<AudioStartPlaybackRequest *>(msg);
- return handleStart(audio::Operation::Type::Playback, msgl->fadeParams, msgl->fileName, msgl->playbackType);
+ return handleStart(audio::Operation::Type::Playback,
+ msgl->fadeParams,
+ msgl->fileName,
+ msgl->playbackType,
+ msgl->playbackMode);
});
connect(typeid(internal::AudioEOFNotificationMessage), [this](sys::Message *msg) -> sys::MessagePointer {
@@ 170,7 174,8 @@ namespace service
auto Audio::handleStart(audio::Operation::Type opType,
std::optional<audio::FadeParams> fadeParams,
const std::string &fileName,
- const audio::PlaybackType &playbackType) -> std::unique_ptr<AudioResponseMessage>
+ const audio::PlaybackType &playbackType,
+ const audio::PlaybackMode &playbackMode) -> std::unique_ptr<AudioResponseMessage>
{
auto retCode = audio::RetCode::Failed;
auto retToken = audio::Token::MakeBadToken();
@@ 183,7 188,7 @@ namespace service
retToken = audioMux.ResetInput(input);
try {
- retCode = (*input)->audio->Start(opType, retToken, fileName, playbackType);
+ retCode = (*input)->audio->Start(opType, retToken, fileName, playbackType, playbackMode);
}
catch (const audio::AudioInitException &audioException) {
retCode = audio::RetCode::FailedToAllocateMemory;
M products/BellHybrid/services/audio/include/audio/AudioMessage.hpp => products/BellHybrid/services/audio/include/audio/AudioMessage.hpp +4 -1
@@ 120,12 120,15 @@ namespace service
public:
AudioStartPlaybackRequest(const std::string &fileName,
const audio::PlaybackType &playbackType,
+ const audio::PlaybackMode &playbackMode = audio::PlaybackMode::Single,
std::optional<audio::FadeParams> fadeParams = std::nullopt)
- : AudioMessage(), fileName(fileName), playbackType(playbackType), fadeParams(fadeParams)
+ : AudioMessage(), fileName(fileName), playbackType(playbackType), playbackMode(playbackMode),
+ fadeParams(fadeParams)
{}
const std::string fileName;
const audio::PlaybackType playbackType;
+ const audio::PlaybackMode playbackMode;
const std::optional<audio::FadeParams> fadeParams;
};
M products/BellHybrid/services/audio/include/audio/ServiceAudio.hpp => products/BellHybrid/services/audio/include/audio/ServiceAudio.hpp +2 -1
@@ 44,7 44,8 @@ namespace service
auto handleStart(audio::Operation::Type opType,
std::optional<audio::FadeParams> fadeParams,
const std::string &fileName = {},
- const audio::PlaybackType &playbackType = audio::PlaybackType::None)
+ const audio::PlaybackType &playbackType = audio::PlaybackType::None,
+ const audio::PlaybackMode &playbackMode = audio::PlaybackMode::Single)
-> std::unique_ptr<AudioResponseMessage>;
auto handleStop(const std::vector<audio::PlaybackType> &stopTypes, const audio::Token &token)
-> std::unique_ptr<AudioResponseMessage>;
M third-party/minimp3/minimp3 => third-party/minimp3/minimp3 +1 -1
@@ 1,1 1,1 @@
-Subproject commit 446d3a32d281eb1dcae7726ba0ff594695182908
+Subproject commit e7202154493fb9dd8ca8c93f432003c4f191c5ae