Reinitialize When Audio Driver Changes Occur Externally

Hi everyone,

I was wondering if there’s a way to get a notification when audio settings have been modified from a device driver’s control panel? It would be nice to update the AudioDeviceManager, and a component if showing (like the AudioDeviceSelectorComponent) when that happens, instead of breaking I/O and having to manually re-init the ADM…

You can see why I brought this up by testing this out in the JuceDemo:
[list]
[]Navigate to the Audio demo[/]
[]Stay on the Audio Device Setup tab[/]
[]Select driver of choice (I’ve had the same result testing this with Windows Audio (WASAPI) and ASIO (which you need to #define in the AppConfig.h…)[/]
[]Select an output device, if one isn’t selected[/]
[]Open your device’s control panel (either from JUCE if you can, or from within the OS)[/]
[]Modify the sample rate and/or bit-depth[/]
[]Expect no sound when pressing the “Test” button until your re-initialize the device driver (I wish there was a way JUCE handled the latter automatically)[/][/list]

Are you talking about ASIO?

ASIO, and WASAPI

(DSound doesn’t suffer from this problem, after a quick test)

Well ASIO already has a callback mechanism when the device changes, which seems to work for the devices I’ve tried… You can check whether it’s being invoked with some breakpoints in the ASIO code.

But AFAIK wasapi devices don’t generally have a global sample rate, do they? Apps can open a wasapi device at any rate it wants, and should be able to carry on using it no matter what you do to the device.

The callback methods do get called when I modify the settings within the ASIO4ALL panel… but the “AudioDeviceSetup” tab component doesn’t reflect the changes (because there’s no way to tell it to repaint in the first place).

From the looks of it, they do, according to what’s going on on my end.

Changing sample-rate/bit-depth in here:
[attachment=0]Good ol’ Realtek.png[/attachment]

Means I have to manually re-init the “audio device type” in here by switching it to something else, and back (otherwise no soup/audio for me/user):
[attachment=1]Windows Audio - Sample Rate.png[/attachment]

Note that there’s only 1 choice for sample rate in the last image, and Realtek’s control panel shows rates of 44.1/48/96/192 kHz, with depths 16 and 24 (8 choices total).

Issue seems the related to this post…

http://www.rawmaterialsoftware.com/viewtopic.php?f=3&t=6830

Of course - that’s exactly what you’d expect. Their control panel is changing some global master settings, but when an app tries to use it, the driver will only report the one sample rate that it’s currently set to. That way they force all apps to use the master rate.

Looks like WASAPI does provide a IMMNotificationClient class for monitoring the device for changes like this, which could be connected to juce’s audioDeviceListChanged() callback, which would update the selector component when it changes. Too busy to do that myself right now though!

I really need this… so I’m on it. (And holy crap Microsoft’s WASAPI code is such a disaster.)

Hey Jules,

I got it working… You may check it out on my fork here with a diff.

You’ll notice I cleaned up the ComSmartPtr class a bit to use nullptr instead of 0.

Thanks! I’ll take a look.

(BTW the smart ptr deliberately avoids nullptr for compatibility with older compilers, because it needs to work in places where it gets included before any other juce headers have had chance to define the fallback nullptr macro)

Ok… just taking a look at this. Puzzled about why you removed these lines:

if (inputDevice != nullptr) inputDevice->close(); if (outputDevice != nullptr) outputDevice->close();

?

…and what’s audioDeviceListChanged() supposed to do? It doesn’t seem to be used.

Dammit! I removed those while testing several things, and had forgotten to put them back!

That would be junk code which I’d mistakenly put there, but didn’t remove for no apparent reason. (ugh)

As for the COM smart ptr class; had a feeling it was setup that way on purpose! Definitely didn’t realize that class was getting included before other major juce headers.

(It’s what I get for playing around with code, and moving about hastily…)

I updated my fork (diff) to fix these things…

I would like to note that the code I wrote only works in WASAPI shared mode. I’m not sure if other people are using this mode, so figured I should warn.

tl;dr: There seem to be very weird bugs when doing

const bool useExclusiveMode = true; and I’m not knowledgeable enough to understand why.

It could be because your attempt at a COM object is a bit dodgy… I’m fixing it now, so try again when I’ve posted my changes.

heh Yeah… just tried to keep it simple - but figured I would be missing things. Looking forward to see what you’ve changed!

Cool, thanks for looking into what I did and putting in there (and cleaning it up) :slight_smile:

Questions;
How did you determine the whole “uuid” thing for IMMNotificationClient (and the other classes)? And what’s the point/benefit of having it there? (I understand the keywords involved… just don’t get the “whys” behind it all)

On the exclusive mode thing; switched it on, and reconfigged the device externally from my app while running and the app crashes at line 645 (WASAPIOutputDevice::copyBuffers()).

I googled for it. It’s necessary for QueryInterface to work.

Alright, thanks