~aleteoryx/muditaos

9c41f6f592b5bf2527a60d802673b10cc9065439 — Jakub Pyszczak 4 years ago 4a1801b
[EGD-6350] Added bluetooth device volume control

Bluetooth device volume control can be set by Pure Phone
while paired and is in A2DP mode. Temporarly disabled
HSP since it's not fully working and causing control issues.
M module-apps/messages/AppMessage.hpp => module-apps/messages/AppMessage.hpp +2 -2
@@ 196,8 196,8 @@ namespace app
        gui::InputEvent event;

      public:
        AppInputEventMessage(gui::InputEvent evt) : AppMessage(MessageType::AppInputEvent), event{evt} {};
        virtual ~AppInputEventMessage(){};
        explicit AppInputEventMessage(gui::InputEvent evt) : AppMessage(MessageType::AppInputEvent), event{evt}
        {}

        const gui::InputEvent &getEvent()
        {

M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +9 -0
@@ 3,6 3,7 @@

#include "BluetoothAudioDevice.hpp"

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

#include <cassert>


@@ 37,6 38,14 @@ auto BluetoothAudioDevice::Stop() -> audio::AudioDevice::RetCode

auto BluetoothAudioDevice::OutputVolumeCtrl(float vol) -> audio::AudioDevice::RetCode
{
    constexpr auto avrcpMaxVolume = std::uint8_t{0x7F}; // from AVRCP documentation
    const auto volumeToSet        = static_cast<std::uint8_t>((vol / audio::maxVolume) * avrcpMaxVolume);
    const auto status             = avrcp_controller_set_absolute_volume(ctx->avrcp_cid, volumeToSet);
    if (status != ERROR_CODE_SUCCESS) {
        LOG_ERROR("Can't set volume level. Status %x", status);
        return audio::AudioDevice::RetCode::Failure;
    }
    currentFormat.outputVolume = vol;
    return audio::AudioDevice::RetCode::Success;
}


M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp +1 -1
@@ 44,4 44,4 @@ namespace bluetooth
        static constexpr Capabilities btCapabilities = {.usesDMA = false, .minBlockSize = 512U, .maxBlockSize = 512U};
    };

}; // namespace bluetooth
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +1 -1
@@ 142,7 142,7 @@ namespace bluetooth
        AVRCP::mediaTracker.local_seid = avdtp_local_seid(local_stream_endpoint);
        avdtp_source_register_delay_reporting_category(AVRCP::mediaTracker.local_seid);

        AVRCP::init();
        AVRCP::init(const_cast<sys::Service *>(ownerService));
        // Initialize SDP,
        sdp_init();


M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp +22 -16
@@ 1,8 1,11 @@
// 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 "AVRCP.hpp"

#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/messages/AudioVolume.hpp>

namespace bluetooth
{



@@ 11,6 14,7 @@ namespace bluetooth
    MediaContext AVRCP::mediaTracker;
    std::array<uint8_t, 200> AVRCP::sdpTargetServiceBuffer;
    std::array<uint8_t, 200> AVRCP::sdpControllerServiceBuffer;
    sys::Service *AVRCP::ownerService = nullptr;

    void AVRCP::packetHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    {


@@ 64,10 68,11 @@ namespace bluetooth
        }
    }

    void AVRCP::targetPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    void AVRCP::targetPacketHandler(uint8_t packetType,
                                    [[maybe_unused]] uint16_t channel,
                                    uint8_t *packet,
                                    [[maybe_unused]] uint16_t size)
    {
        UNUSED(channel);
        UNUSED(size);
        uint8_t status = ERROR_CODE_SUCCESS;

        if (packetType != HCI_EVENT_PACKET) {


@@ 102,9 107,6 @@ namespace bluetooth
                                              AVRCP::playInfo.song_position_ms,
                                              AVRCP::playInfo.status);
            break;
            // case AVRCP_SUBEVENT_NOW_PLAYING_INFO_QUERY:
            //     status = avrcp_target_now_playing_info(avrcp_cid);
            //     break;
        case AVRCP_SUBEVENT_OPERATION: {
            auto operation_id = (avrcp_operation_id_t)avrcp_subevent_operation_get_operation_id(packet);
            switch (operation_id) {


@@ 136,10 138,11 @@ namespace bluetooth
        }
    }

    void AVRCP::controllerPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    void AVRCP::controllerPacketHandler(uint8_t packetType,
                                        [[maybe_unused]] uint16_t channel,
                                        [[maybe_unused]] uint8_t *packet,
                                        uint16_t size)
    {
        UNUSED(channel);
        UNUSED(size);
        uint8_t status = 0xFF;

        if (packetType != HCI_EVENT_PACKET) {


@@ 160,10 163,12 @@ namespace bluetooth
        }

        switch (packet[2]) {
        case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
            LOG_INFO("AVRCP Controller: notification absolute volume changed %d %%\n",
                     avrcp_subevent_notification_volume_changed_get_absolute_volume(packet) * 100 / 127);
            break;
        case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED: {
            const auto volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(packet);
            auto &busProxy    = AVRCP::ownerService->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::AudioVolume>(volume), service::name::bluetooth);
            LOG_INFO("AVRCP Controller: notification absolute volume changed %d %%\n", volume * 100 / 127);
        } break;
        case AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID:
            LOG_INFO("Remote supports EVENT_ID 0x%02x\n", avrcp_subevent_get_capability_event_id_get_event_id(packet));
            break;


@@ 176,8 181,9 @@ namespace bluetooth
            break;
        }
    }
    void AVRCP::init()
    void AVRCP::init(sys::Service *service)
    {
        AVRCP::ownerService = service;
        // Initialize AVRCP Service.
        avrcp_init();
        avrcp_register_packet_handler(&packetHandler);


@@ 189,4 195,4 @@ namespace bluetooth
        avrcp_controller_register_packet_handler(&controllerPacketHandler);
    }

} // namespace Bt
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp +3 -2
@@ 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


@@ 46,6 46,7 @@ namespace bluetooth

        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpTargetServiceBuffer;
        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpControllerServiceBuffer;
        static sys::Service *ownerService;

        static avrcp_track_t tracks[3];
        static int currentTrackIndex;


@@ 55,6 56,6 @@ namespace bluetooth
        static void packetHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void targetPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void controllerPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void init();
        static void init(sys::Service *service);
    };
} // namespace Bt

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +1 -1
@@ 15,7 15,7 @@ namespace bluetooth
    {
        if (!initialized) {
            profilesList = {{AudioProfile::A2DP, std::make_shared<bluetooth::A2DP>()},
                            {AudioProfile::HSP, std::make_shared<bluetooth::HSP>()},
                            {AudioProfile::HSP, nullptr},
                            {AudioProfile::HFP, nullptr},
                            {AudioProfile::None, nullptr}};


M module-services/service-audio/AudioServiceAPI.cpp => module-services/service-audio/AudioServiceAPI.cpp +4 -0
@@ 167,4 167,8 @@ namespace AudioServiceAPI
        return serv->bus.sendUnicast(msg, service::name::audio);
    }

    bool BluetoothVolumeChanged(sys::Service *serv, const uint8_t volume)
    {
        return serv->bus.sendUnicast(std::make_shared<BluetoothDeviceVolumeChanged>(volume), service::name::audio);
    }
} // namespace AudioServiceAPI

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +10 -1
@@ 168,6 168,9 @@ ServiceAudio::ServiceAudio()
    phoneModeObserver->subscribe([&](sys::phone_modes::PhoneMode phoneMode, sys::phone_modes::Tethering tetheringMode) {
        HandlePhoneModeChange(phoneMode, tetheringMode);
    });

    connect(typeid(BluetoothDeviceVolumeChanged),
            [this](sys::Message *msg) -> sys::MessagePointer { return handleVolumeChangedOnBluetoothDevice(msg); });
}

ServiceAudio::~ServiceAudio()


@@ 685,7 688,7 @@ std::string ServiceAudio::getSetting(const Setting &setting,
    std::string path = dbPath(setting, targetPlayback, targetProfile, phoneModeObserver->getCurrentPhoneMode());

    if (const auto set_it = settingsCache.find(path); settingsCache.end() != set_it) {
        LOG_ERROR("Get audio setting %s = %s", path.c_str(), set_it->second.c_str());
        LOG_INFO("Get audio setting %s = %s", path.c_str(), set_it->second.c_str());
        return set_it->second;
    }



@@ 772,3 775,9 @@ void ServiceAudio::settingsChanged(const std::string &name, std::string value)
    }
    LOG_ERROR("ServiceAudio::settingsChanged received notification about not registered setting: %s", name.c_str());
}
auto ServiceAudio::handleVolumeChangedOnBluetoothDevice(sys::Message *msgl) -> sys::MessagePointer
{
    auto *msg = static_cast<BluetoothDeviceVolumeChanged *>(msgl);
    LOG_WARN("Volume chnged on bt device to %u. Handler to be done", msg->getVolume());
    return sys::msgHandled();
}

M module-services/service-audio/service-audio/AudioMessage.hpp => module-services/service-audio/service-audio/AudioMessage.hpp +15 -0
@@ 278,3 278,18 @@ class AudioKeyPressedResponse : public sys::DataMessage
    const ShowPopup showPopup = ShowPopup::False;
    std::pair<audio::Profile::Type, audio::PlaybackType> context;
};

class BluetoothDeviceVolumeChanged : public AudioMessage
{
  public:
    explicit BluetoothDeviceVolumeChanged(std::uint8_t volume) : volume{volume}
    {}

    [[nodiscard]] auto getVolume() const noexcept -> std::uint8_t
    {
        return volume;
    }

  private:
    const std::uint8_t volume;
};

M module-services/service-audio/service-audio/AudioServiceAPI.hpp => module-services/service-audio/service-audio/AudioServiceAPI.hpp +3 -1
@@ 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


@@ 168,4 168,6 @@ namespace AudioServiceAPI
     *  Response will come as message AudioKeyPressedResponse
     */
    bool KeyPressed(sys::Service *serv, const int step);

    bool BluetoothVolumeChanged(sys::Service *serv, const uint8_t volume);
}; // namespace AudioServiceAPI

M module-services/service-audio/service-audio/ServiceAudio.hpp => module-services/service-audio/service-audio/ServiceAudio.hpp +1 -1
@@ 109,8 109,8 @@ class ServiceAudio : public sys::Service
                                         const audio::PlaybackType &playbackType);

    const std::pair<audio::Profile::Type, audio::PlaybackType> getCurrentContext();

    void settingsChanged(const std::string &name, std::string value);
    auto handleVolumeChangedOnBluetoothDevice(sys::Message *msgl) -> sys::MessagePointer;
};

namespace sys

M module-services/service-bluetooth/Constants.hpp => module-services/service-bluetooth/Constants.hpp +2 -2
@@ 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


@@ 6,4 6,4 @@
namespace service::name
{
    inline constexpr auto bluetooth = "ServiceBluetooth";
}
} // namespace service::name

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +10 -0
@@ 10,6 10,7 @@
#include <Service/Service.hpp>
#include <Service/Message.hpp>
#include <service-db/Settings.hpp>
#include "service-bluetooth/messages/AudioVolume.hpp"
#include "service-bluetooth/messages/Connect.hpp"
#include "service-bluetooth/messages/Disconnect.hpp"
#include "service-bluetooth/messages/Status.hpp"


@@ 26,6 27,7 @@
#include <service-desktop/service-desktop/DesktopMessages.hpp>
#include <service-desktop/endpoints/bluetooth/BluetoothEventMessages.hpp>
#include <service-desktop/endpoints/bluetooth/BluetoothHelper.hpp>
#include <service-audio/AudioServiceAPI.hpp>
#include <BtCommand.hpp>
#include <BtKeysStorage.hpp>
#include <Timers/TimerFactory.hpp>


@@ 73,6 75,7 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
    connectHandler<BluetoothAudioStartMessage>();
    connectHandler<BluetoothMessage>();
    connectHandler<BluetoothPairMessage>();
    connectHandler<message::bluetooth::AudioVolume>();
    connectHandler<message::bluetooth::Connect>();
    connectHandler<message::bluetooth::ConnectResult>();
    connectHandler<message::bluetooth::Disconnect>();


@@ 309,6 312,13 @@ auto ServiceBluetooth::handle(sdesktop::developerMode::DeveloperModeRequest *msg
    }
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::AudioVolume *msg) -> std::shared_ptr<sys::Message>
{
    AudioServiceAPI::BluetoothVolumeChanged(this, msg->getVolume());
    return sys::MessageNone{};
}

void ServiceBluetooth::startTimeoutTimer()
{
    if (connectionTimeoutTimer.isValid()) {

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +2 -0
@@ 44,6 44,7 @@ namespace message::bluetooth
    class ConnectResult;
    class Disconnect;
    class DisconnectResult;
    class AudioVolume;

} // namespace message::bluetooth



@@ 91,6 92,7 @@ class ServiceBluetooth : public sys::Service
    [[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>;
    [[nodiscard]] auto handle(message::bluetooth::AudioVolume *msg) -> std::shared_ptr<sys::Message>;
};

namespace sys

A module-services/service-bluetooth/service-bluetooth/messages/AudioVolume.hpp => module-services/service-bluetooth/service-bluetooth/messages/AudioVolume.hpp +24 -0
@@ 0,0 1,24 @@
// 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 "service-bluetooth/BluetoothMessage.hpp"

namespace message::bluetooth
{
    class AudioVolume : public BluetoothMessage
    {
      public:
        explicit AudioVolume(std::uint8_t volume) : volume{volume}
        {}

        [[nodiscard]] auto getVolume() const noexcept -> std::uint8_t
        {
            return volume;
        }

      private:
        const std::uint8_t volume;
    };
} // namespace message::bluetooth

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +1 -1
@@ 148,7 148,7 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy
        handleMinuteUpdate(msg->timestamp);
        handled = true;
    }
    else if (auto msg = dynamic_cast<AudioEventRequest *>(msgl); msg /*&& msgl->sender == this->GetName()*/) {
    else if (auto msg = dynamic_cast<AudioEventRequest *>(msgl); msg) {
        AudioServiceAPI::SendEvent(this, msg->getEvent());
        handled = true;
    }