Cải thiện tính năng phát âm thanh

Khi bạn cố gắng truy cập trực tiếp vào thiết bị ngoại vi âm thanh USB bằng API USB, vấn đề phát sinh. Những vấn đề này có thể bao gồm: vấn đề bảo mật, giới hạn nội dung nghe nhìn phát từ các ứng dụng khác và mất báo thức, thông báo và nhạc chuông Thiết bị USB.

Để cải thiện tính năng phát âm thanh, hãy định cấu hình các thuộc tính bộ trộn.

Định cấu hình thuộc tính của bộ trộn

Bằng cách sử dụng Các API AudioMixerAttributes, bạn có thể định cấu hình ứng dụng bằng các thuộc tính bộ trộn ưu tiên qua USB.

Khi chế độ phát ứng dụng của bạn khớp với định dạng mã hoá, mặt nạ kênh và mẫu tốc độ của các thuộc tính bộ trộn ưu tiên, bản phát đi kèm với âm thanh luồng đầu ra có bộ trộn được định cấu hình bằng các thuộc tính bộ trộn ưu tiên.

Ứng dụng của bạn có thể truyền trực tuyến ở bất kỳ cấu hình nào đến mô hình trừu tượng phần cứng (HAL) và đến thiết bị, miễn là thiết bị USB hỗ trợ .

Hai hành vi trình kết hợp được phép trong AudioMixerAttributesDEFAULTBIT_PERFECT. Khi hành vi của bộ trộn là DEFAULT, điều này cho biết rằng âm thanh dữ liệu từ nhiều nguồn khác nhau.

Khi hành vi của bộ trộn là BIT_PERFECT, sẽ không trộn âm thanh, điều chỉnh âm lượng, hoặc hiệu ứng đã xử lý âm thanh sẽ được áp dụng cho nội dung phát. Dữ liệu được gửi dưới dạng là đến HAL và cuối cùng là xuống thiết bị USB.

Việc sử dụng BIT_PERFECT cho phép bạn phát trực tiếp kỹ thuật số (DSD) qua điều chế mã xung (PCM) trên các thiết bị chạy Android. Mã mẫu sau đây cho thấy cách thực hiện điều này:

Kotlin

val EXPECTED_FORMAT: AudioFormat = AudioFormat.Builder()
  .setEncoding(AudioFormat.ENCODING_PCM_24BIT_PACKED)
  .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
  .setSampleRate(44100)
  .build()

fun startPlayback() {
  // Query all supported mixer attributes
  val mixerAttributesList: List<AudioMixerAttributes?> =
    mAudioManager.getSupportedMixerAttributes(usbDevice)

  // Find the wanted mixer attributes
  val mixerAttributes = mixerAttributesList.stream()
    .filter { mixerAttr: AudioMixerAttributes? ->
      EXPECTED_FORMAT.equals(
        mixerAttr!!.format
      )
    }
    .findAny()
    .orElse(null)

  // Register a listener to mixer attributes changed
  val listener = MyPreferredMixerAttributesChangedListener()
  mAudioManager.addOnPreferredMixerAttributesChangedListener(
    Executors.newSingleThreadExecutor(), listener
  )

  // Currently, only media usage over USB devices will be allowed
  val attr: AudioAttributes = AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_MEDIA).build()
  // Set preferred mixer attributes
  mAudioManager.setPreferredMixerAttributes(
    attr, usbDevice, mixerAttributes
  )

  // Start playback, note the playback and the audio format must
  // match what is set when calling `setPreferredMixerAttriutes`
  // API.
  val audioTrack = AudioTrack.Builder()
    .setAudioAttributes(attr)
    .setAudioFormat(mixerAttributes!!.format)
    .build()

  // Clear all preferred mixer attributes related stuff when
  // playback task is completed
  mAudioManager.clearPreferredMixerAttributes(attr, usbDevice)
  mAudioManager.removeOnPreferredMixerAttributesChangedListener(listener)
}

private class MyPreferredMixerAttributesChangedListener :
  AudioManager.OnPreferredMixerAttributesChangedListener {
  override fun onPreferredMixerAttributesChanged(
    attributes: AudioAttributes,
    device: AudioDeviceInfo,
    mixerAttributes: AudioMixerAttributes?,
  ) {
    // Do something when preferred mixer attributes changed
  }
}

Java

final AudioFormat EXPECTED_FORMAT = new AudioFormat.Builder()
        .setEncoding(AudioFormat.ENCODING_PCM_24BIT_PACKED)
        .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
        .setSampleRate(44100)
        .build();

void startPlayback() {
    // Query all supported mixer attributes
    List<AudioMixerAttributes> mixerAttributesList =
    mAudioManager.getSupportedMixerAttributes(usbDevice);

    // Find the wanted mixer attributes
    AudioMixerAttributes mixerAttributes =
        mixerAttributesList.stream()
            .filter(mixerAttr -> EXPECTED_FORMAT.equals(mixerAttr.getFormat()))
            .findAny()
            .orElse(null);

    // Register a listener to mixer attributes changed
    MyPreferredMixerAttributesChangedListener listener =
        new MyPreferredMixerAttributesChangedListener();
    mAudioManager.addOnPreferredMixerAttributesChangedListener(
            Executors.newSingleThreadExecutor(), listener);

    // Currently, only media usage over USB devices will be allowed
    AudioAttributes attr = new AudioAttributes.Builder()
           .setUsage(AudioAttributes.USAGE_MEDIA).build();
    // Set preferred mixer attributes
    mAudioManager.setPreferredMixerAttributes(
            attr, usbDevice, mixerAttributes);

    // Start playback, note the playback and the audio format must
    // match what is set when calling `setPreferredMixerAttriutes`
    // API.
    AudioTrack audioTrack = new AudioTrack.Builder()
            .setAudioAttributes(attr)
            .setAudioFormat(mixerAttributes.getFormat())
            .build();

    // Clear all preferred mixer attributes related stuff when
    // playback task is completed
    mAudioManager.clearPreferredMixerAttributes(attr, usbDevice);
    mAudioManager.removeOnPreferredMixerAttributesChangedListener(
            listener);
}

private class MyPreferredMixerAttributesChangedListener
        implements AudioManager.OnPreferredMixerAttributesChangedListener {
    @Override
    public void onPreferredMixerAttributesChanged(
        AudioAttributes attributes,
        AudioDeviceInfo device,
        AudioMixerAttributes mixerAttributes) {
        // Do something when preferred mixer attributes changed
    }
}