AudioDeviceManager confusion

In my app, I want to let the user choose one of the available audio output devices (which can be multichannel, like the Fireface 400). Once they select an audio device, I would like to give them further choices, specific to the device, for mapping available input and output channels to my application’s functions.

However, when I enumerate device types and then device names, I get completely unexpected results. It seems that Juce is treating individual channel pairs as their own device? This is what I get from Juce:

[code]int id = 1;
for (int i = 0; i < m_adm.getAvailableDeviceTypes().size(); ++i)
{
const AudioIODeviceType* t = m_adm.getAvailableDeviceTypes()[i];
const StringArray deviceNames = t->getDeviceNames ();

for (int j = 0; j < deviceNames.size (); ++j )
{
const String deviceName = deviceNames[j];

String menuName;

menuName << deviceName << " (" << t->getTypeName() << ")";

c->addItem (menuName, id++);

s += menuName.toCString();
s += "\n";

}
}
[/code]

Produces this list of items in the ComboBox:

Speakers (RME Fireface 400) (Windows Audio) ADAT (1+2) (RME Fireface 400) (Windows Audio) Analog (3+4) (RME Fireface 400) (Windows Audio) ADAT (5+6) (RME Fireface 400) (Windows Audio) Analog (7+8) (RME Fireface 400) (Windows Audio) SPDIF Interface (RME Fireface 400) (Windows Audio) ADAT (3+4) (RME Fireface 400) (Windows Audio) Analog (5+6) (RME Fireface 400) (Windows Audio) ADAT (7+8) (RME Fireface 400) (Windows Audio) Primary Sound Driver (DirectSound) Speakers (RME Fireface 400) (DirectSound) ADAT (1+2) (RME Fireface 400) (DirectSound) Analog (3+4) (RME Fireface 400) (DirectSound) ADAT (5+6) (RME Fireface 400) (DirectSound) Analog (7+8) (RME Fireface 400) (DirectSound) SPDIF Interface (RME Fireface 400) (DirectSound) ADAT (3+4) (RME Fireface 400) (DirectSound) Analog (5+6) (RME Fireface 400) (DirectSound) ADAT (7+8) (RME Fireface 400) (DirectSound) ASIO Fireface (ASIO) ASIO4ALL v2 (ASIO) Stanton 1394 ASIO Driver (ASIO)

I was expecting something more like this:

ASIO Fireface (ASIO) ASIO4ALL v2 (ASIO) Stanton 1394 ASIO Driver (ASIO) RME Fireface 400 (WASAPI (Shared Mode)) Microphone (Webcam 600) (WASAPI (Shared Mode)) High Definition Audio Device (WASAPI (Shared Mode))

Open questions:

  • Why didn’t Juce find Microphone (Webcam 600) for WASAPI?
  • Why didn’t Juce find High Definition Audio Device for WASAPI?
  • Why does the RME Fireface 400 show up as 9 individual devices for WASAPI?

What am I doing wrong??

Uh oh it looks like Juce is treating the each entry in the IMMDeviceCollection as a separate WASAPI device instead of a list of channels…?

Am I on my own here?

That’s the list of devices that the system returns… Not sure what you want me to do about that…

I think that the IMMDeviceCollection implies that the result is a “collection” of channels and not individual devices.

Should I dig into the WASAPI implementation and see what happens if I change it to treat the IMMDeviceCollection as a set of channels instead of individual devices?

I get the feeling that there aren’t too many people out there using Juce and WASAPI to do multichannel output…has anyone else worked with the WASAPI output and encountered this? I mean, its fine for a single stereo channel output but it seems to be not possible to configure Juce to support more than one stereo channel pair with WASAPI.

It’s a while since I wrote the code, and I don’t remember too well how it all works, but any clues you can offer would be interesting.

Gah, it looks like I’m the guinea pig. I’m pretty confident that the IMMDeviceCollection interface is just a poorly named identifier for the list of channels. Along with other stupidly named identifiers (thanks Microsoft). I know this because…well, certain other commercial applications which shall not be named, treat it as such. They correctly list the available channels as a list instead of as different devices.

I will dig into it myself and attempt to re-write the affected portions of juce_win32_WASAPI.cpp

Jules what OS do you develop on?

OSX mostly.

Okay, it seems that the AudioDeviceManager assumes that you have already called setCurrentAudioDeviceType to choose a type.

For example if you call initialise() and pass in a preferred default device name, and the current audio device type doesn’t match that device, the subsequent call to setAudioDeviceSetup() will fail, because the current type doesn’t have a device with that name.

It almost seems like the AudioDeviceSetup is missing a field “String audioDeviceType”. Actually I’m pretty confident of this because initialise() has this code to restore from Xml:

currentDeviceType = e->getStringAttribute ("deviceType");

Without this line the call to setAudioDeviceSetup() can fail.

The “else” statement in initialise has no equivalent code - it assumes you have set the device type ahead of time. It is impossible to fully describe the current audio setup from the AudioDeviceSetup structure alone.

If this is BS please by all means call me out on it

Well, the intention with AudioDeviceSetup isn’t really to provide a class that you can use to store the state of the device manager (that’s what the XML is for). AudioDeviceSetup is just a temporary object - if you want to tweak a device setting, you’d call getAudioDeviceSetup(), mess with a couple of settings, and then apply them with setAudioDeviceSetup().

Yes and yes. However, what I am saying is that AudioDeviceSetup is missing the deviceTypeName field - it is not possible to fully describe the state of the audio hardware without this field. The only way to switch to a different device is to first call setCurrentAudioDeviceType and then call setAudioDeviceSetup.

It would be very convenient if AudioDeviceSetup had a field for deviceTypeName.

Take for example the situation when you call initialise() with preferredDefaultDeviceName set to the name of a valid device, but one that is from a deviceType different than the current one (which will simply be the first one with a freshly created manager), and the default device for the current type cannot be opened - for example, if another application is running that grabbed it exclusively.

When initialise() fails to get the exclusive device, it eventually calls setAudioDeviceSetup() with the preferredDefaultDeviceName, but that fails because the current device type is merely the first one in the list, not the type corresponding to the chosen preferredDefaultDeviceName.

In order to have initialise() work properly one would have to call setCurrentDeviceType() before initialise(), which is a bit of a hack.

I’ve got the same symptom: RME fireface showing up as a series of stereo devices instead of one multichannel device. Not having burrowed into this any further it’s difficult to follow your discussion. Did you find a solution, and did it involve changes in juce?

Yes I have the solution, the windows implementation needs to be changed. I am offering cash money for anyone who is knowledgable in these windows APIs to fix it.

I tracked it down to a specific GUID that can be used to identify when the different channels really belong to the same device. Check out this post:

http://rawmaterialsoftware.com/viewtopic.php?f=14&t=6836&hilit=cash

If you can find someone is a WASAPI and/or DirectSound guru I will pay to get this fixed and into Juce.

Tangentially related - I’ve noticed that Juce DeviceManager seems to think many laptops audio systems (Win 7 this is) don’t have any common sample rates for inputs and outputs (meaning the Device Manager reports that the input is at a different sampling rate than the output).

This is of course not the case. I have verified that this seems to happen on a number of windows 7 laptops (using the JuceDemo or my own programs).

Please, someone take Vinn up on that cash money offer! It would be great to get it cleared up.