~aleteoryx/muditaos

6b36d35507f2d2122ab3589e34fd14bb0ec128be — Hubert Chrzaniuk 5 years ago 17f64cb
[EGD-5009] Change messy callback logic in audio module

Refactor audio module to use only one callback for communication
with audio service. This also simplifies the logic and removes
necessity to define defaults for audio values in multiple places.
M module-audio/Audio/Audio.cpp => module-audio/Audio/Audio.cpp +4 -5
@@ 10,11 10,10 @@
namespace audio
{

    Audio::Audio(AsyncCallback asyncCallback, DbCallback dbCallback)
        : currentOperation(), asyncCallback(asyncCallback), dbCallback(dbCallback)
    Audio::Audio(AudioServiceMessage::Callback callback) : currentOperation(), serviceCallback(callback)
    {

        auto ret = Operation::Create(Operation::Type::Idle, "", audio::PlaybackType::None, dbCallback);
        auto ret = Operation::Create(Operation::Type::Idle, "", audio::PlaybackType::None, callback);
        if (ret) {
            currentOperation = std::move(ret);
        }


@@ 76,7 75,7 @@ namespace audio
    {

        try {
            auto ret = Operation::Create(op, fileName, playbackType, dbCallback);
            auto ret = Operation::Create(op, fileName, playbackType, serviceCallback);
            switch (op) {
            case Operation::Type::Playback:
                currentState = State::Playback;


@@ 108,7 107,7 @@ namespace audio
            return audioException.getErrorCode();
        }

        return currentOperation->Start(asyncCallback, token);
        return currentOperation->Start(token);
    }

    audio::RetCode Audio::Start()

M module-audio/Audio/Audio.hpp => module-audio/Audio/Audio.hpp +2 -3
@@ 29,7 29,7 @@ namespace audio
            Routing,
        };

        Audio(AsyncCallback asyncCallback, DbCallback dbCallback);
        Audio(AudioServiceMessage::Callback callback);

        virtual ~Audio() = default;



@@ 116,8 116,7 @@ namespace audio
        State currentState = State::Idle;
        std::unique_ptr<Operation> currentOperation;

        AsyncCallback asyncCallback;
        DbCallback dbCallback;
        AudioServiceMessage::Callback serviceCallback;

        // for efficiency multiple of 24 and 32 (max audio samples size)
        static constexpr auto defaultAudioStreamBlockSize = 2048;

M module-audio/Audio/AudioCommon.cpp => module-audio/Audio/AudioCommon.cpp +1 -1
@@ 54,7 54,7 @@ namespace audio
        return path;
    }

    auto GetVolumeText(const audio::Volume &volume) -> const std::string
    auto GetVolumeText(const audio::Volume &volume) -> std::string
    {
        return (static_cast<std::ostringstream &&>(std::ostringstream() << "Vol: " << std::to_string(volume))).str();
    }

M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +43 -24
@@ 7,6 7,7 @@
#include <bitset>
#include <bsp/audio/bsp_audio.hpp>
#include <Utils.hpp>
#include <utility>

#include "Profiles/Profile.hpp"



@@ 241,43 242,61 @@ namespace audio
        friend class ::audio::AudioMux;
    };

    class Handle
    RetCode GetDeviceError(bsp::AudioDevice::RetCode retCode);
    const std::string str(RetCode retcode);
    [[nodiscard]] auto GetVolumeText(const audio::Volume &volume) -> std::string;
} // namespace audio

namespace AudioServiceMessage
{

    class Message
    {
      public:
        Handle(const RetCode &retCode = RetCode::Failed, const Token &token = Token())
            : lastRetCode(retCode), token(token)
        virtual ~Message() = default;
    };

    class EndOfFile : public Message
    {
      public:
        explicit EndOfFile(audio::Token &token) : token(token)
        {}
        auto GetLastRetCode() -> RetCode
        {
            return lastRetCode;
        }
        auto GetToken() const -> const Token &
        const audio::Token &GetToken() const
        {
            return token;
        }

      private:
        RetCode lastRetCode;
        Token token;
        audio::Token token = audio::Token::MakeBadToken();
    };

    enum class PlaybackEventType
    class FileSystemNoSpace : public Message
    {
        Empty,
        EndOfFile,
        FileSystemNoSpace
      public:
        explicit FileSystemNoSpace(audio::Token &token) : token(token)
        {}
        const audio::Token &GetToken() const
        {
            return token;
        }

      private:
        audio::Token token = audio::Token::MakeBadToken();
    };

    struct PlaybackEvent
    class DbRequest : public Message
    {
        PlaybackEventType event = PlaybackEventType::Empty;
        audio::Token token      = audio::Token::MakeBadToken();
    };
      public:
        explicit DbRequest(std::string path) : path(std::move(path))
        {}
        const std::string &GetPath() const
        {
            return path;
        }

    typedef std::function<int32_t(PlaybackEvent e)> AsyncCallback;
    typedef std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> DbCallback;
      private:
        const std::string path;
    };

    RetCode GetDeviceError(bsp::AudioDevice::RetCode retCode);
    const std::string str(RetCode retcode);
    [[nodiscard]] auto GetVolumeText(const audio::Volume &volume) -> const std::string;
} // namespace audio
    using Callback = std::function<std::optional<std::string>(const Message *e)>;
} // namespace AudioServiceMessage

M module-audio/Audio/AudioMux.cpp => module-audio/Audio/AudioMux.cpp +2 -3
@@ 18,14 18,13 @@ namespace audio
        };
    } // namespace

    AudioMux::AudioMux(audio::AsyncCallback asyncClbk, audio::DbCallback dbClbk, size_t audioInputsCount)
    AudioMux::AudioMux(AudioServiceMessage::Callback callback, size_t audioInputsCount)
        : audioInputs(audioInputsInternal)
    {
        audioInputsCount = audioInputsCount > 0 ? audioInputsCount : 1;
        audioInputsInternal.reserve(audioInputsCount);
        for (size_t i = 0; i < audioInputsCount; i++) {
            audioInputsInternal.emplace_back(
                Input(std::make_unique<Audio>(asyncClbk, dbClbk), refToken.IncrementToken()));
            audioInputsInternal.emplace_back(Input(std::make_unique<Audio>(callback), refToken.IncrementToken()));
        }
    }


M module-audio/Audio/AudioMux.hpp => module-audio/Audio/AudioMux.hpp +2 -3
@@ 44,11 44,10 @@ namespace audio
        };
        /**
         * Constructs class with fixed number of managed inputs
         * @param asyncClbk Callback for async control events from of managed audio::Audio() instances
         * @param dbClbk Callback for async DB change events from of managed audio::Audio() instances
         * @param callback Callback for async requests to audio service from audio module
         * @param audioInputsCount Number of inputs managed and internal audio::Audio() classes created
         */
        AudioMux(audio::AsyncCallback asyncClbk, audio::DbCallback dbClbk, size_t audioInputsCount = 1);
        AudioMux(AudioServiceMessage::Callback callback, size_t audioInputsCount = 1);
        /**
         * Constructs mux managing externally allocated instances of Input
         * @param extAudioInputs Instances of Input to be managed

M module-audio/Audio/Operation/IdleOperation.cpp => module-audio/Audio/Operation/IdleOperation.cpp +1 -1
@@ 8,7 8,7 @@
namespace audio
{

    IdleOperation::IdleOperation([[maybe_unused]] const char *file)
    IdleOperation::IdleOperation([[maybe_unused]] const char *file) : Operation(nullptr)
    {
        supportedProfiles.emplace_back(Profile::Create(Profile::Type::Idle, nullptr), true);
        currentProfile = supportedProfiles[0].profile;

M module-audio/Audio/Operation/IdleOperation.hpp => module-audio/Audio/Operation/IdleOperation.hpp +2 -2
@@ 15,11 15,11 @@ namespace audio
    class IdleOperation : public Operation
    {
      public:
        IdleOperation(const char *file);
        explicit IdleOperation([[maybe_unused]] const char *file);

        ~IdleOperation() = default;

        audio::RetCode Start([[maybe_unused]] audio::AsyncCallback callback, audio::Token token) final
        audio::RetCode Start(audio::Token token) final
        {
            return audio::RetCode::Success;
        }

M module-audio/Audio/Operation/Operation.cpp => module-audio/Audio/Operation/Operation.cpp +25 -8
@@ 17,11 17,10 @@

namespace audio
{
    std::unique_ptr<Operation> Operation::Create(
        Operation::Type t,
        const char *fileName,
        const audio::PlaybackType &playbackType,
        std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback)
    std::unique_ptr<Operation> Operation::Create(Operation::Type t,
                                                 const char *fileName,
                                                 const audio::PlaybackType &playbackType,
                                                 AudioServiceMessage::Callback callback)
    {
        std::unique_ptr<Operation> inst;



@@ 30,13 29,13 @@ namespace audio
            inst = std::make_unique<IdleOperation>(fileName);
            break;
        case Type::Playback:
            inst = std::make_unique<PlaybackOperation>(fileName, playbackType, dbCallback);
            inst = std::make_unique<PlaybackOperation>(fileName, playbackType, callback);
            break;
        case Type::Router:
            inst = std::make_unique<RouterOperation>(fileName, dbCallback);
            inst = std::make_unique<RouterOperation>(fileName, callback);
            break;
        case Type::Recorder:
            inst = std::make_unique<RecorderOperation>(fileName, dbCallback);
            inst = std::make_unique<RecorderOperation>(fileName, callback);
            break;
        }



@@ 87,4 86,22 @@ namespace audio
            }
        }
    }
    void Operation::AddProfile(const Profile::Type &profile, const PlaybackType &playback, bool isAvailable)
    {
        const auto reqVol  = AudioServiceMessage::DbRequest(audio::dbPath(Setting::Volume, playback, profile));
        const auto reqGain = AudioServiceMessage::DbRequest(audio::dbPath(Setting::Gain, playback, profile));

        std::optional<audio::Volume> volume;
        std::optional<audio::Gain> gain;

        if (auto val = serviceCallback(&reqVol); val) {
            volume = utils::getNumericValue<audio::Volume>(val.value());
        }

        if (auto val = serviceCallback(&reqGain); val) {
            gain = utils::getNumericValue<audio::Gain>(val.value());
        }

        supportedProfiles.emplace_back(Profile::Create(profile, nullptr, volume, gain), isAvailable);
    }
} // namespace audio

M module-audio/Audio/Operation/Operation.hpp => module-audio/Audio/Operation/Operation.hpp +9 -8
@@ 19,7 19,8 @@ namespace audio
    class Operation
    {
      public:
        Operation(const audio::PlaybackType &playbackType = audio::PlaybackType::None) : playbackType{playbackType}
        Operation(AudioServiceMessage::Callback callback, const PlaybackType &playbackType = PlaybackType::None)
            : playbackType(playbackType), serviceCallback(callback)
        {}

        enum class State


@@ 54,13 55,12 @@ namespace audio

        virtual ~Operation() = default;

        static std::unique_ptr<Operation> Create(
            Type t,
            const char *fileName                  = "",
            const audio::PlaybackType &operations = audio::PlaybackType::None,
            std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback = nullptr);
        static std::unique_ptr<Operation> Create(Type t,
                                                 const char *fileName                   = "",
                                                 const audio::PlaybackType &operations  = audio::PlaybackType::None,
                                                 AudioServiceMessage::Callback callback = nullptr);

        virtual audio::RetCode Start(audio::AsyncCallback callback, audio::Token token) = 0;
        virtual audio::RetCode Start(audio::Token token)                                = 0;
        virtual audio::RetCode Stop()                                                   = 0;
        virtual audio::RetCode Pause()                                                  = 0;
        virtual audio::RetCode Resume()                                                 = 0;


@@ 138,9 138,9 @@ namespace audio

        std::vector<SupportedProfile> supportedProfiles;
        void SetProfileAvailability(std::vector<Profile::Type> profiles, bool available);
        void AddProfile(const Profile::Type &profile, const PlaybackType &playback, bool isAvailable);

        State state = State::Idle;
        audio::AsyncCallback eventCallback;

        audio::Token operationToken;
        Type opType = Type::Idle;


@@ 152,6 152,7 @@ namespace audio

        std::shared_ptr<Profile> GetProfile(const Profile::Type type);

        AudioServiceMessage::Callback serviceCallback;
        std::function<int32_t(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer)>
            audioCallback = nullptr;
    };

M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +15 -29
@@ 13,33 13,18 @@
namespace audio
{

    using namespace AudioServiceMessage;
    using namespace utils;

#define PERF_STATS_ON 0

    PlaybackOperation::PlaybackOperation(
        const char *file,
        const audio::PlaybackType &playbackType,
        std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback)
        : Operation(playbackType), dec(nullptr)
    PlaybackOperation::PlaybackOperation(const char *file, const audio::PlaybackType &playbackType, Callback callback)
        : Operation(callback, playbackType), dec(nullptr)
    {

        constexpr audio::Volume defaultLoudspeakerVolume = 10;
        constexpr audio::Volume defaultHeadphonesVolume  = 2;

        const auto dbLoudspeakerVolumePath =
            audio::dbPath(audio::Setting::Volume, playbackType, audio::Profile::Type::PlaybackLoudspeaker);
        const auto loudspeakerVolume = dbCallback(dbLoudspeakerVolumePath, defaultLoudspeakerVolume);

        const auto dbHeadphonesVolumePath =
            audio::dbPath(audio::Setting::Volume, playbackType, audio::Profile::Type::PlaybackHeadphones);
        const auto headphonesVolume = dbCallback(dbHeadphonesVolumePath, defaultHeadphonesVolume);

        // order in vector defines priority
        supportedProfiles.emplace_back(
            Profile::Create(Profile::Type::PlaybackBluetoothA2DP, nullptr, loudspeakerVolume), false);
        supportedProfiles.emplace_back(Profile::Create(Profile::Type::PlaybackHeadphones, nullptr, headphonesVolume),
                                       false);
        supportedProfiles.emplace_back(Profile::Create(Profile::Type::PlaybackLoudspeaker, nullptr, loudspeakerVolume),
                                       true);
        // order defines priority
        AddProfile(Profile::Type::PlaybackBluetoothA2DP, playbackType, false);
        AddProfile(Profile::Type::PlaybackHeadphones, playbackType, false);
        AddProfile(Profile::Type::PlaybackLoudspeaker, playbackType, true);

        auto defaultProfile = GetProfile(Profile::Type::PlaybackLoudspeaker);
        if (!defaultProfile) {


@@ 58,12 43,14 @@ namespace audio
        }

        endOfFileCallback = [this]() {
            state = State::Idle;
            eventCallback({PlaybackEventType::EndOfFile, operationToken});
            state          = State::Idle;
            const auto req = AudioServiceMessage::EndOfFile(operationToken);
            serviceCallback(&req);
            return std::string();
        };
    }

    audio::RetCode PlaybackOperation::Start(audio::AsyncCallback callback, audio::Token token)
    audio::RetCode PlaybackOperation::Start(audio::Token token)
    {
        if (state == State::Active || state == State::Paused) {
            return RetCode::InvokedInIncorrectState;


@@ 77,7 64,6 @@ namespace audio
            tags = dec->fetchTags();
        }

        eventCallback = callback;
        state         = State::Active;

        if (tags->num_channel == channel::stereoSound) {


@@ 200,7 186,7 @@ namespace audio

        case State::Active:
            state = State::Idle;
            Start(eventCallback, operationToken);
            Start(operationToken);
            break;
        }


M module-audio/Audio/Operation/PlaybackOperation.hpp => module-audio/Audio/Operation/PlaybackOperation.hpp +4 -5
@@ 23,14 23,13 @@ namespace audio
    class PlaybackOperation : public Operation
    {
      public:
        PlaybackOperation(
            const char *file,
            const audio::PlaybackType &playbackType,
            std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback = nullptr);
        PlaybackOperation(const char *file,
                          const audio::PlaybackType &playbackType,
                          AudioServiceMessage::Callback callback = nullptr);

        virtual ~PlaybackOperation();

        audio::RetCode Start(audio::AsyncCallback callback, audio::Token token) final;
        audio::RetCode Start(audio::Token token) final;
        audio::RetCode Stop() final;
        audio::RetCode Pause() final;
        audio::RetCode Resume() final;

M module-audio/Audio/Operation/RecorderOperation.cpp => module-audio/Audio/Operation/RecorderOperation.cpp +11 -24
@@ 15,13 15,14 @@

namespace audio
{
    using namespace AudioServiceMessage;
    using namespace utils;

#define PERF_STATS_ON 0

    using namespace bsp;

    RecorderOperation::RecorderOperation(
        const char *file, std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback)
    RecorderOperation::RecorderOperation(const char *file, AudioServiceMessage::Callback callback) : Operation(callback)
    {

        audioCallback = [this](const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer) -> int32_t {


@@ 37,29 38,16 @@ namespace audio
#endif
            if (ret == 0) {
                state = State::Idle;
                eventCallback({PlaybackEventType::FileSystemNoSpace, audio::Token::MakeBadToken()});
                const auto req = AudioServiceMessage::FileSystemNoSpace(operationToken);
                serviceCallback(&req);
            }
            return ret;
        };

        constexpr audio::Gain defaultRecordingOnBoardMicGain = 200;
        constexpr audio::Gain defaultRecordingHeadsetGain    = 100;

        const auto dbRecordingOnBoardMicGainPath =
            audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RecordingBuiltInMic);
        const auto recordingOnBoardMicGain = dbCallback(dbRecordingOnBoardMicGainPath, defaultRecordingOnBoardMicGain);

        const auto dbRecordingHeadsetGainPath =
            audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RecordingHeadphones);
        const auto recordingHeadsetGain = dbCallback(dbRecordingHeadsetGainPath, defaultRecordingHeadsetGain);

        // order in vector defines priority
        supportedProfiles.emplace_back(
            Profile::Create(Profile::Type::RecordingBluetoothHSP, nullptr, 0, recordingHeadsetGain), false);
        supportedProfiles.emplace_back(
            Profile::Create(Profile::Type::RecordingHeadphones, nullptr, 0, recordingHeadsetGain), false);
        supportedProfiles.emplace_back(
            Profile::Create(Profile::Type::RecordingBuiltInMic, nullptr, 0, recordingOnBoardMicGain), true);
        // order defines priority
        AddProfile(Profile::Type::RecordingBluetoothHSP, PlaybackType::None, false);
        AddProfile(Profile::Type::RecordingHeadphones, PlaybackType::None, false);
        AddProfile(Profile::Type::RecordingBuiltInMic, PlaybackType::None, true);

        auto defaultProfile = GetProfile(Profile::Type::PlaybackLoudspeaker);
        if (!defaultProfile) {


@@ 87,14 75,13 @@ namespace audio
        }
    }

    audio::RetCode RecorderOperation::Start(audio::AsyncCallback callback, audio::Token token)
    audio::RetCode RecorderOperation::Start(audio::Token token)
    {

        if (state == State::Paused || state == State::Active) {
            return RetCode::InvokedInIncorrectState;
        }
        operationToken = token;
        eventCallback = callback;
        state         = State::Active;

        if (audioDevice->IsFormatSupported(currentProfile->GetAudioFormat())) {


@@ 183,7 170,7 @@ namespace audio

        case State::Active:
            state = State::Idle;
            Start(eventCallback, operationToken);
            Start(operationToken);
            break;
        }


M module-audio/Audio/Operation/RecorderOperation.hpp => module-audio/Audio/Operation/RecorderOperation.hpp +2 -3
@@ 13,10 13,9 @@ namespace audio
    class RecorderOperation : public Operation
    {
      public:
        RecorderOperation(const char *file,
                          std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback);
        RecorderOperation(const char *file, AudioServiceMessage::Callback callback);

        audio::RetCode Start(audio::AsyncCallback callback, audio::Token token) final;
        audio::RetCode Start(audio::Token token) final;
        audio::RetCode Stop() final;
        audio::RetCode Pause() final;
        audio::RetCode Resume() final;

M module-audio/Audio/Operation/RouterOperation.cpp => module-audio/Audio/Operation/RouterOperation.cpp +9 -48
@@ 17,52 17,14 @@
namespace audio
{

    RouterOperation::RouterOperation(
        [[maybe_unused]] const char *file,
        std::function<std::uint32_t(const std::string &path, const std::uint32_t &defaultValue)> dbCallback)
    RouterOperation::RouterOperation([[maybe_unused]] const char *file, AudioServiceMessage::Callback callback)
        : Operation(callback)
    {
        constexpr audio::Gain defaultRoutingEarspeakerGain       = 20;
        constexpr audio::Volume defaultRoutingEarspeakerVolume   = 10;
        constexpr audio::Gain defaultRoutingSpeakerphoneGain     = 20;
        constexpr audio::Volume defaultRoutingSpeakerphoneVolume = 10;
        constexpr audio::Gain defaultRoutingHeadphonesGain       = 0;
        constexpr audio::Volume defaultRoutingHeadphonesVolume   = 10;

        const auto dbRoutingEarspeakerGainPath =
            audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RoutingEarspeaker);
        const auto routingEarspeakerGain = dbCallback(dbRoutingEarspeakerGainPath, defaultRoutingEarspeakerGain);
        const auto dbRoutingEarspeakerVolumePath =
            audio::dbPath(audio::Setting::Volume, audio::PlaybackType::None, audio::Profile::Type::RoutingEarspeaker);
        const auto routingEarspeakerVolume = dbCallback(dbRoutingEarspeakerVolumePath, defaultRoutingEarspeakerVolume);
        const auto dbRoutingLoudspeakerGainPath =
            audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RoutingLoudspeaker);
        const auto routingLoudspeakerGain = dbCallback(dbRoutingLoudspeakerGainPath, defaultRoutingSpeakerphoneGain);
        const auto dbRoutingLoudspeakerVolumePath =
            audio::dbPath(audio::Setting::Volume, audio::PlaybackType::None, audio::Profile::Type::RoutingLoudspeaker);
        const auto routingLoudspeakerVolume =
            dbCallback(dbRoutingLoudspeakerVolumePath, defaultRoutingSpeakerphoneVolume);
        const auto dbRoutingHeadsetGainPath =
            audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RoutingHeadphones);
        const auto routingHeadphonesGain = dbCallback(dbRoutingHeadsetGainPath, defaultRoutingHeadphonesGain);
        const auto dbRoutingHeadphonesVolumePath =
            audio::dbPath(audio::Setting::Volume, audio::PlaybackType::None, audio::Profile::Type::RoutingHeadphones);
        const auto routingHeadphonesVolume = dbCallback(dbRoutingHeadphonesVolumePath, defaultRoutingHeadphonesVolume);

        // order in vector defines priority
        supportedProfiles.emplace_back(
            Profile::Create(
                Profile::Type::RoutingBluetoothHSP, nullptr, routingHeadphonesVolume, routingHeadphonesGain),
            false);
        supportedProfiles.emplace_back(
            Profile::Create(Profile::Type::RoutingHeadphones, nullptr, routingHeadphonesVolume, routingHeadphonesGain),
            false);
        supportedProfiles.emplace_back(
            Profile::Create(Profile::Type::RoutingEarspeaker, nullptr, routingEarspeakerVolume, routingEarspeakerGain),
            true);
        supportedProfiles.emplace_back(
            Profile::Create(
                Profile::Type::RoutingLoudspeaker, nullptr, routingLoudspeakerVolume, routingLoudspeakerGain),
            true);
        // order defines priority
        AddProfile(Profile::Type::RoutingBluetoothHSP, PlaybackType::None, false);
        AddProfile(Profile::Type::RoutingHeadphones, PlaybackType::None, false);
        AddProfile(Profile::Type::RoutingEarspeaker, PlaybackType::None, true);
        AddProfile(Profile::Type::RoutingLoudspeaker, PlaybackType::None, true);

        auto defaultProfile = GetProfile(Profile::Type::RoutingEarspeaker);
        if (!defaultProfile) {


@@ 90,13 52,12 @@ namespace audio
        return GetDeviceError(ret);
    }

    audio::RetCode RouterOperation::Start(audio::AsyncCallback callback, audio::Token token)
    audio::RetCode RouterOperation::Start(audio::Token token)
    {
        if (state == State::Paused || state == State::Active) {
            return RetCode::InvokedInIncorrectState;
        }
        operationToken = token;
        eventCallback  = callback;
        state          = State::Active;

        if (audioDevice->IsFormatSupported(currentProfile->GetAudioFormat())) {


@@ 219,7 180,7 @@ namespace audio

        case State::Active:
            state = State::Idle;
            Start(eventCallback, operationToken);
            Start(operationToken);
            break;
        }


M module-audio/Audio/Operation/RouterOperation.hpp => module-audio/Audio/Operation/RouterOperation.hpp +2 -4
@@ 27,12 27,10 @@ namespace audio
        static const std::size_t INPUT_BUFFER_START_SIZE = 1024;

      public:
        RouterOperation(
            const char *file,
            std::function<std::uint32_t(const std::string &path, const std::uint32_t &defaultValue)> dbCallback);
        RouterOperation(const char *file, AudioServiceMessage::Callback callback);
        ~RouterOperation() = default;

        audio::RetCode Start([[maybe_unused]] audio::AsyncCallback callback, audio::Token token) final;
        audio::RetCode Start(audio::Token token) final;
        audio::RetCode Stop() final;
        audio::RetCode Pause() final;
        audio::RetCode Resume() final;

M module-audio/Audio/Profiles/Profile.cpp => module-audio/Audio/Profiles/Profile.cpp +24 -11
@@ 24,40 24,53 @@
namespace audio
{

    std::unique_ptr<Profile> Profile::Create(const Type t, std::function<int32_t()> callback, Volume vol, Gain gain)
    std::unique_ptr<Profile> Profile::Create(const Type t,
                                             std::function<int32_t()> callback,
                                             std::optional<Volume> vol,
                                             std::optional<Gain> gain)
    {
        std::unique_ptr<Profile> inst;

        switch (t) {
        case Type::PlaybackHeadphones:
            inst = std::make_unique<ProfilePlaybackHeadphones>(callback, vol);
            assert(vol);
            inst = std::make_unique<ProfilePlaybackHeadphones>(callback, vol.value());
            break;
        case Type::PlaybackLoudspeaker:
            inst = std::make_unique<ProfilePlaybackLoudspeaker>(callback, vol);
            assert(vol);
            inst = std::make_unique<ProfilePlaybackLoudspeaker>(callback, vol.value());
            break;
        case Type::PlaybackBluetoothA2DP:
            inst = std::make_unique<ProfilePlaybackBluetoothA2DP>(callback, vol);
            assert(vol);
            inst = std::make_unique<ProfilePlaybackBluetoothA2DP>(callback, vol.value());
            break;
        case Type::RecordingBuiltInMic:
            inst = std::make_unique<ProfileRecordingOnBoardMic>(callback, gain);
            assert(gain);
            inst = std::make_unique<ProfileRecordingOnBoardMic>(callback, gain.value());
            break;
        case Type::RecordingHeadphones:
            inst = std::make_unique<ProfileRecordingHeadphones>(callback, gain);
            assert(gain);
            inst = std::make_unique<ProfileRecordingHeadphones>(callback, gain.value());
            break;
        case Type::RecordingBluetoothHSP:
            inst = std::make_unique<ProfileRecordingBluetoothHSP>(callback, gain);
            assert(gain);
            inst = std::make_unique<ProfileRecordingBluetoothHSP>(callback, gain.value());
            break;
        case Type::RoutingHeadphones:
            inst = std::make_unique<ProfileRoutingHeadphones>(callback, vol, gain);
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingHeadphones>(callback, vol.value(), gain.value());
            break;
        case Type::RoutingLoudspeaker:
            inst = std::make_unique<ProfileRoutingLoudspeaker>(callback, vol, gain);
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingLoudspeaker>(callback, vol.value(), gain.value());
            break;
        case Type::RoutingEarspeaker:
            inst = std::make_unique<ProfileRoutingEarspeaker>(callback, vol, gain);
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingEarspeaker>(callback, vol.value(), gain.value());
            break;
        case Type::RoutingBluetoothHSP:
            inst = std::make_unique<ProfileRoutingBluetoothHSP>(callback, vol, gain);
            assert(gain && vol);
            inst = std::make_unique<ProfileRoutingBluetoothHSP>(callback, vol.value(), gain.value());
            break;
        case Type::Idle:
            inst = std::make_unique<ProfileIdle>();

M module-audio/Audio/Profiles/Profile.hpp => module-audio/Audio/Profiles/Profile.hpp +2 -2
@@ 44,8 44,8 @@ namespace audio

        static std::unique_ptr<Profile> Create(const Type t,
                                               std::function<int32_t()> callback = nullptr,
                                               Volume vol                        = 0,
                                               Gain gain                         = 0);
                                               std::optional<Volume> vol         = 0,
                                               std::optional<Gain> gain          = 0);

        void SetOutputVolume(Volume vol);


M module-audio/Audio/test/unittest_audio.cpp => module-audio/Audio/test/unittest_audio.cpp +1 -2
@@ 74,8 74,7 @@ class MockAudio : public audio::Audio

  public:
    MockAudio(audio::Audio::State state, audio::PlaybackType plbckType, audio::Operation::State opState)
        : audio::Audio([](audio::PlaybackEvent e) { return 0; },
                       [](const std::string &path, const uint32_t &defaultValue) { return 0; }),
        : audio::Audio([](const AudioServiceMessage::Message *e) { return std::optional<std::string>(); }),
          state(state), plbckType(plbckType), opState(opState)
    {}


M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +24 -27
@@ 19,8 19,7 @@ using namespace audio;

ServiceAudio::ServiceAudio()
    : sys::Service(serviceName, "", 4096 * 2, sys::ServicePriority::Idle),
      audioMux([this](auto... params) { return this->AsyncCallback(params...); },
               [this](auto... params) { return this->DbCallback(params...); }),
      audioMux([this](auto... params) { return this->AudioServicesCallback(params...); }),
      settingsProvider(std::make_unique<settings::Settings>(this))
{
    LOG_INFO("[ServiceAudio] Initializing");


@@ 68,7 67,6 @@ sys::ReturnCodes ServiceAudio::InitHandler()
        // ROUTING
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RoutingBluetoothHSP), "20"},
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RoutingEarspeaker), "20"},
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RoutingHeadphones), "0"},
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RoutingLoudspeaker), "20"},
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RoutingHeadphones), "50"},



@@ 76,7 74,11 @@ sys::ReturnCodes ServiceAudio::InitHandler()
        {dbPath(Setting::Volume, PlaybackType::None, Profile::Type::RoutingEarspeaker), defaultVolumeHigh},
        {dbPath(Setting::Volume, PlaybackType::None, Profile::Type::RoutingHeadphones), defaultVolumeHigh},
        {dbPath(Setting::Volume, PlaybackType::None, Profile::Type::RoutingLoudspeaker), defaultVolumeHigh},
        {dbPath(Setting::Volume, PlaybackType::None, Profile::Type::RoutingHeadphones), defaultVolumeHigh},

        // RECORDING
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RecordingBuiltInMic), "200"},
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RecordingHeadphones), "100"},
        {dbPath(Setting::Gain, PlaybackType::None, Profile::Type::RecordingBluetoothHSP), "100"},

        // MISC
        {dbPath(Setting::EnableVibration, PlaybackType::Multimedia, Profile::Type::Idle), defaultFalse},


@@ 105,32 107,27 @@ sys::ReturnCodes ServiceAudio::DeinitHandler()
    return sys::ReturnCodes::Success;
}

int32_t ServiceAudio::AsyncCallback(PlaybackEvent e)
std::optional<std::string> ServiceAudio::AudioServicesCallback(const AudioServiceMessage::Message *msg)
{
    switch (e.event) {
    case audio::PlaybackEventType::EndOfFile: {
        auto msg = std::make_shared<AudioNotificationMessage>(AudioNotificationMessage::Type::EndOfFile, e.token);
        sys::Bus::SendMulticast(msg, sys::BusChannels::ServiceAudioNotifications, this);
    } break;
    case audio::PlaybackEventType::FileSystemNoSpace:
    case audio::PlaybackEventType::Empty:
        break;
    if (const auto *eof = dynamic_cast<const AudioServiceMessage::EndOfFile *>(msg); eof) {
        auto newMsg =
            std::make_shared<AudioNotificationMessage>(AudioNotificationMessage::Type::EndOfFile, eof->GetToken());
        sys::Bus::SendMulticast(newMsg, sys::BusChannels::ServiceAudioNotifications, this);
    }
    return 0;
};

uint32_t ServiceAudio::DbCallback(const std::string &path, const uint32_t &defaultValue)
{
    LOG_DEBUG("ServiceAudio::DBbCallback(%s, %u)", path.c_str(), static_cast<int>(defaultValue));
    auto settings_it = settingsCache.find(path);
    if (settingsCache.end() == settings_it) {
        settingsCache[path] = defaultValue;
        settingsProvider->setValue(path, std::to_string(defaultValue));
        settingsProvider->registerValueChange(
            path, [this](const std::string &variable, std::string value) { settingsChanged(variable, value); });
        return defaultValue;
    else if (const auto *dbReq = dynamic_cast<const AudioServiceMessage::DbRequest *>(msg); dbReq) {
        LOG_DEBUG("ServiceAudio::DBbCallback(%s)", dbReq->GetPath().c_str());
        auto settings_it = settingsCache.find(dbReq->GetPath());
        if (settingsCache.end() == settings_it) {
            LOG_DEBUG("%s does not exist in cache", dbReq->GetPath().c_str());
            return std::nullopt;
        }
        return settings_it->second;
    }
    return utils::getNumericValue<uint32_t>(settings_it->second);
    else {
        LOG_DEBUG("Message received but not handled - no effect.");
    }

    return std::nullopt;
};

sys::ReturnCodes ServiceAudio::SwitchPowerModeHandler(const sys::ServicePowerMode mode)

M module-services/service-audio/service-audio/AudioMessage.hpp => module-services/service-audio/service-audio/AudioMessage.hpp +1 -1
@@ 52,7 52,7 @@ class AudioNotificationMessage : public AudioMessage
        ServiceSleep,
    };

    explicit AudioNotificationMessage(Type type, audio::Token token = audio::Token()) : type(type), token(token)
    explicit AudioNotificationMessage(Type type, const audio::Token token = audio::Token()) : type(type), token(token)
    {}

    const Type type;

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +1 -2
@@ 56,8 56,7 @@ class ServiceAudio : public sys::Service
        return vibrationMotorStatus == audio::AudioMux::VibrationStatus::On;
    }

    auto AsyncCallback(audio::PlaybackEvent e) -> int32_t;
    auto DbCallback(const std::string &path, const uint32_t &defaultValue) -> uint32_t;
    auto AudioServicesCallback(const AudioServiceMessage::Message *msg) -> std::optional<std::string>;

    auto HandleStart(const audio::Operation::Type opType,
                     const std::string                       = "",