Loading system/audio/Android.bp +0 −4 Original line number Diff line number Diff line Loading @@ -14,9 +14,6 @@ cc_library_static { "asrc/asrc_resampler.cc", "asrc/asrc_tables.cc", ], include_dirs: [ "packages/modules/Bluetooth/system/gd", ], shared_libs: [ "libchrome", ], Loading Loading @@ -44,7 +41,6 @@ cc_library_host_shared { stl: "libc++_static", include_dirs: [ "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", Loading system/audio/asrc/asrc_resampler.cc +10 −12 Original line number Diff line number Diff line Loading @@ -19,15 +19,15 @@ #include <base/logging.h> #include <base/strings/stringprintf.h> #include <algorithm> #include <cmath> #include <utility> #include "asrc_tables.h" #include "hal/nocp_iso_clocker.h" namespace bluetooth::audio::asrc { class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { class SourceAudioHalAsrc::ClockRecovery : public ClockHandler { const int interval_; std::mutex mutex_; Loading Loading @@ -113,8 +113,7 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { } int dt_current = int(timestamp_us - link.local_time); if (std::abs(dt_current) < std::abs(link.decim_dt[1])) link.decim_dt[1] = dt_current; link.decim_dt[1] = std::min(link.decim_dt[1], dt_current); if (link.local_time - link.decim_t0 < 1000 * 1000) return; Loading Loading @@ -237,11 +236,9 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { state_{ .link = {{.state = LinkState::RESET}, {.state = LinkState::RESET}}, .active_link_id = -1}, reference_timing_{0, 0, 0} { ::bluetooth::hal::NocpIsoClocker::Register(this); } reference_timing_{0, 0, 0} {} ~ClockRecovery() override { ::bluetooth::hal::NocpIsoClocker::Unregister(); } ~ClockRecovery() override {} __attribute__((no_sanitize("integer"))) uint32_t Convert( uint32_t stream_time) { Loading Loading @@ -466,16 +463,16 @@ inline int32_t SourceAudioHalAsrc::Resampler::Filter(const int32_t* in, #endif SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms) SourceAudioHalAsrc::SourceAudioHalAsrc( std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms) : sample_rate_(sample_rate), bit_depth_(bit_depth), interval_us_(interval_us), stream_us_(0), drift_us_(0), out_counter_(0), clock_source_(std::move(clock_source)), resampler_pos_{0, 0} { buffers_size_ = 0; Loading Loading @@ -509,6 +506,7 @@ SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate, // when the PCM bit_depth is higher than 16 bits. clock_recovery_ = std::make_unique<ClockRecovery>(interval_us_); clock_source_->Bind(clock_recovery_.get()); resamplers_ = std::make_unique<std::vector<Resampler>>(channels, bit_depth_); // Deduct from the PCM stream characteristics, the size of the pool buffers Loading system/audio/asrc/asrc_resampler.h +17 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,19 @@ namespace bluetooth::audio::asrc { class ClockHandler { public: virtual ~ClockHandler() = default; virtual void OnEvent(uint32_t timestamp, int link_id, int num_of_completed_packets) = 0; }; class ClockSource { public: virtual ~ClockSource() = default; virtual void Bind(ClockHandler*) = 0; }; class SourceAudioHalAsrc { public: // The Asynchronous Sample Rate Conversion (ASRC) is set up from the PCM Loading @@ -36,9 +49,9 @@ class SourceAudioHalAsrc { // `burst_delay_ms` helps to ensure that the synchronization with the // transmission intervals is done. SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers = 2, int burst_delay_ms = 500); SourceAudioHalAsrc(std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers = 2, int burst_delay_ms = 500); ~SourceAudioHalAsrc(); Loading Loading @@ -74,6 +87,7 @@ class SourceAudioHalAsrc { class ClockRecovery; std::unique_ptr<ClockRecovery> clock_recovery_; std::shared_ptr<ClockSource> clock_source_; class Resampler; std::unique_ptr<std::vector<Resampler>> resamplers_; Loading system/audio/asrc/asrc_resampler_test.cc +6 −6 Original line number Diff line number Diff line Loading @@ -19,17 +19,17 @@ #include <cstdio> #include <iostream> namespace bluetooth::hal { void NocpIsoClocker::Register(NocpIsoHandler*) {} void NocpIsoClocker::Unregister() {} } // namespace bluetooth::hal namespace bluetooth::audio::asrc { class MockClockSource : public ClockSource { void Bind(ClockHandler*) override {} }; class SourceAudioHalAsrcTest : public SourceAudioHalAsrc { public: SourceAudioHalAsrcTest(int channels, int bitdepth) : SourceAudioHalAsrc(channels, 48000, bitdepth, 10000) {} : SourceAudioHalAsrc(std::make_unique<MockClockSource>(), channels, 48000, bitdepth, 10000) {} template <typename T> void Resample(double ratio, const T* in, size_t in_length, size_t* in_count, Loading system/bta/hearing_aid/hearing_aid.cc +84 −1 Original line number Diff line number Diff line Loading @@ -32,12 +32,14 @@ #include <mutex> #include <vector> #include "audio/asrc/asrc_resampler.h" #include "bta/include/bta_gatt_api.h" #include "bta/include/bta_gatt_queue.h" #include "bta/include/bta_hearing_aid_api.h" #include "btm_iso_api.h" #include "device/include/controller.h" #include "embdrv/g722/g722_enc_dec.h" #include "hal/link_clocker.h" #include "hardware/bt_gatt_types.h" #include "include/check.h" #include "internal_include/bt_trace.h" Loading Loading @@ -275,6 +277,13 @@ class HearingAidImpl : public HearingAid { const int DROP_FREQUENCY_THRESHOLD = bluetooth::common::init_flags::get_asha_packet_drop_frequency_threshold(); // Resampler context for audio stream. // Clock recovery uses L2CAP Flow Control Credit Ind acknowledgments // from either the left or right connection, whichever is first // connected. std::shared_ptr<bluetooth::hal::L2capCreditIndEvents> asrc_clock_source; std::unique_ptr<bluetooth::audio::asrc::SourceAudioHalAsrc> asrc; public: ~HearingAidImpl() override = default; Loading Loading @@ -354,6 +363,51 @@ class HearingAidImpl : public HearingAid { } } // Reset and configure the ASHA resampling context using the input device // devices as reference for the BT clock estimation. void ConfigureAsrc() { if (!IS_FLAG_ENABLED(asha_asrc)) { log::info("Asha resampling disabled: feature flag off"); return; } // Create a new ASRC context if required. if (asrc == nullptr) { asrc_clock_source = std::make_shared<bluetooth::hal::L2capCreditIndEvents>(); asrc = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>( asrc_clock_source, /*channels*/ 2, /*sample_rate*/ codec_in_use == CODEC_G722_24KHZ ? 24000 : 16000, /*bit_depth*/ 16, /*interval_us*/ default_data_interval_ms * 1000, /*num_burst_buffers*/ 0, /*burst_delay*/ 0); } for (auto& device : hearingDevices.devices) { if (!device.accepting_audio) { continue; } uint16_t lcid = GAP_ConnGetL2CAPCid(device.gap_handle); uint16_t rcid = 0; L2CA_GetRemoteCid(lcid, &rcid); auto conn = btm_acl_for_bda(device.address, BT_TRANSPORT_LE); log::info("Updating ASRC context for handle=0x{:x}, cid=0x{:x}", conn->Handle(), rcid); asrc_clock_source->Update(device.isLeft(), conn->Handle(), rcid); } } // Reset the ASHA resampling context. void ResetAsrc() { log::info("Resetting the Asha resampling context"); asrc_clock_source = nullptr; asrc = nullptr; } uint16_t UpdateBleConnParams(const RawAddress& address) { /* List of parameters that depends on the chosen Connection Interval */ uint16_t min_ce_len = MIN_CE_LEN_20MS_CI; Loading Loading @@ -1171,6 +1225,10 @@ class HearingAidImpl : public HearingAid { } else { log::info("audio_running={}", audio_running); } // Close the ASRC context. ResetAsrc(); audio_running = false; stop_audio_ticks(); Loading Loading @@ -1213,6 +1271,9 @@ class HearingAidImpl : public HearingAid { return; } // Open the ASRC context. ConfigureAsrc(); // TODO: shall we also reset the encoder ? encoder_state_release(); encoder_state_init(); Loading Loading @@ -1352,6 +1413,16 @@ class HearingAidImpl : public HearingAid { return diff_credit < (init_credit / 2 - 1); } void OnAudioDataReadyResample(const std::vector<uint8_t>& data) { if (asrc == nullptr) { return OnAudioDataReady(data); } for (auto const resampled_data : asrc->Run(data)) { OnAudioDataReady(*resampled_data); } } void OnAudioDataReady(const std::vector<uint8_t>& data) { /* For now we assume data comes in as 16bit per sample 16kHz PCM stereo */ bool need_drop = false; Loading Loading @@ -1414,6 +1485,18 @@ class HearingAidImpl : public HearingAid { l2cap_flush_threshold = 1; } // Skipping packets completely messes up the resampler context. // The condition for skipping packets seems to be easily triggered, // causing dropouts that could have been avoided. // // When the resampler is enabled, the flush threshold is set // to the number of credits specified for the ASHA l2cap streaming // channel. This will ensure it is only triggered in case of // critical failure. if (IS_FLAG_ENABLED(asha_asrc)) { l2cap_flush_threshold = 8; } // TODO: monural, binarual check // divide encoded data into packets, add header, send. Loading Loading @@ -2069,7 +2152,7 @@ void encryption_callback(const RawAddress* address, tBT_TRANSPORT, void*, class HearingAidAudioReceiverImpl : public HearingAidAudioReceiver { public: void OnAudioDataReady(const std::vector<uint8_t>& data) override { if (instance) instance->OnAudioDataReady(data); if (instance) instance->OnAudioDataReadyResample(data); } void OnAudioSuspend(const std::function<void()>& stop_audio_ticks) override { if (instance) instance->OnAudioSuspend(stop_audio_ticks); Loading Loading
system/audio/Android.bp +0 −4 Original line number Diff line number Diff line Loading @@ -14,9 +14,6 @@ cc_library_static { "asrc/asrc_resampler.cc", "asrc/asrc_tables.cc", ], include_dirs: [ "packages/modules/Bluetooth/system/gd", ], shared_libs: [ "libchrome", ], Loading Loading @@ -44,7 +41,6 @@ cc_library_host_shared { stl: "libc++_static", include_dirs: [ "packages/modules/Bluetooth/system", "packages/modules/Bluetooth/system/gd", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", Loading
system/audio/asrc/asrc_resampler.cc +10 −12 Original line number Diff line number Diff line Loading @@ -19,15 +19,15 @@ #include <base/logging.h> #include <base/strings/stringprintf.h> #include <algorithm> #include <cmath> #include <utility> #include "asrc_tables.h" #include "hal/nocp_iso_clocker.h" namespace bluetooth::audio::asrc { class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { class SourceAudioHalAsrc::ClockRecovery : public ClockHandler { const int interval_; std::mutex mutex_; Loading Loading @@ -113,8 +113,7 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { } int dt_current = int(timestamp_us - link.local_time); if (std::abs(dt_current) < std::abs(link.decim_dt[1])) link.decim_dt[1] = dt_current; link.decim_dt[1] = std::min(link.decim_dt[1], dt_current); if (link.local_time - link.decim_t0 < 1000 * 1000) return; Loading Loading @@ -237,11 +236,9 @@ class SourceAudioHalAsrc::ClockRecovery : ::bluetooth::hal::NocpIsoHandler { state_{ .link = {{.state = LinkState::RESET}, {.state = LinkState::RESET}}, .active_link_id = -1}, reference_timing_{0, 0, 0} { ::bluetooth::hal::NocpIsoClocker::Register(this); } reference_timing_{0, 0, 0} {} ~ClockRecovery() override { ::bluetooth::hal::NocpIsoClocker::Unregister(); } ~ClockRecovery() override {} __attribute__((no_sanitize("integer"))) uint32_t Convert( uint32_t stream_time) { Loading Loading @@ -466,16 +463,16 @@ inline int32_t SourceAudioHalAsrc::Resampler::Filter(const int32_t* in, #endif SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms) SourceAudioHalAsrc::SourceAudioHalAsrc( std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers, int burst_delay_ms) : sample_rate_(sample_rate), bit_depth_(bit_depth), interval_us_(interval_us), stream_us_(0), drift_us_(0), out_counter_(0), clock_source_(std::move(clock_source)), resampler_pos_{0, 0} { buffers_size_ = 0; Loading Loading @@ -509,6 +506,7 @@ SourceAudioHalAsrc::SourceAudioHalAsrc(int channels, int sample_rate, // when the PCM bit_depth is higher than 16 bits. clock_recovery_ = std::make_unique<ClockRecovery>(interval_us_); clock_source_->Bind(clock_recovery_.get()); resamplers_ = std::make_unique<std::vector<Resampler>>(channels, bit_depth_); // Deduct from the PCM stream characteristics, the size of the pool buffers Loading
system/audio/asrc/asrc_resampler.h +17 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,19 @@ namespace bluetooth::audio::asrc { class ClockHandler { public: virtual ~ClockHandler() = default; virtual void OnEvent(uint32_t timestamp, int link_id, int num_of_completed_packets) = 0; }; class ClockSource { public: virtual ~ClockSource() = default; virtual void Bind(ClockHandler*) = 0; }; class SourceAudioHalAsrc { public: // The Asynchronous Sample Rate Conversion (ASRC) is set up from the PCM Loading @@ -36,9 +49,9 @@ class SourceAudioHalAsrc { // `burst_delay_ms` helps to ensure that the synchronization with the // transmission intervals is done. SourceAudioHalAsrc(int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers = 2, int burst_delay_ms = 500); SourceAudioHalAsrc(std::shared_ptr<ClockSource> clock_source, int channels, int sample_rate, int bit_depth, int interval_us, int num_burst_buffers = 2, int burst_delay_ms = 500); ~SourceAudioHalAsrc(); Loading Loading @@ -74,6 +87,7 @@ class SourceAudioHalAsrc { class ClockRecovery; std::unique_ptr<ClockRecovery> clock_recovery_; std::shared_ptr<ClockSource> clock_source_; class Resampler; std::unique_ptr<std::vector<Resampler>> resamplers_; Loading
system/audio/asrc/asrc_resampler_test.cc +6 −6 Original line number Diff line number Diff line Loading @@ -19,17 +19,17 @@ #include <cstdio> #include <iostream> namespace bluetooth::hal { void NocpIsoClocker::Register(NocpIsoHandler*) {} void NocpIsoClocker::Unregister() {} } // namespace bluetooth::hal namespace bluetooth::audio::asrc { class MockClockSource : public ClockSource { void Bind(ClockHandler*) override {} }; class SourceAudioHalAsrcTest : public SourceAudioHalAsrc { public: SourceAudioHalAsrcTest(int channels, int bitdepth) : SourceAudioHalAsrc(channels, 48000, bitdepth, 10000) {} : SourceAudioHalAsrc(std::make_unique<MockClockSource>(), channels, 48000, bitdepth, 10000) {} template <typename T> void Resample(double ratio, const T* in, size_t in_length, size_t* in_count, Loading
system/bta/hearing_aid/hearing_aid.cc +84 −1 Original line number Diff line number Diff line Loading @@ -32,12 +32,14 @@ #include <mutex> #include <vector> #include "audio/asrc/asrc_resampler.h" #include "bta/include/bta_gatt_api.h" #include "bta/include/bta_gatt_queue.h" #include "bta/include/bta_hearing_aid_api.h" #include "btm_iso_api.h" #include "device/include/controller.h" #include "embdrv/g722/g722_enc_dec.h" #include "hal/link_clocker.h" #include "hardware/bt_gatt_types.h" #include "include/check.h" #include "internal_include/bt_trace.h" Loading Loading @@ -275,6 +277,13 @@ class HearingAidImpl : public HearingAid { const int DROP_FREQUENCY_THRESHOLD = bluetooth::common::init_flags::get_asha_packet_drop_frequency_threshold(); // Resampler context for audio stream. // Clock recovery uses L2CAP Flow Control Credit Ind acknowledgments // from either the left or right connection, whichever is first // connected. std::shared_ptr<bluetooth::hal::L2capCreditIndEvents> asrc_clock_source; std::unique_ptr<bluetooth::audio::asrc::SourceAudioHalAsrc> asrc; public: ~HearingAidImpl() override = default; Loading Loading @@ -354,6 +363,51 @@ class HearingAidImpl : public HearingAid { } } // Reset and configure the ASHA resampling context using the input device // devices as reference for the BT clock estimation. void ConfigureAsrc() { if (!IS_FLAG_ENABLED(asha_asrc)) { log::info("Asha resampling disabled: feature flag off"); return; } // Create a new ASRC context if required. if (asrc == nullptr) { asrc_clock_source = std::make_shared<bluetooth::hal::L2capCreditIndEvents>(); asrc = std::make_unique<bluetooth::audio::asrc::SourceAudioHalAsrc>( asrc_clock_source, /*channels*/ 2, /*sample_rate*/ codec_in_use == CODEC_G722_24KHZ ? 24000 : 16000, /*bit_depth*/ 16, /*interval_us*/ default_data_interval_ms * 1000, /*num_burst_buffers*/ 0, /*burst_delay*/ 0); } for (auto& device : hearingDevices.devices) { if (!device.accepting_audio) { continue; } uint16_t lcid = GAP_ConnGetL2CAPCid(device.gap_handle); uint16_t rcid = 0; L2CA_GetRemoteCid(lcid, &rcid); auto conn = btm_acl_for_bda(device.address, BT_TRANSPORT_LE); log::info("Updating ASRC context for handle=0x{:x}, cid=0x{:x}", conn->Handle(), rcid); asrc_clock_source->Update(device.isLeft(), conn->Handle(), rcid); } } // Reset the ASHA resampling context. void ResetAsrc() { log::info("Resetting the Asha resampling context"); asrc_clock_source = nullptr; asrc = nullptr; } uint16_t UpdateBleConnParams(const RawAddress& address) { /* List of parameters that depends on the chosen Connection Interval */ uint16_t min_ce_len = MIN_CE_LEN_20MS_CI; Loading Loading @@ -1171,6 +1225,10 @@ class HearingAidImpl : public HearingAid { } else { log::info("audio_running={}", audio_running); } // Close the ASRC context. ResetAsrc(); audio_running = false; stop_audio_ticks(); Loading Loading @@ -1213,6 +1271,9 @@ class HearingAidImpl : public HearingAid { return; } // Open the ASRC context. ConfigureAsrc(); // TODO: shall we also reset the encoder ? encoder_state_release(); encoder_state_init(); Loading Loading @@ -1352,6 +1413,16 @@ class HearingAidImpl : public HearingAid { return diff_credit < (init_credit / 2 - 1); } void OnAudioDataReadyResample(const std::vector<uint8_t>& data) { if (asrc == nullptr) { return OnAudioDataReady(data); } for (auto const resampled_data : asrc->Run(data)) { OnAudioDataReady(*resampled_data); } } void OnAudioDataReady(const std::vector<uint8_t>& data) { /* For now we assume data comes in as 16bit per sample 16kHz PCM stereo */ bool need_drop = false; Loading Loading @@ -1414,6 +1485,18 @@ class HearingAidImpl : public HearingAid { l2cap_flush_threshold = 1; } // Skipping packets completely messes up the resampler context. // The condition for skipping packets seems to be easily triggered, // causing dropouts that could have been avoided. // // When the resampler is enabled, the flush threshold is set // to the number of credits specified for the ASHA l2cap streaming // channel. This will ensure it is only triggered in case of // critical failure. if (IS_FLAG_ENABLED(asha_asrc)) { l2cap_flush_threshold = 8; } // TODO: monural, binarual check // divide encoded data into packets, add header, send. Loading Loading @@ -2069,7 +2152,7 @@ void encryption_callback(const RawAddress* address, tBT_TRANSPORT, void*, class HearingAidAudioReceiverImpl : public HearingAidAudioReceiver { public: void OnAudioDataReady(const std::vector<uint8_t>& data) override { if (instance) instance->OnAudioDataReady(data); if (instance) instance->OnAudioDataReadyResample(data); } void OnAudioSuspend(const std::function<void()>& stop_audio_ticks) override { if (instance) instance->OnAudioSuspend(stop_audio_ticks); Loading