From 305d43a1763d40bd804e215ede7780ac680c69fa Mon Sep 17 00:00:00 2001 From: Bartek Cichocki Date: Fri, 11 Sep 2020 09:52:25 +0200 Subject: [PATCH] [EGD-3773] added A2DP playback backend --- changelog.md | 2 + link_key_db | 0 module-apps/CMakeLists.txt | 8 +- .../windows/ScanModesWindow.cpp | 2 +- .../ApplicationSettings.cpp | 6 +- .../windows/BtScanWindow.hpp | 2 +- .../application-settings/windows/BtWindow.cpp | 18 +- module-audio/Audio/decoder/taglib | 2 +- .../Bluetooth/BluetoothWorker.cpp | 42 +- .../Bluetooth/BluetoothWorker.hpp | 17 +- module-bluetooth/Bluetooth/BtstackWorker.cpp | 8 +- module-bluetooth/Bluetooth/Device.hpp | 19 +- .../{interface/profiles => }/btstack_config.h | 19 +- .../Bluetooth/glucode/HCITRANS.cpp | 123 ---- .../Bluetooth/glucode/hal_time_ms.c | 3 +- .../interface/profiles/A2DP/A2DP.cpp | 577 ++++++++++++++++++ .../interface/profiles/A2DP/A2DP.hpp | 31 + .../interface/profiles/A2DP/A2DPImpl.hpp | 72 +++ .../interface/profiles/A2DP/AVDTP.cpp | 21 + .../interface/profiles/A2DP/AVDTP.hpp | 37 ++ .../interface/profiles/A2DP/AVRCP.cpp | 189 ++++++ .../interface/profiles/A2DP/AVRCP.hpp | 57 ++ .../Bluetooth/interface/profiles/GAP.cpp | 10 +- .../Bluetooth/interface/profiles/PAN.cpp | 12 +- .../Bluetooth/interface/profiles/Profile.hpp | 15 + module-bluetooth/CMakeLists.txt | 14 +- module-bluetooth/lib/btstack.cmake | 80 ++- .../rt1051/bluetooth/BluetoothCommon.cpp | 12 +- module-bsp/bsp/bluetooth/Bluetooth.cpp | 1 + module-bsp/bsp/bluetooth/Bluetooth.hpp | 1 + module-db/Interface/ThreadRecord.cpp | 2 +- .../service-bluetooth/ServiceBluetooth.cpp | 9 + .../service-bluetooth/ServiceBluetooth.hpp | 2 +- .../messages/BluetoothMessage.hpp | 41 +- source/MessageType.hpp | 2 + 35 files changed, 1256 insertions(+), 200 deletions(-) delete mode 100644 link_key_db rename module-bluetooth/Bluetooth/{interface/profiles => }/btstack_config.h (69%) create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp create mode 100644 module-bluetooth/Bluetooth/interface/profiles/Profile.hpp diff --git a/changelog.md b/changelog.md index 202d85898b7e5a28f28408cb1cdeba0e2d496eb7..238d334d3761dfd994e3f2b8cdfecec370117e8d 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,8 @@ ### Added * `[gui]` Dynamic Windows building and handling implemented +* `[audio]` Added A2DP playback backend + ### Fixed diff --git a/link_key_db b/link_key_db deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/module-apps/CMakeLists.txt b/module-apps/CMakeLists.txt index 005c8b602fd70c437acfc5a68547f9c85c69e35f..13b0fc3bb70be698e75e04de3b6626d2ee2279fc 100644 --- a/module-apps/CMakeLists.txt +++ b/module-apps/CMakeLists.txt @@ -14,13 +14,13 @@ endif() set( SOURCES "Application.cpp" "GuiTimer.cpp" - "UiCommonActions.cpp" - "WindowsFactory.cpp" + "UiCommonActions.cpp" + "WindowsFactory.cpp" "windows/AppWindow.cpp" "windows/OptionWindow.cpp" "windows/Options.cpp" - "windows/OptionsWindowOption.cpp" - "windows/Dialog.cpp" + "windows/OptionsWindowOption.cpp" + "windows/Dialog.cpp" "windows/NoEvents.cpp" "widgets/SearchBox.cpp" "windows/OptionSetting.cpp" diff --git a/module-apps/application-antenna/windows/ScanModesWindow.cpp b/module-apps/application-antenna/windows/ScanModesWindow.cpp index 4e26335b448151a078c8239357917e64f0c56e1c..ab4532583849e76bf664308813868cf720c7ae61 100644 --- a/module-apps/application-antenna/windows/ScanModesWindow.cpp +++ b/module-apps/application-antenna/windows/ScanModesWindow.cpp @@ -72,7 +72,7 @@ namespace gui } auto result = CellularServiceAPI::SetScanMode(this->application, mode); if (result) { - commandResult->setText("Succes!"); + commandResult->setText("Success!"); } else { commandResult->setText("Failure!"); diff --git a/module-apps/application-settings/ApplicationSettings.cpp b/module-apps/application-settings/ApplicationSettings.cpp index a5f0743b88ce3df5f409bd934428e15278afc711..4bef14d9b02441ea179a39aabfc589d97a17b006 100644 --- a/module-apps/application-settings/ApplicationSettings.cpp +++ b/module-apps/application-settings/ApplicationSettings.cpp @@ -9,10 +9,8 @@ #include "Application.hpp" #include "MessageType.hpp" -#include "windows/SettingsMainWindow.hpp" -#include "windows/LanguageWindow.hpp" -#include "windows/BtWindow.hpp" #include "windows/BtScanWindow.hpp" +#include "windows/BtWindow.hpp" #include "windows/DateTimeWindow.hpp" #include "windows/FotaWindow.hpp" #include "windows/Info.hpp" @@ -32,9 +30,9 @@ #include "windows/CellularPassthroughWindow.hpp" #include "windows/SettingsChange.hpp" +#include #include #include -#include namespace app { diff --git a/module-apps/application-settings/windows/BtScanWindow.hpp b/module-apps/application-settings/windows/BtScanWindow.hpp index cee7fa5631490307dfd26bbdc79e457301d935e1..bffc1958b3e7a6fc15adda67fe6e32ef7102a7c7 100644 --- a/module-apps/application-settings/windows/BtScanWindow.hpp +++ b/module-apps/application-settings/windows/BtScanWindow.hpp @@ -7,8 +7,8 @@ #include "gui/widgets/Image.hpp" #include "gui/widgets/Label.hpp" #include "gui/widgets/Window.hpp" -#include #include +#include #include namespace gui diff --git a/module-apps/application-settings/windows/BtWindow.cpp b/module-apps/application-settings/windows/BtWindow.cpp index 6756a9dbfaf5dc45065b08fa6c6743a90f9d7865..56d09ec0d1d822ef54a09b42f47f13b52c8bfe9a 100644 --- a/module-apps/application-settings/windows/BtWindow.cpp +++ b/module-apps/application-settings/windows/BtWindow.cpp @@ -62,7 +62,7 @@ namespace gui setTitle(utils::localize.get("app_settings_bt")); LOG_INFO("Create box layout"); - box = new gui::VBox(this, 0, title->offset_h(), style::window_width, 5 * style::window::label::default_h); + box = new gui::VBox(this, 0, title->offset_h(), style::window_width, 7 * style::window::label::default_h); box->setPenWidth(style::window::default_border_no_focus_w); // TODO WIP: it's just for usability now @@ -91,12 +91,24 @@ namespace gui return true; }); - add_box_label(box, " -> Visibility", [=](Item &) { - LOG_DEBUG("Callback visibility"); + add_box_label(box, " -> Set visible", [=](Item &) { + LOG_DEBUG("Callback set visibility"); message_bt(application, BluetoothMessage::Request::Visible); return true; }); + add_box_label(box, " -> Play", [=](Item &) { + LOG_DEBUG("Start playback"); + message_bt(application, BluetoothMessage::Request::Play); + return true; + }); + + add_box_label(box, " -> Stop", [=](Item &) { + LOG_DEBUG("Stop playback"); + message_bt(application, BluetoothMessage::Request::Stop); + return true; + }); + // hide all elements except button for `bluetooth on off` - this would cause infinite loop std::for_each(std::next(box->children.begin()), box->children.end(), [](auto &el) { el->visible = false; }); setFocusItem(box); diff --git a/module-audio/Audio/decoder/taglib b/module-audio/Audio/decoder/taglib index fa37896f615f3d347a41f0db711e23746774b15f..b2a6e50aedf0cfe1f808eb23dc9f572a848ddffe 160000 --- a/module-audio/Audio/decoder/taglib +++ b/module-audio/Audio/decoder/taglib @@ -1 +1 @@ -Subproject commit fa37896f615f3d347a41f0db711e23746774b15f +Subproject commit b2a6e50aedf0cfe1f808eb23dc9f572a848ddffe diff --git a/module-bluetooth/Bluetooth/BluetoothWorker.cpp b/module-bluetooth/Bluetooth/BluetoothWorker.cpp index 20ad02b0fb29ddc5a6ed4f2e59affdc8f93cdc79..57237075215c8d9ef6b323037603d9060d735e09 100644 --- a/module-bluetooth/Bluetooth/BluetoothWorker.cpp +++ b/module-bluetooth/Bluetooth/BluetoothWorker.cpp @@ -1,6 +1,7 @@ #include "BluetoothWorker.hpp" -#include "log/log.hpp" #include "BtCommand.hpp" +#include "log/log.hpp" +#include "module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp" extern "C" { @@ -24,7 +25,7 @@ const char *c_str(Bt::Error::Code code) return ""; } -BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service) +BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service), currentProfile(std::make_shared()) { init({ {"qBtIO", sizeof(Bt::Message), 10}, @@ -34,7 +35,9 @@ BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service) BluetoothWorker::~BluetoothWorker() { - if (this->bt_worker_task != nullptr) {} + if (this->bt_worker_task != nullptr) { + vTaskDelete(this->bt_worker_task); + } LOG_INFO("Worker removed"); } @@ -182,8 +185,37 @@ bool BluetoothWorker::handleMessage(uint32_t queueID) return true; } -void BluetoothWorker::set_addr(uint8_t *addr) + +void BluetoothWorker::initAudioBT() +{} + +bool BluetoothWorker::play_audio() +{ + auto profile = dynamic_cast(currentProfile.get()); + if (profile == nullptr) { + return false; + } + + profile->init(); + profile->setOwnerService(service); + profile->start(); + return true; +} +bool BluetoothWorker::stop_audio() +{ + auto profile = dynamic_cast(currentProfile.get()); + if (profile == nullptr) { + return false; + } + + profile->stop(); + return true; +} +void BluetoothWorker::set_addr(bd_addr_t addr) { Bt::GAP::do_pairing(addr); - LOG_INFO("ADDRESS %s SET!", bd_addr_to_str(addr)); + auto profile = dynamic_cast(currentProfile.get()); + if (profile != nullptr) { + profile->setDeviceAddress(addr); + } } diff --git a/module-bluetooth/Bluetooth/BluetoothWorker.hpp b/module-bluetooth/Bluetooth/BluetoothWorker.hpp index 70c9742e10fbe3a05c96a203b1fd92723ed35062..3540755f8222b521f65614ad12e84d5e57a531f4 100644 --- a/module-bluetooth/Bluetooth/BluetoothWorker.hpp +++ b/module-bluetooth/Bluetooth/BluetoothWorker.hpp @@ -1,14 +1,14 @@ #pragma once +#include "Device.hpp" +#include "Service/Worker.hpp" +#include "interface/profiles/Profile.hpp" #include -#include #include #include +#include #include -#include "Device.hpp" -#include "Service/Worker.hpp" - struct HCI; /// debug option for HCI (uart) commands debugging @@ -93,9 +93,16 @@ class BluetoothWorker : private sys::Worker bool start_pan(); + bool play_audio(); + + bool stop_audio(); + Error aud_init(); /// bluetooth stack id in use unsigned long active_features; void stop_scan(); - void set_addr(uint8_t *); + void set_addr(bd_addr_t addr); + void initAudioBT(); + + std::shared_ptr currentProfile; }; diff --git a/module-bluetooth/Bluetooth/BtstackWorker.cpp b/module-bluetooth/Bluetooth/BtstackWorker.cpp index cfbeeb6608bafe3c7b906711db0a3ad059ef716e..edfb5008aa4b178182d9fdaba17a7de6459651f8 100644 --- a/module-bluetooth/Bluetooth/BtstackWorker.cpp +++ b/module-bluetooth/Bluetooth/BtstackWorker.cpp @@ -1,16 +1,16 @@ -#include #include +#include extern "C" { #include +#include #include #include -#include +#include #include #include -#include #include #include @@ -27,9 +27,9 @@ extern "C" } #endif +#include "BtKeysStorage.hpp" #include #include -#include "BtKeysStorage.hpp" // #define TLV_DB_PATH_PREFIX "/tmp/btstack_" // #define TLV_DB_PATH_POSTFIX ".tlv" diff --git a/module-bluetooth/Bluetooth/Device.hpp b/module-bluetooth/Bluetooth/Device.hpp index 1f043ba3d5a94047cbf64f1550b607743736fd2c..9b633589425577ec4ac1340958f10a87eb8798ce 100644 --- a/module-bluetooth/Bluetooth/Device.hpp +++ b/module-bluetooth/Bluetooth/Device.hpp @@ -1,8 +1,9 @@ #pragma once -#include +#include #include -#include #include +#include +#include struct Device { @@ -36,3 +37,17 @@ struct Devicei : public Device memcpy(&address, addr, sizeof address); } }; + +struct DeviceMetadata_t +{ + unsigned int sampleRate; + unsigned short channels; + unsigned int samplesPerFrame; +}; + +constexpr unsigned int DATA_BUFFER_SIZE = 256 * 2; + +struct AudioData_t +{ + std::array data; +}; diff --git a/module-bluetooth/Bluetooth/interface/profiles/btstack_config.h b/module-bluetooth/Bluetooth/btstack_config.h similarity index 69% rename from module-bluetooth/Bluetooth/interface/profiles/btstack_config.h rename to module-bluetooth/Bluetooth/btstack_config.h index df1b1896b988570f2c497f2850995645244dc021..7d0a9325780243f087fa20f80120afb340cadf82 100644 --- a/module-bluetooth/Bluetooth/interface/profiles/btstack_config.h +++ b/module-bluetooth/Bluetooth/btstack_config.h @@ -11,9 +11,8 @@ #define HAVE_POSIX_FILE_IO #endif #define HAVE_BTSTACK_STDIN -// #define HAVE_POSIX_TIME +#define HAVE_POSIX_TIME #define HAVE_EM9304_PATCH_CONTAINER - // BTstack features that can be enabled // #define ENABLE_BLE #define ENABLE_CLASSIC @@ -30,7 +29,7 @@ #define ENABLE_LOG_ERROR #define ENABLE_LOG_INFO #define ENABLE_LOG_WARNING -// #define ENABLE_LOG_DEBUG +#define ENABLE_LOG_DEBUG #define ENABLE_SCO_OVER_HCI #define ENABLE_SDP_DES_DUMP // #define ENABLE_EHCILL @@ -39,6 +38,20 @@ #define HCI_INCOMING_PRE_BUFFER_SIZE 14 // sizeof benep heade, avoid memcpy #define HCI_ACL_PAYLOAD_SIZE (1691 + 4) +#define ENABLE_GATT_CLIENT_PAIRING +#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE +#define ENABLE_SEGGER_RTT +#define ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND +//#define HAVE_EMBEDDED_TIME_MS + +#define MAX_NR_HCI_CONNECTIONS 3 +#define MAX_NR_L2CAP_SERVICES 4 +#define MAX_NR_L2CAP_CHANNELS 10 +#define MAX_NR_RFCOMM_MULTIPLEXERS 2 +#define MAX_NR_RFCOMM_SERVICES 4 +#define MAX_NR_RFCOMM_CHANNELS 4 +#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 10 + // As an option - much slower (according to docs) // HCI Controller to Host Flow Control // #define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL diff --git a/module-bluetooth/Bluetooth/glucode/HCITRANS.cpp b/module-bluetooth/Bluetooth/glucode/HCITRANS.cpp index 2ad81b4c8555774883c86dd31369c4e796acc52e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/module-bluetooth/Bluetooth/glucode/HCITRANS.cpp +++ b/module-bluetooth/Bluetooth/glucode/HCITRANS.cpp @@ -1,123 +0,0 @@ -#include -#include "log/log.hpp" - -using namespace bsp; - -extern "C" -{ - -#include "HCITRANS.h" -#define TRANSPORT_ID 1 - - typedef void(BTPSAPI *HCITR_COMDataCallback_t)(unsigned int HCITransportID, - unsigned int DataLength, - unsigned char *DataBuffer, - unsigned long CallbackParameter); - void RxThread(void *Param); - - int BTPSAPI HCITR_COMOpen(HCI_COMMDriverInformation_t *COMMDriverInformation, - HCITR_COMDataCallback_t COMDataCallback, - unsigned long CallbackParameter) - { - - int ret = TRANSPORT_ID; - LOG_INFO("COM OPEN!"); - Bluetopia *bt = Bluetopia::getInstance(); - bt->set_reset(true); - bt->open(); - bt->com_cb = COMDataCallback; - bt->com_cb_param = CallbackParameter; - bt->rx_thread = xTaskCreate(RxThread, "RxThread", 4096, NULL, 3, &bt->thandle); - if (bt->rx_thread != pdPASS) { - ret = ErrorBtGeneric; - LOG_ERROR("COM OPEN failure: %d", bt->rx_thread); - } - LOG_INFO("COM OPEN! done"); - return ret; - } - - void BTPSAPI HCITR_COMClose(unsigned int HCITransportID) - { - LOG_ERROR("COM CLOSE"); - Bluetopia *dev = Bluetopia::getInstance(); - if (dev->thandle != NULL) { - // vTaskDelete(bt->thandle); - } - dev->close(); - dev->set_reset(false); - if (dev->com_cb) { - dev->com_cb(TRANSPORT_ID, 0, NULL, dev->com_cb_param); - } - LOG_ERROR("COM CLOSED"); - } - - void BTPSAPI HCITR_COMReconfigure(unsigned int HCITransportID, HCI_Driver_Reconfigure_Data_t *DriverReconfigureData) - { - HCI_COMMReconfigureInformation_t *ReconfigureInformation; - Bluetopia *dev = Bluetopia::getInstance(); - if ((HCITransportID == TRANSPORT_ID) && dev->is_open && (DriverReconfigureData)) { - if ((DriverReconfigureData->ReconfigureCommand == - HCI_COMM_DRIVER_RECONFIGURE_DATA_COMMAND_CHANGE_COMM_PARAMETERS) && - (DriverReconfigureData->ReconfigureData)) { - ReconfigureInformation = (HCI_COMMReconfigureInformation_t *)(DriverReconfigureData->ReconfigureData); - if (ReconfigureInformation->ReconfigureFlags & - HCI_COMM_RECONFIGURE_INFORMATION_RECONFIGURE_FLAGS_CHANGE_BAUDRATE) { - LOG_INFO("Set baudrate to: %d", ReconfigureInformation->BaudRate); - dev->set_irq(false); - dev->set_baudrate(ReconfigureInformation->BaudRate); - dev->set_irq(true); - } - } - } - } - - int BTPSAPI HCITR_COMWrite(unsigned int HCITransportID, unsigned int Length, unsigned char *Buffer) - { - int ret = 0; - // LOG_INFO("DATA -> [%d]", Length); - if (HCITransportID) { - ret = Bluetopia::getInstance()->write_blocking(reinterpret_cast(Buffer), Length); - } - else { - ret = -1; - } - return ret; - } - - int BTPSAPI HCITR_COMSuspend(unsigned int HCITransportID) - { - LOG_ERROR("Not implemented!"); - int ret = -1; - if (HCITransportID) {} - else { - ret = -1; - } - return ret; - } - - int BTPSAPI HCITR_EnableDebugLogging(Boolean_t Enable) - { - return 0; - } - - void RxThread(void *Param) - { - LOG_INFO("BT RxThread created"); - Bluetopia *dev = Bluetopia::getInstance(); - while (dev->is_open) { - dev->wait_data(); - dev->set_irq(false); - // LOG_INFO("DATA [%d]<--", dev->in.len); - // for(int i=0; iin.len; ++i) { - // LOG_PRINTF("0x%X ", dev->in.buff[i]); - // } - // LOG_PRINTF("\n"); - if (dev->com_cb) { - dev->com_cb(TRANSPORT_ID, dev->in.len, (unsigned char *)dev->in.buff, dev->com_cb_param); - } - dev->in.flush(); - dev->set_irq(true); - dev->set_rts(true); - } - } -}; diff --git a/module-bluetooth/Bluetooth/glucode/hal_time_ms.c b/module-bluetooth/Bluetooth/glucode/hal_time_ms.c index 4101614d7772f11a2a62acc41f005b21c8db1f63..387ac15a730806d4161f616b0dcbb6461f952bad 100644 --- a/module-bluetooth/Bluetooth/glucode/hal_time_ms.c +++ b/module-bluetooth/Bluetooth/glucode/hal_time_ms.c @@ -1,6 +1,7 @@ +#include "btstack_debug.h" #include -#include #include +#include uint32_t hal_time_ms(void) { diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a03828ab9c339106d9309dcb76a1ecf82a19bc41 --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp @@ -0,0 +1,577 @@ +// +// Created by bartek on 14.09.2020. +// + +#include "A2DP.hpp" +#include "A2DPImpl.hpp" +#include "AVDTP.hpp" +#include "AVRCP.hpp" +#include +#include +#include +#include +#include +extern "C" +{ +#include "module-bluetooth/lib/btstack/src/btstack.h" +#include +} + +namespace Bt +{ + A2DP::A2DP() : pimpl(std::make_unique(A2DPImpl())) + {} + + A2DP::~A2DP() = default; + + A2DP::A2DP(A2DP &other) : pimpl(new A2DPImpl(*other.pimpl)) + {} + + auto A2DP::operator=(A2DP rhs) -> A2DP & + { + swap(pimpl, rhs.pimpl); + return *this; + } + + A2DP::A2DP(A2DP &&other) noexcept : pimpl(std::move(other.pimpl)) + { + other.pimpl = nullptr; + } + + A2DP &A2DP::operator=(A2DP &&other) noexcept + { + if (&other == this) + return *this; + + pimpl = std::move(other.pimpl); + other.pimpl = nullptr; + + return *this; + } + + auto A2DP::init() -> Error + { + return pimpl->init(); + } + void A2DP::start() + { + pimpl->start(); + } + void A2DP::stop() + { + pimpl->stop(); + } + void A2DP::setDeviceAddress(uint8_t *addr) + { + pimpl->setDeviceAddress(addr); + } + void A2DP::setOwnerService(sys::Service *service) + { + pimpl->setOwnerService(service); + } + + sys::Service *A2DP::A2DPImpl::ownerService; + QueueHandle_t A2DP::A2DPImpl::sourceQueue = nullptr; + QueueHandle_t A2DP::A2DPImpl::sinkQueue = nullptr; + bd_addr_t A2DP::A2DPImpl::deviceAddr; + btstack_packet_callback_registration_t A2DP::A2DPImpl::hciEventCallbackRegistration; + std::array A2DP::A2DPImpl::sdpSourceServiceBuffer; + std::array A2DP::A2DPImpl::mediaSbcCodecCapabilities = { + (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO, + 0xFF, //(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS, + 2, + 53}; + + /* LISTING_START(MainConfiguration): Setup Audio Source and AVRCP Target services */ + + auto A2DP::A2DPImpl::init() -> Error + { + // request role change on reconnecting headset to always use them in slave mode + hci_set_master_slave_policy(0); + + l2cap_init(); + // Initialize A2DP Source. + a2dp_source_init(); + a2dp_source_register_packet_handler(&sourcePacketHandler); + + // Create stream endpoint. + avdtp_stream_endpoint_t *local_stream_endpoint = + a2dp_source_create_stream_endpoint(AVDTP_AUDIO, + AVDTP_CODEC_SBC, + mediaSbcCodecCapabilities.data(), + mediaSbcCodecCapabilities.size(), + AVDTP::sbcCodecConfiguration.data(), + AVDTP::sbcCodecConfiguration.size()); + if (local_stream_endpoint == nullptr) { + LOG_INFO("A2DP Source: not enough memory to create local stream endpoint\n"); + return Error(Bt::Error::SystemError); + } + AVRCP::mediaTracker.local_seid = avdtp_local_seid(local_stream_endpoint); + avdtp_source_register_delay_reporting_category(AVRCP::mediaTracker.local_seid); + + AVRCP::init(); + // Initialize SDP, + sdp_init(); + + // Create A2DP Source service record and register it with SDP. + sdpSourceServiceBuffer.fill(0); + a2dp_source_create_sdp_record( + sdpSourceServiceBuffer.data(), 0x10001, AVDTP_SOURCE_FEATURE_MASK_PLAYER, nullptr, nullptr); + sdp_register_service(sdpSourceServiceBuffer.data()); + + // Create AVRCP target service record and register it with SDP. + AVRCP::sdpTargetServiceBuffer.fill(0); + uint16_t supportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER; +#ifdef AVRCP_BROWSING_ENABLED + supported_features |= AVRCP_FEATURE_MASK_BROWSING; +#endif + avrcp_target_create_sdp_record( + AVRCP::sdpTargetServiceBuffer.data(), 0x10002, supportedFeatures, nullptr, nullptr); + sdp_register_service(AVRCP::sdpTargetServiceBuffer.data()); + + // setup AVRCP Controller + AVRCP::sdpControllerServiceBuffer.fill(0); + uint16_t controllerSupportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER; + avrcp_controller_create_sdp_record( + AVRCP::sdpControllerServiceBuffer.data(), 0x10003, controllerSupportedFeatures, nullptr, nullptr); + sdp_register_service(AVRCP::sdpControllerServiceBuffer.data()); + + // Set local name with a template Bluetooth address, that will be automatically + // replaced with a actual address once it is available, i.e. when BTstack boots + // up and starts talking to a Bluetooth module. + gap_set_local_name("PurePhone"); + gap_discoverable_control(1); + gap_set_class_of_device(0x200408); + + // Register for HCI events. + hciEventCallbackRegistration.callback = &hciPacketHandler; + hci_add_event_handler(&hciEventCallbackRegistration); + + LOG_INFO("Init done!"); + + return Error(); + } + + void A2DP::A2DPImpl::sendMediaPacket() + { + int numBytesInFrame = btstack_sbc_encoder_sbc_buffer_length(); + int bytesInStorage = AVRCP::mediaTracker.sbc_storage_count; + uint8_t numFrames = bytesInStorage / numBytesInFrame; + a2dp_source_stream_send_media_payload(AVRCP::mediaTracker.a2dp_cid, + AVRCP::mediaTracker.local_seid, + AVRCP::mediaTracker.sbc_storage, + bytesInStorage, + numFrames, + 0); + AVRCP::mediaTracker.sbc_storage_count = 0; + 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); + LOG_ERROR("Failed to receive!"); + } + } + 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(btstack_run_loop_get_timer_context(timer)); + btstack_run_loop_set_timer(&context->audio_timer, AUDIO_TIMEOUT_MS); + btstack_run_loop_add_timer(&context->audio_timer); + uint32_t now = btstack_run_loop_get_time_ms(); + + uint32_t updatePeriodMs = AUDIO_TIMEOUT_MS; + if (context->time_audio_data_sent_in_ms > 0) { + updatePeriodMs = now - context->time_audio_data_sent_in_ms; + } + + uint32_t numSamples = (updatePeriodMs * AVDTP::sampleRate) / 1000; + context->acc_num_missed_samples += (updatePeriodMs * AVDTP::sampleRate) % 1000; + + while (context->acc_num_missed_samples >= 1000) { + numSamples++; + context->acc_num_missed_samples -= 1000; + } + context->time_audio_data_sent_in_ms = now; + context->samples_ready += numSamples; + + if (context->sbc_ready_to_send != 0u) { + return; + } + + fillSbcAudioBuffer(context); + + if ((context->sbc_storage_count + btstack_sbc_encoder_sbc_buffer_length()) > context->max_media_payload_size) { + // schedule sending + context->sbc_ready_to_send = 1; + a2dp_source_stream_endpoint_request_can_send_now(context->a2dp_cid, context->local_seid); + } + } + + void A2DP::A2DPImpl::startTimer(MediaContext *context) + { + LOG_DEBUG("Timer start"); + + context->max_media_payload_size = + btstack_min(a2dp_max_media_payload_size(context->a2dp_cid, context->local_seid), SBC_STORAGE_SIZE); + context->sbc_storage_count = 0; + context->sbc_ready_to_send = 0; + context->streaming = 1; + btstack_run_loop_remove_timer(&context->audio_timer); + btstack_run_loop_set_timer_handler(&context->audio_timer, audioTimeoutHandler); + btstack_run_loop_set_timer_context(&context->audio_timer, context); + btstack_run_loop_set_timer(&context->audio_timer, A2DP::A2DPImpl::AUDIO_TIMEOUT_MS); + btstack_run_loop_add_timer(&context->audio_timer); + } + + void A2DP::A2DPImpl::stopTimer(MediaContext *context) + { + LOG_DEBUG("Timer stop"); + + context->time_audio_data_sent_in_ms = 0; + context->acc_num_missed_samples = 0; + context->samples_ready = 0; + context->streaming = 1; + context->sbc_storage_count = 0; + context->sbc_ready_to_send = 0; + btstack_run_loop_remove_timer(&context->audio_timer); + } + + void A2DP::A2DPImpl::hciPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size) + { + if (packetType != HCI_EVENT_PACKET) { + return; + } + + if (hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST) { + bd_addr_t address; + LOG_INFO("Pin code request - using '0000'\n"); + hci_event_pin_code_request_get_bd_addr(packet, address); + gap_pin_code_response(address, "0000"); + } + } + + void A2DP::A2DPImpl::sourcePacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size) + { + uint8_t status; + uint8_t local_seid; + bd_addr_t address; + uint16_t cid; + + if (packetType != HCI_EVENT_PACKET) { + return; + } + if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) { + return; + } + + switch (hci_event_a2dp_meta_get_subevent_code(packet)) { + case A2DP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED: + a2dp_subevent_signaling_connection_established_get_bd_addr(packet, address); + cid = a2dp_subevent_signaling_connection_established_get_a2dp_cid(packet); + status = a2dp_subevent_signaling_connection_established_get_status(packet); + + if (status != ERROR_CODE_SUCCESS) { + LOG_INFO("A2DP Source: Connection failed, status 0x%02x, cid 0x%02x, a2dp_cid 0x%02x \n", + status, + cid, + AVRCP::mediaTracker.a2dp_cid); + AVRCP::mediaTracker.a2dp_cid = 0; + break; + } + AVRCP::mediaTracker.a2dp_cid = cid; + AVRCP::mediaTracker.volume = 64; + + LOG_INFO("A2DP Source: Connected to address %s, a2dp cid 0x%02x, local seid %d.\n", + bd_addr_to_str(address), + AVRCP::mediaTracker.a2dp_cid, + AVRCP::mediaTracker.local_seid); + break; + + case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION: { + cid = avdtp_subevent_signaling_media_codec_sbc_configuration_get_avdtp_cid(packet); + if (cid != AVRCP::mediaTracker.a2dp_cid) { + return; + } + AVRCP::mediaTracker.remote_seid = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_acp_seid(packet); + + AVDTP::sbcConfig.reconfigure = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet); + AVDTP::sbcConfig.numChannels = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(packet); + AVDTP::sbcConfig.samplingFrequency = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet); + AVDTP::sbcConfig.channelMode = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(packet); + AVDTP::sbcConfig.blockLength = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_block_length(packet); + AVDTP::sbcConfig.subbands = a2dp_subevent_signaling_media_codec_sbc_configuration_get_subbands(packet); + AVDTP::sbcConfig.allocationMethod = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(packet); + AVDTP::sbcConfig.minBitpoolValue = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(packet); + AVDTP::sbcConfig.maxBitpoolValue = + a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet); + AVDTP::sbcConfig.framesPerBuffer = AVDTP::sbcConfig.subbands * AVDTP::sbcConfig.blockLength; + LOG_INFO( + "A2DP Source: Received SBC codec configuration, sampling frequency %u, a2dp_cid 0x%02x, local seid %d " + "(expected %d), remote seid %d .\n", + AVDTP::sbcConfig.samplingFrequency, + cid, + a2dp_subevent_signaling_media_codec_sbc_configuration_get_int_seid(packet), + AVRCP::mediaTracker.local_seid, + AVRCP::mediaTracker.remote_seid); + + // Adapt Bluetooth spec definition to SBC Encoder expected input + AVDTP::sbcConfig.allocationMethod -= 1; + AVDTP::sbcConfig.numChannels = 2; + switch (AVDTP::sbcConfig.channelMode) { + case AVDTP_SBC_JOINT_STEREO: + AVDTP::sbcConfig.channelMode = 3; + break; + case AVDTP_SBC_STEREO: + AVDTP::sbcConfig.channelMode = 2; + break; + case AVDTP_SBC_DUAL_CHANNEL: + AVDTP::sbcConfig.channelMode = 1; + break; + case AVDTP_SBC_MONO: + AVDTP::sbcConfig.channelMode = 0; + AVDTP::sbcConfig.numChannels = 1; + break; + } + AVDTP::dumpSbcConfiguration(); + + btstack_sbc_encoder_init(&AVDTP::sbcEncoderState, + SBC_MODE_STANDARD, + AVDTP::sbcConfig.blockLength, + AVDTP::sbcConfig.subbands, + AVDTP::sbcConfig.allocationMethod, + AVDTP::sbcConfig.samplingFrequency, + AVDTP::sbcConfig.maxBitpoolValue, + AVDTP::sbcConfig.channelMode); + + DeviceMetadata_t metadata{ + .sampleRate = static_cast(AVDTP::sbcConfig.samplingFrequency), + .channels = static_cast(AVDTP::sbcConfig.numChannels), + .samplesPerFrame = static_cast(btstack_sbc_encoder_num_audio_frames()), + }; + + auto msg = std::make_shared(std::move(metadata)); + sys::Bus::SendUnicast(std::move(msg), "ServiceBluetooth", ownerService); + + break; + } + + case A2DP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY: + LOG_INFO("A2DP Source: remote supports delay report, remote seid %d\n", + avdtp_subevent_signaling_delay_reporting_capability_get_remote_seid(packet)); + break; + case A2DP_SUBEVENT_SIGNALING_CAPABILITIES_DONE: + LOG_INFO("A2DP Source: All capabilities reported, remote seid %d\n", + avdtp_subevent_signaling_capabilities_done_get_remote_seid(packet)); + break; + + case A2DP_SUBEVENT_SIGNALING_DELAY_REPORT: + LOG_INFO("A2DP Source: Received delay report of %d.%0d ms, local seid %d\n", + avdtp_subevent_signaling_delay_report_get_delay_100us(packet) / 10, + avdtp_subevent_signaling_delay_report_get_delay_100us(packet) % 10, + avdtp_subevent_signaling_delay_report_get_local_seid(packet)); + break; + + case A2DP_SUBEVENT_STREAM_ESTABLISHED: + a2dp_subevent_stream_established_get_bd_addr(packet, address); + status = a2dp_subevent_stream_established_get_status(packet); + if (status != 0u) { + LOG_INFO("A2DP Source: Stream failed, status 0x%02x.\n", status); + break; + } + + local_seid = a2dp_subevent_stream_established_get_local_seid(packet); + cid = a2dp_subevent_stream_established_get_a2dp_cid(packet); + LOG_INFO("A2DP_SUBEVENT_STREAM_ESTABLISHED: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid %d " + "(expected %d), remote_seid %d (expected %d)\n", + AVRCP::mediaTracker.a2dp_cid, + cid, + local_seid, + AVRCP::mediaTracker.local_seid, + a2dp_subevent_stream_established_get_remote_seid(packet), + AVRCP::mediaTracker.remote_seid); + + if (local_seid != AVRCP::mediaTracker.local_seid) { + LOG_INFO("A2DP Source: Stream failed, wrong local seid %d, expected %d.\n", + local_seid, + AVRCP::mediaTracker.local_seid); + break; + } + LOG_INFO("A2DP Source: Stream established, address %s, a2dp cid 0x%02x, local seid %d, remote seid %d.\n", + bd_addr_to_str(address), + AVRCP::mediaTracker.a2dp_cid, + AVRCP::mediaTracker.local_seid, + a2dp_subevent_stream_established_get_remote_seid(packet)); + + sourceQueue = xQueueCreate(5, sizeof(AudioData_t)); + sinkQueue = nullptr; + if (sourceQueue != nullptr) { + auto msg = std::make_shared(sourceQueue, sinkQueue); + sys::Bus::SendUnicast(std::move(msg), "ServiceBluetooth", ownerService); + } + else { + LOG_ERROR("failed to create queue!"); + } + + AVRCP::mediaTracker.stream_opened = 1; + a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid); + break; + + case A2DP_SUBEVENT_STREAM_RECONFIGURED: + status = a2dp_subevent_stream_reconfigured_get_status(packet); + local_seid = a2dp_subevent_stream_reconfigured_get_local_seid(packet); + cid = a2dp_subevent_stream_reconfigured_get_a2dp_cid(packet); + + LOG_INFO("A2DP Source: Reconfigured: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, " + "received %d]\n", + AVRCP::mediaTracker.a2dp_cid, + cid, + AVRCP::mediaTracker.local_seid, + local_seid); + LOG_INFO("Status 0x%02x\n", status); + break; + + case A2DP_SUBEVENT_STREAM_STARTED: + local_seid = a2dp_subevent_stream_started_get_local_seid(packet); + cid = a2dp_subevent_stream_started_get_a2dp_cid(packet); + + AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_PLAYING; + if (AVRCP::mediaTracker.avrcp_cid != 0u) { + avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_PLAYING); + } + startTimer(&AVRCP::mediaTracker); + LOG_INFO( + "A2DP Source: Stream started: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, " + "received %d]\n", + AVRCP::mediaTracker.a2dp_cid, + cid, + AVRCP::mediaTracker.local_seid, + local_seid); + break; + + case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW: + local_seid = a2dp_subevent_streaming_can_send_media_packet_now_get_local_seid(packet); + cid = a2dp_subevent_signaling_media_codec_sbc_configuration_get_a2dp_cid(packet); + // LOG_INFO("A2DP Source: can send media packet: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid + // [expected %d, received %d]\n", AVRCP::mediaTracker.a2dp_cid, cid, AVRCP::mediaTracker.local_seid, + // local_seid); + sendMediaPacket(); + break; + + case A2DP_SUBEVENT_STREAM_SUSPENDED: + local_seid = a2dp_subevent_stream_suspended_get_local_seid(packet); + cid = a2dp_subevent_stream_suspended_get_a2dp_cid(packet); + + AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_PAUSED; + if (AVRCP::mediaTracker.avrcp_cid != 0u) { + avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_PAUSED); + } + LOG_INFO( + "A2DP Source: Stream paused: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, " + "received %d]\n", + AVRCP::mediaTracker.a2dp_cid, + cid, + AVRCP::mediaTracker.local_seid, + local_seid); + + stopTimer(&AVRCP::mediaTracker); + break; + + case A2DP_SUBEVENT_STREAM_RELEASED: + AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_STOPPED; + cid = a2dp_subevent_stream_released_get_a2dp_cid(packet); + local_seid = a2dp_subevent_stream_released_get_local_seid(packet); + + LOG_INFO( + "A2DP Source: Stream released: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, " + "received %d]\n", + AVRCP::mediaTracker.a2dp_cid, + cid, + AVRCP::mediaTracker.local_seid, + local_seid); + + if (cid == AVRCP::mediaTracker.a2dp_cid) { + AVRCP::mediaTracker.stream_opened = 0; + LOG_INFO("A2DP Source: Stream released.\n"); + } + if (AVRCP::mediaTracker.avrcp_cid != 0u) { + + avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_STOPPED); + } + + stopTimer(&AVRCP::mediaTracker); + break; + case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED: + cid = a2dp_subevent_signaling_connection_released_get_a2dp_cid(packet); + if (cid == AVRCP::mediaTracker.a2dp_cid) { + AVRCP::mediaTracker.avrcp_cid = 0; + AVRCP::mediaTracker.a2dp_cid = 0; + LOG_INFO("A2DP Source: Signaling released.\n\n"); + } + break; + default: + break; + } + } + + void A2DP::A2DPImpl::start() + { + LOG_INFO("Starting playback to %s", bd_addr_to_str(deviceAddr)); + a2dp_source_establish_stream(deviceAddr, AVRCP::mediaTracker.local_seid, &AVRCP::mediaTracker.a2dp_cid); + } + + void A2DP::A2DPImpl::stop() + { + LOG_INFO("Stopping playback"); + a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid); + l2cap_unregister_service(1); + }; + + void A2DP::A2DPImpl::setDeviceAddress(bd_addr_t addr) + { + bd_addr_copy(deviceAddr, addr); + LOG_INFO("Address set!"); + } + void A2DP::A2DPImpl::setOwnerService(sys::Service *service) + { + ownerService = service; + } + +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cd5fe0c7faa57baaccfeb5241d817365ced55eae --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "FreeRTOS.h" +#include "Profile.hpp" +#include "Service/Service.hpp" +#include "queue.h" +#include +namespace Bt +{ + class A2DP : public Profile + { + public: + A2DP(); + ~A2DP() override; + + A2DP(A2DP &other); + auto operator=(A2DP rhs) -> A2DP &; + A2DP(A2DP &&other) noexcept; + auto operator=(A2DP &&other) noexcept -> A2DP &; + + auto init() -> Error override; + void setDeviceAddress(uint8_t *addr) override; + void setOwnerService(sys::Service *service); + void start(); + void stop(); + + private: + class A2DPImpl; + std::unique_ptr pimpl; + }; +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp new file mode 100644 index 0000000000000000000000000000000000000000..85dfd65c0dafeb5f4902294a0eca825e73d58276 --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp @@ -0,0 +1,72 @@ +#pragma once +#include "A2DP.hpp" +#include +#include +#include +#include +extern "C" +{ +#include +#include +} + +namespace Bt +{ + 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 + { + + static constexpr int NUM_CHANNELS = 2; + static constexpr int BYTES_PER_AUDIO_SAMPLE = (2 * NUM_CHANNELS); + static constexpr int AUDIO_TIMEOUT_MS = 10; + static constexpr int TABLE_SIZE_441HZ = 100; + static constexpr int SDP_BUFFER_LENGTH = 150; + static constexpr int MEDIA_CAP_SIZE = 4; + + static std::array sdpSourceServiceBuffer; + static bd_addr_t deviceAddr; + static btstack_packet_callback_registration_t hciEventCallbackRegistration; + static std::array mediaSbcCodecCapabilities; + static sys::Service *ownerService; + static QueueHandle_t sourceQueue; + static QueueHandle_t sinkQueue; + + static void startTimer(MediaContext *context); + static void stopTimer(MediaContext *context); + static void audioTimeoutHandler(btstack_timer_source_t *timer); + 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; + + public: + auto init() -> Error; + void start(); + void stop(); + void setDeviceAddress(bd_addr_t addr); + void setOwnerService(sys::Service *service); + }; +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3cc3568e15462de244543da350fb4f25ce08b8a --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp @@ -0,0 +1,21 @@ +#include "AVDTP.hpp" + +namespace Bt +{ + AVDTP::SbcConfiguration AVDTP::sbcConfig; + btstack_sbc_encoder_state_t AVDTP::sbcEncoderState; + std::array AVDTP::sbcCodecConfiguration; + int AVDTP::sampleRate = AVDTP::defaultSampleRate; + + void AVDTP::dumpSbcConfiguration() + { + LOG_INFO("Received media codec configuration:"); + LOG_INFO(" - numChannels: %d", sbcConfig.numChannels); + LOG_INFO(" - samplingFrequency: %d", sbcConfig.samplingFrequency); + LOG_INFO(" - channelMode: %d", sbcConfig.channelMode); + LOG_INFO(" - blockLength: %d", sbcConfig.blockLength); + LOG_INFO(" - subbands: %d", sbcConfig.subbands); + LOG_INFO(" - allocationMethod: %d", sbcConfig.allocationMethod); + LOG_INFO(" - bitpool_value [%d, %d] ", sbcConfig.minBitpoolValue, sbcConfig.maxBitpoolValue); + } +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp new file mode 100644 index 0000000000000000000000000000000000000000..eb0a2149beb4a0d014ad5cdcb4996a429dab24d6 --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "A2DPImpl.hpp" +extern "C" +{ +#include +#include +#include +} +namespace Bt +{ + + class AVDTP + { + public: + struct SbcConfiguration + { + int reconfigure; + int numChannels; + int samplingFrequency; + int channelMode; + int blockLength; + int subbands; + int allocationMethod; + int minBitpoolValue; + int maxBitpoolValue; + int framesPerBuffer; + }; + static SbcConfiguration sbcConfig; + static btstack_sbc_encoder_state_t sbcEncoderState; + static std::array sbcCodecConfiguration; + static constexpr int defaultSampleRate = 44100; + static int sampleRate; + + static void dumpSbcConfiguration(); + }; +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d9d62803b0d68df7aa0124d5f5806787d758f89 --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp @@ -0,0 +1,189 @@ +#include "AVRCP.hpp" + +namespace Bt +{ + + AVRCP::PlaybackStatusInfo AVRCP::playInfo; + int AVRCP::currentTrackIndex; + MediaContext AVRCP::mediaTracker; + std::array AVRCP::sdpTargetServiceBuffer; + std::array AVRCP::sdpControllerServiceBuffer; + + void AVRCP::packetHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size) + { + bd_addr_t event_addr; + uint16_t local_cid; + uint8_t status = ERROR_CODE_SUCCESS; + + if (packetType != HCI_EVENT_PACKET) { + return; + } + if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) { + return; + } + + switch (packet[2]) { + case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: + local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet); + status = avrcp_subevent_connection_established_get_status(packet); + if (status != ERROR_CODE_SUCCESS) { + LOG_INFO("AVRCP: Connection failed, local cid 0x%02x, status 0x%02x\n", local_cid, status); + return; + } + AVRCP::mediaTracker.avrcp_cid = local_cid; + avrcp_subevent_connection_established_get_bd_addr(packet, event_addr); + + avrcp_target_set_now_playing_info( + AVRCP::mediaTracker.avrcp_cid, NULL, sizeof(AVRCP::tracks) / sizeof(avrcp_track_t)); + avrcp_target_set_unit_info(AVRCP::mediaTracker.avrcp_cid, AVRCP_SUBUNIT_TYPE_AUDIO, AVRCP::companyId); + avrcp_target_set_subunit_info(AVRCP::mediaTracker.avrcp_cid, + AVRCP_SUBUNIT_TYPE_AUDIO, + (uint8_t *)AVRCP::subunitInfo, + sizeof(AVRCP::subunitInfo)); + + avrcp_controller_get_supported_events(AVRCP::mediaTracker.avrcp_cid); + + LOG_INFO("AVRCP: Channel successfully opened: A2DP::mediaTracker.avrcp_cid 0x%02x\n", + AVRCP::mediaTracker.avrcp_cid); + return; + + case AVRCP_SUBEVENT_CONNECTION_RELEASED: + LOG_INFO("AVRCP Target: Disconnected, avrcp_cid 0x%02x\n", + avrcp_subevent_connection_released_get_avrcp_cid(packet)); + AVRCP::mediaTracker.avrcp_cid = 0; + return; + default: + break; + } + + if (status != ERROR_CODE_SUCCESS) { + LOG_INFO("Responding to event 0x%02x failed with status 0x%02x\n", packet[2], status); + } + } + + void AVRCP::targetPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size) + { + UNUSED(channel); + UNUSED(size); + uint8_t status = ERROR_CODE_SUCCESS; + + if (packetType != HCI_EVENT_PACKET) { + return; + } + if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) { + return; + } + + switch (packet[2]) { + case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED: + AVRCP::mediaTracker.volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(packet); + LOG_INFO("AVRCP Target: Volume set to %d%% (%d)\n", + AVRCP::mediaTracker.volume * 127 / 100, + AVRCP::mediaTracker.volume); + break; + case AVRCP_SUBEVENT_EVENT_IDS_QUERY: + status = avrcp_target_supported_events(AVRCP::mediaTracker.avrcp_cid, + AVRCP::eventsNum, + const_cast(AVRCP::events), + sizeof(AVRCP::events)); + break; + case AVRCP_SUBEVENT_COMPANY_IDS_QUERY: + status = avrcp_target_supported_companies(AVRCP::mediaTracker.avrcp_cid, + AVRCP::companiesNum, + const_cast(AVRCP::companies), + sizeof(AVRCP::companies)); + break; + case AVRCP_SUBEVENT_PLAY_STATUS_QUERY: + status = avrcp_target_play_status(AVRCP::mediaTracker.avrcp_cid, + AVRCP::playInfo.song_length_ms, + 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) { + case AVRCP_OPERATION_ID_PLAY: + LOG_INFO("AVRCP Target: PLAY\n"); + status = a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid); + break; + case AVRCP_OPERATION_ID_PAUSE: + LOG_INFO("AVRCP Target: PAUSE\n"); + status = a2dp_source_pause_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid); + break; + case AVRCP_OPERATION_ID_STOP: + LOG_INFO("AVRCP Target: STOP\n"); + status = a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid); + break; + default: + LOG_INFO("AVRCP Target: operation 0x%2x is not handled\n", operation_id); + return; + } + break; + } + + default: + break; + } + + if (status != ERROR_CODE_SUCCESS) { + LOG_INFO("Responding to event 0x%02x failed with status 0x%02x\n", packet[2], status); + } + } + + void AVRCP::controllerPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size) + { + UNUSED(channel); + UNUSED(size); + uint8_t status = 0xFF; + + if (packetType != HCI_EVENT_PACKET) { + return; + } + if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) { + return; + } + + status = packet[5]; + if (AVRCP::mediaTracker.avrcp_cid == 0u) { + return; + } + + // ignore INTERIM status + if (status == AVRCP_CTYPE_RESPONSE_INTERIM) { + return; + } + + 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_GET_CAPABILITY_EVENT_ID: + LOG_INFO("Remote supports EVENT_ID 0x%02x\n", avrcp_subevent_get_capability_event_id_get_event_id(packet)); + break; + case AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID_DONE: + LOG_INFO("automatically enable notifications\n"); + avrcp_controller_enable_notification(AVRCP::mediaTracker.avrcp_cid, + AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED); + break; + default: + break; + } + } + void AVRCP::init() + { + // Initialize AVRCP Service. + avrcp_init(); + avrcp_register_packet_handler(&packetHandler); + // Initialize AVRCP Target. + avrcp_target_init(); + avrcp_target_register_packet_handler(&targetPacketHandler); + // Initialize AVRCP Controller + avrcp_controller_init(); + avrcp_controller_register_packet_handler(&controllerPacketHandler); + } + +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3467d9233b901953d409210e6c537409f7c80c3e --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp @@ -0,0 +1,57 @@ +#pragma once +#include "A2DPImpl.hpp" +extern "C" +{ +#include "classic/avrcp.h" +#include "classic/avrcp_browsing_controller.h" +#include "classic/avrcp_browsing_target.h" +#include "classic/avrcp_controller.h" +#include "classic/avrcp_media_item_iterator.h" +#include "classic/avrcp_target.h" +#include +#include +} + +namespace Bt +{ + class AVRCP + { + public: + constexpr static const uint8_t subunitInfo[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7}; + constexpr static uint32_t companyId = 0x112233; + constexpr static uint8_t companiesNum = 1; + constexpr static uint8_t companies[] = { + 0x00, 0x19, 0x58 // BT SIG registered CompanyID + }; + + constexpr static uint8_t eventsNum = 6; + constexpr static uint8_t events[] = {AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED, + AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED, + AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED, + AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED, + AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED, + AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED}; + static constexpr int SDP_BUFFER_LENGTH = 200; + struct PlaybackStatusInfo + { + uint8_t track_id[8]; + uint32_t song_length_ms; + avrcp_playback_status_t status; + uint32_t song_position_ms; // 0xFFFFFFFF if not supported + }; + + static std::array sdpTargetServiceBuffer; + static std::array sdpControllerServiceBuffer; + + static avrcp_track_t tracks[3]; + static int currentTrackIndex; + static PlaybackStatusInfo playInfo; + static MediaContext mediaTracker; + + 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(); + }; +} // namespace Bt diff --git a/module-bluetooth/Bluetooth/interface/profiles/GAP.cpp b/module-bluetooth/Bluetooth/interface/profiles/GAP.cpp index ac7efb50cb1197cf7e2918999c880f33545013a2..556f86c9c3b54d855d9a3b5021251afc7caca22f 100644 --- a/module-bluetooth/Bluetooth/interface/profiles/GAP.cpp +++ b/module-bluetooth/Bluetooth/interface/profiles/GAP.cpp @@ -1,11 +1,11 @@ -#include -#include +#include "BluetoothWorker.hpp" +#include "Device.hpp" #include "Service/Bus.hpp" -#include +#include #include +#include #include -#include "BluetoothWorker.hpp" -#include "Device.hpp" +#include extern "C" { diff --git a/module-bluetooth/Bluetooth/interface/profiles/PAN.cpp b/module-bluetooth/Bluetooth/interface/profiles/PAN.cpp index 439c47564fe29b0a9221382fedf496adf6d0b723..251c7fe0fcac635d075949e0c74209b4bbeca786 100644 --- a/module-bluetooth/Bluetooth/interface/profiles/PAN.cpp +++ b/module-bluetooth/Bluetooth/interface/profiles/PAN.cpp @@ -2,30 +2,30 @@ extern "C" { +#include #include #include #include #include -#include -#include "btstack_config.h" +#include "module-bluetooth/Bluetooth/btstack_config.h" #include // #include +#include #include #include #include -#include +#include #include #include -#include // #include +#include +#include #include #include #include -#include -#include }; #include diff --git a/module-bluetooth/Bluetooth/interface/profiles/Profile.hpp b/module-bluetooth/Bluetooth/interface/profiles/Profile.hpp new file mode 100644 index 0000000000000000000000000000000000000000..54c44ae4f715f38b6e9ce3461570ecc75741873c --- /dev/null +++ b/module-bluetooth/Bluetooth/interface/profiles/Profile.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "Error.hpp" + +namespace Bt +{ + class Profile + { + public: + virtual ~Profile() = default; + virtual auto init() -> Error = 0; + virtual void setDeviceAddress(uint8_t *addr) = 0; + }; + +} // namespace Bt diff --git a/module-bluetooth/CMakeLists.txt b/module-bluetooth/CMakeLists.txt index b3e98428c18709ffa855987550061f35465abc74..16a042779330d56ff80ff6fa6d34bb0e87f4579c 100644 --- a/module-bluetooth/CMakeLists.txt +++ b/module-bluetooth/CMakeLists.txt @@ -6,9 +6,12 @@ set(CMAKE_CXX_STANDARD 17) set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BluetoothWorker.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtstackWorker.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtKeysStorage.cpp -) + ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtstackWorker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtKeysStorage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/A2DP.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVRCP.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVDTP.cpp + ) message("Build with BlueKitchen") include(lib/btstack.cmake) @@ -39,13 +42,16 @@ target_compile_options(${PROJECT_NAME} ) target_link_options(${PROJECT_NAME} PUBLIC ${TARGET_LINK_OPTIONS}) - target_include_directories( ${PROJECT_NAME} PUBLIC ${BOARD_DIR_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${TARGET_LIBRARIES_INCLUDES} + ${BT_STACK_ROOT}/src + ${BT_STACK_ROOT}/src/classic + + ) target_link_libraries( diff --git a/module-bluetooth/lib/btstack.cmake b/module-bluetooth/lib/btstack.cmake index 12fc8ec9343dc51b1b134b57a21170ae4e166cb3..d589d9bdc6ce19fb02ca7428516a2ffe2e71a0a3 100644 --- a/module-bluetooth/lib/btstack.cmake +++ b/module-bluetooth/lib/btstack.cmake @@ -2,7 +2,6 @@ set(BT_GLU "${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/glucode/") set(BT_INT "${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/") set(BT_STACK_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/lib/btstack") - set(BT_CORE ${BT_STACK_ROOT}/src/btstack_memory.c ${BT_STACK_ROOT}/src/btstack_linked_list.c @@ -94,6 +93,11 @@ set(BNEP_LWIP # a2dp_sink.c \ # btstack_ring_buffer.c \ +include_directories( + ${BT_STACK_ROOT} + ${BT_STACK_ROOT}/src + ${BT_STACK_ROOT}/src/classic) + set(TARGET_LIBRARIES_INCLUDES "${BT_INT}" ${BT_STACK_ROOT}/platform/freertos/ @@ -134,22 +138,66 @@ list(APPEND TARGET_LIBRARIES_INCLUDES ${LWIP_INCLUDE_DIRS}) list(APPEND TARGET_LIBRARIES_INCLUDES ${BT_STACK_ROOT}/platform/lwip ) - set(BOARD_DIR_SOURCES - ${BT_INT}/GAP.cpp - ${BT_INT}/PAN.cpp - - ${BT_GLU}/bluetooth_init_cc2564C_1.0.c - ${BT_GLU}/btstack_uart_block_rt1051.cpp - ${BT_GLU}/btstack_uart_block_rt1051.h - ${BT_GLU}/hal_time_ms.c - ${BT_STACK_ROOT}/chipset/cc256x/btstack_chipset_cc256x.c - ${BT_STACK_ROOT}/platform/freertos/btstack_run_loop_freertos.c - ${BT_STACK_ROOT}/src/hci_transport_h4.c - ${BT_CORE} - ${BT_COMMON} - ${BT_CLASSIC} - ${BNEP_LWIP} + ${BT_INT}/GAP.cpp + ${BT_INT}/PAN.cpp + + ${BT_GLU}/bluetooth_init_cc2564C_1.0.c + ${BT_GLU}/btstack_uart_block_rt1051.cpp + ${BT_GLU}/btstack_uart_block_rt1051.h + ${BT_GLU}/hal_time_ms.c + ${BT_STACK_ROOT}/chipset/cc256x/btstack_chipset_cc256x.c + ${BT_STACK_ROOT}/platform/freertos/btstack_run_loop_freertos.c + ${BT_STACK_ROOT}/src/hci_transport_h4.c + ${BT_CORE} + ${BT_COMMON} + ${BT_CLASSIC} + ${BNEP_LWIP} + ${BT_STACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c + ${BT_STACK_ROOT}/3rd-party/hxcmod-player/hxcmod.c + ${BT_STACK_ROOT}/src/classic/btstack_sbc_encoder_bluedroid.c + ${BT_STACK_ROOT}/src/classic/a2dp_source.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_encoder.c + ${BT_STACK_ROOT}/src/classic/avdtp_util.c + ${BT_STACK_ROOT}/src/classic/avdtp_source.c + ${BT_STACK_ROOT}/src/classic/avdtp.c + ${BT_STACK_ROOT}/src/classic/avrcp.c + ${BT_STACK_ROOT}/src/classic/avrcp_controller.c + ${BT_STACK_ROOT}/src/classic/avdtp_acceptor.c + ${BT_STACK_ROOT}/src/classic/avdtp_initiator.c + ${BT_STACK_ROOT}/src/classic/sdp_client.c + ${BT_STACK_ROOT}/src/classic/avrcp_target.c + ${BT_STACK_ROOT}/src/classic/hsp_ag.c + ${BT_STACK_ROOT}/src/classic/hfp_msbc.c + ${BT_STACK_ROOT}/src/classic/btstack_cvsd_plc.c + ${BT_STACK_ROOT}/src/classic/btstack_sbc_plc.c + ${BT_STACK_ROOT}/src/classic/sdp_client_rfcomm.c + + ${BT_STACK_ROOT}/src/classic/btstack_sbc_decoder_bluedroid.c + ${BT_STACK_ROOT}/src/btstack_ring_buffer.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_analysis.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_dct.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_dct_coeffs.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_mono.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_ste.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_ste.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_coeffs.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_packing.c + + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/alloc.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/bitalloc.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/bitalloc-sbc.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/bitstream-decode.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/decoder-oina.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/decoder-private.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/decoder-sbc.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/dequant.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/framing.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/framing-sbc.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/oi_codec_version.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/synthesis-sbc.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/synthesis-dct8.c + ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/synthesis-8-generated.c ) if(${PROJECT_TARGET} STREQUAL "TARGET_Linux") diff --git a/module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp b/module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp index 343fc1a9d6695812ef7c8f0e3781bffdf088c6b1..4ecba8a08f92a6f16d4f967ca4678bd9fc99a070 100644 --- a/module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp +++ b/module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp @@ -241,14 +241,16 @@ void BluetoothCommon::set_irq(bool enable) LPUART_EnableTx(BSP_BLUETOOTH_UART_BASE, false); LPUART_ClearStatusFlags(BSP_BLUETOOTH_UART_BASE, 0xFFFFFFFF); if (enable) { - LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE, kLPUART_RxDataRegFullInterruptEnable); + LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE, + kLPUART_RxDataRegFullInterruptEnable | kLPUART_IdleLineInterruptEnable); } else { - LPUART_DisableInterrupts(BSP_BLUETOOTH_UART_BASE, kLPUART_RxDataRegFullInterruptEnable); + LPUART_DisableInterrupts(BSP_BLUETOOTH_UART_BASE, + kLPUART_RxDataRegFullInterruptEnable | kLPUART_IdleLineInterruptEnable); } - // LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE, - // kLPUART_RxDataRegFullInterruptEnable|kLPUART_TxDataRegEmptyInterruptEnable|kLPUART_TransmissionCompleteInterruptEnable|kLPUART_RxOverrunInterruptEnable - // ); + // LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE, + // kLPUART_RxDataRegFullInterruptEnable|kLPUART_TxDataRegEmptyInterruptEnable|kLPUART_TransmissionCompleteInterruptEnable|kLPUART_RxOverrunInterruptEnable + // ); LPUART_EnableRx(BSP_BLUETOOTH_UART_BASE, true); LPUART_EnableTx(BSP_BLUETOOTH_UART_BASE, true); } diff --git a/module-bsp/bsp/bluetooth/Bluetooth.cpp b/module-bsp/bsp/bluetooth/Bluetooth.cpp index 7b7ef9a609162e9eb8a25571c76723654b331341..fe56ec9bf01ccca89215492d9a774e59fe2c93d3 100644 --- a/module-bsp/bsp/bluetooth/Bluetooth.cpp +++ b/module-bsp/bsp/bluetooth/Bluetooth.cpp @@ -21,4 +21,5 @@ namespace bsp { va_end(args); } } + }; diff --git a/module-bsp/bsp/bluetooth/Bluetooth.hpp b/module-bsp/bsp/bluetooth/Bluetooth.hpp index 7525cfa23d51549d0745ada299db2d7b1e4163a9..ef5856c9329950a25c69fd65e22b9aba30c21044 100644 --- a/module-bsp/bsp/bluetooth/Bluetooth.hpp +++ b/module-bsp/bsp/bluetooth/Bluetooth.hpp @@ -132,6 +132,7 @@ namespace bsp { }; /// definitions needed by BT stack + class BlueKitchen : public BluetoothCommon { public: BlueKitchen(unsigned int in_size=default_buff_size, unsigned int out_size=default_buff_size); diff --git a/module-db/Interface/ThreadRecord.cpp b/module-db/Interface/ThreadRecord.cpp index 00b8e3b871175b5d52c5c0219e10cb796cbbe0d3..d24b2c30640414a4e35ec8899737df91fd63082e 100644 --- a/module-db/Interface/ThreadRecord.cpp +++ b/module-db/Interface/ThreadRecord.cpp @@ -84,7 +84,7 @@ std::unique_ptr> ThreadRecordInterface::GetLimitOffset { auto records = std::make_unique>(); - ThreadsTableFields threadsField; + ThreadsTableFields threadsField = ThreadsTableFields(); switch (field) { case ThreadRecordField::ContactID: { threadsField = ThreadsTableFields::ContactID; diff --git a/module-services/service-bluetooth/ServiceBluetooth.cpp b/module-services/service-bluetooth/ServiceBluetooth.cpp index 100a937ecca787799c2ce6f3e2bceabc5ebcb443..1e5ed6a1310f71a9c0f9caa23f3dcd3d2e2c1e29 100644 --- a/module-services/service-bluetooth/ServiceBluetooth.cpp +++ b/module-services/service-bluetooth/ServiceBluetooth.cpp @@ -44,6 +44,7 @@ sys::Message_t ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys: switch (lmsg->req) { case BluetoothMessage::Start: worker->run(); + break; case BluetoothMessage::Scan: if (worker->scan()) { @@ -73,6 +74,13 @@ sys::Message_t ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys: worker->set_visible(); break; + case BluetoothMessage::Play: + worker->play_audio(); + break; + case BluetoothMessage::Stop: + worker->stop_audio(); + break; + default: break; } @@ -90,6 +98,7 @@ sys::Message_t ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys: auto addrMsg = static_cast(msg); worker->set_addr(addrMsg->addr); } + return std::make_shared(); } diff --git a/module-services/service-bluetooth/ServiceBluetooth.hpp b/module-services/service-bluetooth/ServiceBluetooth.hpp index 4f5cb7befd5e0721511ff282bc0b988954ede891..270b3fab52f411a1326ae5967d6da225de8f61a6 100644 --- a/module-services/service-bluetooth/ServiceBluetooth.hpp +++ b/module-services/service-bluetooth/ServiceBluetooth.hpp @@ -1,7 +1,7 @@ #pragma once -#include "Service/Service.hpp" #include "Bluetooth/BluetoothWorker.hpp" +#include "Service/Service.hpp" #include class ServiceBluetooth : public sys::Service diff --git a/module-services/service-bluetooth/messages/BluetoothMessage.hpp b/module-services/service-bluetooth/messages/BluetoothMessage.hpp index 3ace58365415ec743d0a87bbb1edcd65723c21d4..5e8ba78a3a10936ef5c547b7e250960c7806e828 100644 --- a/module-services/service-bluetooth/messages/BluetoothMessage.hpp +++ b/module-services/service-bluetooth/messages/BluetoothMessage.hpp @@ -1,5 +1,7 @@ #pragma once +#include "Bluetooth/Device.hpp" +#include "MessageType.hpp" #include "Service/Message.hpp" #include @@ -22,6 +24,8 @@ class BluetoothMessage : public sys::DataMessage StopScan, PAN, Visible, + Play, + Stop }; enum Request req = Request::None; BluetoothMessage(enum Request req = None) : sys::DataMessage(MessageType::BluetoothRequest), req(req){}; @@ -37,6 +41,23 @@ class BluetoothScanResultMessage : public sys::DataMessage ~BluetoothScanResultMessage() override = default; }; +class BluetoothPairResultMessage : public sys::DataMessage +{ + public: + bool status; + explicit BluetoothPairResultMessage(bool status) + : sys::DataMessage(MessageType::BluetoothPairResult), status(status){}; +}; + +class BluetoothScanMessage : public sys::DataMessage +{ + public: + std::vector devices; + BluetoothScanMessage(std::vector devices) + : sys::DataMessage(MessageType::BluetoothScanResult), devices(std::move(devices)){}; + ~BluetoothScanMessage() override = default; +}; + class BluetoothAddrMessage : public sys::DataMessage { public: @@ -48,12 +69,22 @@ class BluetoothAddrMessage : public sys::DataMessage ~BluetoothAddrMessage() override = default; }; -class BluetoothPairResultMessage : public sys::DataMessage +class BluetoothAudioRegisterMessage : public sys::DataMessage { public: - bool status; - explicit BluetoothPairResultMessage(bool status) - : sys::DataMessage(MessageType::BluetoothPairResult), status(status){}; + QueueHandle_t audioSourceQueue; + QueueHandle_t audioSinkQueue; + BluetoothAudioRegisterMessage(QueueHandle_t audioSourceQueue, QueueHandle_t audioSinkQueue) + : sys::DataMessage(MessageType::BluetoothAudioRegister), audioSourceQueue(audioSourceQueue), + audioSinkQueue(audioSinkQueue){}; + ~BluetoothAudioRegisterMessage() override = default; +}; - ~BluetoothPairResultMessage() 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; }; diff --git a/source/MessageType.hpp b/source/MessageType.hpp index 9cdae92f738cd6ec59b5a5340d8893074be3c3b5..8cc237be4411f6b30e5a0fa7ce4806176b2dda13 100644 --- a/source/MessageType.hpp +++ b/source/MessageType.hpp @@ -184,6 +184,8 @@ enum class MessageType BluetoothScanResult, BluetoothAddrResult, BluetoothPairResult, + BluetoothAudioRegister, + BluetoothDeviceMetadata, LwIP_request, EVM_GPIO,