Created
May 5, 2015 16:43
-
-
Save mstultz/08779f687422e4437069 to your computer and use it in GitHub Desktop.
Naive recording of 16384 frames of the Remote IO input
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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