Support USB Audio devices on Android

Hi,

I am part of a team developing a music app that uses JUCE for the audio engine.

We would like to add support for USB audio recording and playback on Android for USB OTG-capable devices. Target audio interfaces would be e.g. the iRig UA, Scarlett Focusrite, Steinberg etc. At the moment, it looks like JUCE supports USB audio on Android only halfway: it does route the output to the USB device but ignores the input and keeps using the internal microphone. I have tested this using the “Recording” and “Synthesisers” demos with JUCE 4.2.4 on an iRig UA and Scarlett 2i2.

It would be neat if JUCE could add support for USB audio on Android. Ideally, there would be a callback informing the client when an USB audio-capable device is plugged in, and the client would have the ability to either route the input/output to it, or keep using the internal microphone/speakers.

Do you have any plans to add USB audio support to JUCE on Android in the near future?

Thanks for your reply.

1 Like

OK I’ve added this to our back log along with some other android audio fixes which we have been working on.

Just an update on this: OpenSL completely abstracts away the different audio devices and routing. Any Android app using OpenSL (including JUCE) will only ever see a single audio output device called the “Default Android Output”.

The only way to control audio routing is by specifying an intent for your audio output. JUCE will always specify the “Media” intent (the default). With this intent, Android will automatically re-route the audio to an external USB audio device when it is plugged in. JUCE won’t even get a callback when this happens. There is also the “Telephony” intent which will always keep the audio on the ear-piece etc.

It would be great if Android started properly supporting multichannel audio devices. I’m sure it must be something they’re working on…

They already support it with HDMI. The problem is that I’m struggling to figure out the number of channels of the current audio routing with OpenSL. Maybe you can help @adamski?

Even in Java (without OpenSL) it’s hard to get the number of output channels of the currently selected output device.

You can get a list of audio device infos with the getDevices method and each info includes detailed information about the audio channels. But I’m failing to find out which device is currently active with the currently routed output.

It seems that in Java, you can only query the currently routed output when the audio output is actually playing audio (see the getRoutedDevice method and the description saying it’s only valid when the AudioTrack is actually playing).

That would be OK for JUCE but JUCE uses OpenSL. And I can’t find a way to somehow determine the routed device in OpenSL. In fact, OpenSL always just says that the “Default Android Output” is selected even after plugging in a USB or HDMI device.

So ultimately my question is: how can I find the AudioDeviceInfo of the audio output device that OpenSL is currently using?

I’m investigating this but I’m not clear on what the problem is, first it was about audio input, then it changes to output

asavio originally stated: “JUCE supports USB audio on Android only halfway: it does route the output to the USB device but ignores the input and keeps using the internal microphone.”

Fabian you then say “how can I find the AudioDeviceInfo of the audio output device that OpenSL is currently using?”

Please could you clarify the problem so I can take a look? :slight_smile:

Hi @Don_Turner,
thank you for looking into this. The post is a bit confusing as we have fixed a few bugs in the meantime.

Basically, @asavio’ s first part of his problem should be fixed: we’ve changed the audio intent to media and therefore Android should automatically re-route JUCE’s incoming/outgoing audio from/to usb audio devices.

@asavio second request is to have a callback to inform the client when USB audio-capale devices are plugged in. And fixing this problem is similar to @adamski’s problem: how do I query the number of channels of an audio device (for example HDMI may have 7.1).

It’s easy to do both from Java: in Java I can query all the attached audio devices and query their properties (such as number of channels etc.) and register callbacks when devices come and go. But in OpenSL (C++) there is no way to figure out the native number of channels of a device or to register callbacks.

If there would be some way to know which OpenSL device corresponds to which Java audio device then I could work out the number of channels but it seems that OpenSL only ever reports a single device (the “Android OpenSL Default Device” - this is also confirmed by looking at the source code of AOSP’s libwilhelm).

I understand that audio routing is done by the system - so Java gives you a method to find out the currently routed audio device. This, however, will only work if you are playing back audio via an AudioTrack. I’m looking for something similar in OpenSL.

Fabian

1 Like

Android 7.0 (Nougat, API 24) introduced some extra methods to allow you to obtain an AudioRouting object (which will allow you to get the audio device which is currently in use) from OpenSL by using the AcquireJavaProxy method.

Here’s how:

  1. When creating your OpenSL player include the SL_IID_ANDROIDCONFIGURATION interface

  2. Once the player is realized obtain the configuration interface:

    SLresult result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDCONFIGURATION, &configItf);

  3. Use the configuration interface to obtain the AudioRouting handle

    SLresult result = (*configItf)->AcquireJavaProxy(configItf, SL_ANDROID_JAVA_PROXY_ROUTING, &routingObj);

  4. Pass this back to Java (or do some JNI hackery) to obtain an AudioRouting object

  5. Call AudioRouting.getRoutedDevice() to obtain an AudioDeviceInfo object

Example app here: https://android.googlesource.com/platform/cts/+/master/tests/tests/media/libndkaudio

3 Likes

BTW, I’m aware this is cumbersome because of the need to go through Java. Have filed internal feature request (id: 35620262) to address this.

Thanks! This is exactly what I was looking for!

Hi fabian,
Just checking to see if you have any updates on this?

Sorry no updates on this yet. But it’s not forgotten.

Very useful post. I have been able to use the available java proxy object to access the device type and input channel counts via the AudioRouting object’s ‘getRoutedDevice()’ AudioDeviceInfo using JNI.

What I’m hoping to do is add an AudioRouting.OnRoutingChangedListener to the AudioRouting so I am aware when the input device changes, and propagate any change message back to my c++ so I can ultimately adjust the number of input channels I use in my app. Admittedly, I’m not a JNI expert. Is this even possible with JNI?

Anything is possible with JNI… its just a pain to work with!

1 Like

It was indeed a bit of a pain, but I did get it to work swimmingly!