~aleteoryx/muditaos

b890bcd6e32223e624d0d63af50bcd2219469d32 — Marcin Smoczyński 4 years ago 863d805
[EGD-5260] Add A2DP playback to audio

Add basic playback capability using Bluetooth A2DP profile.
Only stereo/44100/16bit files are supported at the moment

Signed-off-by: Marcin Smoczyński <smoczynski.marcin@gmail.com>
37 files changed, 498 insertions(+), 273 deletions(-)

M .gitignore
M .vscode/settings.json
A image/user/music/Ringtone-drum2-stereo.mp3
A image/user/music/SMS-drum2-stereo.mp3
M module-audio/Audio/AudioCommon.hpp
M module-audio/Audio/AudioDeviceFactory.cpp
M module-audio/Audio/AudioDeviceFactory.hpp
M module-audio/Audio/Operation/Operation.cpp
M module-audio/Audio/Operation/Operation.hpp
M module-audio/Audio/Operation/PlaybackOperation.cpp
M module-audio/Audio/Operation/PlaybackOperation.hpp
A module-audio/Audio/ServiceObserver.cpp
A module-audio/Audio/ServiceObserver.hpp
M module-audio/Audio/StreamFactory.cpp
M module-audio/Audio/StreamFactory.hpp
M module-audio/CMakeLists.txt
M module-audio/board/rt1051/RT1051DeviceFactory.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.hpp
A module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp
A module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/MediaContext.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp
M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp
M module-bluetooth/CMakeLists.txt
M module-services/service-audio/ServiceAudio.cpp
M module-services/service-audio/service-audio/ServiceAudio.hpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
M source/MessageType.hpp
M .gitignore => .gitignore +3 -0
@@ 57,3 57,6 @@ update/
# build in docker files
.ccache/
.bash_history

# often created by the visual studio code
/null.d

M .vscode/settings.json => .vscode/settings.json +3 -1
@@ 6,6 6,7 @@
    },
    "testMate.cpp.test.executables": "build-linux-Debug/{googletest,catch2}-*",
    "files.associations": {
        "*.cc": "cpp",
        "array": "cpp",
        "atomic": "cpp",
        "bit": "cpp",


@@ 80,6 81,7 @@
        "pointers": "cpp",
        "any": "cpp",
        "charconv": "cpp",
        "scoped_allocator": "cpp"
        "scoped_allocator": "cpp",
        "*.inc": "cpp"
    },
}
\ No newline at end of file

A image/user/music/Ringtone-drum2-stereo.mp3 => image/user/music/Ringtone-drum2-stereo.mp3 +0 -0
A image/user/music/SMS-drum2-stereo.mp3 => image/user/music/SMS-drum2-stereo.mp3 +0 -0
M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +24 -1
@@ 11,8 11,9 @@
#include <Utils.hpp>
#include <PhoneModes/Common.hpp>

#include <map>
#include <bitset>
#include <map>
#include <memory>
#include <utility>

namespace audio


@@ 314,5 315,27 @@ namespace AudioServiceMessage
        const audio::PlaybackType playback;
    };

    class AudioDeviceCreated : public sys::Message
    {
      public:
        explicit AudioDeviceCreated(std::shared_ptr<audio::AudioDevice> device, audio::AudioDevice::Type type)
            : device(std::move(device)), type(type)
        {}

        auto getDevice() const noexcept -> std::shared_ptr<audio::AudioDevice>
        {
            return device;
        }

        auto getDeviceType() const noexcept -> audio::AudioDevice::Type
        {
            return type;
        }

      private:
        std::shared_ptr<audio::AudioDevice> device;
        audio::AudioDevice::Type type;
    };

    using Callback = std::function<std::optional<std::string>(const sys::Message *msg)>;
} // namespace AudioServiceMessage

M module-audio/Audio/AudioDeviceFactory.cpp => module-audio/Audio/AudioDeviceFactory.cpp +1 -1
@@ 13,7 13,7 @@ std::shared_ptr<AudioDevice> AudioDeviceFactory::CreateDevice(AudioDevice::Type 
    std::shared_ptr<AudioDevice> device = getDeviceFromType(deviceType);

    if (_observer != nullptr && device) {
        _observer->onDeviceCreated(device);
        _observer->onDeviceCreated(device, deviceType);
    }

    return device;

M module-audio/Audio/AudioDeviceFactory.hpp => module-audio/Audio/AudioDeviceFactory.hpp +2 -2
@@ 12,13 12,13 @@ namespace audio

    class AudioDeviceFactory
    {
      public:
        class Observer
        {
          public:
            virtual void onDeviceCreated(std::shared_ptr<AudioDevice> device) = 0;
            virtual void onDeviceCreated(std::shared_ptr<AudioDevice> device, AudioDevice::Type deviceType) = 0;
        };

      public:
        explicit AudioDeviceFactory(Observer *observer = nullptr);

        void setObserver(Observer *observer) noexcept;

M module-audio/Audio/Operation/Operation.cpp => module-audio/Audio/Operation/Operation.cpp +0 -3
@@ 7,7 7,6 @@

#include "Audio/AudioDevice.hpp"
#include "Audio/AudioDeviceFactory.hpp"
#include "Audio/AudioPlatform.hpp"

#include "IdleOperation.hpp"
#include "PlaybackOperation.hpp"


@@ 96,8 95,6 @@ namespace audio

    std::shared_ptr<AudioDevice> Operation::CreateDevice(AudioDevice::Type type)
    {
        auto factory = AudioPlatform::GetDeviceFactory();

        return factory->CreateDevice(type);
    }
} // namespace audio

M module-audio/Audio/Operation/Operation.hpp => module-audio/Audio/Operation/Operation.hpp +13 -3
@@ 7,6 7,9 @@
#include <functional>

#include <Audio/AudioCommon.hpp>
#include <Audio/AudioDeviceFactory.hpp>
#include <Audio/AudioPlatform.hpp>
#include <Audio/ServiceObserver.hpp>
#include <Audio/encoder/Encoder.hpp>
#include <Audio/Profiles/Profile.hpp>



@@ 17,9 20,13 @@ namespace audio
    class Operation
    {
      public:
        Operation(AudioServiceMessage::Callback callback, const PlaybackType &playbackType = PlaybackType::None)
            : playbackType(playbackType), serviceCallback(callback)
        {}
        explicit Operation(AudioServiceMessage::Callback callback,
                           const PlaybackType &playbackType = PlaybackType::None)
            : playbackType(playbackType), serviceCallback(callback), observer(serviceCallback)
        {
            factory = AudioPlatform::GetDeviceFactory();
            factory->setObserver(&observer);
        }

        enum class State
        {


@@ 124,6 131,7 @@ namespace audio
        std::shared_ptr<Profile> currentProfile;
        std::shared_ptr<AudioDevice> audioDevice;
        std::vector<SupportedProfile> supportedProfiles;
        std::unique_ptr<AudioDeviceFactory> factory;

        State state = State::Idle;
        audio::Token operationToken;


@@ 132,6 140,8 @@ namespace audio
        audio::PlaybackType playbackType = audio::PlaybackType::None;

        AudioServiceMessage::Callback serviceCallback;
        ServiceObserver observer;

        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 +5 -7
@@ 32,12 32,6 @@ namespace audio
            return std::string();
        };

        auto defaultProfile = GetProfile(Profile::Type::PlaybackLoudspeaker);
        if (!defaultProfile) {
            throw AudioInitException("Error during initializing profile", RetCode::ProfileNotSet);
        }
        currentProfile = defaultProfile;

        dec = Decoder::Create(file);
        if (dec == nullptr) {
            throw AudioInitException("Error during initializing decoder", RetCode::FileDoesntExist);


@@ 57,7 51,7 @@ namespace audio
        }

        // create stream
        StreamFactory streamFactory(playbackCapabilities);
        StreamFactory streamFactory(playbackCapabilities, playbackBufferingSize);
        dataStreamOut = streamFactory.makeStream(*dec.get(), *audioDevice.get());

        // create audio connection


@@ 157,6 151,10 @@ namespace audio
            return RetCode::UnsupportedProfile;
        }

        if (currentProfile && currentProfile->GetType() == newProfile->GetType()) {
            return RetCode::Success;
        }

        /// profile change - (re)create output device; stop audio first by
        /// killing audio connection
        outputConnection.reset();

M module-audio/Audio/Operation/PlaybackOperation.hpp => module-audio/Audio/Operation/PlaybackOperation.hpp +4 -2
@@ 39,8 39,10 @@ namespace audio
        Position GetPosition() final;

      private:
        static constexpr auto minimumBlockSize = 256U;
        static constexpr auto maximumBlockSize = 2048U;
        // these values are tightly connected to the Bluetooth A2DP update interval
        static constexpr auto playbackBufferingSize = 8U;
        static constexpr auto minimumBlockSize      = 512U;
        static constexpr auto maximumBlockSize      = 512U;
        static constexpr Endpoint::Capabilities playbackCapabilities{.minBlockSize = minimumBlockSize,
                                                                     .maxBlockSize = maximumBlockSize};


A module-audio/Audio/ServiceObserver.cpp => module-audio/Audio/ServiceObserver.cpp +18 -0
@@ 0,0 1,18 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ServiceObserver.hpp"
#include "AudioCommon.hpp"
#include "AudioDevice.hpp"

using namespace audio;

ServiceObserver::ServiceObserver(AudioServiceMessage::Callback serviceCallback)
    : serviceCallback(std::move(serviceCallback))
{}

void ServiceObserver::onDeviceCreated(std::shared_ptr<AudioDevice> device, AudioDevice::Type deviceType)
{
    auto msg = AudioServiceMessage::AudioDeviceCreated(std::move(device), deviceType);
    serviceCallback(&msg);
}

A module-audio/Audio/ServiceObserver.hpp => module-audio/Audio/ServiceObserver.hpp +25 -0
@@ 0,0 1,25 @@
// 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 "AudioCommon.hpp"
#include "AudioDevice.hpp"
#include "AudioDeviceFactory.hpp"

#include <memory>

namespace audio
{

    class ServiceObserver : public AudioDeviceFactory::Observer
    {
      public:
        explicit ServiceObserver(AudioServiceMessage::Callback serviceCallback);
        virtual void onDeviceCreated(std::shared_ptr<AudioDevice> device, AudioDevice::Type deviceType) final;

      private:
        AudioServiceMessage::Callback serviceCallback;
    };

} // namespace audio

M module-audio/Audio/StreamFactory.cpp => module-audio/Audio/StreamFactory.cpp +3 -2
@@ 14,14 14,15 @@

using namespace audio;

StreamFactory::StreamFactory(Endpoint::Capabilities factoryCaps) : caps(std::move(factoryCaps))
StreamFactory::StreamFactory(Endpoint::Capabilities factoryCaps, unsigned int bufferingSize)
    : caps(std::move(factoryCaps)), bufferingSize(bufferingSize)
{}

auto StreamFactory::makeStream(const Source &source, const Sink &sink) -> std::unique_ptr<Stream>
{
    auto negotiatedCaps = negotiateCaps({source, sink});

    return std::make_unique<Stream>(getAllocator(negotiatedCaps.usesDMA), negotiatedCaps.maxBlockSize);
    return std::make_unique<Stream>(getAllocator(negotiatedCaps.usesDMA), negotiatedCaps.maxBlockSize, bufferingSize);
}

auto StreamFactory::negotiateCaps(std::vector<std::reference_wrapper<const Endpoint>> v) -> Endpoint::Capabilities

M module-audio/Audio/StreamFactory.hpp => module-audio/Audio/StreamFactory.hpp +3 -1
@@ 14,7 14,8 @@ namespace audio
    class StreamFactory
    {
      public:
        explicit StreamFactory(Endpoint::Capabilities factoryCaps);
        explicit StreamFactory(Endpoint::Capabilities factoryCaps,
                               unsigned int bufferingSize = Stream::defaultBufferingSize);
        auto makeStream(const Source &source, const Sink &sink) -> std::unique_ptr<Stream>;

      private:


@@ 24,6 25,7 @@ namespace audio
        Endpoint::Capabilities caps;
        StandardStreamAllocator stdAlloc;
        NonCacheableStreamAllocator nonCacheableAlloc;
        unsigned int bufferingSize;
    };

} // namespace audio

M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +10 -11
@@ 7,30 7,29 @@ project(module-audio VERSION 1.0
        DESCRIPTION "Audio module library")

set(SOURCES
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Audio.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioCommon.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioDeviceFactory.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/Decoder.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderWorker.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/decoder/xing_header.c"

        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/Encoder.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/EncoderWAV.cpp"

        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Audio.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioDeviceFactory.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioCommon.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Endpoint.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Stream.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamFactory.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamQueuedEventsListener.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/IdleOperation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/Operation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/PlaybackOperation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RecorderOperation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RouterOperation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/IdleOperation.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Profiles/Profile.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/ServiceObserver.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Stream.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamFactory.cpp"
        "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamQueuedEventsListener.cpp"
)

# include target specific rules

M module-audio/board/rt1051/RT1051DeviceFactory.cpp => module-audio/board/rt1051/RT1051DeviceFactory.cpp +4 -4
@@ 4,6 4,7 @@
#include "RT1051DeviceFactory.hpp"
#include "board/rt1051/RT1051AudioCodec.hpp"
#include "board/rt1051/RT1051CellularAudio.hpp"
#include "audio/BluetoothAudioDevice.hpp"

using audio::AudioDevice;
using audio::RT1051AudioCodec;


@@ 15,16 16,15 @@ std::shared_ptr<AudioDevice> RT1051DeviceFactory::getDeviceFromType(AudioDevice:
    std::shared_ptr<AudioDevice> device;
    switch (deviceType) {
    case AudioDevice::Type::Audiocodec: {
        device = std::make_unique<RT1051AudioCodec>();
        device = std::make_shared<RT1051AudioCodec>();
    } break;

    case AudioDevice::Type::Bluetooth: {
        LOG_FATAL("Bluetooth audio is not yet supported");
        device = nullptr;
        device = std::make_shared<bluetooth::BluetoothAudioDevice>();
    } break;

    case AudioDevice::Type::Cellular: {
        device = std::make_unique<RT1051CellularAudio>();
        device = std::make_shared<RT1051CellularAudio>();
    } break;

    default:

M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +10 -2
@@ 8,6 8,7 @@
#include "interface/BluetoothDriverImpl.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"
#include "audio/BluetoothAudioDevice.hpp"
#include "BtKeysStorage.hpp"

#if DEBUG_BLUETOOTH_HCI_COMS == 1


@@ 27,8 28,8 @@ using namespace bsp;

namespace queues
{
    constexpr inline auto io  = "qBtIO";
    constexpr inline auto cmd = "qBtCmds";
    constexpr inline auto io      = "qBtIO";
    constexpr inline auto cmd     = "qBtCmds";
    constexpr inline auto btstack = "qBtStack";

    constexpr inline auto queueLength        = 10;


@@ 174,6 175,7 @@ auto BluetoothWorker::handleBtStackTrigger(QueueHandle_t queue) -> bool
        return false;
    }
    if (notification) {
        cpp_freertos::LockGuard lock(loopMutex);
        runLoop->process();
        return true;
    }


@@ 276,3 278,9 @@ void BluetoothWorker::removeFromBoundDevices(uint8_t *addr)
        LOG_INFO("Device %s removed from paired devices list", bd_addr_to_str(addr));
    }
}

void BluetoothWorker::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> device)
{
    cpp_freertos::LockGuard lock(loopMutex);
    profileManager->setAudioDevice(std::move(device));
}

M module-bluetooth/Bluetooth/BluetoothWorker.hpp => module-bluetooth/Bluetooth/BluetoothWorker.hpp +21 -10
@@ 3,17 3,25 @@

#pragma once

#include "Device.hpp"
#include "Service/Worker.hpp"
#include "audio/BluetoothAudioDevice.hpp"
#include "glucode/BluetoothRunLoop.hpp"
#include "interface/profiles/Profile.hpp"
#include <FreeRTOS.h>
#include "service-bluetooth/SettingsHolder.hpp"
#include "Service/Worker.hpp"

#include "Device.hpp"
#include "WorkerController.hpp"

#include <bsp/bluetooth/Bluetooth.hpp>
#include <memory>

#include <mutex.hpp>

#include <FreeRTOS.h>
#include <task.h>

#include <memory>
#include <vector>
#include "service-bluetooth/SettingsHolder.hpp"
#include "glucode/BluetoothRunLoop.hpp"
#include "WorkerController.hpp"

struct HCI;

/// debug option for HCI (uart) commands debugging


@@ 63,7 71,7 @@ namespace bluetooth
        {
        };
    };
}; // namespace Bt
}; // namespace bluetooth

class BluetoothWorker : private sys::Worker
{


@@ 76,8 84,9 @@ class BluetoothWorker : private sys::Worker
        queueRunloopTrigger // btstack run_loop queue
    };

    sys::Service *service       = nullptr;
    bool isRunning              = false;
    sys::Service *service = nullptr;
    bool isRunning        = false;
    cpp_freertos::MutexStandard loopMutex;

    void registerQueues();
    void onLinkKeyAdded(const std::string &deviceAddress);


@@ 102,6 111,8 @@ class BluetoothWorker : private sys::Worker
    bool run() override;
    auto deinit() -> bool override;

    void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> device);

    /// bluetooth stack id in use
    unsigned long active_features;
    std::shared_ptr<bluetooth::ProfileManager> profileManager;

A module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +117 -0
@@ 0,0 1,117 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BluetoothAudioDevice.hpp"

#include <Audio/Stream.hpp>

#include <cassert>

using namespace bluetooth;

BluetoothAudioDevice::BluetoothAudioDevice(MediaContext *mediaContext)
    : AudioDevice(btCapabilities, btCapabilities), ctx(mediaContext)
{
    LOG_DEBUG("Bluetooth audio device created");
}

BluetoothAudioDevice::~BluetoothAudioDevice()
{
    LOG_DEBUG("Destroying bluetooth audio device");
}

void BluetoothAudioDevice::setMediaContext(MediaContext *mediaContext)
{
    ctx = mediaContext;
}

auto BluetoothAudioDevice::Start(const Format &format) -> audio::AudioDevice::RetCode
{
    return audio::AudioDevice::RetCode::Success;
}

auto BluetoothAudioDevice::Stop() -> audio::AudioDevice::RetCode
{
    return audio::AudioDevice::RetCode::Success;
}

auto BluetoothAudioDevice::OutputVolumeCtrl(float vol) -> audio::AudioDevice::RetCode
{
    return audio::AudioDevice::RetCode::Success;
}

auto BluetoothAudioDevice::InputGainCtrl(float gain) -> audio::AudioDevice::RetCode
{
    return audio::AudioDevice::RetCode::Success;
}

auto BluetoothAudioDevice::OutputPathCtrl(OutputPath outputPath) -> audio::AudioDevice::RetCode
{
    return audio::AudioDevice::RetCode::Success;
}

auto BluetoothAudioDevice::InputPathCtrl(InputPath inputPath) -> audio::AudioDevice::RetCode
{
    return audio::AudioDevice::RetCode::Success;
}

auto BluetoothAudioDevice::IsFormatSupported(const Format &format) -> bool
{
    return true;
}

void BluetoothAudioDevice::onDataSend()
{
    if (outputEnabled) {
        fillSbcAudioBuffer(ctx);
    }
}

void BluetoothAudioDevice::onDataReceive()
{}

void BluetoothAudioDevice::enableInput()
{}

void BluetoothAudioDevice::enableOutput()
{
    LOG_DEBUG("Enabling bluetooth audio output.");
    outputEnabled = true;
}

void BluetoothAudioDevice::disableInput()
{}

void BluetoothAudioDevice::disableOutput()
{
    LOG_DEBUG("Disabling bluetooth audio output.");
    outputEnabled = false;
}

auto BluetoothAudioDevice::fillSbcAudioBuffer(MediaContext *context) -> int
{
    // perform sbc encodin
    int totalNumBytesRead                    = 0;
    unsigned int numAudioSamplesPerSbcBuffer = btstack_sbc_encoder_num_audio_frames();

    assert(context != nullptr);

    while (context->samples_ready >= numAudioSamplesPerSbcBuffer &&
           (context->max_media_payload_size - context->sbc_storage_count) >= btstack_sbc_encoder_sbc_buffer_length()) {
        audio::Stream::Span dataSpan;

        Sink::_stream->peek(dataSpan);
        btstack_sbc_encoder_process_data(reinterpret_cast<int16_t *>(dataSpan.data));
        Sink::_stream->consume();

        uint16_t sbcFrameSize = btstack_sbc_encoder_sbc_buffer_length();
        uint8_t *sbcFrame     = btstack_sbc_encoder_sbc_buffer();

        totalNumBytesRead += numAudioSamplesPerSbcBuffer;
        memcpy(&context->sbc_storage[context->sbc_storage_count], sbcFrame, sbcFrameSize);
        context->sbc_storage_count += sbcFrameSize;
        context->samples_ready -= numAudioSamplesPerSbcBuffer;
    }

    return totalNumBytesRead;
}

A module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp +47 -0
@@ 0,0 1,47 @@
// 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 <Audio/AudioDevice.hpp>
#include <interface/profiles/A2DP/MediaContext.hpp>

namespace bluetooth
{

    class BluetoothAudioDevice : public audio::AudioDevice
    {
      public:
        BluetoothAudioDevice() = default;
        explicit BluetoothAudioDevice(MediaContext *mediaContext);
        virtual ~BluetoothAudioDevice();

        void setMediaContext(MediaContext *MediaContext);

        auto Start(const Format &format) -> audio::AudioDevice::RetCode override;
        auto Stop() -> audio::AudioDevice::RetCode override;

        auto OutputVolumeCtrl(float vol) -> audio::AudioDevice::RetCode override;
        auto InputGainCtrl(float gain) -> audio::AudioDevice::RetCode override;
        auto OutputPathCtrl(OutputPath outputPath) -> audio::AudioDevice::RetCode override;
        auto InputPathCtrl(InputPath inputPath) -> audio::AudioDevice::RetCode override;
        auto IsFormatSupported(const Format &format) -> bool override;

        // Endpoint control methods
        void onDataSend() override;
        void onDataReceive() override;
        void enableInput() override;
        void enableOutput() override;
        void disableInput() override;
        void disableOutput() override;

      private:
        auto fillSbcAudioBuffer(MediaContext *context) -> int;

        MediaContext *ctx  = nullptr;
        bool outputEnabled = false;

        static constexpr Capabilities btCapabilities = {.usesDMA = false, .minBlockSize = 512U, .maxBlockSize = 512U};
    };

}; // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +21 -49
@@ 20,6 20,8 @@
#include "service-bluetooth/messages/Connect.hpp"
#include "service-bluetooth/messages/Disconnect.hpp"
#include "service-bluetooth/Constants.hpp"
#include <audio/BluetoothAudioDevice.hpp>

extern "C"
{
#include "module-bluetooth/lib/btstack/src/btstack.h"


@@ 73,11 75,6 @@ namespace bluetooth
        pimpl->setOwnerService(service);
    }

    auto A2DP::getStreamData() -> std::shared_ptr<BluetoothStreamData>
    {
        return pimpl->getStreamData();
    }

    void A2DP::connect()
    {
        pimpl->connect();


@@ 98,6 95,11 @@ namespace bluetooth
        pimpl->stop();
    }

    void A2DP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        pimpl->setAudioDevice(std::move(audioDevice));
    }

    const sys::Service *A2DP::A2DPImpl::ownerService;
    QueueHandle_t A2DP::A2DPImpl::sourceQueue = nullptr;
    QueueHandle_t A2DP::A2DPImpl::sinkQueue   = nullptr;


@@ 110,6 112,7 @@ namespace bluetooth
        0xFF, //(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
        2,
        53};
    std::shared_ptr<BluetoothAudioDevice> A2DP::A2DPImpl::audioDevice;

    bool A2DP::A2DPImpl::isConnected = false;
    /* LISTING_START(MainConfiguration): Setup Audio Source and AVRCP Target services */


@@ 195,40 198,6 @@ namespace bluetooth
        AVRCP::mediaTracker.sbc_ready_to_send = 0;
    }

    auto A2DP::A2DPImpl::fillSbcAudioBuffer(MediaContext *context) -> int
    {
        // perform sbc encodin
        int totalNumBytesRead                    = 0;
        unsigned int numAudioSamplesPerSbcBuffer = btstack_sbc_encoder_num_audio_frames();
        while (context->samples_ready >= numAudioSamplesPerSbcBuffer &&
               (context->max_media_payload_size - context->sbc_storage_count) >=
                   btstack_sbc_encoder_sbc_buffer_length()) {

            AudioData_t audioData;

            if (sourceQueue != nullptr) {
                if (xQueueReceive(sourceQueue, &audioData, 10) != pdPASS) {
                    audioData.data.fill(0);
                }
            }
            else {
                audioData.data.fill(0);
                LOG_ERROR("queue is nullptr!");
            }

            btstack_sbc_encoder_process_data(audioData.data.data());

            uint16_t sbcFrameSize = btstack_sbc_encoder_sbc_buffer_length();
            uint8_t *sbcFrame     = btstack_sbc_encoder_sbc_buffer();

            totalNumBytesRead += numAudioSamplesPerSbcBuffer;
            memcpy(&context->sbc_storage[context->sbc_storage_count], sbcFrame, sbcFrameSize);
            context->sbc_storage_count += sbcFrameSize;
            context->samples_ready -= numAudioSamplesPerSbcBuffer;
        }
        return totalNumBytesRead;
    }

    void A2DP::A2DPImpl::audioTimeoutHandler(btstack_timer_source_t *timer)
    {
        auto *context = static_cast<MediaContext *>(btstack_run_loop_get_timer_context(timer));


@@ 255,7 224,9 @@ namespace bluetooth
            return;
        }

        fillSbcAudioBuffer(context);
        if (audioDevice != nullptr) {
            audioDevice->onDataSend();
        }

        if ((context->sbc_storage_count + btstack_sbc_encoder_sbc_buffer_length()) > context->max_media_payload_size) {
            // schedule sending


@@ 347,7 318,7 @@ namespace bluetooth
                     bd_addr_to_str(address),
                     AVRCP::mediaTracker.a2dp_cid,
                     AVRCP::mediaTracker.local_seid);
            isConnected = true;
            isConnected    = true;
            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(std::move(deviceAddress), true),
                                 service::name::bluetooth);


@@ 608,15 579,10 @@ namespace bluetooth
        ownerService = service;
    }

    auto A2DP::A2DPImpl::getStreamData() -> std::shared_ptr<BluetoothStreamData>
    {
        return std::make_shared<BluetoothStreamData>(sinkQueue, sourceQueue, metadata);
    }

    void A2DP::A2DPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    {
        auto evt = std::make_shared<audio::Event>(event, state);
        auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
        auto evt       = std::make_shared<audio::Event>(event, state);
        auto msg       = std::make_shared<AudioEventRequest>(std::move(evt));
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
    }


@@ 634,4 600,10 @@ namespace bluetooth
        a2dp_source_pause_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
    }

} // namespace Bt
    void A2DP::A2DPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> newAudioDevice)
    {
        newAudioDevice->setMediaContext(&AVRCP::mediaTracker);
        A2DP::A2DPImpl::audioDevice = std::move(newAudioDevice);
    }

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp +5 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 8,7 8,7 @@
#include "Service/Service.hpp"
#include "queue.h"
#include <memory>
#include <service-bluetooth/ServiceBluetoothCommon.hpp>

namespace bluetooth
{
    class A2DP : public Profile


@@ 25,15 25,16 @@ namespace bluetooth
        auto init() -> Error::Code override;
        void setDeviceAddress(uint8_t *addr) override;
        void setOwnerService(const sys::Service *service) override;
        [[nodiscard]] auto getStreamData() -> std::shared_ptr<BluetoothStreamData> override;

        void connect() override;
        void disconnect() override;
        void start() override;
        void stop() override;

        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override;

      private:
        class A2DPImpl;
        std::unique_ptr<A2DPImpl> pimpl;
    };
} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +10 -26
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 8,6 8,11 @@
#include <BtCommand.hpp>
#include <log/log.hpp>
#include <Audio/AudioCommon.hpp>
#include <audio/BluetoothAudioDevice.hpp>

#include "MediaContext.hpp"

#include <memory>

extern "C"
{


@@ 17,29 22,6 @@ extern "C"

namespace bluetooth
{
    static constexpr int SBC_STORAGE_SIZE = 1030;
    struct MediaContext
    {
        uint16_t a2dp_cid;
        uint8_t local_seid;
        uint8_t remote_seid;
        uint8_t stream_opened;
        uint16_t avrcp_cid;

        uint32_t time_audio_data_sent_in_ms;
        uint32_t acc_num_missed_samples;
        uint32_t samples_ready;
        btstack_timer_source_t audio_timer;
        uint8_t streaming;
        int max_media_payload_size;

        uint8_t sbc_storage[SBC_STORAGE_SIZE];
        uint16_t sbc_storage_count;
        uint8_t sbc_ready_to_send;

        uint16_t volume;
    };

    class AVRCP;
    class A2DP::A2DPImpl
    {


@@ 66,10 48,11 @@ namespace bluetooth
        static void sourcePacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void hciPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void sendMediaPacket();
        static auto fillSbcAudioBuffer(MediaContext *context) -> int;
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static bool isConnected;

        static std::shared_ptr<BluetoothAudioDevice> audioDevice;

      public:
        auto init() -> Error::Code;
        void connect();


@@ 79,5 62,6 @@ namespace bluetooth
        void setDeviceAddress(bd_addr_t addr);
        void setOwnerService(const sys::Service *service);
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);
    };
} // namespace Bt
} // namespace bluetooth

A module-bluetooth/Bluetooth/interface/profiles/A2DP/MediaContext.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/MediaContext.hpp +39 -0
@@ 0,0 1,39 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

extern "C"
{
#include <btstack.h>
#include <btstack_defines.h>
}

#include <cstdint>

namespace bluetooth
{
    static constexpr int SBC_STORAGE_SIZE = 1030;
    struct MediaContext
    {
        uint16_t a2dp_cid;
        uint8_t local_seid;
        uint8_t remote_seid;
        uint8_t stream_opened;
        uint16_t avrcp_cid;

        uint32_t time_audio_data_sent_in_ms;
        uint32_t acc_num_missed_samples;
        uint32_t samples_ready;
        btstack_timer_source_t audio_timer;
        uint8_t streaming;
        int max_media_payload_size;

        uint8_t sbc_storage[SBC_STORAGE_SIZE];
        uint16_t sbc_storage_count;
        uint8_t sbc_ready_to_send;

        uint16_t volume;
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +5 -10
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "HSPImpl.hpp"


@@ 51,11 51,6 @@ namespace bluetooth
        pimpl->setOwnerService(service);
    }

    auto HSP::getStreamData() -> std::shared_ptr<BluetoothStreamData>
    {
        return pimpl->getStreamData();
    }

    void HSP::connect()
    {
        pimpl->connect();


@@ 89,8 84,8 @@ namespace bluetooth

    void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    {
        auto evt = std::make_shared<audio::Event>(event, state);
        auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
        auto evt       = std::make_shared<audio::Event>(event, state);
        auto msg       = std::make_shared<AudioEventRequest>(std::move(evt));
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
    }


@@ 165,7 160,7 @@ namespace bluetooth
        case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE:
            LOG_DEBUG("Audio connection released.\n\n");
            sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
            scoHandle = HCI_CON_HANDLE_INVALID;
            scoHandle   = HCI_CON_HANDLE_INVALID;
            isConnected = false;
            break;
        case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED:


@@ 259,4 254,4 @@ namespace bluetooth
        hsp_ag_release_audio_connection();
    }

} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +5 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 25,7 25,6 @@ namespace bluetooth

        auto init() -> Error::Code override;
        void setDeviceAddress(uint8_t *addr) override;
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData> override;
        void setOwnerService(const sys::Service *service) override;

        void connect() override;


@@ 33,6 32,9 @@ namespace bluetooth
        void start() override;
        void stop() override;

        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override
        {}

      private:
        class HSPImpl;
        std::unique_ptr<HSPImpl> pimpl;


@@ 40,4 42,4 @@ namespace bluetooth
        btstack_run_loop *runLoopInstance{};
    };

} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +12 -9
@@ 4,23 4,26 @@
#pragma once

#include "Error.hpp"
#include <audio/BluetoothAudioDevice.hpp>
#include <Service/Message.hpp>
#include <service-bluetooth/ServiceBluetoothCommon.hpp>

#include <memory>

namespace bluetooth
{
    class Profile
    {
      public:
        virtual ~Profile()                                                   = default;
        virtual auto init() -> Error::Code                                   = 0;
        virtual void setDeviceAddress(uint8_t *addr)                         = 0;
        virtual void setOwnerService(const sys::Service *service)            = 0;
        virtual auto getStreamData() -> std::shared_ptr<BluetoothStreamData> = 0;
        virtual void connect()                                               = 0;
        virtual void start()                                                 = 0;
        virtual void stop()                                                  = 0;
        virtual void disconnect()                                            = 0;
        virtual ~Profile()                                                                        = default;
        virtual auto init() -> Error::Code                                                        = 0;
        virtual void setDeviceAddress(uint8_t *addr)                                              = 0;
        virtual void setOwnerService(const sys::Service *service)                                 = 0;
        virtual void connect()                                                                    = 0;
        virtual void start()                                                                      = 0;
        virtual void stop()                                                                       = 0;
        virtual void disconnect()                                                                 = 0;
        virtual void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) = 0;
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +11 -0
@@ 82,15 82,26 @@ namespace bluetooth
        start();
        return Error::Success;
    }

    auto ProfileManager::start() -> Error::Code
    {
        currentProfilePtr->start();
        return Error::Success;
    }

    auto ProfileManager::stop() -> Error::Code
    {
        currentProfilePtr->stop();
        return Error::Success;
    }

    auto ProfileManager::setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code
    {
        if (currentProfilePtr == nullptr) {
            return Error::NotReady;
        }

        currentProfilePtr->setAudioDevice(std::move(device));
        return Error::Success;
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +5 -0
@@ 8,6 8,9 @@
#include "Profile.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"
#include "audio/BluetoothAudioDevice.hpp"

#include <memory>

extern "C"
{


@@ 44,6 47,8 @@ namespace bluetooth
        auto start() -> Error::Code;
        auto stop() -> Error::Code;

        auto setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code;

      private:
        sys::Service *ownerService;
        ProfileList profilesList;

M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +5 -1
@@ 1,8 1,12 @@
project(module-bluetooth VERSION 1.0 DESCRIPTION "Bluetooth module library")
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

project(module-bluetooth VERSION 1.0 DESCRIPTION "Bluetooth module library")

set(CMAKE_CXX_STANDARD 17)

set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/audio/BluetoothAudioDevice.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BluetoothWorker.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/WorkerController.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/CommandHandler.cpp

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +30 -7
@@ 6,6 6,7 @@

#include <Audio/Operation/IdleOperation.hpp>
#include <Audio/Operation/PlaybackOperation.hpp>
#include <Bluetooth/audio/BluetoothAudioDevice.hpp>
#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/ServiceBluetoothCommon.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>


@@ 217,6 218,13 @@ std::optional<std::string> ServiceAudio::AudioServicesCallback(const sys::Messag
        }
        return settings_it->second;
    }
    else if (const auto *deviceMsg = dynamic_cast<const AudioServiceMessage::AudioDeviceCreated *>(msg); deviceMsg) {
        if (deviceMsg->getDeviceType() == AudioDevice::Type::Bluetooth) {
            auto startBluetoothAudioMsg = std::make_shared<BluetoothAudioStartMessage>(
                std::static_pointer_cast<bluetooth::BluetoothAudioDevice>(deviceMsg->getDevice()));
            bus.sendUnicast(std::move(startBluetoothAudioMsg), service::name::bluetooth);
        }
    }
    else {
        LOG_DEBUG("Message received but not handled - no effect.");
    }


@@ 377,6 385,13 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:

    if (opType == Operation::Type::Playback) {
        auto input = audioMux.GetPlaybackInput(playbackType);
        // stop bluetooth stream if available
        if (bluetoothConnected) {
            LOG_DEBUG("Sending Bluetooth start stream request");
            bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Play),
                            service::name::bluetooth);
        }

        AudioStart(input);
        return std::make_unique<AudioStartPlaybackResponse>(retCode, retToken);
    }


@@ 395,17 410,20 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:

std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleSendEvent(std::shared_ptr<Event> evt)
{
    auto isBT =
        evt->getType() == EventType::BlutoothHSPDeviceState || evt->getType() == EventType::BlutoothA2DPDeviceState;
    if (isBT && evt->getDeviceState() == audio::Event::DeviceState::Connected) {
        auto req = std::make_shared<BluetoothRequestStreamMessage>();
        bus.sendUnicast(req, service::name::bluetooth);
        return std::make_unique<AudioEventResponse>(RetCode::Success);
    // update bluetooth state
    if (evt->getType() == EventType::BlutoothA2DPDeviceState) {
        auto newState = evt->getDeviceState() == Event::DeviceState::Connected;
        if (newState != bluetoothConnected) {
            LOG_DEBUG("Bluetooth connection status changed: %s", newState ? "connected" : "disconnected");
            bluetoothConnected = newState;
        }
    }

    // update information about endpoints availability
    for (auto &input : audioMux.GetAllInputs()) {
        input.audio->SendEvent(evt);
    }

    return std::make_unique<AudioEventResponse>(RetCode::Success);
}



@@ 451,6 469,12 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStop(const std::vector
        }
    }

    // stop bluetooth stream if available
    if (bluetoothConnected) {
        LOG_DEBUG("Sending Bluetooth stop request");
        bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Stop), service::name::bluetooth);
    }

    // on failure return first false code
    auto it =
        std::find_if_not(retCodes.begin(), retCodes.end(), [](auto p) { return p.second == audio::RetCode::Success; });


@@ 560,7 584,6 @@ sys::MessagePointer ServiceAudio::DataReceivedHandler(sys::DataMessage *msgl, sy
{
    sys::MessagePointer responseMsg;
    bool isBusy = IsBusy();

    auto &msgType = typeid(*msgl);

    if (msgType == typeid(AudioNotificationMessage)) {

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +1 -1
@@ 58,8 58,8 @@ class ServiceAudio : public sys::Service
    audio::AudioMux::VibrationStatus vibrationMotorStatus = audio::AudioMux::VibrationStatus::Off;
    std::unique_ptr<settings::Settings> settingsProvider;
    std::map<std::string, std::string> settingsCache;

    std::unique_ptr<sys::phone_modes::Observer> phoneModeObserver;
    bool bluetoothConnected = false;

    auto IsVibrationMotorOn()
    {

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +22 -63
@@ 69,69 69,20 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
            LOG_ERROR("Disconnecting from timeout timer!");
        });

    connect(message::bluetooth::RequestBondedDevices(), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::RequestBondedDevices *>(msg);
        return handle(request);
    });

    connect(message::bluetooth::RequestStatus(), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::RequestStatus *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::SetStatus), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::SetStatus *>(msg);
        return handle(request);
    });

    connect(typeid(BluetoothPairMessage), [&](sys::Message *msg) {
        auto request = static_cast<BluetoothPairMessage *>(msg);
        return handle(request);
    });
    connect(typeid(message::bluetooth::Unpair), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::Unpair *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::SetDeviceName), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::SetDeviceName *>(msg);
        return handle(request);
    });

    connect(typeid(sdesktop::developerMode::DeveloperModeRequest), [&](sys::Message *msg) {
        auto request = static_cast<sdesktop::developerMode::DeveloperModeRequest *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::Connect), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::Connect *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::Disconnect), [&](sys::Message *msg) {
        auto request = static_cast<message::bluetooth::Disconnect *>(msg);
        return handle(request);
    });

    connect(typeid(BluetoothMessage), [&](sys::Message *msg) {
        auto request = static_cast<BluetoothMessage *>(msg);
        return handle(request);
    });

    connect(typeid(BluetoothAddrMessage), [&](sys::Message *msg) {
        auto request = static_cast<BluetoothAddrMessage *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::ConnectResult), [&](sys::Message *msg) {
        auto result = static_cast<message::bluetooth::ConnectResult *>(msg);
        return handle(result);
    });

    connect(typeid(message::bluetooth::DisconnectResult), [&](sys::Message *msg) {
        auto result = static_cast<message::bluetooth::DisconnectResult *>(msg);
        return handle(result);
    });
    connectHandler<BluetoothAddrMessage>();
    connectHandler<BluetoothAudioStartMessage>();
    connectHandler<BluetoothMessage>();
    connectHandler<BluetoothPairMessage>();
    connectHandler<message::bluetooth::Connect>();
    connectHandler<message::bluetooth::ConnectResult>();
    connectHandler<message::bluetooth::Disconnect>();
    connectHandler<message::bluetooth::DisconnectResult>();
    connectHandler<message::bluetooth::RequestBondedDevices>();
    connectHandler<message::bluetooth::RequestStatus>();
    connectHandler<message::bluetooth::SetDeviceName>();
    connectHandler<message::bluetooth::SetStatus>();
    connectHandler<message::bluetooth::Unpair>();
    connectHandler<sdesktop::developerMode::DeveloperModeRequest>();

    settingsHolder->onStateChange = [this]() {
        auto initialState = std::visit(bluetooth::IntVisitor(), settingsHolder->getValue(bluetooth::Settings::State));


@@ 157,6 108,7 @@ void ServiceBluetooth::ProcessCloseReason(sys::CloseReason closeReason)
sys::MessagePointer ServiceBluetooth::DataReceivedHandler([[maybe_unused]] sys::DataMessage *msg,
                                                          [[maybe_unused]] sys::ResponseMessage *resp)
{
    LOG_ERROR("Unhandled message: %s", typeid(msg).name());
    return std::make_shared<sys::ResponseMessage>();
}



@@ 170,6 122,12 @@ void ServiceBluetooth::sendWorkerCommand(bluetooth::Command command)
    xQueueSend(workerQueue, &command, portMAX_DELAY);
}

auto ServiceBluetooth::handle(BluetoothAudioStartMessage *msg) -> std::shared_ptr<sys::Message>
{
    worker->setAudioDevice(msg->getAudioDevice());
    return std::make_shared<sys::ResponseMessage>();
}

auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestBondedDevices *msg)
    -> std::shared_ptr<sys::Message>
{


@@ 213,6 171,7 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared
    sendWorkerCommand(command);
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(BluetoothPairMessage *msg) -> std::shared_ptr<sys::Message>
{
    const auto addrString = msg->addr;

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +7 -38
@@ 6,8 6,8 @@
#include "ServiceBluetoothCommon.hpp"

#include <Bluetooth/Device.hpp>
#include <Bluetooth/audio/BluetoothAudioDevice.hpp>
#include <Service/Message.hpp>
#include <Audio/Stream.hpp>
#include <MessageType.hpp>

#include <utility>


@@ 85,49 85,18 @@ class BluetoothPairMessage : public sys::DataMessage
    ~BluetoothPairMessage() override = default;
};

class BluetoothAudioRegisterMessage : public sys::DataMessage
class BluetoothAudioStartMessage : public sys::DataMessage
{
  public:
    QueueHandle_t audioSourceQueue;
    QueueHandle_t audioSinkQueue;
    BluetoothAudioRegisterMessage(QueueHandle_t audioSourceQueue, QueueHandle_t audioSinkQueue)
        : sys::DataMessage(MessageType::BluetoothAudioRegister), audioSourceQueue(audioSourceQueue),
          audioSinkQueue(audioSinkQueue)
    explicit BluetoothAudioStartMessage(std::shared_ptr<bluetooth::BluetoothAudioDevice> device)
        : DataMessage(MessageType::BluetoothAudioStart), device(std::move(device))
    {}
    ~BluetoothAudioRegisterMessage() override = default;
};

class BluetoothDeviceMetadataMessage : public sys::DataMessage
{
  public:
    DeviceMetadata_t metadata;
    BluetoothDeviceMetadataMessage(DeviceMetadata_t metadata)
        : DataMessage(MessageType::BluetoothDeviceMetadata), metadata(std::move(metadata))
    {}
    ~BluetoothDeviceMetadataMessage() override = default;
};

class BluetoothRequestStreamMessage : public sys::DataMessage
{
  public:
    BluetoothRequestStreamMessage() : DataMessage(MessageType::BluetoothRequestStream)
    {}
    ~BluetoothRequestStreamMessage() override = default;
};

class BluetoothRequestStreamResultMessage : public sys::DataMessage
{
  public:
    BluetoothRequestStreamResultMessage(std::shared_ptr<BluetoothStreamData> data)
        : DataMessage(MessageType::BluetoothRequestStream), data(data)
    {}
    ~BluetoothRequestStreamResultMessage() override = default;

    std::shared_ptr<BluetoothStreamData> getData()
    auto getAudioDevice() const -> std::shared_ptr<bluetooth::BluetoothAudioDevice>
    {
        return data;
        return device;
    }

  private:
    std::shared_ptr<BluetoothStreamData> data;
    std::shared_ptr<bluetooth::BluetoothAudioDevice> device;
};

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +6 -0
@@ 72,6 72,11 @@ class ServiceBluetooth : public sys::Service
    void startTimeoutTimer();
    void stopTimeoutTimer();

    template <typename T> auto connectHandler() -> bool
    {
        return connect(typeid(T), [&](sys::Message *msg) { return handle(static_cast<T *>(msg)); });
    }

    [[nodiscard]] auto handle(message::bluetooth::RequestBondedDevices *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::RequestStatus *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::SetStatus *msg) -> std::shared_ptr<sys::Message>;


@@ 85,6 90,7 @@ class ServiceBluetooth : public sys::Service
    [[nodiscard]] auto handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothAddrMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothAudioStartMessage *msg) -> std::shared_ptr<sys::Message>;
};

namespace sys

M source/MessageType.hpp => source/MessageType.hpp +1 -12
@@ 192,18 192,7 @@ enum class MessageType
    BluetoothScanResult,
    BluetoothAddrResult,
    BluetoothPairResult,
    BluetoothAudioRegister,
    BluetoothDeviceMetadata,
    BluetoothRequestStream,
    BluetoothDeviceDisconnected,

    // bluetooth proxy
    BluetoothProxyStart,
    BluetoothProxyStop,
    BluetoothProxyOutputVolumeCtrl,
    BluetoothProxyInputGainCtrl,
    BluetoothProxyOutputPathCtrl,
    BluetoothProxyInputPathCtrl,
    BluetoothAudioStart,

    LwIP_request,
    EVM_GPIO,