Archive

Archive for April, 2009

Recording Audio

April 30th, 2009

There are multiple ways to record audio on the iPhone, but some are suited to tasks better than others. I'm going to show you a simple way to record audio into a file and get its power level (volume if you will). This tutorial makes use of the AudioToolbox framework which you must add to your project to get this code working. Firstly I highly recommend you make your own AudioRecorder class to deal with the low level recording, that way you can just port your class from project to project with little hassle, and save time writing your own code. Firstly create a header file like so:

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AudioToolbox/AudioFile.h>
 
@interface AudioRecorder : NSObject
{
	AudioStreamBasicDescription dataFormat;
}
 
-(id) init;
 
@end

This adds a description of your audio stream to your class, now you will want to write your initialisation code in your main file, this is where we will fill the dataFormat instance variable.

-(id) init
{
	dataFormat.mSampleRate = 44100.0f;
	dataFormat.mFormatID = kAudioFormatLinearPCM;
	dataFormat.mFramesPerPacket = 1;
	dataFormat.mChannelsPerFrame = 1;
	dataFormat.mBytesPerFrame = 2;
	dataFormat.mBytesPerPacket = 2;
	dataFormat.mBitsPerChannel = 16;
	dataFormat.mReserved = 0;
	dataFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
	return self;
}

Basically what this code is saying is that we want a mono recording (mChannelsPerFrame) at a sample rate of 44.1kHz (mSampleRate) using SInt16's (mBytesPerFrame and kLinearPCMFormatFlagIsSignedInteger) and that we are recording in Linear PCM (pretty standard). So now that we have sorted those parameters out, we need to add the Audio Queue and the Input Callback, this means not only another instance variable in our header, but also a function that is outside the scope of the class, however by cleverly inputting the right data we can make it work for multiple instances of the class.

@interface AudioRecorder : NSObject
{
	AudioStreamBasicDescription dataFormat;
	AudioQueueRef queue;
}
 
@property AudioQueueRef queue;
// Our new C callback
void AudioInputCallback(void* inUserData,AudioQueueRef inAQ,AudioQueueBufferRef inBuffer,const AudioTimeStamp *inStartTime,UInt32 inNumberPacketDescriptions,const AudioStreamPacketDescription *inPacketDescs)
{
}
// Declaring the get/set of our new public variable
@synthesize queue;
// Add this code to the init function
	if(AudioQueueNewInput(&dataFormat,AudioInputCallback,self,CFRunLoopGetCurrent(),kCFRunLoopCommonModes,0,&queue) == noErr)
	{
	}

So this declares the queue variable public, adds our AudioInputCallback C function, and inputs data into our audio queue. Next we have to deal with our audio buffers:

// Add this variable to our interface
	AudioQueueBufferRef buffers[3];
// Add this code into our init function
		for(int i=0;i<3;i++)
		{
			AudioQueueAllocateBuffer(queue,(dataFormat.mSampleRate/10.0f)*dataFormat.mBytesPerFrame,&buffers[i]);
			AudioQueueEnqueueBuffer(queue,buffers[i],0,nil);
		}

This allocates our buffers with the right amount of space, and then Enqueue's them to await our audio recording input. Next we will kick start the process by using the AudioQueueStart function.

		AudioQueueStart(queue, NULL);

Now we have a functioning audio recording code, you will notice if you put logs into AudioInputCallback, you will see it gets called every so often (ie when the buffers will up), however in the callback we will need to handle the continued Enqueue of our buffers defined in the interface, so we will need to add this into the AudioInputCallback function:

		AudioRecorder* recorder = (AudioRecorder*) inUserData;
	AudioQueueEnqueueBuffer(recorder.queue,inBuffer,0,nil);

Now what this does is grabs our AudioRecorder class from the inUserData, then uses the queue defined in that class to enqueue the next buffer for getting more audio. Now we are functioning, but what about writing the recorded audio data to a file or getting the power of an audio recording? Well that's coming here, first I'll focus on the power level (because it's much easier than writing file data):

// Define these functions in the interface
-(float) getPeakPower;
-(float) getAveragePower;
// At the end of init
	UInt32 enabledLevelMeter = true;
	AudioQueueSetProperty(queue,kAudioQueueProperty_EnableLevelMetering,&enabledLevelMeter,sizeof(UInt32));
// New functions
-(float) getPeakPower
{
	AudioQueueLevelMeterState levelMeter;
	UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState);
	AudioQueueGetProperty(queue,kAudioQueueProperty_CurrentLevelMeterDB,&levelMeter,&levelMeterSize);
	return levelMeter.mPeakPower;
}
 
-(float) getAveragePower
{
	AudioQueueLevelMeterState levelMeter;
	UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState);
	AudioQueueGetProperty(queue,kAudioQueueProperty_CurrentLevelMeterDB,&levelMeter,&levelMeterSize);
	return levelMeter.mAveragePower;
}

Now you can simply call getPeakPower or getAveragePower to obtain the average or peak power of the audio being recorded in decibels. Now I usually gauge power by dividing the Average Power by -80.0, because I use that as a ceiling, and yes I am well aware volume can get up to 120dB in power, but for most purposes a ceiling of 80dB is fine. Now for the complex stuff, file writing. What we are going to do is add a specific function to our class that allows us to define a file to write to, and some functions that tell us whether we are recording to a file or not as well as allowing us to stop the file recording.

// New interface variables
	AudioFileID audioFile;
	bool fileRecording;
	SInt64 currentPacket;
	NSString* recordFile;
// Functions to add to our class
-(bool) isRecording;
-(bool) record:(NSString*)filename;
-(void) stopRecording;
-(bool) isRecording
{
	return fileRecording;
}
 
-(bool) record:(NSString*)file
{
	char cfile[256];
	[file getCString:cfile maxLength:sizeof(cfile) encoding:NSUTF8StringEncoding];
	CFURLRef fileURL = CFURLCreateFromFileSystemRepresentation(NULL,(UInt8*)cfile,strlen(cfile),false);
	OSStatus result = AudioFileCreateWithURL(fileURL,kAudioFileAIFFType,&dataFormat,kAudioFileFlags_EraseFile,&audioFile);
	if(result != noErr)
		fileRecording = false;
	else fileRecording = true;
	if(fileRecording)
		recordFile = file;
	return fileRecording;
}
 
-(void) stopRecording
{
	fileRecording = false;
	AudioFileClose(audioFile);
}

Now there's a lot of code here, so I'll try to explain it. The isRecording function basically informs other classes on whether we are currently recording, the record function is where our imagine happens. It uses AudioFileCreateWithURL to create an audio file header based on our format definition in the earlier part of this tutorial, the rest of that function is basically setting the recording fileRecording variable and changing the NSString to a CFURLRef type. The stop recording function is also very self explanatory, it just closes the audio file and shutdowns the fileRecording variable. But we still aren't using the AudioInputCallback to record! Well that's the last piece of the puzzle:

// Add these properties to the interface
@property AudioFileID audioFile;
@property bool fileRecording;
@property SInt64 currentPacket;
// Add this code to AudioInputCallback
	if(recorder.fileRecording)
	{
		AudioFileWritePackets(recorder.audioFile,false,inBuffer->mAudioDataByteSize,inPacketDescs,recorder.currentPacket,&inNumberPacketDescriptions,inBuffer->mAudioData);
		recorder.currentPacket += inNumberPacketDescriptions;
	}
// Synthesize these variables
@synthesize audioFile,fileRecording,currentPacket;

Now here we basically make the fileRecording, audioFile and currentPacket variable public, so we can use them in the AudioInputCallback, then we write the audio packets to our file. It's as simple as that.

Author: admin Categories: Tutorials Tags:

Using the Accelerometer

April 30th, 2009

Using the accelerometer is actually quite easy compared to the other hardware components in the iPhone (Microphone and Camera). Firstly you have to know how an accelerometer works. The accelerometer basically measures the pull in each each of the 3 axis (3 dimensions). For example the gravity of Earth will always generate a pull, but what axis it pulls on will tell us how the iPhone is tilted. Inertia will also produce effects on the accelerometer, for instance when someone moves the iPhone or sits with the iPhone in a moving vehicle. The principal behind this is simple, and goes all the way back to Newtons laws of motion "Every body persists in its state of being at rest or of moving uniformly straight forward, except insofar as it is compelled to change its state by force impressed". Imagine that the accelerometer is the following structure:

and in that structure are 3 balls, which can only travel along one of the axis. All of these balls rest in the centre (0.0) until a force pushes them to one end of the line. Now the accelerometer in the iPhone presents the 3 axis in floats, with a range of 1.0 to -1.0 (0.0 being no force applied). The iPhone SDK lets us use a simple Delegate protocol for getting updates from the accelerometer, and it really is as simple as the following code:
Header -

@interface SomeClass : SomeSuperClass <UIAccelerometerDelegate>

This adds the UIAccelerometerDelegate protocol to our interface.
Main -

-(id) init
{
	[UIAccelerometer sharedAccelerometer].delegate = self;
	return self;
}
 
-(void) accelerometer:(UIAccelerometer*)acel didAccelerate:(UIAcceleration*)aceler
{
	float x = aceler.x;
	float y = aceler.y;
	float z = aceler.z;
}

It's important to note here that we are setting the shared Accelerometers delegate. We can only do this for 1 class in our app, so if more than 1 interface needs to access the accelerometer, it's best to make an interface dedicated to accessing the accelerometer and use that instead of the sharedAccelerometer.

Author: admin Categories: Tutorials Tags:

Checking whether a file exists

April 28th, 2009

To check whether a file exists is very simple in Objective C, simply create an NSFileManager object, then feed the NSString of the file directory to the fileExistsAtPath method in the NSFileManager object.

NSString* myFileTxt = [[NSBundle mainBundle] pathForResource:@"myFile"
                                             ofType:@"txt"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:myFileTxt])
{
// file doesn't exist
}
Author: admin Categories: Tutorials Tags:

Copying Files

April 27th, 2009

Now in your iPhone application you may find it necessary to copy files from your resources directory into your documents directory, this is to prevent overriding from updates you may distribute. This is a very simple task, one that relies upon the NSData class like so:

NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString* docDir = [paths objectAtIndex:0];
NSString* audioDir = [docDir stringByAppendingString:@"/myfile.txt"];
NSString* origDir = [[NSBundle mainBundle] pathForResource:@"myfile" ofType:@"txt"];
NSData* origData = [NSData dataWithContentsOfFile:origDir];
[origData writeToFile:audioDir atomically:YES];

That's a very simple example of how to copy a file from your resource directory to the documents directory. It's a good idea to check this in applicationDidLaunch, by checking whether the file that should be in the documents directory exists, and if not copying it over.

Author: admin Categories: Tutorials Tags:

Saving objects to a file

April 27th, 2009

In many applications you may need to save settings and reload them at various points. A nice way to do this is using a plist (property list) file, and assigning objects to keys.

NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString* docDir = [paths objectAtIndex:0];
NSString* file = [docDir stringByAppendingString:@"/settings.plist"];
// To get objects
NSString* errorDesc = nil;
NSPropertyListFormat format;
NSData* plistXML = [[NSFileManager defaultManager] contentsAtPath:file];
NSDictionary *temp = (NSDictionary*)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
NSString* savedText = [temp objectForKey:@"Saved_Text"];
float savedFloat = [[temp objectForKey:@"Saved_Float"] floatValue];
int savedInt = [[temp objectForKey:@"Saved_Int"] intValue];
// To set objects
NSMutableDictionary* rootObj = [NSMutableDictionary dictionaryWithCapacity:1];
[rootObj setObject:savedText forKey:@"Saved_Text"];
[rootObj setObject:[[NSString alloc] initWithFormat:@"%f",savedFloat] forKey:@"Saved_Float"];
[rootObj setObject:[[NSString alloc] initWithFormat:@"%d",savedInt] forKey:@"Saved_Int"];
NSString* errorDesc;
NSData* plistData = [NSPropertyListSerialization dataFromPropertyList:rootObj format:NSPropertyListXMLFormat_v1_0 errorDescription:&errorDesc];
if(plistData)
	[plistData writeToFile:file atomically:YES];

It also very good practice to store the settings file in the documents directory, lest it be overwritten by an update in the resources directory.

Author: admin Categories: Tutorials Tags:

Disabling Sleep Mode

April 27th, 2009

Sleep mode occurs in an iPhone when you do not touch the screen (aka provide user input) for about a minute. Some applications that I have made do not require the screen to be touched, so this was very troublesome, however there is a legitimate way around it:

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
	application.idleTimerDisabled = YES;
}

This disables the idle timer, and will keep your app running for longer without input.

Author: admin Categories: Tutorials Tags:

Making the Transition to Objective C

April 24th, 2009

Objective C is like any other programming language, however it's syntax is a lot different from standards you'll see used in Vanilla C, C++ and Java. The good news is Objective C is simply a super set of C, meaning that all vanilla C code (and most C++ I have tried) compiles fine on XCode and within the same projects as Objective C. Having said that you can't rely on C alone to build iPhone applications, you must use Objective C for the vast majority of frameworks. The easiest way to understand Objective C objects is to build you own, and that's what I aim to show you.

Here is a typical Objective C class:

// MyFirstClass.h
#import <Foundation/Foundation.h>
 
@interface MyFirstClass : NSObject
{
}
 
@end
// MyFirstClass.m
#import "MyFirstClass.h"
 
@implementation MyFirstClass
 
@end

This is a skeleton class, which we will fill with our useful instance variables and functions. Notice how I added NSObject as the inherited class, this automatically adds alloc functions to your class, without you having to code them personally. Most classes will need instance variables, and there are 2 types of variables, public and private. In Objective C it isn't as simple as declaring public or private before the variable, each variable is assumed private until it is 'synthesized'. Now I will show you a class with 2 instance variables, one being private and the other being public.

// MyFirstClass.h
#import <Foundation/Foundation.h>
 
@interface MyFirstClass : NSObject
{
	int myPrivateInt;
	int myPublicInt;
}
 
@property int myPublicInt;
 
@end
// MyFirstClass.m
#import "MyFirstClass.h"
 
@implementation MyFirstClass
 
@synthesize myPublicInt;
 
@end

You'll note that the public variable differs from the private variable by 2 declarations. One is the property declaration, that ties it publicly to the 'structure' of the class. The other is the synthesize declaration, all this does is virtually define the get/set methods of the variable. Something important to note is that if you have a variable that is a pointer to an object, you will have to define the property like so:

@property (nonatomic,retain) NSObject* object;

Now we have to distinguish between public and private functions within the object. Again there isn't a simple private or public declaration, instead you define the prototypes of public functions in the header, and the prototypes of private functions in the main file, like so:

// MyFirstClass.h
#import <Foundation/Foundation.h>
 
@interface MyFirstClass : NSObject
{
	int myPrivateInt;
	int myPublicInt;
}
 
@property int myPublicInt;
 
-(int) theIntPlus:(int)anotherInt;
 
@end
// MyFirstClass.m
#import "MyFirstClass.h"
 
@interface MyFirstClass()
-(void) setPrivateInt:(int)theInt;
@end
 
@implementation MyFirstClass
 
@synthesize myPublicInt;
 
-(int) theIntPlus:(int)anotherInt
{
	[self setPrivateInt:anotherInt];
	return myPublicInt+anotherInt;
}
 
-(void) setPrivateInt:(int)theInt
{
	myPrivateInt = theInt;
}
 
@end

As you can see, the public function (theIntPlus) is defined the header file, which is used as a reference to other objects, while the private functions prototype is only defined in the main file, which only the object itself uses. You may be wondering what the - symbol denotes on the functions, it represents a non-static function, while the + symbol represents a static function. I will show you a use of the static function for sharing classes.

// MyFirstClass.h
#import <Foundation/Foundation.h>
 
@interface MyFirstClass : NSObject
{
	int myPrivateInt;
	int myPublicInt;
}
 
@property int myPublicInt;
 
-(int) theIntPlus:(int)anotherInt;
+(id) sharedMyFirstClass;
 
@end
// MyFirstClass.m
#import "MyFirstClass.h"
 
MyFirstClass* shared_MyFirstClass = nil;
 
@interface MyFirstClass()
-(void) setPrivateInt:(int)theInt;
@end
 
@implementation MyFirstClass
 
@synthesize myPublicInt;
 
-(int) theIntPlus:(int)anotherInt
{
	[self setPrivateInt:anotherInt];
	return myPublicInt+anotherInt;
}
 
-(void) setPrivateInt:(int)theInt
{
	myPrivateInt = theInt;
}
 
+(id) sharedMyFirstClass
{
	if(shared_MyFirstClass == nil)
		shared_MyFirstClass = [MyFirstClass alloc];
	return shared_MyFirstClass;
}
 
@end

As you can see the + symbol allows you to access sharedMyFirstClass without allocating the object, and sharing the class allows you to access the same class throughout your program, which is very useful for some objects (such as Audio Recording classes). Lastly I'd like to draw your attention to how I have accessed the private function setPrivateInt, I got the instance variable and then defined the function I'm accessing within the [] brackets. This goes for any instance of any class, whether it be your own (self) or another one you have allocating.

I hope this explains the differences between Objective C and other more syntax orthodox languages.

Author: admin Categories: Tutorials Tags: