Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f46d647 authored by Cheney Ni's avatar Cheney Ni
Browse files

Add a workaround to play A2DP SBC Mono

There is a similar WAR of aosp/522661 at A2DP legacy HAL. In order to
suport MONO channel mode, the PCM audio is pulled as STEREO and mixed
into MONO by the Bluetooth Audio HAL.

Test: Playing SBC mono with Headset
Bug: 127593318
Change-Id: I78f3973ba6c8c733dc18122288a915daed97be65
parent 5cbf0658
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ cc_library_shared {
    header_libs: ["libhardware_headers"],
    shared_libs: [
        "android.hardware.bluetooth.audio@2.0",
        "libaudioutils",
        "libbase",
        "libbluetooth_audio_session",
        "libcutils",
+20 −7
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <audio_utils/primitives.h>
#include <inttypes.h>
#include <log/log.h>
#include <stdlib.h>
@@ -280,7 +281,8 @@ bool BluetoothAudioPortOut::LoadAudioConfig(audio_config_t* audio_cfg) const {
    return false;
  }
  audio_cfg->sample_rate = SampleRateToAudioFormat(pcm_cfg.sampleRate);
  audio_cfg->channel_mask = ChannelModeToAudioFormat(pcm_cfg.channelMode);
  audio_cfg->channel_mask =
      (is_stereo_to_mono_ ? AUDIO_CHANNEL_OUT_STEREO : ChannelModeToAudioFormat(pcm_cfg.channelMode));
  audio_cfg->format = BitsPerSampleToAudioFormat(pcm_cfg.bitsPerSample);
  return true;
}
@@ -321,7 +323,8 @@ bool BluetoothAudioPortOut::Start() {
  }

  LOG(INFO) << __func__ << ": session_type=" << toString(session_type_) << ", cookie=0x"
            << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " request";
            << StringPrintf("%04hx", cookie_) << ", state=" << state_
            << ", mono=" << (is_stereo_to_mono_ ? "true" : "false") << " request";
  bool retval = false;
  if (state_ == BluetoothStreamState::STANDBY) {
    state_ = BluetoothStreamState::STARTING;
@@ -335,7 +338,8 @@ bool BluetoothAudioPortOut::Start() {

  if (retval) {
    LOG(INFO) << __func__ << ": session_type=" << toString(session_type_) << ", cookie=0x"
              << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " done";
              << StringPrintf("%04hx", cookie_) << ", state=" << state_
              << ", mono=" << (is_stereo_to_mono_ ? "true" : "false") << " done";
  } else {
    LOG(ERROR) << __func__ << ": session_type=" << toString(session_type_) << ", cookie=0x"
               << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " failure";
@@ -387,11 +391,20 @@ void BluetoothAudioPortOut::Stop() {
            << StringPrintf("%04hx", cookie_) << ", state=" << state_ << " done";
}

size_t BluetoothAudioPortOut::WriteData(const void* buffer,
                                        size_t bytes) const {
size_t BluetoothAudioPortOut::WriteData(const void* buffer, size_t bytes) const {
  if (!in_use()) return 0;
  return BluetoothAudioSessionControl::OutWritePcmData(session_type_, buffer,
                                                       bytes);
  if (!is_stereo_to_mono_) {
    return BluetoothAudioSessionControl::OutWritePcmData(session_type_, buffer, bytes);
  }

  // WAR to mix the stereo into Mono (16 bits per sample)
  const size_t write_frames = bytes >> 2;
  if (write_frames == 0) return 0;
  auto src = static_cast<const int16_t*>(buffer);
  std::unique_ptr<int16_t[]> dst{new int16_t[write_frames]};
  downmix_to_mono_i16_from_stereo_i16(dst.get(), src, write_frames);
  // a frame is 16 bits, and the size of a mono frame is equal to half a stereo.
  return BluetoothAudioSessionControl::OutWritePcmData(session_type_, dst.get(), write_frames * 2) * 2;
}

bool BluetoothAudioPortOut::GetPresentationPosition(uint64_t* delay_ns,
+7 −0
Original line number Diff line number Diff line
@@ -51,6 +51,11 @@ class BluetoothAudioPortOut {
  // Bluetooth stack
  bool LoadAudioConfig(audio_config_t* audio_cfg) const;

  // WAR to support Mono mode / 16 bits per sample
  void ForcePcmStereoToMono(bool force) {
    is_stereo_to_mono_ = force;
  }

  // When the Audio framework / HAL wants to change the stream state, it invokes
  // these 3 functions to control the Bluetooth stack (Audio Control Path).
  // Note: Both Start() and Suspend() will return ture when there are no errors.
@@ -85,6 +90,8 @@ class BluetoothAudioPortOut {
  uint16_t cookie_;
  mutable std::mutex cv_mutex_;
  std::condition_variable internal_cv_;
  // WR to support Mono: True if fetching Stereo and mixing into Mono
  bool is_stereo_to_mono_ = false;

  // Check and initialize session type for |devices| If failed, this
  // BluetoothAudioPortOut is not initialized and must be deleted.
+7 −0
Original line number Diff line number Diff line
@@ -644,6 +644,13 @@ int adev_open_output_stream(struct audio_hw_device* dev,
    LOG(ERROR) << __func__ << ": state=" << out->bluetooth_output_.GetState()
               << " failed to get audio config";
  }
  // WAR to support Mono / 16 bits per sample as the Bluetooth stack required
  if (config->channel_mask == AUDIO_CHANNEL_OUT_MONO && config->format == AUDIO_FORMAT_PCM_16_BIT) {
    LOG(INFO) << __func__ << ": force channels=0x" << android::base::StringPrintf("%x", out->channel_mask_)
              << " to be AUDIO_CHANNEL_OUT_STEREO";
    out->bluetooth_output_.ForcePcmStereoToMono(true);
    config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
  }
  out->sample_rate_ = config->sample_rate;
  out->channel_mask_ = config->channel_mask;
  out->format_ = config->format;