Programmatically changing input/output audio device doesn't always work

Hello,
I’ve been working on a project in JUCE (Audio app using AudioAppComponent) for a while and I’ve stumbled upon a problem that I can’t understand.
The app uses custom input/output audio devices (myInputDevice and myOutputDevice) made using Device Aggregation (through Audio MIDI Setup) on MacOSx; both input and output audio devices have 4 channels.
Neither of these devices is used a system audio device, as I want to keep using my headphones for that.

For this reason, in the constructor of my AudioAppComponent object (named MainComponent), I included some code I took from the JUCE DemoRunner demo about Audio Settings (AudioSettingsDemo.h). This code should programmatically set the audio device used in the AudioAppComponent to my custom input/output devices in place of the system audio device (i.e. my headphones).

The code below, showing both a snippet of the h and the cpp files works most times but sometimes yields an error at the following jassert:

const Type* getReadPointer (int channelNumber, int sampleIndex) const noexcept
{
    jassert (isPositiveAndBelow (channelNumber, numChannels));
    ...
}

I tried to trace back the reason of this and it seems to be related to the fact that when calling the setAudioChannels() function, the system device is used in place of my custom devices and the former doesn’t have 4 channels, so that when the copyFrom method within getNextAudioBlock() is called, the error occurs.
However, this doesn’t happen all the time, which makes it quite a nuisance to debug…

Now I must admit that I don’t understand some of the details going on in the code, e.g. I tried to call setAudioChannels() after actually setting up the custom audio device using setAudioDeviceSetup but that doesn’t get me any audio in or out the app.
Hopefully, some of you can see what I’m doing wrong.

//============================================================
class MainComponent   : public AudioAppComponent
{
public:
    MainComponent();
    ~MainComponent();
    // --------
    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override;
    void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override;
    void releaseResources() override;

private:

    //  ------ audioDeviceArea
    AudioDeviceManager::AudioDeviceSetup currentAudioSetup;
    AudioDeviceSelectorComponent audioSetupComp;
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

//============================================================

MainComponent::MainComponent() :
                 audioSetupComp (deviceManager,
                      0,     // minimum input channels
                      4,   // maximum input channels
                      0,     // minimum output channels
                      4,   // maximum output channels
                      false, // ability to select midi inputs
                      false, // ability to select midi output device
                      false, // treat channels as stereo pairs
                      false), // hide advanced options
{
...
    // --------------------------------
    // Setup device manager
    deviceManager.getAudioDeviceSetup(currentAudioSetup);

    // I need to make sure that IN/OUT have (or can have) the same sampling freq!!!!
    currentAudioSetup.inputDeviceName = "myInputDevice";
    currentAudioSetup.outputDeviceName = "myOutputDevice";
            
    //    Define IN/OUT channels
    numChannelIN = 4;
    numChannelOUT = 4;
        
    // Set audio device (I/O) buffer size and fs
    currentAudioSetup.bufferSize = currBuffSize;
    currentAudioSetup.sampleRate = currFs;

        
    // Some platforms require permissions to open input channels so request that here
    if (juce::RuntimePermissions::isRequired (juce::RuntimePermissions::recordAudio)
       && ! juce::RuntimePermissions::isGranted (juce::RuntimePermissions::recordAudio))
    {
       juce::RuntimePermissions::request (juce::RuntimePermissions::recordAudio,
                                          [&] (bool granted) { setAudioChannels (granted ? 2 : 0, 2); });
    }
    else
    {
       // Specify the number of input and output channels that we want to open
       setAudioChannels (numChannelIN, numChannelOUT);
    }


    // Use custom bufferSize and sampleRate [ must come after setAudioChannels() ]
    deviceManager.setAudioDeviceSetup(currentAudioSetup, true);
    // --------------------------------
...
}

void MainComponent::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill)
{
    ...
    // Get audio from input 4 
    myBuffer.copyFrom(0, 0, bufferToFill.buffer->getReadPointer( 3, bufferToFill.startSample), currBuffSize);
    ...
}

Hi, have you been able to solve this issue since? I have just posted a similar question to the forum then I noticed your post from 4 years ago. I am not using setAudioChannels, but my code is very similar to what setAudioChannels does in AudioAppComponent. And I am getting the same result as you, as in no audio from my inputs.

Hey, I honestly can’t recall if I found a specific solution for this very problem. :sweat_smile:

But what I’m sure of is that, for the project I was working on, I ended up using a different strategy (i.e. I stopped using the aggregated device). That’s most likely due to the fact that, among other things, I could not solve this very problem.

Sorry about that

Hi, thanks for your reply. I have rolled back my code to a stage when the above setup attempt was still working for me, to see exactly what I was doing then. I think the main difference between that stage and my current one is that I did not have various async event handlers dealing with my AudioDeviceManager object, but all the setup happened in once single function in one single thread, from beginning to end. I think it is mainly important for the functions that end up calling AudioIODeviceCallback::audioDeviceAboutToStart and AudioProcessor::prepareToPlay functions.
At least for now I have two codebases, one does what I need, the other doesn’t. When I find out more I will post it here.

Hi, I have finally found what caused my problems, I am posting in your thread too just in case it helps anyone.

The issue was caused by one line in my CMakeLists.txt file, which caused my audio to stop working:

This is the line because of which my audio got muted:
set (CMAKE_OSX_ARCHITECTURES arm64 x86_64)

And this fixes it (as I am on an Intel Mac):
set (CMAKE_OSX_ARCHITECTURES x86_64)

I have no idea why this was an issue though, and I would still need to see how it affects a potential Apple silicon build.