M changelog.md => changelog.md +2 -0
@@ 5,6 5,8 @@
### Added
* `[gui]` Dynamic Windows building and handling implemented
+* `[audio]` Added A2DP playback backend
+
### Fixed
D link_key_db => link_key_db +0 -0
M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +4 -4
@@ 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"
M module-apps/application-antenna/windows/ScanModesWindow.cpp => module-apps/application-antenna/windows/ScanModesWindow.cpp +1 -1
@@ 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!");
M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +2 -4
@@ 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 <i18/i18.hpp>
#include <module-services/service-evtmgr/api/EventManagerServiceAPI.hpp>
#include <service-bluetooth/messages/BluetoothMessage.hpp>
-#include <i18/i18.hpp>
namespace app
{
M module-apps/application-settings/windows/BtScanWindow.hpp => module-apps/application-settings/windows/BtScanWindow.hpp +1 -1
@@ 7,8 7,8 @@
#include "gui/widgets/Image.hpp"
#include "gui/widgets/Label.hpp"
#include "gui/widgets/Window.hpp"
-#include <memory>
#include <BoxLayout.hpp>
+#include <memory>
#include <service-bluetooth/messages/BluetoothMessage.hpp>
namespace gui
M module-apps/application-settings/windows/BtWindow.cpp => module-apps/application-settings/windows/BtWindow.cpp +15 -3
@@ 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);
M module-audio/Audio/decoder/taglib => module-audio/Audio/decoder/taglib +1 -1
@@ 1,1 1,1 @@
-Subproject commit fa37896f615f3d347a41f0db711e23746774b15f
+Subproject commit b2a6e50aedf0cfe1f808eb23dc9f572a848ddffe
M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +37 -5
@@ 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<Bt::A2DP>())
{
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<Bt::A2DP *>(currentProfile.get());
+ if (profile == nullptr) {
+ return false;
+ }
+
+ profile->init();
+ profile->setOwnerService(service);
+ profile->start();
+ return true;
+}
+bool BluetoothWorker::stop_audio()
+{
+ auto profile = dynamic_cast<Bt::A2DP *>(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<Bt::A2DP *>(currentProfile.get());
+ if (profile != nullptr) {
+ profile->setDeviceAddress(addr);
+ }
}
M module-bluetooth/Bluetooth/BluetoothWorker.hpp => module-bluetooth/Bluetooth/BluetoothWorker.hpp +12 -5
@@ 1,14 1,14 @@
#pragma once
+#include "Device.hpp"
+#include "Service/Worker.hpp"
+#include "interface/profiles/Profile.hpp"
#include <FreeRTOS.h>
-#include <task.h>
#include <bsp/bluetooth/Bluetooth.hpp>
#include <memory>
+#include <task.h>
#include <vector>
-#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<Bt::Profile> currentProfile;
};
M module-bluetooth/Bluetooth/BtstackWorker.cpp => module-bluetooth/Bluetooth/BtstackWorker.cpp +4 -4
@@ 1,16 1,16 @@
-#include <log/log.hpp>
#include <cstdlib>
+#include <log/log.hpp>
extern "C"
{
#include <btstack_event.h>
+#include <bluetooth_company_id.h>
#include <btstack_memory.h>
#include <btstack_run_loop.h>
-#include <bluetooth_company_id.h>
+#include <btstack_stdin.h>
#include <hci.h>
#include <hci_dump.h>
-#include <btstack_stdin.h>
#include <btstack_chipset_cc256x.h>
#include <btstack_link_key_db_memory.h>
@@ 27,9 27,9 @@ extern "C"
}
#endif
+#include "BtKeysStorage.hpp"
#include <Error.hpp>
#include <functional>
-#include "BtKeysStorage.hpp"
// #define TLV_DB_PATH_PREFIX "/tmp/btstack_"
// #define TLV_DB_PATH_POSTFIX ".tlv"
M module-bluetooth/Bluetooth/Device.hpp => module-bluetooth/Bluetooth/Device.hpp +17 -2
@@ 1,8 1,9 @@
#pragma once
-#include <string>
+#include <array>
#include <cstring>
-#include <utility>
#include <module-bluetooth/lib/btstack/src/bluetooth.h>
+#include <string>
+#include <utility>
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<int16_t, DATA_BUFFER_SIZE> data;
+};
R module-bluetooth/Bluetooth/interface/profiles/btstack_config.h => module-bluetooth/Bluetooth/btstack_config.h +16 -3
@@ 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
M module-bluetooth/Bluetooth/glucode/HCITRANS.cpp => module-bluetooth/Bluetooth/glucode/HCITRANS.cpp +0 -123
@@ 1,123 0,0 @@
-#include <bsp/bluetooth/Bluetooth.hpp>
-#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<char *>(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; i<dev->in.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);
- }
- }
-};
M module-bluetooth/Bluetooth/glucode/hal_time_ms.c => module-bluetooth/Bluetooth/glucode/hal_time_ms.c +2 -1
@@ 1,6 1,7 @@
+#include "btstack_debug.h"
#include <FreeRTOS.h>
-#include <task.h>
#include <stdint.h>
+#include <task.h>
uint32_t hal_time_ms(void)
{
A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +577 -0
@@ 0,0 1,577 @@
+//
+// Created by bartek on 14.09.2020.
+//
+
+#include "A2DP.hpp"
+#include "A2DPImpl.hpp"
+#include "AVDTP.hpp"
+#include "AVRCP.hpp"
+#include <Bluetooth/Device.hpp>
+#include <Bluetooth/Error.hpp>
+#include <log/log.hpp>
+#include <module-sys/Service/Bus.hpp>
+#include <service-bluetooth/messages/BluetoothMessage.hpp>
+extern "C"
+{
+#include "module-bluetooth/lib/btstack/src/btstack.h"
+#include <btstack_defines.h>
+}
+
+namespace Bt
+{
+ A2DP::A2DP() : pimpl(std::make_unique<A2DPImpl>(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<uint8_t, 150> A2DP::A2DPImpl::sdpSourceServiceBuffer;
+ std::array<uint8_t, 4> 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<MediaContext *>(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<unsigned int>(AVDTP::sbcConfig.samplingFrequency),
+ .channels = static_cast<unsigned short>(AVDTP::sbcConfig.numChannels),
+ .samplesPerFrame = static_cast<unsigned int>(btstack_sbc_encoder_num_audio_frames()),
+ };
+
+ auto msg = std::make_shared<BluetoothDeviceMetadataMessage>(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<BluetoothAudioRegisterMessage>(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
A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp +31 -0
@@ 0,0 1,31 @@
+#pragma once
+
+#include "FreeRTOS.h"
+#include "Profile.hpp"
+#include "Service/Service.hpp"
+#include "queue.h"
+#include <memory>
+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<A2DPImpl> pimpl;
+ };
+} // namespace Bt
A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +72 -0
@@ 0,0 1,72 @@
+#pragma once
+#include "A2DP.hpp"
+#include <Bluetooth/Device.hpp>
+#include <Bluetooth/Error.hpp>
+#include <BtCommand.hpp>
+#include <log/log.hpp>
+extern "C"
+{
+#include <btstack.h>
+#include <btstack_defines.h>
+}
+
+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<uint8_t, SDP_BUFFER_LENGTH> sdpSourceServiceBuffer;
+ static bd_addr_t deviceAddr;
+ static btstack_packet_callback_registration_t hciEventCallbackRegistration;
+ static std::array<uint8_t, MEDIA_CAP_SIZE> 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
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp +21 -0
@@ 0,0 1,21 @@
+#include "AVDTP.hpp"
+
+namespace Bt
+{
+ AVDTP::SbcConfiguration AVDTP::sbcConfig;
+ btstack_sbc_encoder_state_t AVDTP::sbcEncoderState;
+ std::array<uint8_t, 4> 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
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp +37 -0
@@ 0,0 1,37 @@
+#pragma once
+
+#include "A2DPImpl.hpp"
+extern "C"
+{
+#include <btstack.h>
+#include <btstack_defines.h>
+#include <classic/avdtp.h>
+}
+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<uint8_t, 4> sbcCodecConfiguration;
+ static constexpr int defaultSampleRate = 44100;
+ static int sampleRate;
+
+ static void dumpSbcConfiguration();
+ };
+} // namespace Bt
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp +189 -0
@@ 0,0 1,189 @@
+#include "AVRCP.hpp"
+
+namespace Bt
+{
+
+ AVRCP::PlaybackStatusInfo AVRCP::playInfo;
+ int AVRCP::currentTrackIndex;
+ MediaContext AVRCP::mediaTracker;
+ std::array<uint8_t, 200> AVRCP::sdpTargetServiceBuffer;
+ std::array<uint8_t, 200> 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<uint8_t *>(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<uint8_t *>(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
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp +57 -0
@@ 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 <btstack.h>
+#include <btstack_defines.h>
+}
+
+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<uint8_t, SDP_BUFFER_LENGTH> sdpTargetServiceBuffer;
+ static std::array<uint8_t, SDP_BUFFER_LENGTH> 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
M module-bluetooth/Bluetooth/interface/profiles/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP.cpp +5 -5
@@ 1,11 1,11 @@
-#include <Bluetooth/Device.hpp>
-#include <log/log.hpp>
+#include "BluetoothWorker.hpp"
+#include "Device.hpp"
#include "Service/Bus.hpp"
-#include <vector>
+#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
+#include <log/log.hpp>
#include <module-services/service-bluetooth/messages/BluetoothMessage.hpp>
-#include "BluetoothWorker.hpp"
-#include "Device.hpp"
+#include <vector>
extern "C"
{
M module-bluetooth/Bluetooth/interface/profiles/PAN.cpp => module-bluetooth/Bluetooth/interface/profiles/PAN.cpp +6 -6
@@ 2,30 2,30 @@
extern "C"
{
+#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <signal.h>
-#include "btstack_config.h"
+#include "module-bluetooth/Bluetooth/btstack_config.h"
#include <btstack_event.h>
// #include <btstack_link_key_db_fs.h>
+#include <bluetooth_company_id.h>
#include <btstack_memory.h>
#include <btstack_run_loop.h>
#include <btstack_run_loop_freertos.h>
-#include <bluetooth_company_id.h>
+#include <btstack_stdin.h>
#include <hci.h>
#include <hci_dump.h>
-#include <btstack_stdin.h>
// #include <btstack_tlv_posix.h>
+#include <bluetooth_sdp.h>
+#include <bnep_lwip.h>
#include <btstack_chipset_cc256x.h>
#include <pan.h>
#include <sdp_util.h>
-#include <bnep_lwip.h>
-#include <bluetooth_sdp.h>
};
#include <BtCommand.hpp>
A module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +15 -0
@@ 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
M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +10 -4
@@ 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(
M module-bluetooth/lib/btstack.cmake => module-bluetooth/lib/btstack.cmake +64 -16
@@ 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")
M module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp => module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp +7 -5
@@ 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);
}
M module-bsp/bsp/bluetooth/Bluetooth.cpp => module-bsp/bsp/bluetooth/Bluetooth.cpp +1 -0
@@ 21,4 21,5 @@ namespace bsp {
va_end(args);
}
}
+
};
M module-bsp/bsp/bluetooth/Bluetooth.hpp => module-bsp/bsp/bluetooth/Bluetooth.hpp +1 -0
@@ 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);
M module-db/Interface/ThreadRecord.cpp => module-db/Interface/ThreadRecord.cpp +1 -1
@@ 84,7 84,7 @@ std::unique_ptr<std::vector<ThreadRecord>> ThreadRecordInterface::GetLimitOffset
{
auto records = std::make_unique<std::vector<ThreadRecord>>();
- ThreadsTableFields threadsField;
+ ThreadsTableFields threadsField = ThreadsTableFields();
switch (field) {
case ThreadRecordField::ContactID: {
threadsField = ThreadsTableFields::ContactID;
M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +9 -0
@@ 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<BluetoothAddrMessage *>(msg);
worker->set_addr(addrMsg->addr);
}
+
return std::make_shared<sys::ResponseMessage>();
}
M module-services/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/ServiceBluetooth.hpp +1 -1
@@ 1,7 1,7 @@
#pragma once
-#include "Service/Service.hpp"
#include "Bluetooth/BluetoothWorker.hpp"
+#include "Service/Service.hpp"
#include <memory>
class ServiceBluetooth : public sys::Service
M module-services/service-bluetooth/messages/BluetoothMessage.hpp => module-services/service-bluetooth/messages/BluetoothMessage.hpp +36 -5
@@ 1,5 1,7 @@
#pragma once
+#include "Bluetooth/Device.hpp"
+#include "MessageType.hpp"
#include "Service/Message.hpp"
#include <utility>
@@ 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<Devicei> devices;
+ BluetoothScanMessage(std::vector<Devicei> 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;
};
M source/MessageType.hpp => source/MessageType.hpp +2 -0
@@ 184,6 184,8 @@ enum class MessageType
BluetoothScanResult,
BluetoothAddrResult,
BluetoothPairResult,
+ BluetoothAudioRegister,
+ BluetoothDeviceMetadata,
LwIP_request,
EVM_GPIO,