~aleteoryx/muditaos

3df76ce075a985511aaf02d01978a520f2e79f9c — mkamonMdt 4 years ago 2443cb6
[BH-913] BGSounds audio implementation

The following commit provides an implemention of audio
features for the BGSounds application, that is:
* playback of selected record in either looped or
singleshot mode
* pausing, resuming and stopping the playback

The implementation does not cover a sinchronization
of displayed time with a acctual state of playback in
singleshot mode and changes of a playback volume. The
features will be addressed in a subsuquent tasks.
15 files changed, 381 insertions(+), 50 deletions(-)

M products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp
M products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt
M products/BellHybrid/apps/application-bell-background-sounds/data/BGSoundsAudioData.hpp
M products/BellHybrid/apps/application-bell-background-sounds/include/application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp
A products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.cpp
A products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.hpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.cpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.hpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp
M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.hpp
A products/BellHybrid/apps/application-bell-background-sounds/widgets/BGSoundsPlayer.cpp
A products/BellHybrid/apps/application-bell-background-sounds/widgets/BGSoundsPlayer.hpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.cpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.hpp
M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp
M products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp => products/BellHybrid/apps/application-bell-background-sounds/ApplicationBellBackgroundSounds.cpp +20 -5
@@ 2,6 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationBellBackgroundSounds.hpp"
#include "models/BGSoundsRepository.hpp"
#include "presenter/BGSoundsMainWindowPresenter.hpp"
#include "presenter/BGSoundsTimerSelectPresenter.hpp"
#include "presenter/BGSoundsProgressPresenter.hpp"


@@ 11,15 12,26 @@
#include "windows/BGSoundsProgressWindow.hpp"
#include "windows/BGSoundsTimerSelectWindow.hpp"
#include "windows/BGSoundsVolumeWindow.hpp"
#include "widgets/BGSoundsPlayer.hpp"
#include <service-audio/AudioMessage.hpp>

#include <log/log.hpp>
namespace app
{
    ApplicationBellBackgroundSounds::ApplicationBellBackgroundSounds(std::string name,
                                                                     std::string parent,
                                                                     StatusIndicators statusIndicators,
                                                                     StartInBackground startInBackground)
        : Application(std::move(name), std::move(parent), statusIndicators, startInBackground)
    {}
        : Application(std::move(name), std::move(parent), statusIndicators, startInBackground),
          player{std::make_unique<bgSounds::BGSoundsPlayer>(this)}
    {
        bus.channels.push_back(sys::BusChannel::ServiceAudioNotifications);
        connect(typeid(AudioEOFNotification), [&](sys::Message *msg) -> sys::MessagePointer {
            auto notification = static_cast<AudioEOFNotification *>(msg);
            return player->handle(notification);
        });
    }
    ApplicationBellBackgroundSounds::~ApplicationBellBackgroundSounds() = default;

    sys::ReturnCodes ApplicationBellBackgroundSounds::InitHandler()
    {


@@ 34,8 46,10 @@ namespace app

    void ApplicationBellBackgroundSounds::createUserInterface()
    {
        windowsFactory.attach(gui::name::window::main_window, [](ApplicationCommon *app, const std::string &name) {
            auto presenter = std::make_unique<bgSounds::BGSoundsMainWindowPresenter>();
        windowsFactory.attach(gui::name::window::main_window, [this](ApplicationCommon *app, const std::string &name) {
            auto tagsFetcher      = std::make_unique<bgSounds::BGSoundsTagsFetcher>(app);
            auto soundsRepository = std::make_shared<bgSounds::BGSoundsRepository>(std::move(tagsFetcher));
            auto presenter = std::make_unique<bgSounds::BGSoundsMainWindowPresenter>(std::move(soundsRepository));
            return std::make_unique<gui::BGSoundsMainWindow>(app, std::move(presenter));
        });
        windowsFactory.attach(


@@ 45,7 59,7 @@ namespace app
            });
        windowsFactory.attach(
            gui::window::name::bgSoundsProgress, [this](ApplicationCommon *app, const std::string &name) {
                auto presenter = std::make_unique<bgSounds::BGSoundsProgressPresenter>(app, settings.get());
                auto presenter = std::make_unique<bgSounds::BGSoundsProgressPresenter>(settings.get(), *player);
                return std::make_unique<gui::BGSoundsProgressWindow>(app, std::move(presenter));
            });
        windowsFactory.attach(gui::window::name::bgSoundsPaused, [](ApplicationCommon *app, const std::string &name) {


@@ 70,6 84,7 @@ namespace app
            response != nullptr && response->retCode == sys::ReturnCodes::Success) {
            return retMsg;
        }

        return handleAsyncResponse(resp);
    }
} // namespace app

M products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt => products/BellHybrid/apps/application-bell-background-sounds/CMakeLists.txt +5 -0
@@ 13,10 13,12 @@ target_include_directories(application-bell-background-sounds
target_sources(application-bell-background-sounds
    PRIVATE
        ApplicationBellBackgroundSounds.cpp
        models/BGSoundsRepository.cpp
        presenter/BGSoundsMainWindowPresenter.cpp
        presenter/BGSoundsProgressPresenter.cpp
        presenter/BGSoundsTimerSelectPresenter.cpp
        presenter/BGSoundsVolumePresenter.cpp
        widgets/BGSoundsPlayer.cpp
        windows/BGSoundsMainWindow.cpp
        windows/BGSoundsPausedWindow.cpp
        windows/BGSoundsProgressWindow.cpp


@@ 26,6 28,9 @@ target_sources(application-bell-background-sounds
        data/BGSoundsCommon.hpp
        data/BGSoundsStyle.hpp
        data/BGSoundsAudioData.hpp
        models/BGSoundsRepository.hpp
        widgets/BGSoundsPlayer.hpp
        windows/BGSoundsMainWindow.hpp
        presenter/BGSoundsMainWindowPresenter.hpp
        presenter/BGSoundsProgressPresenter.hpp
        presenter/BGSoundsTimerSelectPresenter.hpp

M products/BellHybrid/apps/application-bell-background-sounds/data/BGSoundsAudioData.hpp => products/BellHybrid/apps/application-bell-background-sounds/data/BGSoundsAudioData.hpp +5 -4
@@ 3,6 3,7 @@

#pragma once
#include <SwitchData.hpp>
#include <tags_fetcher/TagsFetcher.hpp>

#include <chrono>
#include <memory>


@@ 12,14 13,14 @@ namespace gui
{
    class BGSoundsAudioContext
    {
        std::string title;
        tags::fetcher::Tags tags;

      public:
        explicit BGSoundsAudioContext(std::string title) : title{std::move(title)}
        explicit BGSoundsAudioContext(const tags::fetcher::Tags &tags) : tags{tags}
        {}
        [[nodiscard]] const std::string &getTitle() const noexcept
        [[nodiscard]] const tags::fetcher::Tags &getTags() const noexcept
        {
            return title;
            return tags;
        }
    };


M products/BellHybrid/apps/application-bell-background-sounds/include/application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp => products/BellHybrid/apps/application-bell-background-sounds/include/application-bell-background-sounds/ApplicationBellBackgroundSounds.hpp +7 -1
@@ 14,16 14,22 @@ namespace gui::window::name
} // namespace gui::window::name
namespace app
{
    namespace bgSounds
    {
        class BGSoundsPlayer;
    }
    inline constexpr auto applicationBellBackgroundSoundsName = "ApplicationBellBackgroundSounds";

    class ApplicationBellBackgroundSounds : public Application
    {
        std::unique_ptr<bgSounds::BGSoundsPlayer> player;

      public:
        ApplicationBellBackgroundSounds(std::string name                    = applicationBellBackgroundSoundsName,
                                        std::string parent                  = "",
                                        StatusIndicators statusIndicators   = StatusIndicators{},
                                        StartInBackground startInBackground = {false});

        ~ApplicationBellBackgroundSounds();
        sys::ReturnCodes InitHandler() override;

        void createUserInterface() override;

A products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.cpp => products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.cpp +60 -0
@@ 0,0 1,60 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BGSoundsRepository.hpp"

#include <algorithm>
#include <log/log.hpp>
#include <time/ScopedTime.hpp>
#include <tags_fetcher/TagsFetcher.hpp>

#include <filesystem>

namespace app::bgSounds
{
    BGSoundsTagsFetcher::BGSoundsTagsFetcher(ApplicationCommon *application) : application(application)
    {}

    std::optional<tags::fetcher::Tags> BGSoundsTagsFetcher::getFileTags(const std::string &filePath) const
    {
        return tags::fetcher::fetchTags(filePath);
    }

    BGSoundsRepository::BGSoundsRepository(std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                           std::string musicFolderName)
        : tagsFetcher(std::move(tagsFetcher)), musicFolderName(std::move(musicFolderName))
    {}

    void BGSoundsRepository::scanMusicFilesList()
    {
        musicFiles.clear();

        LOG_INFO("Scanning music folder: %s", musicFolderName.c_str());
        {
            auto time = utils::time::Scoped("fetch tags time");
            for (const auto &entry : std::filesystem::directory_iterator(musicFolderName)) {
                if (!std::filesystem::is_directory(entry)) {
                    const auto &filePath = entry.path();
                    const auto fileTags  = tagsFetcher->getFileTags(filePath);
                    if (fileTags) {
                        musicFiles.push_back(*fileTags);
                    }
                    else {
                        LOG_ERROR("Scanned not an audio file, skipped");
                    }
                }
            }
            std::sort(
                musicFiles.begin(), musicFiles.end(), [](const tags::fetcher::Tags &t1, const tags::fetcher::Tags &t2) {
                    return t1.filePath < t2.filePath;
                });
        }
        LOG_INFO("Total number of music files found: %u", static_cast<unsigned int>(musicFiles.size()));
    }

    std::vector<tags::fetcher::Tags> BGSoundsRepository::getMusicFilesList() const
    {
        return musicFiles;
    }

} // namespace app::bgSounds

A products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.hpp => products/BellHybrid/apps/application-bell-background-sounds/models/BGSoundsRepository.hpp +63 -0
@@ 0,0 1,63 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/ApplicationCommon.hpp>
#include <tags_fetcher/TagsFetcher.hpp>
#include <purefs/filesystem_paths.hpp>

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include <cstddef>

namespace app::bgSounds
{
    class AbstractTagsFetcher
    {
      public:
        virtual ~AbstractTagsFetcher() noexcept = default;

        virtual std::optional<tags::fetcher::Tags> getFileTags(const std::string &filePath) const = 0;
    };

    class BGSoundsTagsFetcher : public AbstractTagsFetcher
    {
      public:
        explicit BGSoundsTagsFetcher(ApplicationCommon *application);

        std::optional<tags::fetcher::Tags> getFileTags(const std::string &filePath) const final;

      private:
        ApplicationCommon *application = nullptr;
    };

    class AbstractSoundsRepository
    {
      public:
        virtual ~AbstractSoundsRepository() noexcept = default;

        virtual void scanMusicFilesList()                                  = 0;
        virtual std::vector<tags::fetcher::Tags> getMusicFilesList() const = 0;
    };

    class BGSoundsRepository : public AbstractSoundsRepository
    {
        static constexpr auto musicSubfolderName = "music";

      public:
        explicit BGSoundsRepository(std::unique_ptr<AbstractTagsFetcher> tagsFetcher,
                                    std::string musicFolderName = purefs::dir::getUserDiskPath() / musicSubfolderName);

        void scanMusicFilesList() override;
        std::vector<tags::fetcher::Tags> getMusicFilesList() const override;

      private:
        std::unique_ptr<AbstractTagsFetcher> tagsFetcher;
        std::string musicFolderName;
        std::vector<tags::fetcher::Tags> musicFiles;
    };
} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.cpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.cpp +8 -3
@@ 2,13 2,18 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BGSoundsMainWindowPresenter.hpp"
#include "models/BGSoundsRepository.hpp"
#include <application-music-player/models/SongsRepository.hpp>

namespace app::bgSounds
{
    BGSoundsMainWindowPresenter::BGSoundsMainWindowPresenter()
    {}
    BGSoundsMainWindowPresenter::BGSoundsMainWindowPresenter(std::shared_ptr<AbstractSoundsRepository> soundsRepository)
        : soundsRepository{std::move(soundsRepository)}
    {
        this->soundsRepository->scanMusicFilesList();
    }
    void BGSoundsMainWindowPresenter::loadAudioRecords()
    {
        getView()->setSoundsList();
        getView()->setSoundsList(soundsRepository->getMusicFilesList());
    }
} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.hpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsMainWindowPresenter.hpp +5 -2
@@ 4,6 4,7 @@
#pragma once

#include <apps-common/BasePresenter.hpp>
#include <tags_fetcher/TagsFetcher.hpp>
#include <memory>
#include <string>
#include <vector>


@@ 18,7 19,7 @@ namespace app::bgSounds
          public:
            virtual ~View() = default;

            virtual void setSoundsList() = 0;
            virtual void setSoundsList(std::vector<tags::fetcher::Tags> soundsTags) = 0;
        };

        class Presenter : public BasePresenter<BGSoundsMainWindowContract::View>


@@ 28,12 29,14 @@ namespace app::bgSounds
        };
    };

    class AbstractSoundsRepository;
    class BGSoundsMainWindowPresenter : public BGSoundsMainWindowContract::Presenter
    {
        std::shared_ptr<AbstractSoundsRepository> soundsRepository;
        void loadAudioRecords() override;

      public:
        explicit BGSoundsMainWindowPresenter();
        explicit BGSoundsMainWindowPresenter(std::shared_ptr<AbstractSoundsRepository> soundsRepository);
    };

} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.cpp +38 -12
@@ 3,6 3,7 @@

#include "BGSoundsProgressPresenter.hpp"
#include "data/BGSoundsCommon.hpp"
#include "widgets/BGSoundsPlayer.hpp"
#include <ApplicationBellBackgroundSounds.hpp>
#include <apps-common/widgets/ProgressTimer.hpp>
#include <service-db/Settings.hpp>


@@ 13,8 14,8 @@

namespace app::bgSounds
{
    BGSoundsProgressPresenter::BGSoundsProgressPresenter(app::ApplicationCommon *app, settings::Settings *settings)
        : settings{settings}
    BGSoundsProgressPresenter::BGSoundsProgressPresenter(settings::Settings *settings, AbstractBGSoundsPlayer &player)
        : settings{settings}, player{player}
    {}

    void BGSoundsProgressPresenter::setTimer(std::unique_ptr<app::TimerWithCallbacks> &&_timer)


@@ 23,36 24,61 @@ namespace app::bgSounds
        timer->registerOnFinishedCallback([this]() { onFinished(); });
    }

    void BGSoundsProgressPresenter::activate()
    void BGSoundsProgressPresenter::activate(const tags::fetcher::Tags &tags)
    {
        Expects(timer != nullptr);
        AbstractBGSoundsPlayer::PlaybackMode mode;
        const auto value = settings->getValue(timerValueDBRecordName, settings::SettingsScope::AppLocal);
        runTimer         = utils::is_number(value);
        if (runTimer) {
        if (utils::is_number(value) && utils::getNumericValue<int>(value) != 0) {
            timer->reset(std::chrono::minutes{utils::getNumericValue<int>(value)});
            timer->start();
            mode = AbstractBGSoundsPlayer::PlaybackMode::Looped;
        }
        else {
            timer->reset(std::chrono::seconds{tags.total_duration_s});
            mode = AbstractBGSoundsPlayer::PlaybackMode::SingleShot;
        }
        auto onStartCallback = [this](audio::RetCode retCode) {
            if (retCode == audio::RetCode::Success) {
                timer->start();
            }
        };
        player.start(tags.filePath, mode, std::move(onStartCallback));
    }
    void BGSoundsProgressPresenter::stop()
    {
        timer->stop();
        onFinished();
        timer->stop();
    }
    void BGSoundsProgressPresenter::onFinished()
    {
        getView()->onFinished();
        auto onStopCallback = [this](audio::RetCode retCode) {
            if (retCode == audio::RetCode::Success) {
                getView()->onFinished();
            }
        };
        player.stop(std::move(onStopCallback));
    }
    void BGSoundsProgressPresenter::pause()
    {
        if (not timer->isStopped()) {
            timer->stop();
            getView()->onPaused();
            auto onPauseCallback = [this](audio::RetCode retCode) {
                if (retCode == audio::RetCode::Success) {
                    timer->stop();
                    getView()->onPaused();
                }
            };
            player.pause(std::move(onPauseCallback));
        }
    }
    void BGSoundsProgressPresenter::resume()
    {
        if (runTimer && timer->isStopped()) {
            timer->start();
        if (timer->isStopped()) {
            auto onResumeCallback = [this](audio::RetCode retCode) {
                if (retCode == audio::RetCode::Success) {
                    timer->start();
                }
            };
            player.resume(std::move(onResumeCallback));
        }
    }


M products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.hpp => products/BellHybrid/apps/application-bell-background-sounds/presenter/BGSoundsProgressPresenter.hpp +6 -5
@@ 5,6 5,7 @@

#include <apps-common/BasePresenter.hpp>
#include <apps-common/widgets/TimerWithCallbacks.hpp>
#include <tags_fetcher/TagsFetcher.hpp>
#include <memory>
namespace app
{


@@ 35,7 36,7 @@ namespace app::bgSounds
        class Presenter : public BasePresenter<BGSoundsProgressContract::View>
        {
          public:
            virtual void activate()                                                 = 0;
            virtual void activate(const tags::fetcher::Tags &tags)                  = 0;
            virtual void stop()                                                     = 0;
            virtual void pause()                                                    = 0;
            virtual void resume()                                                   = 0;


@@ 43,15 44,15 @@ namespace app::bgSounds
        };
    };

    class AlarmController;
    class AbstractBGSoundsPlayer;

    class BGSoundsProgressPresenter : public BGSoundsProgressContract::Presenter
    {
        settings::Settings *settings = nullptr;
        AbstractBGSoundsPlayer &player;
        std::unique_ptr<app::TimerWithCallbacks> timer;
        bool runTimer = false;

        void activate() override;
        void activate(const tags::fetcher::Tags &tags) override;
        void stop() override;
        void pause() override;
        void resume() override;


@@ 60,6 61,6 @@ namespace app::bgSounds
        void onFinished();

      public:
        BGSoundsProgressPresenter(app::ApplicationCommon *app, settings::Settings *settings);
        BGSoundsProgressPresenter(settings::Settings *settings, AbstractBGSoundsPlayer &player);
    };
} // namespace app::bgSounds

A products/BellHybrid/apps/application-bell-background-sounds/widgets/BGSoundsPlayer.cpp => products/BellHybrid/apps/application-bell-background-sounds/widgets/BGSoundsPlayer.cpp +101 -0
@@ 0,0 1,101 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BGSoundsPlayer.hpp"
#include <service-audio/AudioMessage.hpp>
#include <service-audio/AudioServiceName.hpp>

namespace app::bgSounds
{
    BGSoundsPlayer::BGSoundsPlayer(app::ApplicationCommon *app) : app::AsyncCallbackReceiver{app}, app{app}
    {}
    void BGSoundsPlayer::start(const std::string &filePath, PlaybackMode mode, OnStateChangeCallback callback)
    {
        auto msg  = std::make_unique<AudioStartPlaybackRequest>(filePath, audio::PlaybackType::Multimedia);
        auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::name::audio);
        auto cb   = [this, _callback = std::move(callback), filePath, mode](auto response) {
            auto result = dynamic_cast<AudioStartPlaybackResponse *>(response);
            if (result == nullptr) {
                return false;
            }
            token          = result->token;
            recentFilePath = std::move(filePath);
            playbackMode   = mode;
            if (_callback) {
                _callback(result->retCode);
            }
            return true;
        };
        task->execute(app, this, std::move(cb));
    }
    void BGSoundsPlayer::stop(OnStateChangeCallback callback)
    {
        if (token.IsValid()) {
            auto msg  = std::make_unique<AudioStopRequest>(token);
            auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::name::audio);
            auto cb   = [_callback = std::move(callback)](auto response) {
                auto result = dynamic_cast<AudioStopResponse *>(response);
                if (result == nullptr) {
                    return false;
                }
                if (_callback) {
                    _callback(result->retCode);
                }
                return true;
            };
            task->execute(app, this, std::move(cb));
        }
    }
    void BGSoundsPlayer::pause(OnStateChangeCallback callback)
    {
        if (token.IsValid()) {
            auto msg  = std::make_unique<AudioPauseRequest>(token);
            auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::name::audio);
            auto cb   = [_callback = std::move(callback)](auto response) {
                auto result = dynamic_cast<AudioPauseResponse *>(response);
                if (result == nullptr) {
                    return false;
                }
                if (_callback) {
                    _callback(result->retCode);
                }
                return true;
            };
            task->execute(app, this, std::move(cb));
        }
    }
    void BGSoundsPlayer::resume(OnStateChangeCallback callback)
    {
        auto msg  = std::make_unique<AudioResumeRequest>(token);
        auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::name::audio);
        auto cb   = [_callback = std::move(callback)](auto response) {
            auto result = dynamic_cast<AudioResumeResponse *>(response);
            if (result == nullptr) {
                return false;
            }
            if (_callback) {
                _callback(result->retCode);
            }
            return true;
        };
        task->execute(app, this, std::move(cb));
    }

    auto BGSoundsPlayer::handle(AudioEOFNotification *msg) -> std::shared_ptr<sys::Message>
    {
        if (token == msg->token && playbackMode == PlaybackMode::Looped) {
            auto msg  = std::make_unique<AudioStartPlaybackRequest>(recentFilePath, audio::PlaybackType::Multimedia);
            auto task = app::AsyncRequest::createFromMessage(std::move(msg), service::name::audio);
            auto cb   = [this](auto response) {
                auto result = dynamic_cast<AudioStartPlaybackResponse *>(response);
                if (result == nullptr) {
                    return false;
                }
                token = result->token;
                return true;
            };
            task->execute(app, this, std::move(cb));
        }
        return sys::msgHandled();
    }
} // namespace app::bgSounds

A products/BellHybrid/apps/application-bell-background-sounds/widgets/BGSoundsPlayer.hpp => products/BellHybrid/apps/application-bell-background-sounds/widgets/BGSoundsPlayer.hpp +50 -0
@@ 0,0 1,50 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <module-audio/Audio/AudioCommon.hpp>
#include <AsyncTask.hpp>

namespace app
{
    class ApplicationCommon;
}
class AudioEOFNotification;
namespace app::bgSounds
{
    class AbstractBGSoundsPlayer
    {
      public:
        enum class PlaybackMode
        {
            Looped,
            SingleShot
        };

        virtual ~AbstractBGSoundsPlayer() = default;

        using OnStateChangeCallback = std::function<void(audio::RetCode retCode)>;
        virtual void start(const std::string &filePath, PlaybackMode mode, OnStateChangeCallback callback) = 0;
        virtual void stop(OnStateChangeCallback callback)                                                  = 0;
        virtual void pause(OnStateChangeCallback callback)                                                 = 0;
        virtual void resume(OnStateChangeCallback callback)                                                = 0;
    };

    class BGSoundsPlayer : public AbstractBGSoundsPlayer, public app::AsyncCallbackReceiver
    {
        app::ApplicationCommon *app{};
        audio::Token token;
        std::string recentFilePath;
        PlaybackMode playbackMode = PlaybackMode::SingleShot;

        void start(const std::string &filePath, PlaybackMode mode, OnStateChangeCallback callback) override;
        void stop(OnStateChangeCallback callback) override;
        void pause(OnStateChangeCallback callback) override;
        void resume(OnStateChangeCallback callback) override;

      public:
        explicit BGSoundsPlayer(app::ApplicationCommon *app);

        auto handle(AudioEOFNotification *msg) -> std::shared_ptr<sys::Message>;
    };
} // namespace app::bgSounds

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.cpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.cpp +9 -13
@@ 9,11 9,6 @@
#include <common/options/OptionBellMenu.hpp>
#include <i18n/i18n.hpp>

namespace
{
    inline constexpr auto songNamePlaceholder = "Meditative Surprise";
    inline constexpr auto placeHoldersCount   = 6;
} // namespace
namespace gui
{
    BGSoundsMainWindow::BGSoundsMainWindow(


@@ 24,21 19,22 @@ namespace gui
        buildInterface();
        setListTitle(utils::translate("app_bellmain_background_sounds"));
    }
    void BGSoundsMainWindow::setSoundsList()
    void BGSoundsMainWindow::setSoundsList(std::vector<tags::fetcher::Tags> soundsTags)
    {
        std::list<gui::Option> menuOptionList;
        auto addRecord = [&](const UTF8 &name) {
        auto addRecord = [&](const tags::fetcher::Tags &soundTags) {
            menuOptionList.emplace_back(std::make_unique<gui::option::OptionBellMenu>(
                name,
                soundTags.title,
                [=](gui::Item &item) {
                    onActivated(name);
                    onActivated(soundTags);
                    return true;
                },
                [=](gui::Item &item) { return true; },
                this));
        };
        for (int i = 0; i < placeHoldersCount; i++)
            addRecord(songNamePlaceholder);
        for (const auto &tag : soundsTags) {
            addRecord(tag);
        }

        addOptions(std::move(menuOptionList));
    }


@@ 48,9 44,9 @@ namespace gui
        presenter->loadAudioRecords();
    }

    void BGSoundsMainWindow::onActivated(std::string recordName)
    void BGSoundsMainWindow::onActivated(const tags::fetcher::Tags &selectedSoundTags)
    {
        auto audioContext = std::make_unique<BGSoundsAudioContext>(std::move(recordName));
        auto audioContext = std::make_unique<BGSoundsAudioContext>(selectedSoundTags);
        auto switchData   = std::make_unique<BGSoundsSwitchData>(std::move(audioContext));
        application->switchWindow(gui::window::name::bgSoundsTimerSelect, std::move(switchData));
    }

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.hpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsMainWindow.hpp +2 -2
@@ 11,10 11,10 @@ namespace gui
    {
        std::unique_ptr<app::bgSounds::BGSoundsMainWindowContract::Presenter> presenter;

        void setSoundsList();
        void setSoundsList(std::vector<tags::fetcher::Tags> soundsTags);
        void buildInterface() override;

        void onActivated(std::string recordName);
        void onActivated(const tags::fetcher::Tags &selectedSoundTags);

      public:
        BGSoundsMainWindow(app::ApplicationCommon *app,

M products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp => products/BellHybrid/apps/application-bell-background-sounds/windows/BGSoundsProgressWindow.cpp +2 -3
@@ 71,10 71,9 @@ namespace gui
        if (data && typeid(*data) == typeid(BGSoundsSwitchData)) {
            auto *audioSwitchData = static_cast<BGSoundsSwitchData *>(data);
            audioContext          = audioSwitchData->getAudioContext();
            title->setText(audioContext->getTitle());
            title->setText(audioContext->getTags().title);
            presenter->activate(audioContext->getTags());
        }

        presenter->activate();
    }

    void BGSoundsProgressWindow::buildInterface()