بهبود پخش صدا

وقتی می‌خواهید مستقیماً با استفاده از APIهای USB به دستگاه جانبی صوتی USB دسترسی پیدا کنید، مشکلاتی ایجاد می‌شود. این مشکلات می تواند شامل موارد زیر باشد: مشکلات امنیتی، محدود کردن پخش رسانه از برنامه های دیگر، و از دست دادن هشدارها، اعلان ها و آهنگ های زنگ از طریق دستگاه های USB.

برای بهبود پخش صدا، به جای آن ویژگی های میکسر را پیکربندی کنید.

پیکربندی ویژگی های میکسر

با استفاده از API های AudioMixerAttributes ، می توانید برنامه خود را با ویژگی های میکسر ترجیحی از طریق USB پیکربندی کنید.

هنگامی که پخش برنامه شما با فرمت رمزگذاری، ماسک کانال و نرخ نمونه ویژگی‌های میکسر ترجیحی مطابقت دارد، پخش به جریان خروجی صوتی متصل می‌شود که میکسر آن با ویژگی‌های میکسر ترجیحی پیکربندی شده است.

تا زمانی که دستگاه USB از پیکربندی پشتیبانی می‌کند، برنامه شما می‌تواند با هر پیکربندی به لایه انتزاعی سخت‌افزار (HAL) و به دستگاه پخش شود.

دو رفتار میکسر مجاز در AudioMixerAttributes DEFAULT و BIT_PERFECT هستند. وقتی رفتار میکسر DEFAULT است، نشان می دهد که داده های صوتی از منابع مختلف مخلوط شده اند.

وقتی رفتار میکسر BIT_PERFECT است، هیچ میکس صدا، تنظیم صدا، یا جلوه پردازش صدا در پخش اعمال نمی شود. داده ها همانطور که هست به HAL و در نهایت به دستگاه USB ارسال می شود.

استفاده از BIT_PERFECT به شما امکان می دهد جریان دیجیتال (DSD) را از طریق مدولاسیون کد پالس (PCM) در دستگاه های مجهز به Android مستقیم کنید. نمونه کد زیر نشان می دهد که چگونه می توان این کار را انجام داد:

کاتلین

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
  }
}

جاوا

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
    }
}