بهبود بازپخش صوتی

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

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

پیکربندی مشخصه‌های میکسر

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

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

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

دو رفتار مجاز میکسر در 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
    }
}