Open-source IAA Transport control panel for TAAE?

Is there anyone in the community who is willing to share their solution for getting an IAA transport control panel into their TAAE project? Sure, I can release my app with IAA, but it would be a much better user experience if I could provide her/him with a convenient control panel to start/stop recording.

I've been wrestling with implementing an IAA Transport control panel into my app, but haven't had much success. I'm referencing the CAUITransportView files from Apple's Inter-App Audio Suite code example found here:

https://developer.apple.com/library/ios/samplecode/InterAppAudioSuite/Introduction/Intro.html

For me, it's not a simple "plug and play" bit of code. But, if and when I figure it out, I'll be sure to post it for others to use.

Thanks,
Mark

Comments

  • So, I attempted to implement the transport view and app switcher via the iOS 8-only CoreAudioKit framework info from the "What's new in Core Audio" presentation at WWDC 2014. But, I still can't get the IAA host to show up or be controllable. Here's the keynote video showing the code to integrate the IAA UI into your app (jump to 16:00 minutes):

    https://developer.apple.com/videos/wwdc/2014/#501

         // Add IAA SwitcherView in viewDidLoad method
         
         CAInterAppAudioSwitcherView *switcherView = [[CAInterAppAudioSwitcherView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
         switcherView.backgroundColor = [UIColor darkGrayColor];
         [switcherView setOutputAudioUnit:_audioController.audioUnit];
         [self.view addSubview:switcherView];
    
         // Add IAA TransportView in viewDidLoad method
         
         CAInterAppAudioTransportView *transportView = [[CAInterAppAudioTransportView alloc] initWithFrame:CGRectMake(0, 60, self.view.frame.size.width, 50)];
         transportView.backgroundColor = [UIColor blackColor];
         transportView.recordButtonColor = [UIColor redColor];
         transportView.playButtonColor = [UIColor whiteColor];
         transportView.rewindButtonColor = [UIColor whiteColor];
         transportView.pauseButtonColor = [UIColor whiteColor];
         transportView.labelColor = [UIColor lightGrayColor];
         [transportView setOutputAudioUnit:_audioController.audioUnit];
         [self.view addSubview:transportView];
    

    Perhaps, I'm not setting the 'setOutputAudioUnit' from TAAE correctly?

    Thanks,
    Mark

  • @Michael, If I were to add the CoreAudioKit's TransportView and SwitcherView views to TheEngineSample, what would I specify for the 'setOutputAudioUnit' from the following code example? I attempt to connect the switcher and transport views, but to no avail. The transport controls appear fine, but they don't work. Also, I can't get the switcher view host icon to display. But, I think that they should display once the audioUnit is connected properly. The IAA works as expected, and my app, DrumKick, appears in GarageBand and AudioShare as an IAA connection. Perhaps, I'm not capturing the audioUnit correctly from the AppDelegate to the ViewController.

    Here's the code example from the 2014 presentation on What's New in Core Audio:

    // CAInterAppAudioSwitcherView
    
    // Creating from a nib file
    #import 
    
    IBOutlet CAInterAppAudioSwitcherView *switcherView;
    
    -(void) viewDidLoad {
        [super viewDidLoad];
        ...
        switcherView.backgroundColor = [UIColor darkGrayColor];
        [switcherView setOutputAudioUnit: outputAU];
    }
    

    And here's the code for adding the transportView programmatically:

    CAInterAppAudioTransportView *transportView =
        [[CAInterAppAudioTransportView alloc] initWithFrame:
           CGRectMake(0, 0, kTransportViewHeight, kTransportViewWidth)];
    
    transportView.rewindButtonColor = transportView.playButtonColor = [UIColor whiteColor];
    transportView.pauseButtonColor = [UIColor whiteColor];
    transportView.labelColor = [UIColor lightGrayColor]; transportView.backgroundColor = [UIColor darkGrayColor]; [transportView setOutputAudioUnit: outputAU];
    
    [self addSubview: transportView];
    

    Where can I obtain 'outputAU' to work with this Core Audio example?

    Here's a link to the PDF: http://devstreaming.apple.com/videos/wwdc/2014/501xxfo4d68h054/501/501_whats_new_in_core_audio.pdf

    And the WWDC video presentation:
    https://developer.apple.com/videos/play/wwdc2014-501/

    Thanks for your help in advance.

    Take care,

    Mark

  • Hey @markjeschke - Huh. I would've done exactly what you did, provide _audioController.audioUnit as the output unit. If that doesn't work, I dunno! (Applause for Apple, btw, for making this as hard as possible. Wtf?)

  • @Michael, Thanks for your response. I'll keep trying, and will keep you posted with my progress. If and when I figure out how to add IAA transport and app switcher panels with TAAE, I'd like to add it into the helpful TheEngineSample, along with Audiobus SDK, Audiobus Remote triggers, AUSampler, and MIDI integration.

    Thanks,

    Mark

  • @markjeschke, I just implemented it in my Gender for iOS (Gamelan Gender) following your links and had no problems with providing _audioController.audiounit for outputAU.

    otmar

  • edited November 2015

    @okramis, Would you be willing to provide a GitHub Gist or code example in your comment? I'm probably missing something essential. Did you add the transportVIew, switcherView, or both? If so, did you place them in the ViewController or the AppDelegate?

    Thanks, in advance!

    Take care,

    Mark

  • @markjeschke, I just added the transportView, as switching back to the host with iOS9 works great with the top left corner link. As I initialize the audioController in the ViewController, I add the transportView there. But I just tried with initializing audioController in AppDelegate and transportView in ViewController --> works the same.

    What I do (just what's relevant to TAAE/Audiobus/TransportView):

    Add CoreAudioKit.framework

    AppDelegate.h

    import "TheAmazingAudioEngine.h"

    @property (nonatomic, strong) AEAudioController *audioController;

    AppDelegate.m

    AudioStreamBasicDescription asbd = AEAudioStreamBasicDescriptionNonInterleaved16BitStereo;

    AVAudioSession *session = [AVAudioSession sharedInstance];

    asbd.mSampleRate = session.sampleRate;

    self.audioController = [[AEAudioController alloc] initWithAudioDescription:asbd inputEnabled:NO];

    _audioController.preferredBufferDuration = 0.005f;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];

    [_audioController start:NULL];

    self.viewController = [[ViewController alloc] initWithNibName:@ViewController bundle:nil controller:_audioController];

    ViewController.h

    import "Audiobus.h"

    @class AEAudioController;

    • (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil controller:(AEAudioController *)audioController;

    @property (nonatomic, strong) AEAudioController *audioController;

    @property (nonatomic, strong) ABAudiobusController *audiobusController;

    ViewController.m

    import "TheAmazingAudioEngine.h"
    import <CoreAudioKit/CoreAudioKit.h>

    • (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil controller:(AEAudioController *)audioController {

    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {

    self.audioController = audioController;

    _audiobusController = [[ABAudiobusController alloc] initWithApiKey:@MyAppKey];

    AudioUnit outputUnit = _audioController.audioUnit;

    ABSenderPort *absender = [[ABSenderPort alloc] initWithName:@MyApp (Generator)
    title:NSLocalizedString(@MyApp (Generator), @"")
    audioComponentDescription:(AudioComponentDescription) {
    .componentType = kAudioUnitType_RemoteGenerator,
    .componentSubType = 'xxxx',
    .componentManufacturer = 'xxxx' }
    audioUnit:self.audioController.audioUnit];

    [_audiobusController addSenderPort:absender];

    CGRect _viewrect = CGRectMake(10, self.view.frame.size.height - 65, self.view.frame.size.width - 20, 20);

    CAInterAppAudioTransportView *transportView = [[CAInterAppAudioTransportView alloc] initWithFrame:_viewrect];

    transportView.rewindButtonColor = transportView.playButtonColor = transportView.pauseButtonColor = [UIColor whiteColor];

    transportView.labelColor = [UIColor lightTextColor];

    transportView.backgroundColor = [UIColor colorWithWhite:0.27f alpha:0];

    [transportView setOutputAudioUnit: _audioController.audioUnit];

    [transportView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];

    [self.view addSubview: transportView];

  • Here's a sample solution (TheEngineSample) with audiobus and the IAA transport view. Audiobus is not really functional, as there's no valid key, but IAA works....

  • edited November 2015

    Wow! @okramis, thank you so much for this! This was a big help with troubleshooting what the issue was. For some reason, it wasn't working because my ViewController.mm C++ compiler extension, rather than just the .m extension. I have no idea why. But, the transportView controller works as expected, so thank you for sharing your code!

    I have a couple more questions, if you have the time?

    First question: How do you detect whether IAA is connected to the app? Ideally, I would like to hide the IAA transportView UI until it's actually connected to another IAA-enabled app, such as GarageBand, rather than have it appear in a disabled state when it's not connected. In your Xcode example, I attempted to use the following, but to no avail:

    @property (nonatomic, strong) CAInterAppAudioTransportView *transportView;
    
    @end
    ...
    
    CGRect _viewrect = CGRectMake(10, 30, 300, 30);
        _transportView = [[CAInterAppAudioTransportView alloc] initWithFrame:_viewrect];
        _transportView.rewindButtonColor = _transportView.playButtonColor = _transportView.pauseButtonColor = [UIColor blackColor];
        _transportView.labelColor = [UIColor darkTextColor];
        //transportView.backgroundColor = [UIColor colorWithWhite:0.27f alpha:0];
        [_transportView setOutputAudioUnit: outputUnit];
        [_transportView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
        [self.view addSubview: _transportView];
    
    if (_audiobusController.interAppAudioConnected) {
            _transportView.backgroundColor = [UIColor greenColor];
        } else {
            _transportView.backgroundColor = [UIColor blueColor];
        }
    

    Second question: When I attempt to implement the IAA switcherView, my app crashes when attempting to connect from GarageBand. Here's my code:

    outputUnit = _audioController.audioUnit;
        
    CGRect _switchview = CGRectMake(30, 10, 80, 40);
    
    switcherView = [[CAInterAppAudioSwitcherView alloc] initWithFrame:_switchview];
    switcherView.backgroundColor = [UIColor darkGrayColor];
    [switcherView setOutputAudioUnit: outputUnit];
    [switcherView setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
    [self.view addSubview: switcherView];
    

    If I comment out [switcherView setOutputAudioUnit: outputUnit]; the app doesn't crash upon opening from GarageBand, but the GarageBand app icon doesn't appear in TheEngineSample for switching back. :(

    Thanks again for your time and help. I truly appreciate the effort and expertise! You're a life-saver!

    Take care,

    Mark

  • @markjeschke, I added an additional observer on the audiounit:

            AudioUnitAddPropertyListener(outputUnit,
                                     kAudioUnitProperty_IsInterAppConnected,
                                     AudioUnitPropertyChanged,
                                     (__bridge void*)self);
    

    •(void)audioUnitPropertyChanged:(void *)object unit:(AudioUnit)unit

                          propID:(AudioUnitPropertyID)propID scope:(AudioUnitScope)scope
                         element:(AudioUnitElement)element {
    if (propID == kAudioUnitProperty_IsInterAppConnected) {
        UInt32 connected;
        UInt32 dataSize = sizeof(UInt32); AudioUnitGetProperty(outputUnit,
                                                               kAudioUnitProperty_IsInterAppConnected, kAudioUnitScope_Global,
                                                               0,
                                                               &connected,
                                                               &dataSize); connectedIAA = (BOOL)connected;
        [self initIAATransport];
    }
    

    }

    //then on property change, I do this:

    •(void)initIAATransport {

    if (connectedIAA && !_audiobusController.memberOfActiveAudiobusSession) {
        if (!transportView) {
            CGRect _viewrect;
            if ( _IS_PAD) {
                _viewrect = CGRectMake(0, 0, 300, 25);
            } else {
                _viewrect = CGRectMake(0, 0, 260, 20);
            }
            transportView = [[CAInterAppAudioTransportView alloc] initWithFrame:_viewrect];
            transportView.rewindButtonColor = transportView.playButtonColor =
            transportView.pauseButtonColor = [UIColor whiteColor];
            transportView.labelColor = [UIColor lightTextColor];
            transportView.backgroundColor = [UIColor colorWithWhite:0.27f alpha:0];
            [transportView setOutputAudioUnit: _audioController.audioUnit];
        }
        [transportHolder addSubview: transportView];
    } else {
        if (transportView) {
            [transportView removeFromSuperview];
        }
    }
    

    }

    As kAudioUnitProperty_IsInterAppConnected is also true, when the app is connected to audiobus, I only show the transportView, when it's connectedIAA but not memberOfActiveAudiobusSession.

    Because of view-size changes on the iPhone when the host app activates the microphone (red statusbar in double hight), I place a view (transportHolder) on viewWillAppear and add the transportView to that.

    As I haven't played with the switcher, I can't give you advice on that. But I had the issue of frozen app when it got launched from IAA-host due to too long initialization. I then moved some initialization tasks async to the global queue to fix that. You might have a similar issue.

    regards
    Otmar

  • @okramis, Thanks for the response. It all makes sense, but I'm having trouble with how to implement the additional observer on the audioUnit. Can you tell me where this goes in TheEngineSample ViewController? Where are you defining the 'scope' variable? Thanks in advance.

    As for the host app switcher, that's more of a convenience for the user. But, the transportView was more important.

    Thanks, again!

    Take care,

    Mark

  • edited November 2015

    @markjeschke; Oh I see there are some parts missing:

    place in ViewController.m before @end after the properties

    – (void)audioUnitPropertyChanged:(void *)object
                            unit:(AudioUnit)unit
                          propID:(AudioUnitPropertyID)propID
                           scope:(AudioUnitScope)scope
                         element:(AudioUnitElement)element;
    

    place after @end before @implementation

    void AudioUnitPropertyChanged(void *inRefCon,
                              AudioUnit inUnit,
                              AudioUnitPropertyID inID,
                              AudioUnitScope inScope,
                              AudioUnitElement inElement)
    {
    ViewController *SELF = (__bridge ViewController *)inRefCon;
    
    [SELF audioUnitPropertyChanged:inRefCon
                              unit:inUnit
                            propID:inID
                             scope:inScope
                           element:inElement];
    }
    
  • @okramis, Thanks so much for the clarification. I added the code, but I'm still unable to get the connectedIAA boolean to register. Can you please tell me how you're establishing the connectedIAA boolean? Is it a normal @property (nonatomic) BOOL connectedIAA? I'm also receiving a warning on the void AudioUnitPropertyChanged(void *inRefCon, where it's saying, "No previous prototype for function 'AudioUnitPropertyChanged'" (Please see screenshot).

    void AudioUnitPropertyChanged(void *inRefCon,
                                  AudioUnit inUnit,
                                  AudioUnitPropertyID inID,
                                  AudioUnitScope inScope,
                                  AudioUnitElement inElement)
    {
        ViewController *SELF = (__bridge ViewController *)inRefCon;
        
        [SELF audioUnitPropertyChanged:inRefCon
                                  unit:inUnit
                                propID:inID
                                 scope:inScope
                               element:inElement];
    }
    

    I'm sorry if this is trivial, but I truly appreciate your help.

    Thanks,

    Mark

  • Here's the updated sample solution including the AU observer and transportHolder.

    For the connectedIAA I just use a local bool variable declared inside @implementation { }

    Best regards
    Otmar

  • @okramis, WOW! Thank you so much for this! It turns out that I had the following code outside of the initAudiobus method:

    AudioUnitAddPropertyListener(outputUnit,
                                     kAudioUnitProperty_IsInterAppConnected,
                                     AudioUnitPropertyChanged,
                                     (__bridge void*)self);
    

    It works like a charm! I truly appreciate your help and time! Thanks, again!

    Take care,

    Mark

Sign In or Register to comment.