عندما تحاول الوصول مباشرةً إلى جهاز الصوت الملحق عبر USB باستخدام واجهات برمجة تطبيقات USB، ستُنشأ مشاكل. وقد تتضمن هذه المشاكل ما يلي: مشاكل متعلقة بالأمان والحدّ من تشغيل الوسائط من التطبيقات الأخرى وفقدان المنبّهات والإشعارات ونغمات الرنين على أجهزة USB.
لتحسين تشغيل الصوت، يمكنك بدلاً من ذلك ضبط سمات جهاز المزج.
إعداد سمات أداة المزج
باستخدام واجهات برمجة تطبيقات
AudioMixerAttributes
، يمكنك
ضبط سمات أداة المزج المفضّلة في تطبيقك عبر USB.
عندما يتطابق تشغيل التطبيق مع تنسيق الترميز وقناع القناة ومعدل العينة لسمات أداة المزج المفضّلة، يتم إرفاق التشغيل ببثّ إخراج الصوت الذي تم ضبط أداة المزج فيه باستخدام سمات أداة المزج المفضّلة.
يمكن لتطبيقك البث باستخدام أي إعداد إلى طبقة تجريد الأجهزة (HAL) وإلى الجهاز، ما دام جهاز USB متوافقًا مع الضبط.
سلوكا المزج المسموح به في AudioMixerAttributes
هما DEFAULT
وBIT_PERFECT
. عندما يكون سلوك جهاز المزج DEFAULT
، يعني ذلك أنّ البيانات الصوتية
من مصادر مختلفة تكون مختلطة.
عندما يكون سلوك جهاز المزج BIT_PERFECT
، لا يتم تطبيق خلط الصوت أو تعديل مستوى الصوت أو
تأثير تمت معالجة الصوت على التشغيل. يتم إرسال البيانات كما هي إلى HAL
وأخيرًا إلى جهاز USB.
يتيح لك استخدام BIT_PERFECT
البث المباشر الرقمي (DSD) عبر تعديل الرمز النبضي (PCM) على الأجهزة التي تعمل بنظام التشغيل Android.
يوضح الرمز البرمجي التالي كيفية تحقيق ذلك:
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 } }