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, sẽ phát sinh sự cố. Các vấn đề này có thể bao gồm: vấn đề bảo mật, hạn chế phát nội dung nghe nhìn từ các ứng dụng khác và mất chuông báo, thông báo và nhạc chuông qua thiết bị USB.

Để cải thiện khả 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 các thuộc tính của trình kết hợp

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 của ứng dụng khớp với định dạng mã hoá, mặt nạ kênh và tốc độ lấy mẫu của các thuộc tính bộ trộn ưu tiên, quá trình phát sẽ được đính kèm vào luồng đầu ra âm thanh có bộ trộn được định cấu hình với các thuộc tính bộ trộn ưu tiên.

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

Hai hành vi 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 dữ liệu âm thanh từ nhiều nguồn sẽ bị trộn lẫn.

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

Việc sử dụng BIT_PERFECT cho phép bạn phát trực tuyến 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 việc 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
    }
}