~aleteoryx/muditaos

ef3f840a4dfa76f16dd009c536782a8b28fec6e9 — Marcin Smoczyński 4 years ago fbb820c
[EGD-6049] Add voice call over HSP

Add support for voice calls over HSP.

Signed-off-by: Marcin Smoczyński <smoczynski.marcin@gmail.com>
M module-audio/Audio/AudioDevice.hpp => module-audio/Audio/AudioDevice.hpp +2 -1
@@ 27,7 27,8 @@ namespace audio
            None,
            Audiocodec,
            Cellular,
            Bluetooth
            BluetoothA2DP,
            BluetoothHSP
        };

        virtual ~AudioDevice() = default;

M module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp => module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp +1 -1
@@ 21,7 21,7 @@ namespace audio
                                                  .inputGain     = 0,
                                                  .inputPath     = audio::codec::InputPath::None,
                                                  .outputPath    = audio::codec::OutputPath::None},
                      AudioDevice::Type::Bluetooth)
                      AudioDevice::Type::BluetoothA2DP)
        {}
    };


M module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp +1 -1
@@ 23,7 23,7 @@ namespace audio
                                              .inputGain    = static_cast<float>(gain),
                                              .inputPath    = audio::codec::InputPath::None,
                                              .outputPath   = audio::codec::OutputPath::None},
                  AudioDevice::Type::Bluetooth)
                  AudioDevice::Type::BluetoothHSP)
        {}
    };


M module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp +1 -1
@@ 23,7 23,7 @@ namespace audio
                          .inputGain    = static_cast<float>(gain),
                          .inputPath    = audio::codec::InputPath::None,
                          .outputPath   = audio::codec::OutputPath::None},
                      AudioDevice::Type::Bluetooth)
                      AudioDevice::Type::BluetoothHSP)
        {}
    };


M module-audio/Audio/transcode/InputTranscodeProxy.cpp => module-audio/Audio/transcode/InputTranscodeProxy.cpp +5 -0
@@ 23,6 23,11 @@ bool InputTranscodeProxy::push(const Span &span)
    return getWrappedStream().push(transform->transform(span, transcodingSpaceSpan));
}

bool InputTranscodeProxy::push(void *data, std::size_t dataSize)
{
    return push(Span{.data = reinterpret_cast<std::uint8_t *>(data), .dataSize = dataSize});
}

void InputTranscodeProxy::commit()
{
    if (isReserved) {

M module-audio/Audio/transcode/InputTranscodeProxy.hpp => module-audio/Audio/transcode/InputTranscodeProxy.hpp +1 -0
@@ 27,6 27,7 @@ namespace audio::transcode
                                     std::shared_ptr<Transform> transform) noexcept;

        bool push(const Span &span) override;
        bool push(void *data, std::size_t dataSize);
        void commit() override;
        bool reserve(Span &span) override;
        [[nodiscard]] auto getInputTraits() const noexcept -> Traits override;

M module-audio/board/rt1051/RT1051DeviceFactory.cpp => module-audio/board/rt1051/RT1051DeviceFactory.cpp +6 -2
@@ 21,8 21,12 @@ std::shared_ptr<AudioDevice> RT1051DeviceFactory::getDevice(const audio::Profile
        device = std::make_shared<RT1051AudioCodec>(profile.GetAudioConfiguration());
    } break;

    case AudioDevice::Type::Bluetooth: {
        device = std::make_shared<bluetooth::BluetoothAudioDevice>();
    case AudioDevice::Type::BluetoothA2DP: {
        device = std::make_shared<bluetooth::A2DPAudioDevice>();
    } break;

    case AudioDevice::Type::BluetoothHSP: {
        device = std::make_shared<bluetooth::HSPAudioDevice>();
    } break;

    case AudioDevice::Type::Cellular: {

M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +158 -21
@@ 3,42 3,46 @@

#include "BluetoothAudioDevice.hpp"

#include <Audio/AudioCommon.hpp>
#include <interface/profiles/A2DP/AVDTP.hpp>
#include <module-audio/Audio/VolumeScaler.hpp>
#include <interface/profiles/A2DP/AVRCP.hpp>
#include <interface/profiles/HSP/SCO.hpp>

#include <Audio/AudioCommon.hpp>
#include <Audio/VolumeScaler.hpp>
#include <Audio/Stream.hpp>

#include <chrono>
#include <cassert>

using audio::AudioFormat;
using namespace bluetooth;
using bluetooth::A2DPAudioDevice;
using bluetooth::BluetoothAudioDevice;
using bluetooth::HSPAudioDevice;

using namespace std::chrono_literals;

BluetoothAudioDevice::BluetoothAudioDevice(MediaContext *mediaContext) : ctx(mediaContext)
{
    LOG_DEBUG("Bluetooth audio device created");
}
BluetoothAudioDevice::BluetoothAudioDevice(AudioProfile audioProfile) : profile(audioProfile)
{}

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

void BluetoothAudioDevice::setMediaContext(MediaContext *mediaContext)
auto BluetoothAudioDevice::getProfileType() const -> AudioProfile
{
    ctx = mediaContext;
    return profile;
}

auto BluetoothAudioDevice::setOutputVolume(float vol) -> audio::AudioDevice::RetCode
{
    const auto volumeToSet = audio::volume::scaler::toAvrcpVolume(vol);
    const auto status      = avrcp_controller_set_absolute_volume(ctx->avrcp_cid, volumeToSet);
    const auto status      = avrcp_controller_set_absolute_volume(AVRCP::mediaTracker.avrcp_cid, volumeToSet);
    if (status != ERROR_CODE_SUCCESS) {
        LOG_ERROR("Can't set volume level. Status %x", status);
        return audio::AudioDevice::RetCode::Failure;
    }

    outputVolume = vol;
    return audio::AudioDevice::RetCode::Success;
}


@@ 48,19 52,125 @@ auto BluetoothAudioDevice::setInputGain(float gain) -> audio::AudioDevice::RetCo
    return audio::AudioDevice::RetCode::Success;
}

void BluetoothAudioDevice::onDataSend()
auto BluetoothAudioDevice::isInputEnabled() const -> bool
{
    return inputEnabled;
}

auto BluetoothAudioDevice::isOutputEnabled() const -> bool
{
    if (outputEnabled) {
        fillSbcAudioBuffer(ctx);
    return outputEnabled;
}

void A2DPAudioDevice::onDataSend()
{
    if (isOutputEnabled()) {
        fillSbcAudioBuffer();
    }
}

void BluetoothAudioDevice::onDataReceive()
void A2DPAudioDevice::onDataReceive()
{}

void BluetoothAudioDevice::enableInput()
void HSPAudioDevice::onDataSend()
{}

void HSPAudioDevice::onDataSend(std::uint16_t scoHandle)
{
    if (!isOutputEnabled()) {
        return;
    }

    hci_reserve_packet_buffer();
    auto scoPacket = hci_get_outgoing_packet_buffer();

    // get data to send
    audio::AbstractStream::Span dataSpan;
    Sink::_stream->peek(dataSpan);

    // prepare packet to send
    std::copy(dataSpan.data, dataSpan.dataEnd(), &scoPacket[packetDataOffset]);
    Sink::_stream->consume();
    little_endian_store_16(scoPacket, packetHandleOffset, scoHandle);
    scoPacket[packetLengthOffset] = dataSpan.dataSize;

    // send packet
    hci_send_sco_packet_buffer(dataSpan.dataSize + packetDataOffset);
    hci_request_sco_can_send_now_event();
}

void HSPAudioDevice::receiveCVSD(audio::AbstractStream::Span receivedData)
{
    if (!isInputEnabled()) {
        return;
    }

    auto blockSize          = Source::_stream->getInputTraits().blockSize;
    auto decodedData        = decodeCVSD(receivedData);
    auto processedDataIndex = 0;

    // try to complete leftovers to the full block size
    if (leftoversSize != 0) {
        auto maxFillSize = blockSize - leftoversSize;
        auto fillSize    = std::min(maxFillSize, decodedData.dataSize);

        std::copy_n(decodedData.data, fillSize, &rxLeftovers[leftoversSize]);

        if (fillSize + leftoversSize < blockSize) {
            leftoversSize += fillSize;
            return;
        }

        Source::_stream->push(&rxLeftovers[0], blockSize);
        leftoversSize      = 0;
        processedDataIndex = fillSize;
    }

    // push as many blocks as possible
    while (decodedData.dataSize - processedDataIndex >= blockSize) {
        Source::_stream->push(&decodedData.data[processedDataIndex], blockSize);
        processedDataIndex += blockSize;
    }

    // save leftovers
    leftoversSize = decodedData.dataSize - processedDataIndex;
    if (leftoversSize > 0) {
        std::copy_n(&decodedData.data[processedDataIndex], leftoversSize, &rxLeftovers[0]);
    }
}

auto HSPAudioDevice::decodeCVSD(audio::AbstractStream::Span dataToDecode) -> audio::AbstractStream::Span
{
    auto decodedData = dataToDecode;
    std::array<std::int16_t, scratchBufferSize> scratchBuffer;

    const auto audioBytesRead = dataToDecode.dataSize - packetDataOffset;
    const auto samplesCount   = audioBytesRead / sizeof(std::int16_t);
    auto dataStart            = &dataToDecode.data[packetDataOffset];

    for (auto i = 0; i < samplesCount; ++i) {
        scratchBuffer[i] = little_endian_read_16(dataStart, i * sizeof(std::uint16_t));
    }

    auto packetStatusByte = dataToDecode.data[packetStatusOffset];
    auto isBadFrame       = (packetStatusByte & allGoodMask) != 0;

    btstack_cvsd_plc_process_data(&cvsdPlcState, isBadFrame, &scratchBuffer[0], samplesCount, &decoderBuffer[0]);

    decodedData.data     = reinterpret_cast<std::uint8_t *>(decoderBuffer.get());
    decodedData.dataSize = audioBytesRead;

    return decodedData;
}

void HSPAudioDevice::onDataReceive()
{}

void BluetoothAudioDevice::enableInput()
{
    inputEnabled = true;
}

void BluetoothAudioDevice::enableOutput()
{
    LOG_DEBUG("Enabling bluetooth audio output.");


@@ 68,7 178,9 @@ void BluetoothAudioDevice::enableOutput()
}

void BluetoothAudioDevice::disableInput()
{}
{
    inputEnabled = false;
}

void BluetoothAudioDevice::disableOutput()
{


@@ 76,11 188,21 @@ void BluetoothAudioDevice::disableOutput()
    outputEnabled = false;
}

auto BluetoothAudioDevice::fillSbcAudioBuffer(MediaContext *context) -> int
void HSPAudioDevice::enableInput()
{
    auto blockSize = Source::_stream->getInputTraits().blockSize;
    rxLeftovers    = std::make_unique<std::uint8_t[]>(blockSize);
    decoderBuffer  = std::make_unique<std::int16_t[]>(scratchBufferSize);
    btstack_cvsd_plc_init(&cvsdPlcState);
    BluetoothAudioDevice::enableInput();
}

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

    assert(context != nullptr);



@@ 104,7 226,7 @@ auto BluetoothAudioDevice::fillSbcAudioBuffer(MediaContext *context) -> int
    return totalNumBytesRead;
}

auto BluetoothAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
auto A2DPAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
{
    constexpr static auto supportedBitWidth = 16U;
    return std::vector<AudioFormat>{AudioFormat{static_cast<unsigned>(AVDTP::sbcConfig.samplingFrequency),


@@ 112,12 234,27 @@ auto BluetoothAudioDevice::getSupportedFormats() -> std::vector<audio::AudioForm
                                                static_cast<unsigned>(AVDTP::sbcConfig.numChannels)}};
}

auto BluetoothAudioDevice::getSourceFormat() -> audio::AudioFormat
auto HSPAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
{
    return audio::nullFormat;
    return std::vector<AudioFormat>{getSourceFormat()};
}

auto BluetoothAudioDevice::getTraits() const -> Traits
auto A2DPAudioDevice::getTraits() const -> ::audio::Endpoint::Traits
{
    return Traits{.usesDMA = false, .blockSizeConstraint = 512U, .timeConstraint = 10ms};
}

auto HSPAudioDevice::getTraits() const -> ::audio::Endpoint::Traits
{
    return Traits{.usesDMA = false, .blockSizeConstraint = 32U, .timeConstraint = 16ms};
}

auto A2DPAudioDevice::getSourceFormat() -> ::audio::AudioFormat
{
    return audio::nullFormat;
}

auto HSPAudioDevice::getSourceFormat() -> ::audio::AudioFormat
{
    return AudioFormat{bluetooth::SCO::CVSD_SAMPLE_RATE, supportedBitWidth, supportedChannels};
}

M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp +70 -14
@@ 3,9 3,16 @@

#pragma once

#include <Audio/Endpoint.hpp>
#include <Audio/AudioDevice.hpp>
#include <Audio/AudioFormat.hpp>
#include <interface/profiles/A2DP/MediaContext.hpp>
#include <interface/profiles/AudioProfile.hpp>

extern "C"
{
#include "classic/btstack_cvsd_plc.h"
}

namespace bluetooth
{


@@ 13,31 20,80 @@ namespace bluetooth
    class BluetoothAudioDevice : public audio::AudioDevice
    {
      public:
        BluetoothAudioDevice() = default;
        explicit BluetoothAudioDevice(MediaContext *mediaContext);
        explicit BluetoothAudioDevice(AudioProfile audioProfile);
        virtual ~BluetoothAudioDevice();

        RetCode setOutputVolume(float vol) override;
        RetCode setInputGain(float gain) override;
        void setMediaContext(MediaContext *MediaContext);
        auto getTraits() const -> Traits override;
        auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
        auto getSourceFormat() -> audio::AudioFormat override;
        virtual auto getProfileType() const -> AudioProfile;

        auto setOutputVolume(float vol) -> audio::AudioDevice::RetCode override;
        auto setInputGain(float gain) -> audio::AudioDevice::RetCode override;

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

      protected:
        auto isInputEnabled() const -> bool;
        auto isOutputEnabled() const -> bool;
        auto fillSbcAudioBuffer() -> int;

      private:
        bool outputEnabled   = false;
        bool inputEnabled    = false;
        AudioProfile profile = AudioProfile::None;
        float outputVolume;
    };

    class A2DPAudioDevice : public BluetoothAudioDevice
    {
      public:
        explicit A2DPAudioDevice() : BluetoothAudioDevice(AudioProfile::A2DP)
        {}

        void onDataSend() override;
        void onDataReceive() override;
        auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
        auto getTraits() const -> Traits override;
        auto getSourceFormat() -> ::audio::AudioFormat override;
    };

    class HSPAudioDevice : public BluetoothAudioDevice
    {
      public:
        explicit HSPAudioDevice() : BluetoothAudioDevice(AudioProfile::HSP)
        {}

        void onDataSend() override;
        void onDataSend(std::uint16_t scoHandle);
        void onDataReceive() override;
        auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
        auto getTraits() const -> Traits override;
        auto getSourceFormat() -> ::audio::AudioFormat override;
        void enableInput() override;

        void receiveCVSD(audio::AbstractStream::Span receivedData);

      private:
        auto fillSbcAudioBuffer(MediaContext *context) -> int;
        static constexpr std::size_t scratchBufferSize = 128;

        static constexpr auto packetHandleOffset = 0;
        static constexpr auto packetStatusOffset = 1;
        static constexpr auto packetLengthOffset = 2;
        static constexpr auto packetDataOffset   = 3;

        constexpr static auto supportedBitWidth = 16U;
        constexpr static auto supportedChannels = 1;

        constexpr static auto allGoodMask = 0x30;

        auto decodeCVSD(audio::AbstractStream::Span dataToDecode) -> audio::AbstractStream::Span;

        MediaContext *ctx  = nullptr;
        bool outputEnabled = false;
        float outputVolume = 0.0;
        std::unique_ptr<std::uint8_t[]> rxLeftovers;
        std::unique_ptr<std::int16_t[]> decoderBuffer;
        std::size_t leftoversSize = 0;
        btstack_cvsd_plc_state_t cvsdPlcState;
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +1 -1
@@ 97,6 97,7 @@ namespace bluetooth

    auto A2DP::startRinging() const noexcept -> Error::Code
    {
        LOG_ERROR("Can't ring in A2DP profile");
        return Error::SystemError;
    }



@@ 622,7 623,6 @@ namespace bluetooth

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


M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +0 -1
@@ 50,7 50,6 @@ namespace bluetooth
        static void sendMediaPacket();
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static bool isConnected;

        static std::shared_ptr<BluetoothAudioDevice> audioDevice;

      public:

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

#pragma once

namespace bluetooth
{
    enum class AudioProfile
    {
        A2DP,
        HSP,
        HFP,
        None
    };
};

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +18 -2
@@ 111,6 111,7 @@ namespace bluetooth
    const sys::Service *HSP::HSPImpl::ownerService;
    std::string HSP::HSPImpl::agServiceName = "PurePhone HSP";
    bool HSP::HSPImpl::isConnected          = false;
    std::shared_ptr<HSPAudioDevice> HSP::HSPImpl::audioDevice;

    void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    {


@@ 127,7 128,9 @@ namespace bluetooth
            if (READ_SCO_CONNECTION_HANDLE(event) != scoHandle) {
                break;
            }
            sco->receive(event, eventSize);
            if (audioDevice != nullptr) {
                audioDevice->receiveCVSD(audio::AbstractStream::Span{.data = event, .dataSize = eventSize});
            }
            break;

        case HCI_EVENT_PACKET:


@@ 142,7 145,9 @@ namespace bluetooth
    {
        switch (hci_event_packet_get_type(event)) {
        case HCI_EVENT_SCO_CAN_SEND_NOW:
            sco->send(scoHandle);
            if (audioDevice != nullptr) {
                audioDevice->onDataSend(scoHandle);
            }
            break;
        case HCI_EVENT_HSP_META:
            processHSPEvent(event);


@@ 163,6 168,7 @@ namespace bluetooth
                break;
            }
            LOG_DEBUG("RFCOMM connection established.\n");
            sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Connected);
            isConnected = true;
            break;
        case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE:


@@ 314,4 320,14 @@ namespace bluetooth
        stopRinging();
        establishAudioConnection();
    }

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

    void HSP::HSPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        HSP::HSPImpl::audioDevice = std::static_pointer_cast<HSPAudioDevice>(audioDevice);
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +1 -2
@@ 55,8 55,7 @@ namespace bluetooth
        /// @return Success
        [[nodiscard]] auto initializeCall() const noexcept -> Error::Code override;

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

      private:
        class HSPImpl;

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp +3 -1
@@ 27,6 27,7 @@ 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);

      private:
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);


@@ 43,5 44,6 @@ namespace bluetooth
        static bd_addr_t deviceAddr;
        static const sys::Service *ownerService;
        static bool isConnected;
        static std::shared_ptr<HSPAudioDevice> audioDevice;
    };
} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.cpp +0 -1
@@ 32,7 32,6 @@ namespace bluetooth
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;

      private:
        static constexpr auto CVSD_SAMPLE_RATE    = 8000;
        static constexpr auto BYTES_PER_FRAME     = 2;
        static constexpr auto ALL_GOOD_MASK       = 0x30;
        static constexpr auto AUDIO_BUFFER_LENGTH = 128;

M module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.hpp +3 -1
@@ 30,8 30,10 @@ namespace bluetooth
        [[nodiscard]] auto getStreamData() const -> std::shared_ptr<BluetoothStreamData>;
        void setOwnerService(const sys::Service *service);

        static constexpr auto CVSD_SAMPLE_RATE = 8000;

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

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +7 -3
@@ 97,6 97,7 @@ namespace bluetooth

    auto ProfileManager::startRinging() -> Error::Code
    {
        switchProfile(AudioProfile::HSP);
        return currentProfilePtr->startRinging();
    }



@@ 107,16 108,19 @@ namespace bluetooth

    auto ProfileManager::initializeCall() -> Error::Code
    {
        switchProfile(AudioProfile::HSP);
        return currentProfilePtr->initializeCall();
    }

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

        if (currentProfilePtr == nullptr || profilesList[profileType] == nullptr) {
            return Error::NotReady;
        }

        currentProfilePtr->setAudioDevice(std::move(device));
        return Error::Success;
        profilesList[profileType]->setAudioDevice(device);
        return switchProfile(profileType);
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +1 -8
@@ 6,6 6,7 @@
#include <Service/Service.hpp>
#include <Error.hpp>
#include "Profile.hpp"
#include "AudioProfile.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"
#include "audio/BluetoothAudioDevice.hpp"


@@ 21,14 22,6 @@ extern "C"
namespace bluetooth
{

    enum class AudioProfile
    {
        A2DP,
        HSP,
        HFP,
        None
    };

    using ProfileList = std::map<AudioProfile, std::shared_ptr<bluetooth::Profile>>;

    class ProfileManager

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +31 -13
@@ 26,11 26,11 @@ using namespace audio;

inline constexpr auto audioServiceStackSize = 1024 * 4;

static constexpr auto defaultVolumeHigh  = "10";
static constexpr auto defaultVolumeLow   = "2";
static constexpr auto defaultVolumeMuted = "0";
static constexpr auto defaultTrue        = "1";
static constexpr auto defaultFalse       = "0";
static constexpr auto defaultVolumeHigh              = "10";
static constexpr auto defaultVolumeLow               = "2";
static constexpr auto defaultVolumeMuted             = "0";
static constexpr auto defaultTrue                    = "1";
static constexpr auto defaultFalse                   = "0";
static constexpr auto defaultCallRingtonePath        = "assets/audio/ringtone/ringtone_drum_2.mp3";
static constexpr auto defaultTextMessageRingtonePath = "assets/audio/sms/sms_drum_2.mp3";
static constexpr auto defaultNotificationsPath       = "assets/audio/alarm/alarm_hang_drum.mp3";


@@ 153,9 153,14 @@ 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) {
        if (deviceMsg->getDeviceType() == AudioDevice::Type::BluetoothA2DP) {
            auto startBluetoothAudioMsg = std::make_shared<BluetoothAudioStartMessage>(
                std::static_pointer_cast<bluetooth::BluetoothAudioDevice>(deviceMsg->getDevice()));
                std::static_pointer_cast<bluetooth::A2DPAudioDevice>(deviceMsg->getDevice()));
            bus.sendUnicast(std::move(startBluetoothAudioMsg), service::name::bluetooth);
        }
        else if (deviceMsg->getDeviceType() == AudioDevice::Type::BluetoothHSP) {
            auto startBluetoothAudioMsg = std::make_shared<BluetoothAudioStartMessage>(
                std::static_pointer_cast<bluetooth::HSPAudioDevice>(deviceMsg->getDevice()));
            bus.sendUnicast(std::move(startBluetoothAudioMsg), service::name::bluetooth);
        }
    }


@@ 325,11 330,14 @@ 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) {
        if (bluetoothA2DPConnected || bluetoothHSPConnected) {
            if (playbackType == audio::PlaybackType::CallRingtone) {
                HandleSendEvent(std::make_shared<audio::Event>(EventType::BlutoothHSPDeviceState));

                LOG_DEBUG("Sending Bluetooth start ringing");
                bus.sendUnicast(std::make_shared<message::bluetooth::Ring>(message::bluetooth::Ring::State::Enable),
                                service::name::bluetooth);
                return std::make_unique<AudioStartPlaybackResponse>(audio::RetCode::Success, retToken);
            }
            else {
                LOG_DEBUG("Sending Bluetooth start stream request");


@@ 348,7 356,10 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:
    }
    else if (opType == Operation::Type::Router) {
        auto input = audioMux.GetRoutingInput(true);
        if (bluetoothConnected) {
        if (bluetoothA2DPConnected) {
            HandleSendEvent(std::make_shared<audio::Event>(EventType::BlutoothHSPDeviceState));
        }
        if (bluetoothHSPConnected) {
            LOG_DEBUG("Sending Bluetooth start routing");
            bus.sendUnicast(std::make_shared<message::bluetooth::StartAudioRouting>(), service::name::bluetooth);
        }


@@ 363,11 374,10 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleSendEvent(std::shared_
    // update bluetooth state
    if (evt->getType() == EventType::BlutoothA2DPDeviceState) {
        auto newState = evt->getDeviceState() == Event::DeviceState::Connected;
        if (newState != bluetoothConnected) {
        if (newState != bluetoothA2DPConnected) {
            LOG_DEBUG("Bluetooth connection status changed: %s", newState ? "connected" : "disconnected");
            bluetoothConnected = newState;
            bluetoothA2DPConnected = newState;
            HandleStop({audio::PlaybackType::Alarm,
                        audio::PlaybackType::CallRingtone,
                        audio::PlaybackType::Meditation,
                        audio::PlaybackType::Notifications,
                        audio::PlaybackType::TextMessageRingtone},


@@ 375,6 385,14 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleSendEvent(std::shared_
        }
    }

    if (evt->getType() == EventType::BlutoothHSPDeviceState) {
        auto newState = evt->getDeviceState() == Event::DeviceState::Connected;
        if (newState != bluetoothHSPConnected) {
            LOG_DEBUG("Bluetooth connection status changed: %s", newState ? "connected" : "disconnected");
            bluetoothHSPConnected = newState;
        }
    }

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


@@ 426,7 444,7 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStop(const std::vector
    }

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

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +2 -1
@@ 57,7 57,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;
    bool bluetoothConnected = false;
    bool bluetoothA2DPConnected = false;
    bool bluetoothHSPConnected  = false;

    auto IsVibrationMotorOn()
    {