Using Airplay 2 on iOS with JUCE 7.0.2

Hello! I am currently developing a mobile application which uses a static library I made with JUCE for handling audio. One of the usecases is streaming audio from an iPad (iOS 16 or higher) to speakers using Airplay 2.

According to this old thread, it should be possible (or was possible in the past) to just setup Airplay with the AudioDeviceManager class from JUCE. I tried using the getAvailableDeviceTypes method, using the code snipped from the AudioIODeviceType documentation to list all available device types, but I can only see the standard iOS Audio device.

auto& deviceTypes = _deviceManager.getAvailableDeviceTypes();
    std::cout << "Nr of available device types: " << deviceTypes.size() << std::endl;
    
    for (auto& deviceType : deviceTypes)
    {
        juce::String typeName(deviceType->getTypeName());
        std::cout << "Device type: " << typeName << std::endl;
        deviceType->scanForDevices();
        juce::StringArray deviceNames(deviceType->getDeviceNames());
        std::cout << "Nr of available devices of this type: "  << deviceNames.size() << std::endl;
    
        for (const auto& deviceName : deviceNames)
        {
            std::cout << "Device name: " << deviceName << std::endl;
        }
    }
Console output:
Nr of available device types: 1
Device type: iOS Audio
Nr of available devices of this type: 1
Device name: iOS Audio

The iPad is connected to the same network as the speakers and Airplay is setup correctly on the device. I tried giving the app all necessary permissions in XCode and the Projucer, like network and audio background permissions, but it did not help much. I then looked into the JUCE source and noticed that in juce_ios_Audio.cpp on line 283 in the function definition of setAudioSessionCategory, the option to allow AirPlay was missing - see also the documentation in AVAudioSessionTypes.h from the AVFAudio Framework. I added it as an option in the JUCE Code, but I still can only see the iOS Audio Device.

    static void setAudioSessionCategory (NSString* category)
    {
        NSUInteger options = 0;
        options |= AVAudioSessionCategoryOptionAllowAirPlay;

       #if ! JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
        options |= AVAudioSessionCategoryOptionMixWithOthers; // Alternatively AVAudioSessionCategoryOptionDuckOthers
       #endif

        if (category == AVAudioSessionCategoryPlayAndRecord)
        {
            options |= (AVAudioSessionCategoryOptionDefaultToSpeaker
                      | AVAudioSessionCategoryOptionAllowBluetooth);

            if (@available (iOS 10.0, *))
                options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
        }

        JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setCategory: category
                                                             withOptions: options
                                                                   error: &error]);
    }

Any ideas or suggestions on how I could get Airplay working? Has anyone experienced similar behavior?

Thanks in advance,
Raphael

This commit adds the required options to the AVAudioSession.

Actually connecting to an AirPlay device is very specific to iOS and needs a native component. Here’s a quick way to add a button to bring up the connection dialog:

#include <JuceHeader.h>

#define Point CarbonDummyPointName
#define AudioBuffer CarbonDummyAudioBufferName

#import <MediaPlayer/MediaPlayer.h>

std::unique_ptr<UIViewComponent> createAirplayButton()
{
    auto comp = std::make_unique<UIViewComponent>();
    auto* deviceSelector = [MPVolumeView new];
    deviceSelector.showsVolumeSlider = NO;
    comp->setView (deviceSelector);
    return comp;
}

You’ll also need to add the MediaPlayer framework.

There are nicer ways to do this

but you’re much more limited in your deployment targets.

1 Like