Saat Anda mencoba mengakses periferal audio USB secara langsung menggunakan USB API, akan ada masalah. Masalah ini dapat mencakup: masalah keamanan, membatasi pemutaran media dari aplikasi lain, dan hilangnya alarm, notifikasi, dan nada dering melalui perangkat USB.
Untuk meningkatkan pemutaran audio, konfigurasi atribut mixer sebagai gantinya.
Mengonfigurasi atribut mixer
Dengan API
AudioMixerAttributes
,
Anda dapat mengonfigurasi aplikasi dengan atribut mixer pilihan melalui USB.
Jika pemutaran aplikasi Anda cocok dengan format encoding, mask saluran, dan frekuensi sampel dari atribut mixer yang diinginkan, pemutaran akan dikaitkan ke streaming output audio yang mixernya dikonfigurasi dengan atribut mixer pilihan.
Aplikasi Anda dapat melakukan streaming pada konfigurasi apa pun ke lapisan abstraksi hardware (HAL), dan ke perangkat, selama perangkat USB mendukung konfigurasi.
Dua perilaku mixer yang diizinkan di AudioMixerAttributes
adalah DEFAULT
dan
BIT_PERFECT
. Jika perilaku mixer DEFAULT
, ini menunjukkan bahwa data
audio dari sumber yang berbeda dicampur.
Jika perilaku mixer BIT_PERFECT
, tidak ada pencampuran audio, penyesuaian volume,
atau efek audio yang diproses yang diterapkan ke pemutaran. Data dikirim apa adanya
ke HAL dan akhirnya ke perangkat USB.
Menggunakan BIT_PERFECT
memungkinkan Anda melakukan streaming langsung
digital (DSD) melalui modulasi kode pulsa (PCM) di perangkat Android.
Contoh kode berikut menunjukkan cara melakukannya:
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 } }