| Index | Submit class | Submit snippet | Submission feed | List |

UKSoundFileRecorder

Language: Objective-C, Author: Uli Kursterer
License: MIT/X11

This is a Cocoa class that uses CoreAudio to record sound from the system's default sound input and writes it to a file.

UKSoundFileRecorder source preview

//
//  UKSoundFileRecorder.m
//  UKSoundFileRecorder
//
//  Created by Uli Kusterer on 14.07.07.
//  Copyright 2007 M. Uli Kusterer. All rights reserved.
//

// -----------------------------------------------------------------------------
//  Headers:
// -----------------------------------------------------------------------------

#import "UKSoundFileRecorder.h"
#import "NSString+NDCarbonUtilities.h"
#import <sys/param.h>  // for MAX().


// -----------------------------------------------------------------------------
//  Private method prototypes:
// -----------------------------------------------------------------------------

@interface UKSoundFileRecorder (UKSoundFileRecorderPrivateMethods)

-(void)        cleanUp;
-(NSString*)    setupAudioFile;    // Returns error string, NIL on success.
-(NSString*)    configureAU;    // Returns error string, NIL on success.
-(AudioBufferList*)  allocateAudioBufferListWithNumChannels: (UInt32)numChannels size: (UInt32)size;
-(void)        destroyAudioBufferList: (AudioBufferList*)list;

@end



@implementation UKSoundFileRecorder

// -----------------------------------------------------------------------------
//  defaultOutputFormat:
//    Returns a dictionary containing our default output format for the sound
//    data. This is what you get if you don't call setOutputFormat:.
// -----------------------------------------------------------------------------

+(NSDictionary*)  defaultOutputFormat
{
  static NSDictionary*  sDict = nil;
  if( !sDict )
  {
    sDict = [[NSDictionary alloc] initWithObjectsAndKeys:
                        [NSNumber numberWithDouble: 44100.0], UKAudioStreamSampleRate,
                        UKAudioStreamFormatMPEG4AAC, UKAudioStreamFormat,
                        [NSNumber numberWithUnsignedInt: 1024], UKAudioStreamFramesPerPacket,
                        [NSNumber numberWithUnsignedInt: 2], UKAudioStreamChannelsPerFrame,
                        UKAudioOutputFileTypeM4A, UKAudioOutputFileType,
                        nil];
  }
  
  return sDict;
}


// -----------------------------------------------------------------------------
//  * DESIGNATED INITIALIZER:
// -----------------------------------------------------------------------------

-(id)  init
{
  self = [super init];
  if( self )
  {
    outputFormat = [[[self class] defaultOutputFormat] retain];  // Apply a sensible default.
  }
  
  return self;
}

// -----------------------------------------------------------------------------
//  * CONVENIENCE INITIALIZER:
// -----------------------------------------------------------------------------

-(id)  initWithOutputFilePath: (NSString*)ofp
{
  self = [self init];
  if( self )
  {
    [self setOutputFilePath: ofp];
  }
  
  return self;
}


// -----------------------------------------------------------------------------
//  * DESTRUCTOR:
// -----------------------------------------------------------------------------

-(void)  dealloc
{
  NS_DURING
    [self cleanUp];  // cleanUp calls stop, which may throw.
  NS_HANDLER
    NSLog(@"[UKSoundFileRecorder dealloc]: Ignoring exception during clean-up. %@ : %@",[localException name],[localException reason]);
  NS_ENDHANDLER
  [self destroyAudioBufferList: audioBuffer];
  
  [outputFilePath release];
  outputFilePath = nil;
  
  [outputFormat release];
  outputFormat = nil;
  
  [super dealloc];
}


// -----------------------------------------------------------------------------
//  Delegate accessors:
// -----------------------------------------------------------------------------

-(void)  setDelegate: (id)dele
{
  delegate = dele;  // Don't retain delegate, it's very likely our owner. Wouldn't want a retain circle!
  delegateWantsTimeChanges = (delegate && [delegate respondsToSelector: @selector(soundFileRecorder:reachedDuration:)]);
}


-(id)  delegate
{
  return delegate;
}


// -----------------------------------------------------------------------------
//  AudioInputProc:
//    Callback function that is called by the audio unit on its high-priority
//    thread when we have sound input. Here's where we write out the data
//    to the file. Try not to do too much here.
// -----------------------------------------------------------------------------

OSStatus AudioInputProc( void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
{
  UKSoundFileRecorder *  afr = (UKSoundFileRecorder*)inRefCon;
  OSStatus        err = noErr;

  // Render into audio buffer
  err = AudioUnitRender( afr->audioUnit, ioActionFlags, inTimeStamp,
              inBusNumber, inNumberFrames, afr->audioBuffer);
  if( err )
    fprintf( stderr, "AudioUnitRender() failed with error %i\n", err );
  
  // Write to file, ExtAudioFile auto-magicly handles conversion/encoding
  // NOTE: Async writes may not be flushed to disk until a the file
  // reference is disposed using ExtAudioFileDispose
  err = ExtAudioFileWriteAsync( afr->outputAudioFile, inNumberFrames, afr->audioBuffer);
  if( err != noErr )
  {
    char  formatID[5] = { 0 };
    *(UInt32 *)formatID = CFSwapInt32HostToBig(err);
    formatID[4] = '\0';
    fprintf(stderr, "ExtAudioFileWrite FAILED! %d '%-4.4s'\n",err, formatID);
    return err;
  }
  
  UInt64  nanos = AudioConvertHostTimeToNanos( inTimeStamp->mHostTime -afr->startHostTime );
  afr->currSeconds = ((double)nanos) * 0.000000001;
  
  if( afr->delegateWantsTimeChanges )  // Don't waste time syncing to other threads if nobody is listening:
    [afr performSelectorOnMainThread: @selector(notifyDelegateOfTimeChange) withObject: nil waitUntilDone: NO];
  
  return err;
}


// Used by our AudioInputProc to easily call this delegate method from another thread:
-(void)  notifyDelegateOfTimeChange
{
  if( isRecording )  // In case we queued one up but were already finished by the time it got executed.
    [delegate soundFileRecorder: self reachedDuration: currSeconds];
}


// -----------------------------------------------------------------------------
//  outputFilePath Accessors:
//    The file at outputFilePath mustn't exist yet.
// -----------------------------------------------------------------------------

-(void)    setOutputFilePath: (NSString*)ofp
{
  if( outputFilePath != ofp )
  {
    [self willChangeValueForKey: @"outputFilePath"];
    [outputFilePath release];
    outputFilePath = [ofp copy];
    
    [self cleanUp];  // Make sure we recreate our objects for the new format.
    [self didChangeValueForKey: @"outputFilePath"];
  }
}


-(NSString*)  outputFilePath
{
  return outputFilePath;
}


// Handy method for hooking up this object to a text field:
-(IBAction)    takeOutputFilePathFrom: (id)sender
{
  [self setOutputFilePath: [sender stringValue]];
}



// -----------------------------------------------------------------------------
//  outputFormat Accessors:
// -----------------------------------------------------------------------------

-(void)    setOutputFormat: (NSDictionary*)inASBD
{
  if( outputFormat != inASBD )
  {
    [outputFormat release];
    outputFormat = [inASBD retain];
    
    [self cleanUp];  // Make sure we recreate our objects for the new format.
  }
}


-(NSDictionary*)  outputFormat
{
  return outputFormat;
}


// -----------------------------------------------------------------------------
//  prepare:
//    Set up all the CoreAudio magic that makes this work.
// -----------------------------------------------------------------------------

-(void)    prepare
{
  NSString*  errStr = nil;
  
  errStr = [self configureAU];
  if( errStr == nil )
    errStr = [self setupAudioFile];
  
  if( errStr )
  {
    [self cleanUp];
    [NSException raise: @"UKSoundFileRecorderCantPrepare" format: @"%@.", errStr];
  }
}


// -----------------------------------------------------------------------------
//  isRecording accessor:
//    Returns YES if we are currently recording, NO otherwise.
// -----------------------------------------------------------------------------

-(BOOL)  isRecording
{
  return isRecording;
}


// -----------------------------------------------------------------------------
//  start:
//    Start recording sound, like, right now.
// -----------------------------------------------------------------------------

-(void)  start: (id)sender
{
  if( isRecording )
    return;
  
  if( !audioUnit )
    [self prepare];
  
  // Start pulling audio data:
  startHostTime = AudioGetCurrentHostTime();
  OSStatus err = AudioOutputUnitStart( audioUnit );
  if( err == noErr )
  {
    [self willChangeValueForKey: @"isRecording"];
    isRecording = YES;
    [self didChangeValueForKey: @"isRecording"];
    if( delegate && [delegate respondsToSelector: @selector(soundFileRecorderWasStarted:)] )
      [(NSObject*)delegate soundFileRecorderWasStarted: self];
  }
  else
    [NSException raise: @"UKSoundFileRecorderCantStart" format: @"Could not start recording (ID=%d)", err];
}


// -----------------------------------------------------------------------------
//  stop:
//    Stop recording sound and flush the file to disk.
// -----------------------------------------------------------------------------

-(void)  stop: (id)sender
{
  if( isRecording )
  {
    if( audioUnit != NULL )
    {
      // Stop pulling audio data
      OSStatus err = AudioOutputUnitStop( audioUnit );
      if( err != noErr )
        [NSException raise: @"UKSoundFileRecorderCantStop" format: @"Could not stop recording (ID=%d)", err];
    }
    
    [self willChangeValueForKey: @"isRecording"];
    isRecording = NO;
    [self didChangeValueForKey: @"isRecording"];
    if( delegate && [delegate respondsToSelector: @selector(soundFileRecorderWasStopped:)] )
      [(NSObject*)delegate soundFileRecorderWasStopped: self];
    
    [self cleanUp];  // Make sure file gets flushed to disk.
    [[NSWorkspace sharedWorkspace] noteFileSystemChanged: outputFilePath];  // Make sure Finder updates file size.
  }
}


@end


@implementation UKSoundFileRecorder (UKSoundFileRecorderPrivateMethods)


// -----------------------------------------------------------------------------
//  cleanUp:
//    Called in various places, but also in the destructor, to tear down our
//    CoreAudio stuff. This also causes the file to be written to disk.
//    This is essentially the opposite to -prepare.
// -----------------------------------------------------------------------------

-(void)  cleanUp
{
  // Stop pulling audio data.
  [self stop: self];
  
  // Dispose our audio file reference.
  // Also responsible for flushing async data to disk.
  if( outputAudioFile )
  {
    ExtAudioFileDispose( outputAudioFile );
    outputAudioFile = nil;
  }
  
  if( audioUnit )
  {
    CloseComponent( audioUnit );
    audioUnit = NULL;
  }
}


// -----------------------------------------------------------------------------
//  setupAudioFile:
//    Init our ExtAudioFileRef object so it writes the correct kind of audio
//    to the correct file system location.
//    
//    Returns NIL on success, an error string on failure.
// -----------------------------------------------------------------------------

-(NSString*)  setupAudioFile
{
  OSStatus          err = noErr;
  AudioConverterRef      conv = NULL;
  NSString*          outputDirectory = [outputFilePath stringByDeletingLastPathComponent];
  NSString*          outputFileName = [outputFilePath lastPathComponent];
  FSRef            parentDirectory;
  AudioStreamBasicDescription  desiredOutputFormat;
  NSString*          fileFormatStr = [outputFormat objectForKey: UKAudioOutputFileType];
  AudioFileTypeID        fileFormat = fileFormatStr ? UKAudioStreamFormatIDFromString( fileFormatStr ) : kAudioFileM4AType;
  
  UKAudioStreamDescriptionFromDictionary( outputFormat, &desiredOutputFormat );
  
  if( ![outputDirectory getFSRef: &parentDirectory] )
    return [NSString stringWithFormat: @"Could not get reference to directory \"%@\"",outputDirectory];
  
  if( outputAudioFile != NULL )  // Have an audio file already? Get rid of that.
    [self cleanUp];
  
  // Create new MP4 file (kAudioFileM4AType)
  err = ExtAudioFileCreateNew( &parentDirectory, (CFStringRef)outputFileName, fileFormat, &desiredOutputFormat, NULL, &outputAudioFile );
  if( err != noErr )
  {
    char formatID[5];
    *(UInt32 *)formatID = CFSwapInt32HostToBig(err);
    formatID[4] = '\0';
    return [NSString stringWithFormat: @"Could not create the audio file (ID=%d/'%-4.4s')",err, formatID];
  }

  // Inform the file what format the data is we're going to give it, should be pcm
  // You must set this in order to encode or decode a non-PCM file data format.
  err = ExtAudioFileSetProperty( outputAudioFile, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &actualOutputFormat);
  if( err != noErr )
  {
    char formatID[5];
    *(UInt32 *)formatID = CFSwapInt32HostToBig(err);
    formatID[4] = '\0';
    return [NSString stringWithFormat: @"Could not set up data format for output file (ID=%d/'%-4.4s')",err, formatID];
  }

  // If we're recording from a mono source, setup a simple channel map to split to stereo
  if( deviceFormat.mChannelsPerFrame == 1 && actualOutputFormat.mChannelsPerFrame == 2)
  {
    // Get the underlying AudioConverterRef
    UInt32 size = sizeof(AudioConverterRef);
    err = ExtAudioFileGetProperty( outputAudioFile, kExtAudioFileProperty_AudioConverter, &size, &conv );
    if( conv )
    {
      // This should be as large as the number of output channels,
      // each element specifies which input channel's data is routed to that output channel
      SInt32 channelMap[] = { 0, 0 };
      err = AudioConverterSetProperty( conv, kAudioConverterChannelMap, 2 * sizeof(SInt32), channelMap );
    }
  }

  // Initialize async writes thus preparing it for IO
  err = ExtAudioFileWriteAsync( outputAudioFile, 0, NULL );
  if( err != noErr )
  {
    char formatID[5];
    *(UInt32 *)formatID = CFSwapInt32HostToBig(err);
    formatID[4] = '\0';
    return [NSString stringWithFormat: @"Could not initialize asynchronous writing (ID=%d/'%-4.4s')",err, formatID];
  }

  return nil;
}


// -----------------------------------------------------------------------------
//  configureAU:
//    Create our Audio Unit that gives us data from the microphone.
//    
//    Returns NIL on success, an error string on failure.
// -----------------------------------------------------------------------------

-(NSString*)  configureAU
{
  Component          component = NULL;
  ComponentDescription    description;
  OSStatus          err = noErr;
  UInt32            param;
  AURenderCallbackStruct    callback;
  
  if( audioUnit )
  {
    CloseComponent( audioUnit );
    audioUnit = NULL;
  }
  
  // Open the AudioOutputUnit
  // There are several different types of Audio Units.
  // Some audio units serve as Outputs, Mixers, or DSP
  // units. See AUComponent.h for listing
  description.componentType = kAudioUnitType_Output;
  description.componentSubType = kAudioUnitSubType_HALOutput;
  description.componentManufacturer = kAudioUnitManufacturer_Apple;
  description.componentFlags = 0;
  description.componentFlagsMask = 0;
  if( component = FindNextComponent( NULL, &description ) )
  {
    err = OpenAComponent( component, &audioUnit );
    if( err != noErr )
    {
      audioUnit = NULL;
      return [NSString stringWithFormat: @"Couldn't open AudioUnit component (ID=%d)", err];
    }
  }

  // Configure the AudioOutputUnit
  // You must enable the Audio Unit (AUHAL) for input and output for the same  device.
  // When using AudioUnitSetProperty the 4th parameter in the method
  // refers to an AudioUnitElement.  When using an AudioOutputUnit
  // for input the element will be '1' and the output element will be '0'.  
  
  // Enable input on the AUHAL
  param = 1;
  err = AudioUnitSetProperty( audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &param, sizeof(UInt32) );
  if(err == noErr)
  {
    // Disable Output on the AUHAL
    param = 0;
    err = AudioUnitSetProperty( audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(UInt32) );
    if( err != noErr )
    {
      [self cleanUp];
      return [NSString stringWithFormat: @"Couldn't set EnableIO property on the audio unit (ID=%d)", err];
    }
  }

  // Select the default input device
  param = sizeof(AudioDeviceID);
  err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &param, &inputDeviceID );
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Couldn't get default input device (ID=%d)", err];
  }
  
  // Set the current device to the default input unit.
  err = AudioUnitSetProperty( audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &inputDeviceID, sizeof(AudioDeviceID) );
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Failed to hook up input device to our AudioUnit (ID=%d)", err];
  }
  
  // Setup render callback
  // This will be called when the AUHAL has input data
  callback.inputProc = AudioInputProc;
  callback.inputProcRefCon = self;
  err = AudioUnitSetProperty( audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(AURenderCallbackStruct) );
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Could not install render callback on our AudioUnit (ID=%d)", err];
  }
  
  // get hardware device format
  param = sizeof(AudioStreamBasicDescription);
  err = AudioUnitGetProperty( audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &deviceFormat, &param );
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Could not install render callback on our AudioUnit (ID=%d)", err];
  }
  
  // Twiddle the format to our liking
  audioChannels = MAX( deviceFormat.mChannelsPerFrame, 2 );
  actualOutputFormat.mChannelsPerFrame = audioChannels;
  actualOutputFormat.mSampleRate = deviceFormat.mSampleRate;
  actualOutputFormat.mFormatID = kAudioFormatLinearPCM;
  actualOutputFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
  if( actualOutputFormat.mFormatID == kAudioFormatLinearPCM && audioChannels == 1 )
    actualOutputFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
#if __BIG_ENDIAN__
  actualOutputFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
#endif
  actualOutputFormat.mBitsPerChannel = sizeof(Float32) * 8;
  actualOutputFormat.mBytesPerFrame = actualOutputFormat.mBitsPerChannel / 8;
  actualOutputFormat.mFramesPerPacket = 1;
  actualOutputFormat.mBytesPerPacket = actualOutputFormat.mBytesPerFrame;

  // Set the AudioOutputUnit output data format
  err = AudioUnitSetProperty( audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &actualOutputFormat, sizeof(AudioStreamBasicDescription));
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Could not change the stream format of the output device (ID=%d)", err];
  }
  
  // Get the number of frames in the IO buffer(s)
  param = sizeof(UInt32);
  err = AudioUnitGetProperty( audioUnit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, &audioSamples, &param );
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Could not determine audio sample size (ID=%d)", err];
  }
  
  // Initialize the AU
  err = AudioUnitInitialize( audioUnit );
  if(err != noErr)
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Could not initialize the AudioUnit (ID=%d)", err];
  }
  
  // Allocate our audio buffers
  audioBuffer = [self allocateAudioBufferListWithNumChannels: actualOutputFormat.mChannelsPerFrame size: audioSamples * actualOutputFormat.mBytesPerFrame];
  if( audioBuffer == NULL )
  {
    [self cleanUp];
    return [NSString stringWithFormat: @"Could not allocate buffers for recording (ID=%d)", err];
  }
  
  return nil;
}

// -----------------------------------------------------------------------------
//  allocateAudioBufferListWithNumChannels:size:
//    Create our audio buffer list. A buffer list is the storage we use in
//    our AudioInputProc to get the sound data and hand it on to the sound
//    file writer.
// -----------------------------------------------------------------------------

-(AudioBufferList*)  allocateAudioBufferListWithNumChannels: (UInt32)numChannels size: (UInt32)size
{
  AudioBufferList*      list = NULL;
  UInt32            i = 0;
  
  list = (AudioBufferList*) calloc( 1, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer) );
  if( list == NULL )
    return NULL;
  
  list->mNumberBuffers = numChannels;
  
  for( i = 0; i < numChannels; ++i )
  {
    list->mBuffers[i].mNumberChannels = 1;
    list->mBuffers[i].mDataByteSize = size;
    list->mBuffers[i].mData = malloc(size);
    if(list->mBuffers[i].mData == NULL)
    {
      [self destroyAudioBufferList: list];
      return NULL;
    }
  }
  
  return list;
}


// -----------------------------------------------------------------------------
//  destroyAudioBufferList:size:
//    Dispose of our audio buffer list. A buffer list is the storage we use in
//    our AudioInputProc to get the sound data and hand it on to the sound
//    file writer.
// -----------------------------------------------------------------------------

-(void)    destroyAudioBufferList: (AudioBufferList*)list
{
  UInt32            i = 0;
  
  if( list )
  {
    for( i = 0; i < list->mNumberBuffers; i++ )
    {
      if( list->mBuffers[i].mData )
        free( list->mBuffers[i].mData );
    }
    free( list );
  }
}


@end

UKSoundFileRecorder header preview

//
//  UKSoundFileRecorder.h
//  UKSoundFileRecorder
//
//  Created by Uli Kusterer on 14.07.07.
//  Copyright 2007 M. Uli Kusterer. All rights reserved.
//

/*
  A class that records audio from standard sound input into a file.
  
  To use, simply create a new UKSoundFileRecorder object and point it at
  a file on disk using -setOutputFilePath:. You can also specify an output
  format if you wish, default is 44000kHz AAC Stereo.
  
  The class /should/ be KVC/KVO compliant, but this hasn't been extensively
  tested yet. Please let me know of any problems you have using this with
  bindings.
*/

// -----------------------------------------------------------------------------
//  Headers:
// -----------------------------------------------------------------------------

#import <Cocoa/Cocoa.h>
#import <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
#import "UKAudioStreamBasicDescription.h"


// -----------------------------------------------------------------------------
//  UKSoundFileRecorder:
// -----------------------------------------------------------------------------

@interface UKSoundFileRecorder : NSObject
{
  AudioBufferList    *    audioBuffer;
  AudioUnit          audioUnit;
  ExtAudioFileRef        outputAudioFile;
  NSString      *    outputFilePath;

  AudioDeviceID        inputDeviceID;
  UInt32            audioChannels;
  UInt32            audioSamples;
  AudioStreamBasicDescription  actualOutputFormat;
  AudioStreamBasicDescription  deviceFormat;
  NSDictionary*        outputFormat;
  double            currSeconds;
  UInt64            startHostTime;
  
  BOOL            isRecording;
  
  id              delegate;
  BOOL            delegateWantsTimeChanges;
}

+(NSDictionary*)  defaultOutputFormat;

//-(id)        init;  // Designated initializer. You can use -init and then do setOutputFilePath: or use -initWithOutputFilePath:.
-(id)        initWithOutputFilePath: (NSString*)ofp;  

// Setup:
-(void)        setOutputFilePath: (NSString*)ofp;
-(NSString*)    outputFilePath;
-(IBAction)      takeOutputFilePathFrom: (id)sender;      // Calls [self setOutputFilePath: [sender stringValue]].

-(void)        setOutputFormat: (NSDictionary*)inASBD;    // Keys for this dictionary can be found in UKAudioStreamBasicDescription.h and below.
-(NSDictionary*)  outputFormat;

-(void)        setDelegate: (id)dele;
-(id)        delegate;

// Recording:
-(void)        start: (id)sender;
-(BOOL)        isRecording;
-(void)        stop: (id)sender;

// You probably don't need this:
-(void)        prepare;    // Called as needed by start:, if nobody called it before that.

@end

// -----------------------------------------------------------------------------
//  Additional OutputFormat keys:
// -----------------------------------------------------------------------------

#define UKAudioOutputFileType            @"UKAudioOutputFileFormat"  // This is not an HFS OSType, nor a file suffix!!! These are equivalent to AudioFileTypeID, just that they've been stringified using UKStringFromAudioStreamFormatID().
  #define UKAudioOutputFileTypeAIFF        @"AIFF"
  #define UKAudioOutputFileTypeAIFC        @"AIFC"
  #define UKAudioOutputFileTypeWAVE        @"WAVE"
  #define UKAudioOutputFileTypeSoundDesigner2    @"Sd2f"
  #define UKAudioOutputFileTypeNext        @"NeXT"
  #define UKAudioOutputFileTypeMP3        @"MPG3"
  #define UKAudioOutputFileTypeAC3        @"ac-3"
  #define UKAudioOutputFileTypeAAC_ADTS      @"adts"
  #define UKAudioOutputFileTypeMPEG4        @"mp4f"
  #define UKAudioOutputFileTypeM4A        @"m4af"
  #define UKAudioOutputFileTypeCAF        @"caff"


// -----------------------------------------------------------------------------
//  Delegate protocol:
// -----------------------------------------------------------------------------

@interface NSObject (UKSoundFileRecorderDelegate)

// Sent on a successful start:
-(void)  soundFileRecorderWasStarted: (UKSoundFileRecorder*)sender;

// Sent while we're recording:
-(void)  soundFileRecorder: (UKSoundFileRecorder*)sender reachedDuration: (NSTimeInterval)timeInSeconds;

// Sent after a successful stop:
-(void)  soundFileRecorderWasStopped: (UKSoundFileRecorder*)sender;

@end


Download Archive

Dependencies:

Download Dependencies

Compatible with:


Comments

Maksymilian Wojakowski says:

Excellent example! It is the first time I saw a complete example using no C++ code at all, just pure Objective-C and C.

Name

Website

Do you hate spammers? (Answer "Yes")