Skip to content

Instantly share code, notes, and snippets.

@mstultz
Created May 5, 2015 16:43
Show Gist options
  • Save mstultz/08779f687422e4437069 to your computer and use it in GitHub Desktop.
Save mstultz/08779f687422e4437069 to your computer and use it in GitHub Desktop.
Naive recording of 16384 frames of the Remote IO input
//
// RecordRemoteIO.m
// AudioTest
//
// Created by Mark Stultz on 5/5/15.
// Copyright (c) 2015 Mark Stultz. All rights reserved.
//
#import <AudioToolbox/AudioToolbox.h>
#import "RecordRemoteIO.h"
#define kOutputBus 0
#define kInputBus 1
@interface RecordRemoteIO ()
{
@public
AUGraph m_graph;
AUNode m_nodeRemoteIO;
AudioUnit m_unitRemoteIO;
uint8_t *m_buffer;
ExtAudioFileRef m_file;
UInt32 m_frames;
}
@end
OSStatus OnRenderNotify(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
AudioEngine *engine = (__bridge AudioEngine *)inRefCon;
AudioBufferList bufferList;
// redundant
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mData = engine->m_buffer;
bufferList.mBuffers[0].mDataByteSize = 2 * inNumberFrames;
bufferList.mBuffers[0].mNumberChannels = 1;
ioData = &bufferList;
OSStatus status = AudioUnitRender(engine->m_unitRemoteIO, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
if (status != noErr) {
printf("AudioUnitRender error: %d\n", status);
return status;
}
// the sample buffer now contains samples you can work with
if (engine->m_file) {
OSStatus status = ExtAudioFileWrite(engine->m_file, inNumberFrames, ioData);
if (status != noErr) {
printf("ExtAudioFileWrite error: %d\n", status);
return status;
}
engine->m_frames += inNumberFrames;
if (engine->m_frames > 4096 * 4) {
OSStatus status = ExtAudioFileDispose(engine->m_file);
if (status != noErr) {
printf("ExtAudioFileDispose error: %d\n", status);
return status;
}
engine->m_file = nil;
}
}
return noErr;
}
@implementation AudioEngine
- (instancetype)init
{
self = [super init];
if (self != nil) {
m_buffer = malloc(4 * 1024);
m_frames = 0;
// 1. Create graph
{
OSStatus status = NewAUGraph(&m_graph);
if (status != noErr) {
NSLog(@"NewAUGraph failed: %d", status);
return nil;
}
status = AUGraphOpen(m_graph);
if (status != noErr) {
NSLog(@"NewAUGraph failed: %d", status);
return nil;
}
}
// 2. Create, add, and configure RemoteIO
{
AudioComponentDescription description;
memset(&description, 0, sizeof(AudioComponentDescription));
description.componentType = kAudioUnitType_Output;
description.componentSubType = kAudioUnitSubType_RemoteIO;
description.componentManufacturer = kAudioUnitManufacturer_Apple;
OSStatus status = AUGraphAddNode(m_graph, &description, &m_nodeRemoteIO);
if (status != noErr) {
NSLog(@"AUGraphAddNode failed: %d", status);
return nil;
}
status = AUGraphNodeInfo(m_graph, m_nodeRemoteIO, &description, &m_unitRemoteIO);
if (status != noErr) {
NSLog(@"AUGraphNodeInfo failed: %d", status);
return nil;
}
// Enable IO
UInt32 enableIO = 1;
UInt32 size = sizeof(UInt32);
status = AudioUnitSetProperty(m_unitRemoteIO, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enableIO, size);
if (status != noErr) {
NSLog(@"AudioUnitSetProperty failed: %d", status);
return nil;
}
status = AudioUnitGetProperty(m_unitRemoteIO, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enableIO, &size);
if (status != noErr) {
NSLog(@"AudioUnitGetProperty failed: %d", status);
return nil;
}
// Configure format
AudioStreamBasicDescription format;
format.mSampleRate = 44100.00;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
format.mFramesPerPacket = 1;
format.mChannelsPerFrame = 1;
format.mBitsPerChannel = 16;
format.mBytesPerPacket = 2;
format.mBytesPerFrame = 2;
status = AudioUnitSetProperty(m_unitRemoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &format, sizeof(AudioStreamBasicDescription));
if (status != noErr) {
NSLog(@"AudioUnitSetProperty failed: %d", status);
return nil;
}
status = AudioUnitSetProperty(m_unitRemoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &format, sizeof(AudioStreamBasicDescription));
if (status != noErr) {
NSLog(@"AudioUnitSetProperty failed: %d", status);
return nil;
}
// Add callback
AURenderCallbackStruct callback;
callback.inputProc = OnRenderNotify;
callback.inputProcRefCon = (__bridge void *)(self);
status = AudioUnitSetProperty(m_unitRemoteIO, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(AURenderCallbackStruct));
if (status != noErr) {
NSLog(@"AudioUnitSetProperty failed: %d", status);
return nil;
}
}
// 3. Start it up
{
OSStatus status = AUGraphInitialize(m_graph);
if (status != noErr) {
switch (status) {
case kAUGraphErr_NodeNotFound:
case kAUGraphErr_InvalidConnection:
case kAUGraphErr_OutputNodeErr:
case kAUGraphErr_CannotDoInCurrentContext:
case kAUGraphErr_InvalidAudioUnit:
break;
}
NSLog(@"AUGraphInitialize failed: %d", status);
return nil;
}
status = AUGraphStart(m_graph);
if (status != noErr) {
NSLog(@"AUGraphInitialize failed: %d", status);
return nil;
}
}
// 4. Create file
{
AudioStreamBasicDescription format;
format.mSampleRate = 44100.00;
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
format.mFramesPerPacket = 1;
format.mChannelsPerFrame = 1;
format.mBitsPerChannel = 16;
format.mBytesPerPacket = 2;
format.mBytesPerFrame = 2;
NSURL *URL = [NSURL URLWithString:[NSTemporaryDirectory() stringByAppendingPathComponent:@"mic.wav"]];
NSLog(@"%@", URL.path);
OSStatus status = ExtAudioFileCreateWithURL((__bridge CFURLRef)(URL), kAudioFileWAVEType, &format, 0, kAudioFileFlags_EraseFile, &m_file);
if (status != noErr) {
NSLog(@"AUGraphInitialize failed: %d", status);
return nil;
}
}
CAShow(m_graph);
}
return self;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment