M harmony_changelog.md => harmony_changelog.md +1 -0
@@ 6,6 6,7 @@
* Fixed source clock frequency computation for PWM module
* Fixed initial watchdog configuration
* Fixed alarm rings when deactivated during snooze
+* Fixed popup about file deletion showing in Relaxation app even if no file was deleted
### Added
* Added setting onboarding year to build date year
M module-apps/application-music-player/ApplicationMusicPlayer.cpp => module-apps/application-music-player/ApplicationMusicPlayer.cpp +6 -1
@@ 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 <application-music-player/ApplicationMusicPlayer.hpp>
@@ 82,6 82,11 @@ namespace app
music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
return audioNotificationHandler.handleAudioEofNotification(notification);
});
+ connect(typeid(AudioFileDeletedNotification), [&](sys::Message *msg) -> sys::MessagePointer {
+ const auto notification = static_cast<AudioStopNotification *>(msg);
+ music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
+ return audioNotificationHandler.handleAudioEofNotification(notification);
+ });
connect(typeid(AudioPausedNotification), [&](sys::Message *msg) -> sys::MessagePointer {
const auto notification = static_cast<AudioPausedNotification *>(msg);
music_player::AudioNotificationsHandler audioNotificationHandler{priv->songsPresenter};
M module-audio/Audio/Audio.cpp => module-audio/Audio/Audio.cpp +1 -3
@@ 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 "Audio.hpp"
@@ 9,10 9,8 @@
namespace audio
{
-
Audio::Audio(AudioServiceMessage::Callback callback) : currentOperation(), serviceCallback(callback)
{
-
auto ret = Operation::Create(Operation::Type::Idle, "", audio::PlaybackType::None, callback);
if (ret) {
currentOperation = std::move(ret);
M module-audio/Audio/Audio.hpp => module-audio/Audio/Audio.hpp +1 -3
@@ 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
#pragma once
@@ 14,7 14,6 @@
namespace audio
{
-
class Audio
{
enum class Muted : bool
@@ 131,5 130,4 @@ namespace audio
AudioServiceMessage::Callback serviceCallback;
};
-
} // namespace audio
M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +15 -1
@@ 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
#pragma once
@@ 308,6 308,20 @@ namespace AudioServiceMessage
audio::Token token = audio::Token::MakeBadToken();
};
+ class FileDeleted : public sys::DataMessage
+ {
+ public:
+ explicit FileDeleted(audio::Token &token) : token(token)
+ {}
+ const audio::Token &GetToken() const
+ {
+ return token;
+ }
+
+ private:
+ audio::Token token = audio::Token::MakeBadToken();
+ };
+
class FileSystemNoSpace : public sys::DataMessage
{
public:
M module-audio/Audio/AudioDeviceFactory.hpp => module-audio/Audio/AudioDeviceFactory.hpp +2 -3
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, 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
#pragma once
@@ 11,7 11,6 @@
namespace audio
{
-
class AudioDeviceFactory
{
public:
@@ 22,6 21,7 @@ namespace audio
};
explicit AudioDeviceFactory(Observer *observer = nullptr);
+ virtual ~AudioDeviceFactory() = default;
void setObserver(Observer *observer) noexcept;
std::shared_ptr<AudioDevice> CreateDevice(const Profile &profile);
@@ 33,5 33,4 @@ namespace audio
private:
Observer *_observer = nullptr;
};
-
}; // namespace audio
M module-audio/Audio/AudioMux.cpp => module-audio/Audio/AudioMux.cpp +2 -2
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, 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 "AudioMux.hpp"
@@ 25,7 25,7 @@ namespace audio
{
audioInputsCount = audioInputsCount > 0 ? audioInputsCount : 1;
audioInputsInternal.reserve(audioInputsCount);
- for (size_t i = 0; i < audioInputsCount; i++) {
+ for (std::size_t i = 0; i < audioInputsCount; i++) {
audioInputsInternal.emplace_back(Input(std::make_unique<Audio>(callback), refToken.IncrementToken()));
}
}
M module-audio/Audio/AudioMux.hpp => module-audio/Audio/AudioMux.hpp +1 -2
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, 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
#pragma once
@@ 111,5 111,4 @@ namespace audio
std::vector<Input> audioInputsInternal;
std::vector<Input> &audioInputs;
};
-
} // namespace audio
M module-audio/Audio/Operation/Operation.hpp => module-audio/Audio/Operation/Operation.hpp +1 -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
#pragma once
@@ 162,5 162,4 @@ namespace audio
std::shared_ptr<AudioDevice> CreateDevice(const Profile &profile);
std::shared_ptr<AudioDevice> createCellularAudioDevice();
};
-
} // namespace audio
M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +10 -7
@@ 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 "PlaybackOperation.hpp"
@@ 13,7 13,6 @@
namespace audio
{
-
using namespace AudioServiceMessage;
PlaybackOperation::PlaybackOperation(const std::string &filePath,
@@ 28,9 27,14 @@ namespace audio
endOfFileCallback = [this]() {
state = State::Idle;
- const auto req = AudioServiceMessage::EndOfFile(operationToken);
- serviceCallback(&req);
- return std::string();
+ const auto msg = AudioServiceMessage::EndOfFile(operationToken);
+ serviceCallback(&msg);
+ };
+
+ fileDeletedCallback = [this]() {
+ state = State::Idle;
+ const auto msg = AudioServiceMessage::FileDeleted(operationToken);
+ serviceCallback(&msg);
};
dec = Decoder::Create(filePath);
@@ 66,7 70,7 @@ namespace audio
outputConnection = std::make_unique<StreamConnection>(dec.get(), audioDevice.get(), dataStreamOut.get());
// decoder worker soft start - must be called after connection setup
- dec->startDecodingWorker(endOfFileCallback);
+ dec->startDecodingWorker(endOfFileCallback, fileDeletedCallback);
// start output device and enable audio connection
auto ret = audioDevice->Start();
@@ 225,5 229,4 @@ namespace audio
{
Stop();
}
-
} // namespace audio
M module-audio/Audio/Operation/PlaybackOperation.hpp => module-audio/Audio/Operation/PlaybackOperation.hpp +3 -8
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2022, 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
#pragma once
@@ 10,13 10,8 @@
#include "Audio/decoder/Decoder.hpp"
#include <chrono>
-using namespace std::chrono_literals;
-namespace audio::playbackDefaults
-{
- constexpr audio::Volume defaultLoudspeakerVolume = 10;
- constexpr audio::Volume defaultHeadphonesVolume = 2;
-} // namespace audio::playbackDefaults
+using namespace std::chrono_literals;
namespace audio
{
@@ 49,6 44,6 @@ namespace audio
std::unique_ptr<StreamConnection> outputConnection;
DecoderWorker::EndOfFileCallback endOfFileCallback;
+ DecoderWorker::FileDeletedCallback fileDeletedCallback;
};
-
} // namespace audio
M module-audio/Audio/StreamQueuedEventsListener.cpp => module-audio/Audio/StreamQueuedEventsListener.cpp +3 -3
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, 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 "StreamQueuedEventsListener.hpp"
@@ 30,7 30,7 @@ void StreamQueuedEventsListener::onEvent(AbstractStream *stream, Stream::Event e
}
}
-StreamQueuedEventsListener::queuedEvent StreamQueuedEventsListener::waitForEvent()
+StreamQueuedEventsListener::QueuedEvent StreamQueuedEventsListener::waitForEvent()
{
EventStorage queueStorage;
if (queue->Dequeue(&queueStorage)) {
@@ 44,7 44,7 @@ std::size_t StreamQueuedEventsListener::getEventsCount() const
return queue->NumItems();
}
-StreamQueuedEventsListener::queuedEvent StreamQueuedEventsListener::getEvent()
+StreamQueuedEventsListener::QueuedEvent StreamQueuedEventsListener::getEvent()
{
EventStorage queueStorage;
M module-audio/Audio/StreamQueuedEventsListener.hpp => module-audio/Audio/StreamQueuedEventsListener.hpp +5 -6
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, 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
#pragma once
@@ 23,21 23,20 @@ namespace audio
};
public:
- using queueInfo = std::pair<QueueHandle_t, std::string>;
- using queuedEvent = std::pair<AbstractStream *, AbstractStream::Event>;
+ using QueuedEvent = std::pair<AbstractStream *, AbstractStream::Event>;
static constexpr auto listenerElementSize = sizeof(EventStorage);
explicit StreamQueuedEventsListener(std::shared_ptr<cpp_freertos::Queue> eventsQueue);
+ virtual ~StreamQueuedEventsListener() = default;
void onEvent(AbstractStream *stream, Stream::Event event) override;
- queuedEvent waitForEvent();
- queuedEvent getEvent();
+ QueuedEvent waitForEvent();
+ QueuedEvent getEvent();
std::size_t getEventsCount() const;
private:
std::shared_ptr<cpp_freertos::Queue> queue;
};
-
}; // namespace audio
M module-audio/Audio/decoder/Decoder.cpp => module-audio/Audio/decoder/Decoder.cpp +17 -18
@@ 1,12 1,12 @@
-// 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 <cstdio>
#include <Utils.hpp>
#include "Decoder.hpp"
-#include "decoderMP3.hpp"
-#include "decoderFLAC.hpp"
-#include "decoderWAV.hpp"
+#include "DecoderMP3.hpp"
+#include "DecoderFLAC.hpp"
+#include "DecoderWAV.hpp"
#include "tag.h"
#include <tags_fetcher/TagsFetcher.hpp>
@@ 15,12 15,12 @@ namespace audio
{
Decoder::Decoder(const std::string &path) : filePath(path)
{
- fd = std::fopen(path.c_str(), "r");
+ fd = std::fopen(path.c_str(), "rb");
if (fd == nullptr) {
return;
}
- constexpr size_t streamBufferSize = 16 * 1024;
+ constexpr std::size_t streamBufferSize = 16 * 1024;
streamBuffer = std::make_unique<char[]>(streamBufferSize);
setvbuf(fd, streamBuffer.get(), _IOFBF, streamBufferSize);
@@ 55,13 55,13 @@ namespace audio
std::unique_ptr<Decoder> dec;
if (extensionLowercase == ".wav") {
- dec = std::make_unique<decoderWAV>(filePath);
+ dec = std::make_unique<DecoderWAV>(filePath);
}
else if (extensionLowercase == ".mp3") {
- dec = std::make_unique<decoderMP3>(filePath);
+ dec = std::make_unique<DecoderMP3>(filePath);
}
else if (extensionLowercase == ".flac") {
- dec = std::make_unique<decoderFLAC>(filePath);
+ dec = std::make_unique<DecoderFLAC>(filePath);
}
else {
return nullptr;
@@ 74,16 74,16 @@ namespace audio
return dec;
}
- void Decoder::startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback)
+ void Decoder::startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
+ const DecoderWorker::FileDeletedCallback &fileDeletedCallback)
{
assert(_stream != nullptr);
- if (!audioWorker) {
+ if (audioWorker == nullptr) {
+ const auto channelMode = (tags->num_channel == 1) ? DecoderWorker::ChannelMode::ForceStereo
+ : DecoderWorker::ChannelMode::NoConversion;
+
audioWorker =
- std::make_unique<DecoderWorker>(_stream,
- this,
- endOfFileCallback,
- tags->num_channel == 1 ? DecoderWorker::ChannelMode::ForceStereo
- : DecoderWorker::ChannelMode::NoConversion);
+ std::make_unique<DecoderWorker>(_stream, this, endOfFileCallback, fileDeletedCallback, channelMode);
audioWorker->init();
audioWorker->run();
}
@@ 120,7 120,7 @@ namespace audio
auto bitWidth = getBitWidth();
// this is a decoder mono to stereo hack, will be removed when proper
// transcoding implementation is added
- auto channels = tags->num_channel == 1 ? 2U : tags->num_channel;
+ auto channels = (tags->num_channel == 1) ? 2U : tags->num_channel;
return AudioFormat{tags->sample_rate, bitWidth, channels};
}
@@ 134,5 134,4 @@ namespace audio
{
return Endpoint::Traits{};
}
-
} // namespace audio
M module-audio/Audio/decoder/Decoder.hpp => module-audio/Audio/decoder/Decoder.hpp +13 -13
@@ 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
#pragma once
@@ 16,19 16,19 @@ namespace audio
{
namespace channel
{
- constexpr inline auto monoSound = 1;
- constexpr inline auto stereoSound = 2;
+ inline constexpr auto monoSound = 1;
+ inline constexpr auto stereoSound = 2;
} // namespace channel
class Decoder : public Source
{
-
public:
- Decoder(const std::string &path);
+ static constexpr auto fileDeletedRetCode = -1;
+ explicit Decoder(const std::string &path);
virtual ~Decoder();
- virtual std::uint32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) = 0;
+ virtual std::int32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) = 0;
// Range 0 - 1
virtual void setPosition(float pos) = 0;
@@ 38,9 38,9 @@ namespace audio
return sampleRate;
}
- std::uint32_t getChannelNumber()
+ std::uint32_t getChannelCount()
{
- return chanNumber;
+ return channelCount;
}
float getCurrentPosition()
@@ 57,7 57,8 @@ namespace audio
auto getTraits() const -> Endpoint::Traits override;
- void startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback);
+ void startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
+ const DecoderWorker::FileDeletedCallback &fileDeletedCallback);
void stopDecodingWorker();
// Factory method
@@ 68,12 69,13 @@ namespace audio
{
return bitsPerSample;
}
+
virtual std::unique_ptr<tags::fetcher::Tags> fetchTags();
static constexpr Endpoint::Traits decoderCaps = {.usesDMA = false};
std::uint32_t sampleRate = 0;
- std::uint32_t chanNumber = 0;
+ std::uint32_t channelCount = 0;
std::uint32_t bitsPerSample;
float position = 0;
std::FILE *fd = nullptr;
@@ 84,9 86,7 @@ namespace audio
std::unique_ptr<tags::fetcher::Tags> tags;
bool isInitialized = false;
- // decoding worker
+ // Decoding worker
std::unique_ptr<DecoderWorker> audioWorker;
- DecoderWorker::EndOfFileCallback _endOfFileCallback;
};
-
} // namespace audio
A module-audio/Audio/decoder/DecoderCommon.hpp => module-audio/Audio/decoder/DecoderCommon.hpp +18 -0
@@ 0,0 1,18 @@
+// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <sys/stat.h>
+#include <cstdio>
+
+namespace audio
+{
+ inline bool fileExists(FILE *fd)
+ {
+ struct stat fdStat
+ {};
+ constexpr auto statFailed = -1;
+ return (fstat(fileno(fd), &fdStat) != statFailed);
+ }
+} // namespace audio
R module-audio/Audio/decoder/decoderFLAC.cpp => module-audio/Audio/decoder/DecoderFLAC.cpp +30 -31
@@ 1,10 1,9 @@
-// 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 <Utils.hpp>
-#include "decoderFLAC.hpp"
+#include "DecoderFLAC.hpp"
+#include "DecoderCommon.hpp"
#include "flac/flacfile.h"
-#include "decoderCommon.hpp"
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO
@@ 15,44 14,47 @@
namespace audio
{
-
- decoderFLAC::decoderFLAC(const std::string &filePath) : Decoder(filePath)
+ DecoderFLAC::DecoderFLAC(const std::string &filePath) : Decoder(filePath)
{
if (fileSize == 0) {
return;
}
- flac = drflac_open(drflac_read, drflac_seek, this, nullptr);
+ flac = drflac_open(drflacRead, drflacSeek, this, nullptr);
if (flac == nullptr) {
LOG_ERROR("Unable to initialize FLAC decoder");
return;
}
- chanNumber = flac->channels;
- sampleRate = flac->sampleRate;
- // NOTE: Always convert to S16LE as internal format
+ /* NOTE: Always convert to S16LE as an internal format */
+ channelCount = flac->channels;
+ sampleRate = flac->sampleRate;
bitsPerSample = 16;
isInitialized = true;
}
- decoderFLAC::~decoderFLAC()
+ DecoderFLAC::~DecoderFLAC()
{
drflac_close(flac);
}
- std::uint32_t decoderFLAC::decode(std::uint32_t samplesToRead, int16_t *pcmData)
+ std::int32_t DecoderFLAC::decode(std::uint32_t samplesToRead, int16_t *pcmData)
{
- std::uint32_t samples_read =
- drflac_read_pcm_frames_s16(flac, samplesToRead / chanNumber, reinterpret_cast<drflac_int16 *>(pcmData));
- if (samples_read > 0) {
- /* Calculate frame duration in seconds */
- position += static_cast<float>(samples_read) / static_cast<float>(sampleRate);
+ if (!fileExists(fd)) {
+ LOG_WARN("File '%s' was deleted during playback!", filePath.c_str());
+ return fileDeletedRetCode;
}
- return samples_read * chanNumber;
+ const auto samplesRead =
+ drflac_read_pcm_frames_s16(flac, samplesToRead / channelCount, reinterpret_cast<drflac_int16 *>(pcmData));
+ if (samplesRead > 0) {
+ /* Calculate frame duration in seconds */
+ position += static_cast<float>(samplesRead) / static_cast<float>(sampleRate);
+ }
+ return samplesRead * channelCount;
}
- void decoderFLAC::setPosition(float pos)
+ void DecoderFLAC::setPosition(float pos)
{
if (!isInitialized) {
LOG_ERROR("FLAC decoder not initialized");
@@ 65,29 67,26 @@ namespace audio
}
/* Data encoded in UTF-8 */
- void decoderFLAC::flac_parse_text(
+ void DecoderFLAC::parseText(
std::uint8_t *in, std::uint32_t taglen, std::uint32_t datalen, std::uint8_t *out, std::uint32_t outlen)
{
- /* Little Endian here. */
- std::uint32_t size = ((datalen - taglen) > (outlen - 1)) ? (outlen - 1) : (datalen - taglen);
+ /* Little Endian here */
+ const auto size = std::min(datalen - taglen, outlen - 1);
memcpy(out, in + taglen, size);
out[size] = '\0';
}
- size_t decoderFLAC::drflac_read(void *pUserData, void *pBufferOut, size_t bytesToRead)
+ std::size_t DecoderFLAC::drflacRead(void *pUserData, void *pBufferOut, std::size_t bytesToRead)
{
- const auto decoderContext = reinterpret_cast<decoderFLAC *>(pUserData);
- return !statFd(decoderContext->fd, "FLAC audio file deleted by user!")
- ? 0
- : std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd);
+ const auto decoderContext = reinterpret_cast<DecoderFLAC *>(pUserData);
+ return std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd);
}
- drflac_bool32 decoderFLAC::drflac_seek(void *pUserData, int offset, drflac_seek_origin origin)
+ drflac_bool32 DecoderFLAC::drflacSeek(void *pUserData, int offset, drflac_seek_origin origin)
{
- const auto decoderContext = reinterpret_cast<decoderFLAC *>(pUserData);
- const int seekError =
+ const auto decoderContext = reinterpret_cast<DecoderFLAC *>(pUserData);
+ const auto seekError =
std::fseek(decoderContext->fd, offset, origin == drflac_seek_origin_start ? SEEK_SET : SEEK_CUR);
return (seekError == 0) ? DRFLAC_TRUE : DRFLAC_FALSE;
}
-
} // namespace audio
R module-audio/Audio/decoder/decoderFLAC.hpp => module-audio/Audio/decoder/DecoderFLAC.hpp +11 -14
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2022, 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
#pragma once
@@ 8,16 8,13 @@
namespace audio
{
-
- class decoderFLAC : public Decoder
+ class DecoderFLAC : public Decoder
{
-
public:
- explicit decoderFLAC(const std::string &filePath);
+ explicit DecoderFLAC(const std::string &filePath);
+ ~DecoderFLAC();
- ~decoderFLAC();
-
- uint32_t decode(uint32_t samplesToRead, int16_t *pcmData) override;
+ std::int32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) override;
void setPosition(float pos) override;
@@ 25,7 22,8 @@ namespace audio
drflac *flac = nullptr;
/* Data encoded in UTF-8 */
- void flac_parse_text(uint8_t *in, uint32_t taglen, uint32_t datalen, uint8_t *out, uint32_t outlen);
+ void parseText(
+ std::uint8_t *in, std::uint32_t taglen, std::uint32_t datalen, std::uint8_t *out, std::uint32_t outlen);
// Callback for when data needs to be read from the client.
//
@@ 37,7 35,7 @@ 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 size_t drflac_read(void *pUserData, void *pBufferOut, size_t bytesToRead);
+ static std::size_t drflacRead(void *pUserData, void *pBufferOut, std::size_t bytesToRead);
// Callback for when data needs to be seeked.
//
@@ 45,12 43,11 @@ namespace audio
// 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 or not the seek was successful.
+ // Returns whether the seek was successful.
//
- // The offset will never be negative. Whether or not it is relative to the beginning or current position is
+ // 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 drflac_seek_origin_start or
// drflac_seek_origin_current.
- static drflac_bool32 drflac_seek(void *pUserData, int offset, drflac_seek_origin origin);
+ static drflac_bool32 drflacSeek(void *pUserData, int offset, drflac_seek_origin origin);
};
-
} // namespace audio
R module-audio/Audio/decoder/decoderMP3.cpp => module-audio/Audio/decoder/DecoderMP3.cpp +25 -22
@@ 1,11 1,11 @@
-// 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
#define DR_MP3_IMPLEMENTATION
#define DR_MP3_NO_STDIO
-#include "decoderCommon.hpp"
-#include "decoderMP3.hpp"
+#include "DecoderCommon.hpp"
+#include "DecoderMP3.hpp"
#include <cstdio>
namespace
@@ 49,7 49,7 @@ namespace
namespace audio
{
- decoderMP3::decoderMP3(const std::string &filePath) : Decoder(filePath)
+ DecoderMP3::DecoderMP3(const std::string &filePath) : Decoder(filePath)
{
if (fileSize == 0) {
return;
@@ 65,25 65,25 @@ namespace audio
mp3 = std::make_unique<drmp3>();
- drmp3_init(mp3.get(), drmp3_read, drmp3_seek, this, nullptr);
+ drmp3_init(mp3.get(), drmp3Read, drmp3Seek, this, nullptr);
if (mp3 == nullptr) {
LOG_ERROR("Unable to initialize MP3 decoder");
return;
}
- chanNumber = mp3->channels;
- sampleRate = mp3->sampleRate;
- // NOTE: Always convert to S16LE as internal format
+ /* NOTE: Always convert to S16LE as an internal format */
+ channelCount = mp3->channels;
+ sampleRate = mp3->sampleRate;
bitsPerSample = 16;
isInitialized = true;
}
- decoderMP3::~decoderMP3()
+ DecoderMP3::~DecoderMP3()
{
drmp3_uninit(mp3.get());
}
- void decoderMP3::setPosition(float pos)
+ void DecoderMP3::setPosition(float pos)
{
if (!isInitialized) {
LOG_ERROR("MP3 decoder not initialized");
@@ 94,28 94,31 @@ namespace audio
position = static_cast<float>(totalFramesCount) * pos / static_cast<float>(sampleRate);
}
- std::uint32_t decoderMP3::decode(std::uint32_t samplesToRead, std::int16_t *pcmData)
+ std::int32_t DecoderMP3::decode(std::uint32_t samplesToRead, std::int16_t *pcmData)
{
- const auto samplesRead =
- drmp3_read_pcm_frames_s16(mp3.get(), samplesToRead / chanNumber, reinterpret_cast<drmp3_int16 *>(pcmData));
+ if (!fileExists(fd)) {
+ LOG_WARN("File '%s' was deleted during playback!", filePath.c_str());
+ return fileDeletedRetCode;
+ }
+
+ const auto samplesRead = drmp3_read_pcm_frames_s16(
+ mp3.get(), samplesToRead / channelCount, reinterpret_cast<drmp3_int16 *>(pcmData));
if (samplesRead > 0) {
+ /* Calculate frame duration in seconds */
position += static_cast<float>(samplesRead) / static_cast<float>(sampleRate);
}
-
- return samplesRead * chanNumber;
+ return samplesRead * channelCount;
}
- std::size_t decoderMP3::drmp3_read(void *pUserData, void *pBufferOut, std::size_t bytesToRead)
+ std::size_t DecoderMP3::drmp3Read(void *pUserData, void *pBufferOut, std::size_t bytesToRead)
{
- const auto decoderContext = reinterpret_cast<decoderMP3 *>(pUserData);
- return !statFd(decoderContext->fd, "MP3 audio file deleted by user!")
- ? 0
- : std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd);
+ const auto decoderContext = reinterpret_cast<DecoderMP3 *>(pUserData);
+ return std::fread(pBufferOut, 1, bytesToRead, decoderContext->fd);
}
- drmp3_bool32 decoderMP3::drmp3_seek(void *pUserData, int offset, drmp3_seek_origin origin)
+ drmp3_bool32 DecoderMP3::drmp3Seek(void *pUserData, int offset, drmp3_seek_origin origin)
{
- const auto decoderContext = reinterpret_cast<decoderMP3 *>(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;
R module-audio/Audio/decoder/decoderMP3.hpp => module-audio/Audio/decoder/DecoderMP3.hpp +9 -11
@@ 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
#pragma once
@@ 8,15 8,13 @@
namespace audio
{
- class decoderMP3 : public Decoder
+ class DecoderMP3 : public Decoder
{
-
public:
- explicit decoderMP3(const std::string &filePath);
-
- ~decoderMP3();
+ explicit DecoderMP3(const std::string &filePath);
+ ~DecoderMP3();
- std::uint32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) override;
+ std::int32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) override;
void setPosition(float pos) override;
@@ 33,7 31,7 @@ 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 drmp3_read(void *pUserData, void *pBufferOut, std::size_t bytesToRead);
+ static std::size_t drmp3Read(void *pUserData, void *pBufferOut, std::size_t bytesToRead);
// Callback for when data needs to be seeked.
//
@@ 41,11 39,11 @@ namespace audio
// 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 or not the seek was successful.
+ // Returns whether the seek was successful.
//
- // The offset will never be negative. Whether or not it is relative to the beginning or current position is
+ // 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 drflac_seek_origin_start or
// drflac_seek_origin_current.
- static drmp3_bool32 drmp3_seek(void *pUserData, int offset, drmp3_seek_origin origin);
+ static drmp3_bool32 drmp3Seek(void *pUserData, int offset, drmp3_seek_origin origin);
};
} // namespace audio
A module-audio/Audio/decoder/DecoderWAV.cpp => module-audio/Audio/decoder/DecoderWAV.cpp +66 -0
@@ 0,0 1,66 @@
+// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "DecoderWAV.hpp"
+#include "DecoderCommon.hpp"
+#include <log/log.hpp>
+#include <memory>
+
+#define DR_WAV_IMPLEMENTATION
+#include <src/dr_wav.h>
+
+namespace audio
+{
+ DecoderWAV::DecoderWAV(const std::string &filePath) : Decoder(filePath), wav(std::make_unique<drwav>())
+ {
+ if (drwav_init_file(wav.get(), filePath.c_str(), nullptr) == DRWAV_FALSE) {
+ LOG_ERROR("Unable to init WAV decoder");
+ return;
+ }
+
+ /* NOTE: Always convert to S16LE as an internal format */
+ channelCount = wav->channels;
+ sampleRate = wav->sampleRate;
+ bitsPerSample = 16;
+ isInitialized = true;
+ }
+
+ DecoderWAV::~DecoderWAV()
+ {
+ if (isInitialized) {
+ drwav_uninit(wav.get());
+ }
+ }
+
+ std::int32_t DecoderWAV::decode(std::uint32_t samplesToRead, std::int16_t *pcmData)
+ {
+ if (!isInitialized) {
+ LOG_ERROR("WAV decoder not initialized");
+ return 0;
+ }
+
+ if (!fileExists(fd)) {
+ LOG_WARN("File '%s' was deleted during playback!", filePath.c_str());
+ return fileDeletedRetCode;
+ }
+
+ const auto samplesRead = drwav_read_pcm_frames_s16(wav.get(), samplesToRead / channelCount, pcmData);
+ if (samplesRead > 0) {
+ /* Calculate frame duration in seconds */
+ position += static_cast<float>(samplesRead) / static_cast<float>(sampleRate);
+ }
+ return samplesRead * channelCount;
+ }
+
+ void DecoderWAV::setPosition(float pos)
+ {
+ if (!isInitialized) {
+ LOG_ERROR("WAV decoder not initialized");
+ return;
+ }
+ drwav_seek_to_pcm_frame(wav.get(), wav->totalPCMFrameCount * pos);
+
+ /* Calculate new position */
+ position = static_cast<float>(wav->totalPCMFrameCount) * pos / static_cast<float>(sampleRate);
+ }
+} // namespace audio
R module-audio/Audio/decoder/decoderWAV.hpp => module-audio/Audio/decoder/DecoderWAV.hpp +7 -11
@@ 1,28 1,24 @@
-// Copyright (c) 2017-2022, 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
#pragma once
#include "Decoder.hpp"
+#include <src/dr_wav.h>
namespace audio
{
- namespace internal
- {
- struct wavContext;
- }
- class decoderWAV : public Decoder
+ class DecoderWAV : public Decoder
{
public:
- explicit decoderWAV(const std::string &filePath);
- virtual ~decoderWAV();
+ explicit DecoderWAV(const std::string &filePath);
+ ~DecoderWAV();
- std::uint32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) override;
+ std::int32_t decode(std::uint32_t samplesToRead, std::int16_t *pcmData) override;
void setPosition(float pos) override;
private:
- std::unique_ptr<internal::wavContext> decoderContext;
+ std::unique_ptr<drwav> wav;
};
-
} // namespace audio
M module-audio/Audio/decoder/DecoderWorker.cpp => module-audio/Audio/decoder/DecoderWorker.cpp +16 -12
@@ 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 "DecoderWorker.hpp"
@@ 7,11 7,12 @@
audio::DecoderWorker::DecoderWorker(audio::AbstractStream *audioStreamOut,
Decoder *decoder,
- EndOfFileCallback endOfFileCallback,
+ const EndOfFileCallback &endOfFileCallback,
+ const FileDeletedCallback &fileDeletedCallback,
ChannelMode mode)
: sys::Worker(DecoderWorker::workerName, DecoderWorker::workerPriority, stackDepth), audioStreamOut(audioStreamOut),
- decoder(decoder), endOfFileCallback(std::move(endOfFileCallback)),
- bufferSize(audioStreamOut->getInputTraits().blockSize / sizeof(BufferInternalType)), channelMode(mode)
+ decoder(decoder), bufferSize(audioStreamOut->getInputTraits().blockSize / sizeof(BufferInternalType)),
+ channelMode(mode), endOfFileCallback(endOfFileCallback), fileDeletedCallback(fileDeletedCallback)
{}
audio::DecoderWorker::~DecoderWorker()
@@ 41,7 42,7 @@ auto audio::DecoderWorker::init(std::list<sys::WorkerQueueInfo> queues) -> bool
return isSuccessful;
}
-bool audio::DecoderWorker::handleMessage(uint32_t queueID)
+bool audio::DecoderWorker::handleMessage(std::uint32_t queueID)
{
auto queue = queues[queueID];
auto &queueName = queue->GetQueueName();
@@ 58,7 59,6 @@ bool audio::DecoderWorker::handleMessage(uint32_t queueID)
case Stream::Event::StreamFull:
break;
case Stream::Event::StreamHalfUsed:
- [[fallthrough]];
case Stream::Event::StreamEmpty:
pushAudioData();
}
@@ 88,13 88,17 @@ bool audio::DecoderWorker::handleMessage(uint32_t queueID)
void audio::DecoderWorker::pushAudioData()
{
- auto samplesRead = 0;
- const unsigned int readScale = channelMode == ChannelMode::ForceStereo ? 2 : 1;
+ const auto readScale = (channelMode == ChannelMode::ForceStereo) ? channel::stereoSound : channel::monoSound;
+ std::int32_t samplesRead = 0;
while (!audioStreamOut->isFull() && playbackEnabled) {
auto buffer = decoderBuffer.get();
samplesRead = decoder->decode(bufferSize / readScale, buffer);
+ if (samplesRead == Decoder::fileDeletedRetCode) {
+ fileDeletedCallback();
+ break;
+ }
if (samplesRead == 0) {
endOfFileCallback();
break;
@@ 102,13 106,13 @@ void audio::DecoderWorker::pushAudioData()
// pcm mono to stereo force conversion
if (channelMode == ChannelMode::ForceStereo) {
- for (unsigned int i = bufferSize / 2; i > 0; i--) {
+ for (auto i = bufferSize / 2; i > 0; i--) {
buffer[i * 2 - 1] = buffer[i * 2 - 2] = buffer[i - 1];
}
}
if (!audioStreamOut->push(decoderBuffer.get(), samplesRead * sizeof(BufferInternalType) * readScale)) {
- LOG_FATAL("Decoder failed to push to stream.");
+ LOG_ERROR("Decoder failed to push to stream");
break;
}
}
@@ 116,13 120,13 @@ void audio::DecoderWorker::pushAudioData()
bool audio::DecoderWorker::enablePlayback()
{
- return sendCommand({.command = static_cast<uint32_t>(Command::EnablePlayback), .data = nullptr}) &&
+ return sendCommand({.command = static_cast<std::uint32_t>(Command::EnablePlayback), .data = nullptr}) &&
stateChangeWait();
}
bool audio::DecoderWorker::disablePlayback()
{
- return sendCommand({.command = static_cast<uint32_t>(Command::DisablePlayback), .data = nullptr}) &&
+ return sendCommand({.command = static_cast<std::uint32_t>(Command::DisablePlayback), .data = nullptr}) &&
stateChangeWait();
}
M module-audio/Audio/decoder/DecoderWorker.hpp => module-audio/Audio/decoder/DecoderWorker.hpp +10 -5
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2022, 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
#pragma once
@@ 16,6 16,8 @@ namespace audio
{
public:
using EndOfFileCallback = std::function<void()>;
+ using FileDeletedCallback = std::function<void()>;
+
enum class Command
{
EnablePlayback,
@@ 30,7 32,8 @@ namespace audio
DecoderWorker(AbstractStream *audioStreamOut,
Decoder *decoder,
- EndOfFileCallback endOfFileCallback,
+ const EndOfFileCallback &endOfFileCallback,
+ const FileDeletedCallback &fileDeletedCallback,
ChannelMode mode);
~DecoderWorker() override;
@@ 42,11 45,11 @@ namespace audio
private:
static constexpr std::size_t stackDepth = 12 * 1024;
- virtual auto handleMessage(uint32_t queueID) -> bool override;
+ virtual auto handleMessage(std::uint32_t queueID) -> bool override;
void pushAudioData();
bool stateChangeWait();
- using BufferInternalType = int16_t;
+ using BufferInternalType = std::int16_t;
static constexpr auto workerName = "DecoderWorker";
static constexpr auto workerPriority = static_cast<UBaseType_t>(sys::ServicePriority::Idle);
@@ 55,7 58,6 @@ namespace audio
AbstractStream *audioStreamOut = nullptr;
Decoder *decoder = nullptr;
- EndOfFileCallback endOfFileCallback;
std::unique_ptr<StreamQueuedEventsListener> queueListener;
bool playbackEnabled = false;
cpp_freertos::BinarySemaphore stateSemaphore;
@@ 63,5 65,8 @@ namespace audio
const int bufferSize;
std::unique_ptr<BufferInternalType[]> decoderBuffer;
ChannelMode channelMode = ChannelMode::NoConversion;
+
+ EndOfFileCallback endOfFileCallback;
+ FileDeletedCallback fileDeletedCallback;
};
} // namespace audio
D module-audio/Audio/decoder/decoderCommon.hpp => module-audio/Audio/decoder/decoderCommon.hpp +0 -26
@@ 1,26 0,0 @@
-// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
-// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-
-#pragma once
-
-#include <log/log.hpp>
-#include <sys/stat.h>
-#include <cerrno>
-#include <cstdio>
-
-namespace
-{
- inline bool statFd(FILE *fd, char const *logMessage)
- {
- struct stat fdStats;
- constexpr int statFailed = -1;
- if (fstat(fileno(fd), &fdStats) != statFailed) {
- return true;
- }
-
- auto originalErrno = errno;
- LOG_WARN("%s", logMessage);
- errno = originalErrno;
- return false;
- }
-} // namespace
D module-audio/Audio/decoder/decoderWAV.cpp => module-audio/Audio/decoder/decoderWAV.cpp +0 -72
@@ 1,72 0,0 @@
-// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
-// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-
-#include "decoderWAV.hpp"
-#include <log/log.hpp>
-#include <memory>
-
-#define DR_WAV_IMPLEMENTATION
-#include <src/dr_wav.h>
-
-namespace audio
-{
- namespace internal
- {
- struct wavContext
- {
- drwav wav;
- };
- } // namespace internal
-
- decoderWAV::decoderWAV(const std::string &filePath)
- : Decoder(filePath), decoderContext(std::make_unique<internal::wavContext>())
- {
- auto dwav = &decoderContext->wav;
- if (drwav_init_file(dwav, filePath.c_str(), nullptr) == DRWAV_FALSE) {
- LOG_ERROR("Unable to init wav decoder");
- return;
- }
- sampleRate = dwav->sampleRate;
- // NOTE: Always convert to S16LE as internal format
- bitsPerSample = 16;
- // Number of channels
- chanNumber = dwav->channels;
- isInitialized = true;
- }
-
- decoderWAV::~decoderWAV()
- {
- if (isInitialized) {
- auto dwav = &decoderContext->wav;
- drwav_uninit(dwav);
- }
- }
-
- std::uint32_t decoderWAV::decode(std::uint32_t samplesToRead, std::int16_t *pcmData)
- {
- if (!isInitialized) {
- LOG_ERROR("Wav decoder not initialized");
- return 0;
- }
- auto dwav = &decoderContext->wav;
- const auto samples_read = drwav_read_pcm_frames_s16(dwav, samplesToRead / chanNumber, pcmData);
- if (samples_read) {
- /* Calculate frame duration in seconds */
- position += static_cast<float>(samplesToRead) / static_cast<float>(sampleRate);
- }
- return samples_read * chanNumber;
- }
-
- void decoderWAV::setPosition(float pos)
- {
- if (!isInitialized) {
- LOG_ERROR("Wav decoder not initialized");
- return;
- }
- auto dwav = &decoderContext->wav;
- drwav_seek_to_pcm_frame(dwav, dwav->totalPCMFrameCount * pos);
-
- // Calculate new position
- position = static_cast<float>(dwav->totalPCMFrameCount) * pos / static_cast<float>(sampleRate);
- }
-} // namespace audio
M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +5 -5
@@ 21,9 21,9 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioFormat.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/Decoder.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderMP3.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderWAV.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderFLAC.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderMP3.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderWAV.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderWorker.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/Encoder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/EncoderWAV.cpp
@@ 54,11 54,11 @@ target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_TARGET})
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_INCLUDES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-# supress warning for flac decoder
+# Suppress warnings for FLAC decoder
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp
PROPERTIES COMPILE_FLAGS
- "-Wno-implicit-fallthrough -Wno-error=maybe-uninitialized"
+ "-Wno-implicit-fallthrough -Wno-error=maybe-uninitialized"
)
target_link_libraries(${PROJECT_NAME}
M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +35 -14
@@ 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 <AudioMessage.hpp>
@@ 171,15 171,19 @@ void ServiceAudio::ProcessCloseReason(sys::CloseReason closeReason)
std::optional<std::string> ServiceAudio::AudioServicesCallback(const sys::Message *msg)
{
- if (const auto *eof = dynamic_cast<const AudioServiceMessage::EndOfFile *>(msg); eof) {
- bus.sendUnicast(std::make_shared<AudioInternalEOFNotificationMessage>(eof->GetToken()), service::name::audio);
+ if (const auto eofMsg = dynamic_cast<const AudioServiceMessage::EndOfFile *>(msg); eofMsg) {
+ bus.sendUnicast(std::make_shared<AudioInternalEOFNotificationMessage>(eofMsg->GetToken()),
+ service::name::audio);
}
- else if (const auto *dbReq = dynamic_cast<const AudioServiceMessage::DbRequest *>(msg); dbReq) {
-
- const auto selectedPlayback = generatePlayback(dbReq->playback, dbReq->setting);
- const auto selectedProfile = generateProfile(dbReq->profile, dbReq->playback);
+ else if (const auto fileDeletedMsg = dynamic_cast<const AudioServiceMessage::FileDeleted *>(msg); fileDeletedMsg) {
+ bus.sendUnicast(std::make_shared<AudioInternalFileDeletedNotificationMessage>(fileDeletedMsg->GetToken()),
+ service::name::audio);
+ }
+ else if (const auto dbRequestMsg = dynamic_cast<const AudioServiceMessage::DbRequest *>(msg); dbRequestMsg) {
+ const auto selectedPlayback = generatePlayback(dbRequestMsg->playback, dbRequestMsg->setting);
+ const auto selectedProfile = generateProfile(dbRequestMsg->profile, dbRequestMsg->playback);
- const auto &path = dbPath(dbReq->setting, selectedPlayback, selectedProfile);
+ const auto &path = dbPath(dbRequestMsg->setting, selectedPlayback, selectedProfile);
LOG_DEBUG("ServiceAudio::DBbCallback(%s)", path.c_str());
const auto settings_it = settingsCache.find(path);
@@ 207,7 211,7 @@ std::optional<std::string> ServiceAudio::AudioServicesCallback(const sys::Messag
}
}
else {
- LOG_DEBUG("Message received but not handled - no effect.");
+ LOG_DEBUG("Message received but not handled - no effect");
}
return std::nullopt;
@@ 553,19 557,25 @@ auto ServiceAudio::StopInput(audio::AudioMux::Input *input, StopReason stopReaso
if (input->audio->GetCurrentState() == Audio::State::Idle) {
return audio::RetCode::Success;
}
- const auto rCode = input->audio->Stop();
+ const auto retCode = input->audio->Stop();
+
// Send notification that audio file was stopped
std::shared_ptr<AudioNotificationMessage> msg;
- if (stopReason == StopReason::Eof) {
+ switch (stopReason) {
+ case StopReason::Eof:
msg = std::make_shared<AudioEOFNotification>(input->token);
- }
- else {
+ break;
+ case StopReason::FileDeleted:
+ msg = std::make_shared<AudioFileDeletedNotification>(input->token);
+ break;
+ default:
msg = std::make_shared<AudioStopNotification>(input->token);
+ break;
}
bus.sendMulticast(std::move(msg), sys::BusChannel::ServiceAudioNotifications);
audioMux.ResetInput(input);
VibrationUpdate();
- return rCode;
+ return retCode;
}
void ServiceAudio::HandleEOF(const Token &token)
@@ 583,6 593,13 @@ void ServiceAudio::HandleEOF(const Token &token)
}
}
+void ServiceAudio::HandleFileDeleted(const audio::Token &token)
+{
+ if (const auto input = audioMux.GetInput(token); input) {
+ StopInput(*input, StopReason::FileDeleted);
+ }
+}
+
auto ServiceAudio::HandleKeyPressed(const int step) -> sys::MessagePointer
{
auto context = getCurrentContext();
@@ 651,6 668,10 @@ sys::MessagePointer ServiceAudio::DataReceivedHandler(sys::DataMessage *msgl, sy
auto *msg = static_cast<AudioInternalEOFNotificationMessage *>(msgl);
HandleEOF(msg->token);
}
+ else if (msgType == typeid(AudioInternalFileDeletedNotificationMessage)) {
+ auto *msg = static_cast<AudioInternalEOFNotificationMessage *>(msgl);
+ HandleFileDeleted(msg->token);
+ }
else if (msgType == typeid(AudioGetSetting)) {
auto *msg = static_cast<AudioGetSetting *>(msgl);
auto value = getSetting(msg->setting, Profile::Type::Idle, msg->playbackType);
M module-services/service-audio/include/service-audio/AudioMessage.hpp => module-services/service-audio/include/service-audio/AudioMessage.hpp +18 -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
#pragma once
@@ 35,7 35,16 @@ class AudioResponseMessage : public sys::ResponseMessage
class AudioInternalEOFNotificationMessage : public AudioMessage
{
public:
- explicit AudioInternalEOFNotificationMessage(audio::Token token) : token(token)
+ explicit AudioInternalEOFNotificationMessage(audio::Token token) : token{token}
+ {}
+
+ const audio::Token token;
+};
+
+class AudioInternalFileDeletedNotificationMessage : public AudioMessage
+{
+ public:
+ explicit AudioInternalFileDeletedNotificationMessage(audio::Token token) : token{token}
{}
const audio::Token token;
@@ 64,6 73,13 @@ class AudioEOFNotification : public AudioNotificationMessage
{}
};
+class AudioFileDeletedNotification : public AudioNotificationMessage
+{
+ public:
+ explicit AudioFileDeletedNotification(audio::Token token) : AudioNotificationMessage{token}
+ {}
+};
+
class AudioPausedNotification : public AudioNotificationMessage
{
public:
M module-services/service-audio/include/service-audio/ServiceAudio.hpp => module-services/service-audio/include/service-audio/ServiceAudio.hpp +3 -1
@@ 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
#pragma once
@@ 80,6 80,7 @@ class ServiceAudio : public sys::Service
enum class StopReason
{
Eof,
+ FileDeleted,
Other
};
@@ 89,6 90,7 @@ class ServiceAudio : public sys::Service
auto HandlePause(std::optional<audio::AudioMux::Input *> input) -> std::unique_ptr<AudioResponseMessage>;
auto HandleResume(const audio::Token &token) -> std::unique_ptr<AudioResponseMessage>;
void HandleEOF(const audio::Token &token);
+ void HandleFileDeleted(const audio::Token &token);
auto HandleKeyPressed(const int step) -> sys::MessagePointer;
void MuteCurrentOperation();
void VibrationUpdate(const audio::PlaybackType &type = audio::PlaybackType::None,
M products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp => products/BellHybrid/apps/application-bell-relaxation/ApplicationBellRelaxation.cpp +1 -4
@@ 26,12 26,9 @@
#include <common/models/BatteryModel.hpp>
#include <common/models/AudioModel.hpp>
#include <common/windows/AppsBatteryStatusWindow.hpp>
-#include <audio/AudioMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <system/messages/SentinelRegistrationMessage.hpp>
-#include <log/log.hpp>
-
namespace
{
constexpr auto relaxationRebuildTimer = "RelaxationRebuildTimer";
@@ 44,7 41,7 @@ namespace app
std::string parent,
StatusIndicators statusIndicators,
StartInBackground startInBackground,
- uint32_t stackDepth)
+ std::uint32_t stackDepth)
: Application(std::move(name), std::move(parent), statusIndicators, startInBackground, stackDepth),
audioModel{std::make_unique<AudioModel>(this)}
{
M products/BellHybrid/apps/application-bell-relaxation/include/application-bell-relaxation/ApplicationBellRelaxation.hpp => products/BellHybrid/apps/application-bell-relaxation/include/application-bell-relaxation/ApplicationBellRelaxation.hpp +2 -2
@@ 18,7 18,6 @@ namespace gui::window::name
inline constexpr auto relaxationEnded = "RelaxationEndedWindow";
inline constexpr auto relaxationLowBattery = "RelaxationLowBatteryWindow";
inline constexpr auto relaxationError = "RelaxationError";
-
} // namespace gui::window::name
namespace app
{
@@ 26,6 25,7 @@ namespace app
{
class RelaxationPlayer;
}
+
inline constexpr auto applicationBellRelaxationName = "ApplicationBellRelaxation";
class ApplicationBellRelaxation : public Application
@@ 46,7 46,7 @@ namespace app
std::string parent = "",
StatusIndicators statusIndicators = StatusIndicators{},
StartInBackground startInBackground = {false},
- uint32_t stackDepth = 4096 * 2);
+ std::uint32_t stackDepth = 1024 * 8);
~ApplicationBellRelaxation();
sys::ReturnCodes InitHandler() override;
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningLoopPresenter.cpp +9 -3
@@ 29,6 29,7 @@ namespace app::relaxation
void RelaxationRunningLoopPresenter::activate(const db::multimedia_files::MultimediaFilesRecord &song)
{
Expects(timer != nullptr);
+
AbstractRelaxationPlayer::PlaybackMode mode;
const auto value = settings->getValue(timerValueDBRecordName, settings::SettingsScope::AppLocal);
if (utils::is_number(value) && utils::getNumericValue<int>(value) != 0) {
@@ 54,12 55,17 @@ namespace app::relaxation
}
else {
getView()->handleError();
- return;
}
};
- auto onErrorCallback = [this]() { getView()->handleDeletedFile(); };
- player.start(song.fileInfo.path, mode, std::move(onStartCallback), std::move(onErrorCallback));
+ auto onFinishedCallback = [this](AbstractAudioModel::PlaybackFinishStatus status) {
+ if (status == AbstractAudioModel::PlaybackFinishStatus::Error) {
+ timer->stop();
+ getView()->handleDeletedFile(); // Deleted file is currently the only error handled by player
+ }
+ };
+
+ player.start(song.fileInfo.path, mode, std::move(onStartCallback), std::move(onFinishedCallback));
}
void RelaxationRunningLoopPresenter::stop()
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.cpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.cpp +16 -9
@@ 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 "RelaxationRunningProgressPresenter.hpp"
@@ 10,9 10,9 @@
namespace
{
- bool songLengthEqualsToSelectedPeriod(std::chrono::minutes period, std::chrono::seconds songLength)
+ bool isSongLengthEqualToPeriod(std::chrono::seconds songLength, std::chrono::minutes period)
{
- auto periodInSeconds = std::chrono::duration_cast<std::chrono::seconds>(period);
+ const auto periodInSeconds = std::chrono::duration_cast<std::chrono::seconds>(period);
return periodInSeconds.count() == songLength.count();
}
} // namespace
@@ 39,11 39,12 @@ namespace app::relaxation
void RelaxationRunningProgressPresenter::activate(const db::multimedia_files::MultimediaFilesRecord &song)
{
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 &&
- !songLengthEqualsToSelectedPeriod(std::chrono::minutes{utils::getNumericValue<int>(value)}, 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);
mode = AbstractRelaxationPlayer::PlaybackMode::Looped;
@@ 58,18 59,24 @@ namespace app::relaxation
return;
}
}
+
auto onStartCallback = [this](audio::RetCode retCode) {
if (retCode == audio::RetCode::Success) {
timer->start();
}
else {
getView()->handleError();
- return;
}
};
- auto onErrorCallback = [this]() { getView()->handleDeletedFile(); };
- player.start(song.fileInfo.path, mode, std::move(onStartCallback), std::move(onErrorCallback));
+ auto onFinishedCallback = [this](AbstractAudioModel::PlaybackFinishStatus status) {
+ if (status == AbstractAudioModel::PlaybackFinishStatus::Error) {
+ timer->stop();
+ getView()->handleDeletedFile(); // Deleted file is currently the only error handled by player
+ }
+ };
+
+ player.start(song.fileInfo.path, mode, std::move(onStartCallback), std::move(onFinishedCallback));
}
void RelaxationRunningProgressPresenter::stop()
@@ 90,7 97,7 @@ namespace app::relaxation
void RelaxationRunningProgressPresenter::pause()
{
- if (not timer->isStopped()) {
+ if (!timer->isStopped()) {
auto onPauseCallback = [this](audio::RetCode retCode) {
if (retCode == audio::RetCode::Success) {
timer->stop();
M products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.hpp => products/BellHybrid/apps/application-bell-relaxation/presenter/RelaxationRunningProgressPresenter.hpp +3 -1
@@ 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
#pragma once
@@ 16,10 16,12 @@ namespace app
class AbstractBatteryModel;
class ApplicationCommon;
} // namespace app
+
namespace gui
{
class Item;
} // namespace gui
+
namespace settings
{
class Settings;
M products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.cpp => products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.cpp +25 -14
@@ 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 "RelaxationPlayer.hpp"
@@ 10,48 10,59 @@ namespace app::relaxation
{
return playbackMode;
}
+
RelaxationPlayer::RelaxationPlayer(AbstractAudioModel &audioModel) : audioModel{audioModel}
{}
+
void RelaxationPlayer::start(const std::string &filePath,
AbstractRelaxationPlayer::PlaybackMode mode,
- AbstractAudioModel::OnStateChangeCallback &&callback,
+ AbstractAudioModel::OnStateChangeCallback &&stateChangeCallback,
AbstractAudioModel::OnPlaybackFinishedCallback &&finishedCallback)
{
+ using Status = AbstractAudioModel::PlaybackFinishStatus;
+ using Type = AbstractAudioModel::PlaybackType;
+
recentFilePath = filePath;
playbackMode = mode;
- audioModel.play(filePath, AbstractAudioModel::PlaybackType::Multimedia, std::move(callback));
-
- auto finishedCb = [_callback = finishedCallback, this]() {
- auto cb = [&](audio::RetCode retCode) {
- if (retCode != audio::RetCode::Success) {
- _callback();
- }
- };
- if (playbackMode == PlaybackMode::Looped) {
- audioModel.play(recentFilePath, AbstractAudioModel::PlaybackType::Multimedia, std::move(cb));
+
+ 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
+ }
+ });
}
else {
- _callback();
+ callback(Status::Normal); // Normal finish in single shot mode
}
};
- audioModel.setPlaybackFinishedCb(std::move(finishedCb));
+ audioModel.setPlaybackFinishedCb(std::move(onPlayerFinished));
+ audioModel.play(filePath, Type::Multimedia, std::move(stateChangeCallback));
}
+
void RelaxationPlayer::stop(AbstractAudioModel::OnStateChangeCallback &&callback)
{
paused = false;
audioModel.stopPlayedByThis(std::move(callback));
}
+
void RelaxationPlayer::pause(AbstractAudioModel::OnStateChangeCallback &&callback)
{
paused = true;
audioModel.pause(std::move(callback));
}
+
void RelaxationPlayer::resume(AbstractAudioModel::OnStateChangeCallback &&callback)
{
paused = false;
audioModel.resume(std::move(callback));
}
+
bool RelaxationPlayer::isPaused()
{
return paused;
M products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.hpp => products/BellHybrid/apps/application-bell-relaxation/widgets/RelaxationPlayer.hpp +2 -1
@@ 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
#pragma once
@@ 9,6 9,7 @@ namespace app
{
class ApplicationCommon;
}
+
namespace service
{
class AudioEOFNotification;
M products/BellHybrid/apps/common/include/common/models/AbstractAudioModel.hpp => products/BellHybrid/apps/common/include/common/models/AbstractAudioModel.hpp +15 -10
@@ 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
#pragma once
@@ 14,14 14,6 @@ namespace app
class AbstractAudioModel
{
public:
- /// 0-15 range
- static constexpr auto minVolume = 1;
- static constexpr auto maxVolume = 15;
- using Volume = std::uint32_t;
- using OnStateChangeCallback = std::function<void(const audio::RetCode code)>;
- using OnGetValueCallback = std::function<void(const audio::RetCode, Volume)>;
- using OnPlaybackFinishedCallback = std::function<void()>;
-
enum class PlaybackType
{
Multimedia,
@@ 32,6 24,20 @@ namespace app
Meditation
};
+ enum class PlaybackFinishStatus
+ {
+ Normal,
+ Error
+ };
+
+ /// 0-15 range
+ static constexpr auto minVolume = 1;
+ static constexpr auto maxVolume = 15;
+ using Volume = std::uint32_t;
+ using OnStateChangeCallback = std::function<void(const audio::RetCode code)>;
+ using OnGetValueCallback = std::function<void(const audio::RetCode, Volume)>;
+ using OnPlaybackFinishedCallback = std::function<void(PlaybackFinishStatus)>;
+
virtual ~AbstractAudioModel() noexcept = default;
virtual void setVolume(Volume volume, PlaybackType playbackType, OnStateChangeCallback &&callback) = 0;
virtual std::optional<Volume> getVolume(PlaybackType playbackType) = 0;
@@ 47,5 53,4 @@ 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 +5 -3
@@ 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
#pragma once
@@ 30,9 30,11 @@ namespace app
private:
audio::Token lastPlayedToken = audio::Token::MakeBadToken();
- void stop(audio::Token token, OnStateChangeCallback &&callback);
- std::function<void()> playbackFinishedCallback{nullptr};
bool playbackFinishedFlag{false};
ApplicationCommon *app{};
+
+ OnPlaybackFinishedCallback playbackFinishedCallback{nullptr};
+
+ void stop(audio::Token token, OnStateChangeCallback &&callback);
};
} // namespace app
M products/BellHybrid/apps/common/include/common/widgets/BellConnectionStatus.hpp => products/BellHybrid/apps/common/include/common/widgets/BellConnectionStatus.hpp +1 -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
#pragma once
@@ 9,7 9,6 @@
namespace gui
{
-
class BellConnectionStatus : public gui::HBox
{
public:
M products/BellHybrid/apps/common/src/AudioModel.cpp => products/BellHybrid/apps/common/src/AudioModel.cpp +23 -12
@@ 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 "models/AudioModel.hpp"
@@ 50,27 50,35 @@ namespace
LOG_ERROR("Command %d Failed with %d error", msgType, static_cast<int>(ret.first));
return std::make_shared<service::AudioResponseMessage>(audio::RetCode::Failed);
}
-
} // namespace
namespace app
{
-
AudioModel::AudioModel(ApplicationCommon *app) : app::AsyncCallbackReceiver{app}, app{app}
{
- app->connect(typeid(service::AudioEOFNotification), [&](sys::Message *msg) -> sys::MessagePointer {
- playbackFinishedFlag = true;
- if (playbackFinishedCallback) {
- playbackFinishedCallback();
- }
-
- return sys::msgHandled();
- });
+ app->connect(typeid(service::AudioEOFNotification),
+ [&]([[maybe_unused]] sys::Message *msg) -> sys::MessagePointer {
+ playbackFinishedFlag = true;
+ if (playbackFinishedCallback) {
+ playbackFinishedCallback(AbstractAudioModel::PlaybackFinishStatus::Normal);
+ }
+ return sys::msgHandled();
+ });
+
+ app->connect(typeid(service::AudioFileDeletedNotification),
+ [&]([[maybe_unused]] sys::Message *msg) -> sys::MessagePointer {
+ playbackFinishedFlag = true;
+ if (playbackFinishedCallback) {
+ playbackFinishedCallback(AbstractAudioModel::PlaybackFinishStatus::Error);
+ }
+ return sys::msgHandled();
+ });
}
AudioModel::~AudioModel()
{
app->disconnect(typeid(service::AudioEOFNotification));
+ app->disconnect(typeid(service::AudioFileDeletedNotification));
}
void AudioModel::play(const std::string &filePath,
@@ 146,6 154,7 @@ namespace app
};
task->execute(app, this, std::move(cb));
}
+
void AudioModel::pause(OnStateChangeCallback &&callback)
{
auto msg = std::make_unique<service::AudioPauseRequest>();
@@ 164,6 173,7 @@ namespace app
};
task->execute(app, this, std::move(cb));
}
+
void AudioModel::resume(OnStateChangeCallback &&callback)
{
auto msg = std::make_unique<service::AudioResumeRequest>();
@@ 181,6 191,7 @@ namespace app
};
task->execute(app, this, std::move(cb));
}
+
void AudioModel::getVolume(AbstractAudioModel::PlaybackType playbackType,
AbstractAudioModel::OnGetValueCallback &&callback)
{
@@ 200,6 211,7 @@ namespace app
};
task->execute(app, this, std::move(cb));
}
+
std::optional<AbstractAudioModel::Volume> AudioModel::getVolume(AbstractAudioModel::PlaybackType playbackType)
{
auto msg = std::make_shared<service::AudioGetVolume>(convertPlaybackType(playbackType));
@@ 220,5 232,4 @@ namespace app
{
return playbackFinishedFlag;
}
-
} // namespace app
M products/BellHybrid/apps/common/src/widgets/BellConnectionStatus.cpp => products/BellHybrid/apps/common/src/widgets/BellConnectionStatus.cpp +3 -5
@@ 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 <EventStore.hpp>
@@ 9,9 9,8 @@
namespace
{
constexpr auto usb_connected_status = "app_bellmain_usb_status_connected";
- constexpr inline auto status_text_max_w = 350U;
- constexpr inline auto status_text_max_h = 102U;
-
+ constexpr auto status_text_max_w = 350U;
+ constexpr auto status_text_max_h = 102U;
} // namespace
namespace gui
@@ 46,5 45,4 @@ namespace gui
{
statusText->setVisible(true);
}
-
} // namespace gui
M products/BellHybrid/services/audio/ServiceAudio.cpp => products/BellHybrid/services/audio/ServiceAudio.cpp +64 -28
@@ 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 "ServiceAudio.hpp"
@@ 12,7 12,7 @@ namespace
{
// 4kB is too small because internally drflac_open() uses cache which by default has 4kB.
// Alternatively smaller DR_FLAC_BUFFER_SIZE could be defined.
- constexpr auto stackSize = 1024 * 8;
+ constexpr auto serviceAudioStackSize = 1024 * 8;
constexpr auto defaultVolume = "11";
constexpr auto defaultSnoozeVolume = "10";
constexpr auto defaultBedtimeVolume = "12";
@@ 26,18 26,18 @@ namespace
using namespace audio;
// clang-format off
constexpr std::initializer_list<std::pair<audio::DbPathElement, const char *>> values{
- {DbPathElement{Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackLoudspeaker},defaultVolume},
- {DbPathElement{Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackLoudspeaker},defaultVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackLoudspeaker}, defaultVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackLoudspeaker}, defaultVolume},
{DbPathElement{Setting::Volume, PlaybackType::Alarm, Profile::Type::PlaybackLoudspeaker}, defaultVolume},
{DbPathElement{Setting::Volume, PlaybackType::Bedtime, Profile::Type::PlaybackLoudspeaker}, defaultBedtimeVolume},
- {DbPathElement{Setting::Volume, PlaybackType::PreWakeUp, Profile::Type::PlaybackLoudspeaker},defaultSnoozeVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::PreWakeUp, Profile::Type::PlaybackLoudspeaker}, defaultSnoozeVolume},
{DbPathElement{Setting::Volume, PlaybackType::Snooze, Profile::Type::PlaybackLoudspeaker}, defaultSnoozeVolume},
/// Profiles below are not used but unfortunately, must exist in order to satisfy audio module requirements
- {DbPathElement{Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackHeadphones},defaultVolume},
- {DbPathElement{Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackBluetoothA2DP},defaultVolume},
- {DbPathElement{Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackHeadphones},defaultVolume},
- {DbPathElement{Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackBluetoothA2DP},defaultVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackHeadphones}, defaultVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::Meditation, Profile::Type::PlaybackBluetoothA2DP}, defaultVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackHeadphones}, defaultVolume},
+ {DbPathElement{Setting::Volume, PlaybackType::Multimedia, Profile::Type::PlaybackBluetoothA2DP}, defaultVolume},
{DbPathElement{Setting::Volume, PlaybackType::Alarm, Profile::Type::PlaybackHeadphones}, defaultVolume},
{DbPathElement{Setting::Volume, PlaybackType::Alarm, Profile::Type::PlaybackBluetoothA2DP}, defaultVolume},
{DbPathElement{Setting::Volume, PlaybackType::Bedtime, Profile::Type::PlaybackHeadphones}, defaultVolume},
@@ 50,19 50,29 @@ namespace
// clang-format on
} // namespace initializer
- class AudioInternalEOFNotificationMessage : public service::AudioNotificationMessage
+ namespace internal
{
- public:
- explicit AudioInternalEOFNotificationMessage(audio::Token token) : AudioNotificationMessage(token)
- {}
- };
+ class AudioEOFNotificationMessage : public service::AudioNotificationMessage
+ {
+ public:
+ explicit AudioEOFNotificationMessage(audio::Token token) : AudioNotificationMessage(token)
+ {}
+ };
+
+ class AudioFileDeletedNotificationMessage : public service::AudioNotificationMessage
+ {
+ public:
+ explicit AudioFileDeletedNotificationMessage(audio::Token token) : AudioNotificationMessage(token)
+ {}
+ };
+ } // namespace internal
} // namespace
namespace service
{
Audio::Audio()
- : sys::Service(audioServiceName, "", stackSize, sys::ServicePriority::Idle),
- audioMux([this](auto... params) { return this->AudioServicesCallback(params...); }),
+ : sys::Service(audioServiceName, "", serviceAudioStackSize, sys::ServicePriority::Idle),
+ audioMux([this](auto... params) { return AudioServicesCallback(params...); }),
cpuSentinel(std::make_shared<sys::CpuSentinel>(audioServiceName, this)),
settingsProvider(std::make_unique<settings::Settings>())
{
@@ 86,12 96,19 @@ namespace service
return handleStart(audio::Operation::Type::Playback, msgl->fadeIn, msgl->fileName, msgl->playbackType);
});
- connect(typeid(AudioInternalEOFNotificationMessage), [this](sys::Message *msg) -> sys::MessagePointer {
- auto *msgl = static_cast<AudioInternalEOFNotificationMessage *>(msg);
+ connect(typeid(internal::AudioEOFNotificationMessage), [this](sys::Message *msg) -> sys::MessagePointer {
+ auto *msgl = static_cast<internal::AudioEOFNotificationMessage *>(msg);
handleEOF(msgl->token);
return sys::msgHandled();
});
+ connect(typeid(internal::AudioFileDeletedNotificationMessage),
+ [this](sys::Message *msg) -> sys::MessagePointer {
+ auto *msgl = static_cast<internal::AudioEOFNotificationMessage *>(msg);
+ handleFileDeleted(msgl->token);
+ return sys::msgHandled();
+ });
+
connect(typeid(AudioStopRequest), [this](sys::Message *msg) -> sys::MessagePointer {
volumeFadeIn->Stop();
auto *msgl = static_cast<AudioStopRequest *>(msg);
@@ 211,13 228,19 @@ namespace service
return audio::RetCode::Success;
}
const auto retCode = input->audio->Stop();
+
// Send notification that audio file was stopped
std::shared_ptr<AudioNotificationMessage> msg;
- if (stopReason == StopReason::Eof) {
+ switch (stopReason) {
+ case StopReason::Eof:
msg = std::make_shared<AudioEOFNotification>(input->token);
- }
- else {
+ break;
+ case StopReason::FileDeleted:
+ msg = std::make_shared<AudioFileDeletedNotification>(input->token);
+ break;
+ default:
msg = std::make_shared<AudioStopNotification>(input->token);
+ break;
}
bus.sendMulticast(std::move(msg), sys::BusChannel::ServiceAudioNotifications);
audioMux.ResetInput(input);
@@ 257,23 280,36 @@ namespace service
}
}
+ void Audio::handleFileDeleted(const audio::Token &token)
+ {
+ if (const auto input = audioMux.GetInput(token); input) {
+ stopInput(*input, StopReason::FileDeleted);
+ }
+ }
+
auto Audio::AudioServicesCallback(const sys::Message *msg) -> std::optional<std::string>
{
std::optional<std::string> ret;
- if (const auto *eof = dynamic_cast<const AudioServiceMessage::EndOfFile *>(msg); eof) {
- bus.sendUnicast(std::make_shared<AudioInternalEOFNotificationMessage>(eof->GetToken()), audioServiceName);
+ if (const auto eofMsg = dynamic_cast<const AudioServiceMessage::EndOfFile *>(msg); eofMsg) {
+ bus.sendUnicast(std::make_shared<internal::AudioEOFNotificationMessage>(eofMsg->GetToken()),
+ audioServiceName);
+ }
+ else if (const auto fileDeletedMsg = dynamic_cast<const AudioServiceMessage::FileDeleted *>(msg);
+ fileDeletedMsg) {
+ bus.sendUnicast(std::make_shared<internal::AudioFileDeletedNotificationMessage>(fileDeletedMsg->GetToken()),
+ audioServiceName);
}
- else if (const auto *dbReq = dynamic_cast<const AudioServiceMessage::DbRequest *>(msg); dbReq) {
- auto selectedPlayback = dbReq->playback;
- auto selectedProfile = dbReq->profile;
+ else if (const auto dbRequestMsg = dynamic_cast<const AudioServiceMessage::DbRequest *>(msg); dbRequestMsg) {
+ auto selectedPlayback = dbRequestMsg->playback;
+ auto selectedProfile = dbRequestMsg->profile;
if (const auto result =
- settingsProvider->getValue(dbPath(dbReq->setting, selectedPlayback, selectedProfile));
+ settingsProvider->getValue(dbPath(dbRequestMsg->setting, selectedPlayback, selectedProfile));
not result.empty()) {
ret.emplace(result);
}
}
else {
- LOG_DEBUG("Message received but not handled - no effect.");
+ LOG_DEBUG("Message received but not handled - no effect");
}
return ret;
M products/BellHybrid/services/audio/include/audio/AudioMessage.hpp => products/BellHybrid/services/audio/include/audio/AudioMessage.hpp +8 -1
@@ 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
#pragma once
@@ 55,6 55,13 @@ namespace service
{}
};
+ class AudioFileDeletedNotification : public AudioNotificationMessage
+ {
+ public:
+ explicit AudioFileDeletedNotification(audio::Token token) : AudioNotificationMessage{token}
+ {}
+ };
+
class AudioSettingsMessage : public AudioMessage
{
public:
M products/BellHybrid/services/audio/include/audio/ServiceAudio.hpp => products/BellHybrid/services/audio/include/audio/ServiceAudio.hpp +3 -1
@@ 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
#pragma once
@@ 37,6 37,7 @@ namespace service
enum class StopReason
{
Eof,
+ FileDeleted,
Other
};
@@ 57,6 58,7 @@ namespace service
auto handleResume() -> std::unique_ptr<AudioResponseMessage>;
void handleEOF(const audio::Token &token);
+ void handleFileDeleted(const audio::Token &token);
auto AudioServicesCallback(const sys::Message *msg) -> std::optional<std::string>;
M third-party/dr_libs/src => third-party/dr_libs/src +1 -1
@@ 1,1 1,1 @@
-Subproject commit 4d577ffbba6979fcf198a5c42825ad33f542e5a7
+Subproject commit 190a6a50ca40879ffddcd054c417d64b7dd6ff99