Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set AVAudioEngine Input and Output Devices

I've been playing around with Apple's shiny new AVFoundation library, but so far I've unable to set the input or output devices (e.g. a USB sound card) used by an AVAudioEngine, and I can't seem to find anything in the documentation to say it's even possible.

Does anyone have any experience with this?

like image 917
Benjineer Avatar asked Feb 28 '15 11:02

Benjineer


People also ask

How do you change input device to output device?

Press the Windows key + I to open Settings. Click System. Click Sound on the left pane. On the right pane, under the Input section, for the Choose your input device option, click the drop-down and select the input device you want.

What is Avaudioengine?

An object that manages a graph of audio nodes, controls playback, and configures real-time rendering constraints.


Video Answer


2 Answers

Here's a complete, if somewhat rough, function which will play some audio for testing purposes (pick a different the file if you don't have GarageBand installed there, of course). To avoid hard-coding a device ID, it switches to your alert ("sound effects") device which you can set in System Preferences.

AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AudioUnit outputUnit = engine.outputNode.audioUnit;

OSStatus err = noErr;
AudioDeviceID outputDeviceID;
UInt32 propertySize;

AudioObjectPropertyAddress propertyAddress = {
    kAudioHardwarePropertyDefaultSystemOutputDevice,
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster };
propertySize = sizeof(outputDeviceID);
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &outputDeviceID);
if (err) { NSLog(@"AudioHardwareGetProperty: %d", (int)err); return; }

err = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &outputDeviceID, sizeof(outputDeviceID));
if (err) { NSLog(@"AudioUnitSetProperty: %d", (int)err); return; }

NSURL *url = [NSURL URLWithString:@"/Applications/GarageBand.app/Contents/Frameworks/MAAlchemy.framework/Versions/A/Resources/Libraries/WaveNoise/Liquid.wav"];
NSError *error = nil;
AVAudioFile *file = [[AVAudioFile alloc] initForReading:url error:&error];
if (file == nil) { NSLog(@"AVAudioFile error: %@", error); return; }

AVAudioPlayerNode *player = [[AVAudioPlayerNode alloc] init];
[engine attachNode:player];
[engine connect:player to:engine.outputNode format:nil];

NSLog(@"engine: %@", engine);

if (![engine startAndReturnError:&error]) {
    NSLog(@"engine failed to start: %@", error);
    return;
}

[player scheduleFile:file atTime:[AVAudioTime timeWithHostTime:mach_absolute_time()] completionHandler:^{
    NSLog(@"complete");
}];
[player play];
like image 62
Nicholas Riley Avatar answered Oct 18 '22 22:10

Nicholas Riley


Ok, after re-reading the docs for the 10th time, I noticed AVAudioEngine has members inputNode and outputNode (not sure how I missed that!).

The following code seems to do the job:

AudioDeviceID inputDeviceID = 53; // get this using AudioObjectGetPropertyData
AVAudioEngine *engine = [[AVAudioEngine alloc] init];
AudioUnit audioUnit = [[engine inputNode] audioUnit];

OSStatus error = AudioUnitSetProperty(audioUnit,
                                      kAudioOutputUnitProperty_CurrentDevice,
                                      kAudioUnitScope_Global,
                                      0,
                                      &inputDeviceID,
                                      sizeof(inputDeviceID));

I borrowed the non-AVFoundation C code from the CAPlayThrough example.

like image 33
Benjineer Avatar answered Oct 18 '22 22:10

Benjineer