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

Commit 35bff08f authored by Jakub Pawlowski's avatar Jakub Pawlowski
Browse files

Hearing Aid Audio HAL

The code is cloned from the existing A2DP Audio HAL.
In the future it will expand to include hearing-aid specific
mechanism.

Test: code compilation
Bug: 64038649
Change-Id: I976aa189929c3051b83615d1536315a099f31a67
parent 5417bc16
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
cc_defaults {
    name: "audio_hearing_aid_hw_defaults",
    defaults: ["fluoride_defaults"],
    include_dirs: [
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/include",
        "packages/modules/Bluetooth/system/audio_hearing_aid_hw/include",
    ]
}

// Audio A2DP shared library for target
// ========================================================
cc_library {
    name: "audio.hearing_aid.default",
    defaults: ["audio_hearing_aid_hw_defaults"],
    relative_install_path: "hw",
    srcs: [
        "src/audio_hearing_aid_hw.cc",
        "src/audio_hearing_aid_hw_utils.cc",
    ],
    shared_libs: [
        "liblog",
    ],
    static_libs: ["libosi"],
}

cc_library_static {
    name: "libaudio-hearing-aid-hw-utils",
    defaults: ["audio_hearing_aid_hw_defaults"],
    srcs: [
        "src/audio_hearing_aid_hw_utils.cc",
    ],
}

// Audio A2DP library unit tests for target and host
// ========================================================
cc_test {
    name: "net_test_audio_hearing_aid_hw",
    test_suites: ["device-tests"],
    defaults: ["audio_hearing_aid_hw_defaults"],
    srcs: [
        "test/audio_hearing_aid_hw_test.cc",
    ],
    shared_libs: [
        "liblog",
    ],
    static_libs: [
        "audio.hearing_aid.default",
        "libosi",
    ],
}
+150 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2016 The Android Open Source Project
 *
 *  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.
 *
 ******************************************************************************/

/*****************************************************************************
 *
 *  Filename:      audio_hearing_aid_hw.h
 *
 *  Description:
 *
 *****************************************************************************/

#ifndef AUDIO_HEARING_AID_HW_H
#define AUDIO_HEARING_AID_HW_H

#include <stdint.h>

#include <hardware/bt_av.h>

/*****************************************************************************
 *  Constants & Macros
 *****************************************************************************/

#define HEARING_AID_AUDIO_HARDWARE_INTERFACE "audio.hearing_aid"
#define HEARING_AID_CTRL_PATH "/data/misc/bluedroid/.hearing_aid_ctrl"
#define HEARING_AID_DATA_PATH "/data/misc/bluedroid/.hearing_aid_data"

// AUDIO_STREAM_OUTPUT_BUFFER_SZ controls the size of the audio socket buffer.
// If one assumes the write buffer is always full during normal BT playback,
// then increasing this value increases our playback latency.
//
// FIXME: The BT HAL should consume data at a constant rate.
// AudioFlinger assumes that the HAL draws data at a constant rate, which is
// true for most audio devices; however, the BT engine reads data at a variable
// rate (over the short term), which confuses both AudioFlinger as well as
// applications which deliver data at a (generally) fixed rate.
//
// 20 * 512 is not sufficient to smooth the variability for some BT devices,
// resulting in mixer sleep and throttling. We increase this to 28 * 512 to help
// reduce the effect of variable data consumption.
#define AUDIO_STREAM_OUTPUT_BUFFER_SZ (28 * 512)
#define AUDIO_STREAM_CONTROL_OUTPUT_BUFFER_SZ 256

// AUDIO_STREAM_OUTPUT_BUFFER_PERIODS controls how the socket buffer is divided
// for AudioFlinger data delivery. The AudioFlinger mixer delivers data in
// chunks of AUDIO_STREAM_OUTPUT_BUFFER_SZ / AUDIO_STREAM_OUTPUT_BUFFER_PERIODS.
// If the number of periods is 2, the socket buffer represents "double
// buffering" of the AudioFlinger mixer buffer.
//
// In general, AUDIO_STREAM_OUTPUT_BUFFER_PERIODS * 16 * 4 should be a divisor
// of AUDIO_STREAM_OUTPUT_BUFFER_SZ.
//
// These values should be chosen such that
//
// AUDIO_STREAM_BUFFER_SIZE * 1000 / (AUDIO_STREAM_OUTPUT_BUFFER_PERIODS
//         * AUDIO_STREAM_DEFAULT_RATE * 4) > 20 (ms)
//
// to avoid introducing the FastMixer in AudioFlinger. Using the FastMixer
// results in unnecessary latency and CPU overhead for Bluetooth.
#define AUDIO_STREAM_OUTPUT_BUFFER_PERIODS 2

#define AUDIO_SKT_DISCONNECTED (-1)

typedef enum {
  HEARING_AID_CTRL_CMD_NONE,
  HEARING_AID_CTRL_CMD_CHECK_READY,
  HEARING_AID_CTRL_CMD_START,
  HEARING_AID_CTRL_CMD_STOP,
  HEARING_AID_CTRL_CMD_SUSPEND,
  HEARING_AID_CTRL_GET_INPUT_AUDIO_CONFIG,
  HEARING_AID_CTRL_GET_OUTPUT_AUDIO_CONFIG,
  HEARING_AID_CTRL_SET_OUTPUT_AUDIO_CONFIG,
  HEARING_AID_CTRL_CMD_OFFLOAD_START,
} tHEARING_AID_CTRL_CMD;

typedef enum {
  HEARING_AID_CTRL_ACK_SUCCESS,
  HEARING_AID_CTRL_ACK_FAILURE,
  HEARING_AID_CTRL_ACK_INCALL_FAILURE, /* Failure when in Call*/
  HEARING_AID_CTRL_ACK_UNSUPPORTED
} tHEARING_AID_CTRL_ACK;

typedef uint32_t tHA_SAMPLE_RATE;
typedef uint8_t tHA_CHANNEL_COUNT;

/*****************************************************************************
 *  Type definitions for callback functions
 *****************************************************************************/

/*****************************************************************************
 *  Type definitions and return values
 *****************************************************************************/

/*****************************************************************************
 *  Extern variables and functions
 *****************************************************************************/

/*****************************************************************************
 *  Functions
 *****************************************************************************/

// Computes the Audio Hearing Aid HAL output buffer size.
// |codec_sample_rate| is the sample rate of the output stream.
// |codec_bits_per_sample| is the number of bits per sample of the output
// stream.
// |codec_channel_mode| is the channel mode of the output stream.
//
// The buffer size is computed by using the following formula:
//
// AUDIO_STREAM_OUTPUT_BUFFER_SIZE =
//    (TIME_PERIOD_MS * AUDIO_STREAM_OUTPUT_BUFFER_PERIODS *
//     SAMPLE_RATE_HZ * NUMBER_OF_CHANNELS * (BITS_PER_SAMPLE / 8)) / 1000
//
// AUDIO_STREAM_OUTPUT_BUFFER_PERIODS controls how the socket buffer is
// divided for AudioFlinger data delivery. The AudioFlinger mixer delivers
// data in chunks of
// (AUDIO_STREAM_OUTPUT_BUFFER_SIZE / AUDIO_STREAM_OUTPUT_BUFFER_PERIODS) .
// If the number of periods is 2, the socket buffer represents "double
// buffering" of the AudioFlinger mixer buffer.
//
// Furthermore, the AudioFlinger expects the buffer size to be a multiple
// of 16 frames.
//
// NOTE: Currently, the computation uses the conservative 20ms time period.
//
// Returns the computed buffer size. If any of the input parameters is
// invalid, the return value is the default |AUDIO_STREAM_OUTPUT_BUFFER_SZ|.
extern size_t audio_ha_hw_stream_compute_buffer_size(
    btav_a2dp_codec_sample_rate_t codec_sample_rate,
    btav_a2dp_codec_bits_per_sample_t codec_bits_per_sample,
    btav_a2dp_codec_channel_mode_t codec_channel_mode);

// Returns a string representation of |event|.
extern const char* audio_ha_hw_dump_ctrl_event(tHEARING_AID_CTRL_CMD event);

#endif /* AUDIO_HEARING_AID_HW_H */
+1875 −0

File added.

Preview size limit exceeded, changes collapsed.

+41 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2018 The Android Open Source Project
 *
 *  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.
 *
 ******************************************************************************/

#include "audio_hearing_aid_hw.h"

#define CASE_RETURN_STR(const) \
  case const:                  \
    return #const;

const char* audio_ha_hw_dump_ctrl_event(tHEARING_AID_CTRL_CMD event) {
  switch (event) {
    CASE_RETURN_STR(HEARING_AID_CTRL_CMD_NONE)
    CASE_RETURN_STR(HEARING_AID_CTRL_CMD_CHECK_READY)
    CASE_RETURN_STR(HEARING_AID_CTRL_CMD_START)
    CASE_RETURN_STR(HEARING_AID_CTRL_CMD_STOP)
    CASE_RETURN_STR(HEARING_AID_CTRL_CMD_SUSPEND)
    CASE_RETURN_STR(HEARING_AID_CTRL_GET_INPUT_AUDIO_CONFIG)
    CASE_RETURN_STR(HEARING_AID_CTRL_GET_OUTPUT_AUDIO_CONFIG)
    CASE_RETURN_STR(HEARING_AID_CTRL_SET_OUTPUT_AUDIO_CONFIG)
    CASE_RETURN_STR(HEARING_AID_CTRL_CMD_OFFLOAD_START)
    default:
      break;
  }

  return "UNKNOWN HEARING_AID_CTRL_CMD";
}
+144 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2017 The Android Open Source Project
 *
 *  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.
 *
 ******************************************************************************/

#include <gtest/gtest.h>

#include "audio_hearing_aid_hw/include/audio_hearing_aid_hw.h"

namespace {
static uint32_t codec_sample_rate2value(
    btav_a2dp_codec_sample_rate_t codec_sample_rate) {
  switch (codec_sample_rate) {
    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
      return 44100;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
      return 48000;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
      return 88200;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
      return 96000;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
      return 176400;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
      return 192000;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
      return 16000;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
      break;
  }
  return 0;
}

static uint32_t codec_bits_per_sample2value(
    btav_a2dp_codec_bits_per_sample_t codec_bits_per_sample) {
  switch (codec_bits_per_sample) {
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
      return 16;
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
      return 24;
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
      return 32;
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
      break;
  }
  return 0;
}

static uint32_t codec_channel_mode2value(
    btav_a2dp_codec_channel_mode_t codec_channel_mode) {
  switch (codec_channel_mode) {
    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
      return 1;
    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
      return 2;
    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
      break;
  }
  return 0;
}

}  // namespace

class AudioA2dpHwTest : public ::testing::Test {
 protected:
  AudioA2dpHwTest() {}

 private:
};

TEST_F(AudioA2dpHwTest, test_compute_buffer_size) {
  const btav_a2dp_codec_sample_rate_t codec_sample_rate_array[] = {
      BTAV_A2DP_CODEC_SAMPLE_RATE_NONE,  BTAV_A2DP_CODEC_SAMPLE_RATE_44100,
      BTAV_A2DP_CODEC_SAMPLE_RATE_48000, BTAV_A2DP_CODEC_SAMPLE_RATE_88200,
      BTAV_A2DP_CODEC_SAMPLE_RATE_96000, BTAV_A2DP_CODEC_SAMPLE_RATE_176400,
      BTAV_A2DP_CODEC_SAMPLE_RATE_192000};

  const btav_a2dp_codec_bits_per_sample_t codec_bits_per_sample_array[] = {
      BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE, BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16,
      BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24, BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32};

  const btav_a2dp_codec_channel_mode_t codec_channel_mode_array[] = {
      BTAV_A2DP_CODEC_CHANNEL_MODE_NONE, BTAV_A2DP_CODEC_CHANNEL_MODE_MONO,
      BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO};

  for (const auto codec_sample_rate : codec_sample_rate_array) {
    for (const auto codec_bits_per_sample : codec_bits_per_sample_array) {
      for (const auto codec_channel_mode : codec_channel_mode_array) {
        size_t buffer_size = audio_ha_hw_stream_compute_buffer_size(
            codec_sample_rate, codec_bits_per_sample, codec_channel_mode);

        // Check for invalid input
        if ((codec_sample_rate == BTAV_A2DP_CODEC_SAMPLE_RATE_NONE) ||
            (codec_bits_per_sample == BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE) ||
            (codec_channel_mode == BTAV_A2DP_CODEC_CHANNEL_MODE_NONE)) {
          EXPECT_EQ(buffer_size,
                    static_cast<size_t>(AUDIO_STREAM_OUTPUT_BUFFER_SZ));
          continue;
        }

        uint32_t sample_rate = codec_sample_rate2value(codec_sample_rate);
        EXPECT_TRUE(sample_rate != 0);

        uint32_t bits_per_sample =
            codec_bits_per_sample2value(codec_bits_per_sample);
        EXPECT_TRUE(bits_per_sample != 0);

        uint32_t number_of_channels =
            codec_channel_mode2value(codec_channel_mode);
        EXPECT_TRUE(number_of_channels != 0);

        const uint64_t time_period_ms = 20;  // TODO: Must be a parameter
        size_t expected_buffer_size =
            (time_period_ms * AUDIO_STREAM_OUTPUT_BUFFER_PERIODS * sample_rate *
             number_of_channels * (bits_per_sample / 8)) /
            1000;

        // Compute the divisor and adjust the buffer size
        const size_t divisor = (AUDIO_STREAM_OUTPUT_BUFFER_PERIODS * 16 *
                                number_of_channels * bits_per_sample) /
                               8;
        const size_t remainder = expected_buffer_size % divisor;
        if (remainder != 0) {
          expected_buffer_size += divisor - remainder;
        }

        EXPECT_EQ(buffer_size, expected_buffer_size);
      }
    }
  }
}