/* * Copyright 2019 HIMSA II K/S - www.himsa.dk. Represented by EHIMA - * www.ehima.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "BTAudioClientLeAudio" #include "le_audio_software.h" #include #include "client_interface.h" #include "osi/include/log.h" #include "osi/include/properties.h" #include "btif/include/btif_hf.h" bool gLeSinkState; bool gLeSourceState; namespace { /** M: Fix for build pass @{ */ using ::vendor::mediatek::hardware::bluetooth::audio::V2_1::LhdcLowLatencyEn; /** @} */ using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::CodecType; using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::LeAudioCodecConfiguration; using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::PlcMethod; using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::Lc3Parameters; using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::Lc3FrameDuration; using ::vendor::mediatek::hardware::bluetooth::audio::V2_2::ConnParam; using ::bluetooth::audio::AudioConfiguration; using ::bluetooth::audio::AudioConfiguration_2_1; using ::bluetooth::audio::BitsPerSample; using ::bluetooth::audio::ChannelMode; using ::bluetooth::audio::PcmParameters; using ::bluetooth::audio::PcmParameters_2_1; using ::bluetooth::audio::SampleRate; using ::bluetooth::audio::SampleRate_2_1; using ::bluetooth::audio::SessionType; using ::bluetooth::audio::SessionType_2_1; using ::bluetooth::audio::BluetoothAudioCtrlAck; using ::bluetooth::audio::le_audio::StreamCallbacks; using ::bluetooth::audio::BluetoothAudioSinkClientInterface; using ::bluetooth::audio::BluetoothAudioSourceClientInterface; #define AUDIO_STREAM_OUTPUT_BUFFER_SZ (28 * 512) #define LE_AUDIO_OFFLOAD_PROP "persist.vendor.bluetooth.leaudio_mode" #define LE_AUDIO_OFFLOAD_UMS 0x01 #define LE_AUDIO_OFFLOAD_CG 0x02 #define LE_AUDIO_OFFLOAD_MODE_UNINIT 0xF0 unsigned int g_leaudio_offload_mode = LE_AUDIO_OFFLOAD_MODE_UNINIT; // Sink transport implementation for Le Audio class LeAudioSinkTransport : public bluetooth::audio::IBluetoothSinkTransportInstance { public: LeAudioSinkTransport(SessionType_2_1 sessionType, StreamCallbacks stream_cb) : IBluetoothSinkTransportInstance( sessionType, {}), stream_cb_(std::move(stream_cb)), remote_delay_report_ms_(0), total_bytes_read_(0), data_position_({}), /*pcm_config_({SampleRate::RATE_16000, ChannelMode::STEREO, BitsPerSample::BITS_16}){};*/ /** M: Fix for build pass @{ */ pcm_config_({SampleRate_2_1::RATE_16000, ChannelMode::STEREO, BitsPerSample::BITS_16, LhdcLowLatencyEn::Disabled,0}){}; /** @} */ BluetoothAudioCtrlAck StartRequest() override { LOG(INFO) << __func__; // Don't send START request to stack while we are in a call if (!bluetooth::headset::IsCallIdleForCG()) { LOG(ERROR) << __func__ << ": call state is busy"; return BluetoothAudioCtrlAck::FAILURE; } if (stream_cb_.on_resume_(true)) { return BluetoothAudioCtrlAck::SUCCESS_FINISHED; } return BluetoothAudioCtrlAck::FAILURE; } BluetoothAudioCtrlAck SuspendRequest() override { LOG(INFO) << __func__; if (stream_cb_.on_suspend_()) { if (!::bluetooth::audio::le_audio::is_leaudio_offload_enabled()) { uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2]; ::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf)); } return BluetoothAudioCtrlAck::SUCCESS_FINISHED; } else { return BluetoothAudioCtrlAck::FAILURE; } } void StopRequest() override { LOG(INFO) << __func__; if (stream_cb_.on_suspend_()) { // flush uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2]; ::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf)); } } bool GetPresentationPosition(uint64_t* remote_delay_report_ns, uint64_t* total_bytes_read, timespec* data_position) override { /*VLOG(2) << __func__ << ": data=" << total_bytes_read_ << " byte(s), timestamp=" << data_position_.tv_sec << "." << data_position_.tv_nsec << "s, delay report=" << remote_delay_report_ms_ << " msec.";*/ if (remote_delay_report_ns != nullptr) { *remote_delay_report_ns = remote_delay_report_ms_ * 1000000u; } if (total_bytes_read != nullptr) *total_bytes_read = total_bytes_read_; if (data_position != nullptr) *data_position = data_position_; return true; } void MetadataChanged(const source_metadata_t& source_metadata) override { auto track_count = source_metadata.track_count; auto tracks = source_metadata.tracks; LOG(INFO) << __func__ << ": " << track_count << " track(s) received"; while (track_count) { VLOG(1) << __func__ << ": usage=" << tracks->usage << ", content_type=" << tracks->content_type << ", gain=" << tracks->gain; --track_count; ++tracks; } } void EnterGameMode(int enter) override { VLOG(1) << __func__ << ": ble audio Sink enter = " << enter; stream_cb_.on_gamemode_(enter); } void ResetPresentationPosition() override { VLOG(2) << __func__ << ": called."; remote_delay_report_ms_ = 0; total_bytes_read_ = 0; data_position_ = {}; } void LogBytesRead(size_t bytes_read) override { if (bytes_read) { total_bytes_read_ += bytes_read; clock_gettime(CLOCK_MONOTONIC, &data_position_); } } bool* GetBluetoothStateAddress() { return &gLeSinkState; } void SetRemoteDelay(uint16_t delay_report_ms) { LOG(INFO) << __func__ << ": delay_report=" << delay_report_ms << " msec"; remote_delay_report_ms_ = delay_report_ms; } const PcmParameters_2_1& LeAudioGetSelectedHalPcmConfig() { return pcm_config_; } void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate, BitsPerSample bit_rate, ChannelMode channel_mode, int gamemode, int data_interval_ms) { pcm_config_.sampleRate = sample_rate; pcm_config_.bitsPerSample = bit_rate; pcm_config_.channelMode = channel_mode; pcm_config_.isLowLatencyEnabled = (gamemode == 1) ? LhdcLowLatencyEn::Enabled : LhdcLowLatencyEn::Disabled; pcm_config_.dataIntervalUs = data_interval_ms*1000; } const LeAudioCodecConfiguration& LeAudioGetSelectedHalCodecConfig() { return leAudioCodecConfig_; } void LeAudioSetSelectedHalCodecConfig(CodecType codec_type, uint32_t audio_channel_allocation, uint32_t encoded_audio_bitrate, uint8_t le_audio_type, Lc3Parameters lc3_config) { leAudioCodecConfig_.codecType = codec_type; leAudioCodecConfig_.audioChannelAllocation = audio_channel_allocation; leAudioCodecConfig_.encodedAudioBitrate = encoded_audio_bitrate; leAudioCodecConfig_.le_audio_type = le_audio_type; leAudioCodecConfig_.bfi_ext = false; leAudioCodecConfig_.plc_method = PlcMethod::STANDARD_PLC; leAudioCodecConfig_.lc3Config = lc3_config; LOG(INFO) << __func__ << ": LeAudioCodecConfiguration=" << toString(leAudioCodecConfig_); return; } const ConnParam& LeAudioSetConnParameter(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) { connParam_.conn_handle_R = l_cis_conn_hdl; connParam_.conn_handle_L = r_cis_conn_hdl; connParam_.bn = bn; LOG(INFO) << __func__ << ": Connection Parameter=" << toString(connParam_); return connParam_; } private: StreamCallbacks stream_cb_; uint16_t remote_delay_report_ms_; uint64_t total_bytes_read_; timespec data_position_; PcmParameters_2_1 pcm_config_; LeAudioCodecConfiguration leAudioCodecConfig_; ConnParam connParam_; }; class LeAudioSourceTransport : public bluetooth::audio::IBluetoothSourceTransportInstance { public: LeAudioSourceTransport(SessionType_2_1 sessionType, StreamCallbacks stream_cb) : IBluetoothSourceTransportInstance( sessionType, {}), stream_cb_(std::move(stream_cb)), remote_delay_report_ms_(0), total_bytes_written_(0), data_position_({}), /*pcm_config_({SampleRate::RATE_16000, ChannelMode::MONO, BitsPerSample::BITS_16}){};*/ /** M: Fix for build pass @{ */ pcm_config_({SampleRate_2_1::RATE_16000, ChannelMode::MONO, BitsPerSample::BITS_16, LhdcLowLatencyEn::Disabled, 0}){}; /** @} */ BluetoothAudioCtrlAck StartRequest() override { LOG(INFO) << __func__; if (stream_cb_.on_resume_(true)) { return BluetoothAudioCtrlAck::SUCCESS_FINISHED; } return BluetoothAudioCtrlAck::FAILURE; } BluetoothAudioCtrlAck SuspendRequest() override { LOG(INFO) << __func__; if (stream_cb_.on_suspend_()) { if (!::bluetooth::audio::le_audio::is_leaudio_offload_enabled()) { /* TODO (gkolodziejczyk): Clean exact value of buffer */ uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2]; ::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf)); } return BluetoothAudioCtrlAck::SUCCESS_FINISHED; } else { return BluetoothAudioCtrlAck::FAILURE; } } void StopRequest() override { LOG(INFO) << __func__; if (stream_cb_.on_suspend_()) { if (!::bluetooth::audio::le_audio::is_leaudio_offload_enabled()) { // flush /* TODO (gkolodziejczyk): Clean exact value of buffer */ uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2]; ::bluetooth::audio::le_audio::read(p_buf, sizeof(p_buf)); } } } /* TODO (gkolodziejczyk): Shouldn't this be in oposite ? Set for AF */ bool GetPresentationPosition(uint64_t* remote_delay_report_ns, uint64_t* total_bytes_written, timespec* data_position) override { VLOG(2) << __func__ << ": data=" << total_bytes_written_ << " byte(s), timestamp=" << data_position_.tv_sec << "." << data_position_.tv_nsec << "s, delay report=" << remote_delay_report_ms_ << " msec."; if (remote_delay_report_ns != nullptr) { *remote_delay_report_ns = remote_delay_report_ms_ * 1000000u; } if (total_bytes_written != nullptr) *total_bytes_written = total_bytes_written_; if (data_position != nullptr) *data_position = data_position_; return true; } void MetadataChanged(const source_metadata_t& source_metadata) override { auto track_count = source_metadata.track_count; auto tracks = source_metadata.tracks; LOG(INFO) << __func__ << ": " << track_count << " track(s) received"; while (track_count) { VLOG(1) << __func__ << ": usage=" << tracks->usage << ", content_type=" << tracks->content_type << ", gain=" << tracks->gain; --track_count; ++tracks; } } void EnterGameMode(int enter) override { VLOG(1) << __func__ << ": ble audio Source enter = " << enter; stream_cb_.on_gamemode_(enter); } void ResetPresentationPosition() override { VLOG(2) << __func__ << ": called."; remote_delay_report_ms_ = 0; total_bytes_written_ = 0; data_position_ = {}; } void LogBytesWritten(size_t bytes_written) override { if (bytes_written) { total_bytes_written_ += bytes_written; clock_gettime(CLOCK_MONOTONIC, &data_position_); } } void LogBytesRead(size_t bytes_read) override { } bool* GetBluetoothStateAddress() { return &gLeSourceState; } void SetRemoteDelay(uint16_t delay_report_ms) { LOG(INFO) << __func__ << ": delay_report=" << delay_report_ms << " msec"; remote_delay_report_ms_ = delay_report_ms; } const PcmParameters_2_1& LeAudioGetSelectedHalPcmConfig() { return pcm_config_; } void LeAudioSetSelectedHalPcmConfig(SampleRate_2_1 sample_rate, BitsPerSample bit_rate, ChannelMode channel_mode, int data_interval_ms) { pcm_config_.sampleRate = sample_rate; pcm_config_.bitsPerSample = bit_rate; pcm_config_.channelMode = channel_mode; pcm_config_.dataIntervalUs = data_interval_ms*1000; } const LeAudioCodecConfiguration& LeAudioGetSelectedHalCodecConfig() { return leAudioCodecConfig_; } void LeAudioSetSelectedHalCodecConfig(CodecType codec_type, uint32_t audio_channel_allocation, uint32_t encoded_audio_bitrate, uint8_t le_audio_type, Lc3Parameters lc3_config) { leAudioCodecConfig_.codecType = codec_type; leAudioCodecConfig_.audioChannelAllocation = audio_channel_allocation; leAudioCodecConfig_.encodedAudioBitrate = encoded_audio_bitrate; leAudioCodecConfig_.le_audio_type = le_audio_type; leAudioCodecConfig_.bfi_ext = false; leAudioCodecConfig_.plc_method = PlcMethod::STANDARD_PLC; leAudioCodecConfig_.lc3Config = lc3_config; LOG(INFO) << __func__ << ": LeAudioCodecConfiguration=" << toString(leAudioCodecConfig_); return; } const ConnParam& LeAudioSetConnParameter(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) { connParam_.conn_handle_R = l_cis_conn_hdl; connParam_.conn_handle_L = r_cis_conn_hdl; connParam_.bn = bn; LOG(INFO) << __func__ << ": Connection Parameter=" << toString(connParam_); return connParam_; } private: StreamCallbacks stream_cb_; uint16_t remote_delay_report_ms_; uint64_t total_bytes_written_; timespec data_position_; PcmParameters_2_1 pcm_config_; LeAudioCodecConfiguration leAudioCodecConfig_; ConnParam connParam_; }; // Instance of Le Audio to provide call-in APIs for Bluetooth Audio Hal LeAudioSinkTransport* le_audio_sink = nullptr; LeAudioSourceTransport* le_audio_source = nullptr; // Common interfaces to call-out into Bluetooth Audio Hal bluetooth::audio::BluetoothAudioSinkClientInterface* le_audio_sink_hal_clientinterface = nullptr; bluetooth::audio::BluetoothAudioSourceClientInterface* le_audio_source_hal_clientinterface = nullptr; bool btaudio_le_audio_disabled = false; bool is_configured = false; // Save the value if the remote reports its delay before le_audio_sink is // initialized uint16_t remote_delay_ms = 0; bool is_hal_2_0_force_disabled() { if (!is_configured) { btaudio_le_audio_disabled = osi_property_get_bool(BLUETOOTH_AUDIO_HAL_PROP_DISABLED, false); is_configured = true; } return btaudio_le_audio_disabled; } } // namespace namespace bluetooth { namespace audio { namespace le_audio { /* TODO (gkolodziejczyk): Do it smarter. btw. do this really check if hal 2.0 is * enabled ? */ bool is_sink_hal_enabled() { return le_audio_sink_hal_clientinterface != nullptr; } bool is_source_hal_enabled() { return le_audio_source_hal_clientinterface != nullptr; } //TODO bool is_leaudio_offload_enabled() { if (g_leaudio_offload_mode == LE_AUDIO_OFFLOAD_MODE_UNINIT) { char value[PROPERTY_VALUE_MAX]; value[0] = '\0'; osi_property_get(LE_AUDIO_OFFLOAD_PROP, value, "off"); g_leaudio_offload_mode = 0; if (strcmp(value,"ums-cg") == 0) { g_leaudio_offload_mode |= LE_AUDIO_OFFLOAD_UMS; g_leaudio_offload_mode |= LE_AUDIO_OFFLOAD_CG; } LOG(INFO) << __func__ << ", le offload prop value: " << value <<" mode: " << g_leaudio_offload_mode; } return (g_leaudio_offload_mode & (LE_AUDIO_OFFLOAD_UMS | LE_AUDIO_OFFLOAD_CG)) ? true:false; } /* TODO (gkolodziejczyk): Think about optimization sink and source methods */ bool init_sink(StreamCallbacks stream_cb, bluetooth::common::MessageLoopThread* message_loop) { LOG(INFO) << __func__; if (is_hal_2_0_force_disabled()) { LOG(ERROR) << __func__ << ": BluetoothAudio HAL is disabled"; return false; } if (is_leaudio_offload_enabled()) { le_audio_sink = new LeAudioSinkTransport( SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH,std::move(stream_cb)); } else { le_audio_sink = new LeAudioSinkTransport( SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH, std::move(stream_cb)); } le_audio_sink_hal_clientinterface = new BluetoothAudioSinkClientInterface(le_audio_sink,message_loop); if (!le_audio_sink_hal_clientinterface->IsValid()) { LOG(WARNING) << __func__ << ": BluetoothAudio HAL for Le Audio is invalid?!"; delete le_audio_sink_hal_clientinterface; le_audio_sink_hal_clientinterface = nullptr; delete le_audio_sink; le_audio_sink = nullptr; return false; } if (remote_delay_ms != 0) { LOG(INFO) << __func__ << ": restore DELAY " << remote_delay_ms << " ms"; le_audio_sink->SetRemoteDelay(remote_delay_ms); remote_delay_ms = 0; } gLeSinkState = true; LOG(INFO) << __func__ << ": gLeSinkState = " << (bool*)&gLeSinkState; return true; } bool init_source(StreamCallbacks stream_cb, bluetooth::common::MessageLoopThread* message_loop) { LOG(INFO) << __func__; if (is_hal_2_0_force_disabled()) { LOG(ERROR) << __func__ << ": BluetoothAudio HAL is disabled"; return false; } if (is_leaudio_offload_enabled()) { le_audio_source = new LeAudioSourceTransport( SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,std::move(stream_cb)); } else { le_audio_source = new LeAudioSourceTransport( SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH, std::move(stream_cb)); } le_audio_source_hal_clientinterface = new bluetooth::audio::BluetoothAudioSourceClientInterface(le_audio_source, message_loop); if (!le_audio_sink_hal_clientinterface->IsValid()) { LOG(WARNING) << __func__ << ": BluetoothAudio HAL for Le Audio is invalid?!"; delete le_audio_source_hal_clientinterface; le_audio_source_hal_clientinterface = nullptr; delete le_audio_source; le_audio_source = nullptr; return false; } if (remote_delay_ms != 0) { LOG(INFO) << __func__ << ": restore DELAY " << remote_delay_ms << " ms"; le_audio_source->SetRemoteDelay(remote_delay_ms); remote_delay_ms = 0; } gLeSourceState = true; LOG(INFO) << __func__ << ": gLeSourceState = " << (bool*)&gLeSourceState; return true; } void cleanup_sink() { LOG(INFO) << __func__; gLeSinkState = false; if (!is_sink_hal_enabled()) return; end_sink_session(); delete le_audio_sink_hal_clientinterface; le_audio_sink_hal_clientinterface = nullptr; delete le_audio_sink; le_audio_sink = nullptr; remote_delay_ms = 0; } void cleanup_source() { LOG(INFO) << __func__; gLeSourceState = false; if (!is_source_hal_enabled()) return; end_source_session(); delete le_audio_source_hal_clientinterface; le_audio_source_hal_clientinterface = nullptr; delete le_audio_source; le_audio_source = nullptr; remote_delay_ms = 0; } void start_sink_session() { LOG(INFO) << __func__; if (!is_sink_hal_enabled()) return; AudioConfiguration_2_1 audio_config; if (is_leaudio_offload_enabled()) { audio_config.leAudioCodecConfig(le_audio_sink->LeAudioGetSelectedHalCodecConfig()); } else { audio_config.pcmConfig(le_audio_sink->LeAudioGetSelectedHalPcmConfig()); } if (!le_audio_sink_hal_clientinterface->UpdateAudioConfig_2_1(audio_config)) { LOG(ERROR) << __func__ << ": cannot update audio config to HAL"; return; } le_audio_sink_hal_clientinterface->StartSession_2_1(); } void start_source_session() { LOG(INFO) << __func__; if (!is_source_hal_enabled()) return; AudioConfiguration_2_1 audio_config; if (is_leaudio_offload_enabled()) { audio_config.leAudioCodecConfig(le_audio_source->LeAudioGetSelectedHalCodecConfig()); } else { audio_config.pcmConfig(le_audio_source->LeAudioGetSelectedHalPcmConfig()); } if (!le_audio_source_hal_clientinterface->UpdateAudioConfig_2_1(audio_config)) { LOG(ERROR) << __func__ << ": cannot update audio config to HAL"; return; } le_audio_source_hal_clientinterface->StartSession_2_1(); } void update_source_conn_param(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) { LOG(INFO) << __func__; if (!is_source_hal_enabled()) return; AudioConfiguration_2_1 audio_config; if (is_leaudio_offload_enabled()) { if (!le_audio_source_hal_clientinterface->UpdateConnParam( le_audio_source->LeAudioSetConnParameter(l_cis_conn_hdl, r_cis_conn_hdl, bn))) { LOG(ERROR) << __func__ << ": cannot update connection parameter to HAL"; return; } } } void update_sink_conn_param(uint16_t l_cis_conn_hdl, uint16_t r_cis_conn_hdl, uint8_t bn) { LOG(INFO) << __func__; if (!is_source_hal_enabled()) return; if (is_leaudio_offload_enabled()) { if (!le_audio_sink_hal_clientinterface->UpdateConnParam( le_audio_sink->LeAudioSetConnParameter(l_cis_conn_hdl, r_cis_conn_hdl, bn))) { LOG(ERROR) << __func__ << ": cannot update connection parameter to HAL"; return; } } } void end_sink_session() { LOG(INFO) << __func__; if (!is_sink_hal_enabled()) return; le_audio_sink_hal_clientinterface->EndSession(); } void end_source_session() { LOG(INFO) << __func__; if (!is_source_hal_enabled()) return; le_audio_source_hal_clientinterface->EndSession(); } size_t read(uint8_t* p_buf, uint32_t len) { if (!is_sink_hal_enabled()) return 0; return le_audio_sink_hal_clientinterface->ReadAudioData(p_buf, len); } size_t write(const uint8_t* p_buf, uint32_t len) { if (!is_source_hal_enabled()) return 0; return le_audio_source_hal_clientinterface->WriteAudioData(p_buf, len); } // Update Le Audio delay report to BluetoothAudio HAL void set_sink_remote_delay(uint16_t delay_report_ms) { if (!is_sink_hal_enabled()) { LOG(INFO) << __func__ << ": not ready for DelayReport " << delay_report_ms << " ms"; remote_delay_ms = delay_report_ms; return; } LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms"; le_audio_sink->SetRemoteDelay(delay_report_ms); } void set_source_remote_delay(uint16_t delay_report_ms) { if (!is_source_hal_enabled()) { LOG(INFO) << __func__ << ": not ready for DelayReport " << delay_report_ms << " ms"; remote_delay_ms = delay_report_ms; return; } LOG(INFO) << __func__ << ": delay_report_ms=" << delay_report_ms << " ms"; le_audio_source->SetRemoteDelay(delay_report_ms); } static SampleRate_2_1 le_audio_sample_rate2audio_hal(uint32_t sample_rate) { switch (sample_rate) { case 44100: return SampleRate_2_1::RATE_44100; case 48000: return SampleRate_2_1::RATE_48000; case 88200: return SampleRate_2_1::RATE_88200; case 96000: return SampleRate_2_1::RATE_96000; case 176400: return SampleRate_2_1::RATE_176400; case 192000: return SampleRate_2_1::RATE_192000; case 24000: return SampleRate_2_1::RATE_24000; case 16000: return SampleRate_2_1::RATE_16000; case 32000: return SampleRate_2_1::RATE_32000; case 8000: return SampleRate_2_1::RATE_8000; }; return SampleRate_2_1::RATE_UNKNOWN; } static BitsPerSample le_audio_bit_rate2audio_hal(uint8_t bitrate) { switch (bitrate) { case 16: return BitsPerSample::BITS_16; case 24: return BitsPerSample::BITS_24; case 32: return BitsPerSample::BITS_32; }; return BitsPerSample::BITS_UNKNOWN; } static ChannelMode le_audio_channel_mode2audio_hal(uint8_t channel_mode) { switch (channel_mode) { case 1: return ChannelMode::MONO; case 2: return ChannelMode::STEREO; } return ChannelMode::UNKNOWN; } static Lc3FrameDuration le_audio_duration_mode2audio_hal(uint16_t frame_duration) { switch (frame_duration) { case 10: return Lc3FrameDuration::DURATION_10000US; } return Lc3FrameDuration::DURATION_7500US; } static CodecType le_audio_duration_codec_hal(uint8_t codec_type) { return CodecType::LC3; } void set_sink_pcm_parameters(uint32_t sample_rate, uint8_t bit_rate, uint8_t channel_mode, int gamemode, int data_interval_ms) { le_audio_sink->LeAudioSetSelectedHalPcmConfig( le_audio_sample_rate2audio_hal(sample_rate), le_audio_bit_rate2audio_hal(bit_rate), le_audio_channel_mode2audio_hal(channel_mode), gamemode,data_interval_ms); } void set_sink_offload_parameters(uint8_t codec_type, uint8_t audio_channel_allocation, uint32_t encoded_audio_bitrate, uint8_t le_audio_type, uint32_t bits_per_sample, uint32_t sample_rate, uint16_t data_interval_ms, uint32_t sdu_size) { Lc3Parameters lc3_config; lc3_config.pcmBitDepth = le_audio_bit_rate2audio_hal(bits_per_sample); lc3_config.samplingFrequency = le_audio_sample_rate2audio_hal(sample_rate); lc3_config.frameDuration = le_audio_duration_mode2audio_hal(data_interval_ms); lc3_config.octetsPerFrame = sdu_size; lc3_config.blocksPerSdu = 0; le_audio_sink->LeAudioSetSelectedHalCodecConfig(le_audio_duration_codec_hal(codec_type), (uint32_t)audio_channel_allocation, encoded_audio_bitrate, le_audio_type, lc3_config); } void set_source_pcm_parameters(uint32_t sample_rate, uint8_t bit_rate, uint8_t channel_mode, int data_interval_ms) { le_audio_source->LeAudioSetSelectedHalPcmConfig( le_audio_sample_rate2audio_hal(sample_rate), le_audio_bit_rate2audio_hal(bit_rate), le_audio_channel_mode2audio_hal(channel_mode), data_interval_ms); } void set_source_offload_parameters(uint8_t codec_type, uint8_t audio_channel_allocation, uint32_t encoded_audio_bitrate, uint8_t le_audio_type, uint32_t bits_per_sample, uint32_t sample_rate, uint16_t data_interval_ms, uint32_t sdu_size) { Lc3Parameters lc3_config; lc3_config.pcmBitDepth = le_audio_bit_rate2audio_hal(bits_per_sample); lc3_config.samplingFrequency = le_audio_sample_rate2audio_hal(sample_rate); lc3_config.frameDuration = le_audio_duration_mode2audio_hal(data_interval_ms); lc3_config.octetsPerFrame = sdu_size; lc3_config.blocksPerSdu = 0; le_audio_source->LeAudioSetSelectedHalCodecConfig(le_audio_duration_codec_hal(codec_type), (uint32_t)audio_channel_allocation, encoded_audio_bitrate, le_audio_type, lc3_config); } } // namespace le_audio } // namespace audio } // namespace bluetooth