This is another article aimed at the Java programmer.  This will touch very very briefly on protocols.


Protocols are Objective C's version of what is known in Java as "interfaces". 


Defining the protocol

To make one, first create a blank .h header file.
Then type:

@protocol MyProtocolName <NSObject>
//Method declarations go here
@end

Replace "MyProtocolName" with the name of your choice.

You will notice a couple of things...first, there are no curly brackets.  That's because variables go in curly brackets, and protocols have no variables associated with them.

Second, notice the "<NSObject>".  This actually means that the current protocol is a derivative of the NSObject protocol.  (There is both an NSObject class and an NSObject protocol...this is the protocol...)

In objective C, you have protocols and categories.  Pointed brackets are associated with protocols.  Curved brackets (aka parentheses) are associated with categories.   So just remember, "pointy protocol, curved category"...

Using the protocol

In Java, you specify that a class implements an interface with the "implements" keyword.  In Objective C, you use the pointy brackets in the interface declaration (and by "interface" here, I mean the part of the class in the header file, not "interface" in the Java sense), following the class you extend. For example, suppose your class was normally declared like:
@interface CustomView : UIView

To specify that it implements a protocol, simply change it to this:

@interface CustomView : UIView <MyProtocolName>


Protocols as variables

Here is where it differs from Java the most.  In Java, when declaring a variable, you would use an interface name just like you would a class.  In Objective C, you declare a variable this way:

id<MyProtocolName> myNewVariable;

So the new type is "id<MyProtocolName>".  "id" is the generic object...even though it is a pointer to an object, it doesn't have an asterisk...that's assumed.

You can also use this notation when defining a method...e.g.

- (void) doSomethingWithThisObject: (id<MyProtocolName>) aObject

There's a lot more to protocols than what I've mentioned, but this should be enough to get you started.














Note: This entry (and everything before it) refers to firmware version 1.x...i.e. the original jaibroken/toolchain type.  This information will be of limited use for development on firmware version 2.0+.

AudioQueue Header

To start off, you will need the AudioQueue.h file...this wasn't included in original toolchain releases, but it should be fairly common now.  Check your usr/local/arm-apple-darwin/include/AudioToolbox folder for it.  If it's not there, you will need to put it there. 

The Output Callback

AudioQueue output is pretty straightforward.  I strongly recommend you check out the documentation on Apple's website (in the AudioToolbox part for Leopard).  As far as explaining goes, It does a better job than I plan on doing.  It will also have sample sourcecode ...AudioQueueTest...I recommend you use this as a template...

Notice that AudioQueue is a C routine, and not an Objective C one.  The main function is a "callback"...if you are strictly a Java person, then you might not be familiar with this concept.  Instead of you calling a method everytime you want to play something, you instead designate a callback function, and the system calls YOU whenever it's ready for more audio data.  "Don't call us...we'll call you," says the sound system.

And because this is C (and not ObjC), this callback method won't be inside of any class...it won't be "object oriented".  It's a static function that doesn't have access to member variables of a class it may or may not be affiliated with.  (It can still be in the same file, it just won't technically be part of that class).  And let's assume you aren't going to use static variables...then how will this static function know what to send?  Through a custom struct...you design it yourself, and the system will pass it to the callback whenever it is called.  This way, you can make the callback function independent. 

In Apple's example, the callback is named AQTestBufferCallback, and the custom data struct is called AQTestInfo.  Don't reuse "AQTestInfo" in the Apple example...start from scratch and make the custom struct contain whatever you think it should.

I would get rid of all the XThrowIfError stuff...it's good to check for errors, but you can do it other ways later.  DON'T get rid of the stuff inside of the XThrowIfError part...it cocoons the important function calls.

The two main things you need to have in the callback are:
1)inCompleteAQBuffer->mAudioDataByteSize = (# of bytes you plan on putting in the queue)
2)AudioQueueEnqueueBuffer command...this is what actually sends the audio data off to the system.


The Rest

AudioQueue works by recycling a certain number of buffers.  While it's playing the data in one, it's letting you fill a different one.  It's sort of like the security checkpoint in an airport...those trays that you stick the carry-on bags into so they can run through the x-ray machine.  While one is being scanned, a few others are being filled by passengers.  After the one being scanned has made it out the other end, it is returned to be filled again.  And so on.  You get to choose how many buffers you want in circulation.  I recommend 3.

Before the callback gets called, you obviously need to inform the audio system that you will be supplying audio data.
This is done by these essential calls:
  • AudioQueueNewOutput - creates the new output system...you specify your callback function here.
  • AudioQueueAllocateBuffer - this is called each time you want to create a buffer.  So you would probably want to call this three times.
  • AudioQueueStart - this starts the AudioQueue...surprise.
The example has a while-loop following the AudioQueueStart.  This is needed if you are running the AudioQueue in its own thread.  You don't need this if either 1) you are running the AudioQueue in the main thread, or 2) you let the AudioQueue choose its own thread to run it in,  (The latter is accomplished by passing a NULL in the AudioQueueNewOutput function instead of CFRunLoopGetCurrent).

  • AudioQueueDispose - This isn't part of the init, but rather part of the destruction of the AudioQueue.  This is VERY important...you need to free up the audio hardware...don't assume this will be done automatically when the program ends.













.

 





Sound on the iPhone

If you want to do high level (i.e. "easy") audio on the iPhone, I suggest looking at the Celestial framework.  It has what you need to load and play media files.  You can also check out GraphicsServices...it has a method that will allow you to play a sound simply by specifying the path.

But if you want to do low-level stuff, i.e. get down to the individual samples, then you're probably going to need AudioQueue.  What is AudioQueue?  It's like a brief vacation in hell...well, at least the way it's implemented on the iPhone.  An official SDK is supposedly going to be released next week--perhaps it will shed light on this...but then again, documentation for AudioQueue is already up on Apple's website (for OS X 10.5), and it's still a bitter journey.  Not that it's bad inherently...if you are merely looking at the API, it doesn't seem so tough.  But the way it's implemented on the iPhone, it's hardly ideal for 3rd party developers, especially those trying to do input and output at the same time.  Do this wrong or in the wrong thread, then the AudioQueue will stop...do this, and it will lose routing, or something will go haywire.  Again, this may be due to my ignorance...I'm figuring it out as I go.  [A more technical definition: AudioQueue is a group of C routines used for output and input of low-level audio.]

Who needs AudioQueue?  If you want to simply play wav files, then you probably don't need to use low-level stuff...stick with Celestial.  If you want to synthesize sounds, or analyze sounds via a Fast Fourier Transform, then you probably will have to use AudioQueue. 

To use AudioQueue, you will need the AudioQueue.h header...if you are using the toolchain, then you will probably need to find the header yourself.  Search for it on the Internet.  It may be available in the XCode 3.0 download from Apple.

Low-level sound in a nutshell
Here is a very quick intro to low level audio that isn't iPhone specific.  If you are new to all of this, it's probably best to do a search of the Internet to learn more about audio formats, samples, etc.  Moreover, I recommend downloading audio programs like Audacity (freeware/open source) or Goldwave (shareware) to see what all this stuff means...use these programs to experiment.

Sound is all in your mind...what is actually out there in the real world is varying waves of pressure.  As the air pressure rises and falls (perhaps 1000 times/sec), your eardrums move back and forth, and this is translated into sound by your brain.  Imagine your eardrum as being horizontal...when the air pressure changes, it moves up and down.  If you were to graph the location of the ear drum as time went by, you would end up with one of those waves you see in Audacity:
Audio01.jpg

(You should open up a sound in Audacity, and zoom in until the line looks curvy and smooth.)

This is an "analog" wave.  However, on a computer, you are going to have to be able to express this wave in terms of 0s and 1s...i.e. discrete measurements.   So typically, an analog-to-digital converter measure the y-value (i.e. the height) of the wave.  This will be repeated at a certain time interval.  This is called "sampling", and each measurement is called a "sample".  So instead of a smooth wave, you now end up with a digitized wave.

Audio02.jpg

This is an image from Goldwave, which shows what a digitized sound wave actually looks like.  It's not as smooth as a real one.

The rate at which the wave is measured is called the "sample rate", and is expressed in units of Hertz.  (1 Hertz means once per second.)  A sample rate of 8000 Hz means the wave is being measured 8000 times every second, with each measurement being 0.125 milliseconds after the previous one.  This sounds like a lot, but really, 8000 Hz isn't great in terms of quality.  The higher the sample rate is, the "less choppy" the wave. (See image above).

Also affecting quality is the number of bytes used to specify the y-value.  For example, a byte (aka 8 bits) represents a value from -128 to 127.  (This is called a "signed byte" because a negative sign is allowed...an "unsigned byte" would have values from 0 to 255).  If we increase the sample size from 8 bits to 16 bits (i.e. 2 bytes), we'll be able to express the y-value more precisely.

The term "channel" is how many simultaneous sound waves you have..."mono" means 1 channel, while "stereo" usually means 2 channels.  Surround sound uses multiple channels. 
The term "sample frame", often just "frame", represents a set of samples (across all the channels) that were recorded at the same time.  Mono audio only has one channel, so a frame will only contain a single sample.  With two channels, a frame will contain two samples.  (Those samples will have occurred simultaneously).   If you record two channels (stereo) at a sample rate of 8000 Hz for exactly 1 second...you will end up with a total of 8000 frames, but actually 16000 samples.  (Note that sample rate usually refers to the number of samples per channel...in other words, the number of frames.)


What AudioQueue does
AudioQueue will allow you to take an array, with each member of the array representing a sample, and feed that array into it to produce sound.   It also allows the reverse to occur.  You can save the samples you get from the iPhone's microphone.  Funiculus Musical Instrument Tuner does exactly this...it reads in samples, and continually analyzes them for pitch.

What AudioQueue doesn't do

Work.  At least not all the time unless certain arbitrary conditions are perfect...again, this could be due to my own ignorance.


To be continued...

Snooping around the frameworks

| | Comments (2) | TrackBacks (0)
Uncharted Frameworks
If you go to your iPhone file system, and look in the directory /System/Library/Frameworks, you'll see a  list of the different frameworks available for iPhone programming.  For ones that are mainly in ObjC (e.g. UIKit, etc), you can find a listing of all the methods on sites like Erica Sadun's. 

For Frameworks that are mainly in C (or C++), assuming there is no available listing somewhere on the Internet, you will have to snoop to discover the method names. 

Here are a few tools that you can use on the binaries within these folders to see what they contain.  These are run from your personal computer, and should be available after you install the toolchain. 
  • arm-apple-darwin-nm : this will print out a list of symbols
  • arm-apple-darwin-otool : this will (among other things) print out a disassembly
  • strings : this will print out the strings in the binary
Example of use:
(I'm on Windows, so I use cygwin for the prompt.)
$ cd /usr/local/share/iphone-filesystem/System/Library/Frameworks/AudioToolbox.framework
$ arm-apple-darwin-nm AudioToolbox > AudioToolboxSymbols.txt
This will put the symbols of the AudioToolbox framework into a new file called AudioToolboxSymbols.txt.

Mangled names
When you look at the raw list of symbols, some of the method names may look garbled.  That's because they are "mangled"...this ensures that every method has a unique name.
Mangled C names will often be preceeded with an underscore. 
Mangled C++ names will look something like this:
__ZN24MeCCA_AudioRoutingPolicy16setRoutingPolicyEPKc

The unmangled version would be:
MeCCA_AudioRoutingPolicy::setRoutingPolicy(char const*)

If you want to unmangle the names in the symbols text file you just created, you can use a command called c++filt
Example of usage:
$ c++filt < MySymbolsFile.txt > MyUnmangledSymbolsFile.txt










Audio dependencies

| | Comments (0) | TrackBacks (0)
Here is a graph of the relationship between audio frameworks on the iPhone.  (Courtesy of Samuel Vinson)

dependencies.png






No Garbage Collection
    If you are coming from Java, you are probably used to creating variables and having someone else clean up your mess after you are finished with them.  Unfortunately, on the iPhone, you have to clean up after yourself.   If you don't, your program will leak memory. 
    It isn't exactly manual labor, however.  The NSObject class has some accounting stuff that keeps track of how many other objects are currently using the object in question ("retain count").  But unlike Java, it isn't automatic...you will have to adjust this count yourself.
    The analogy that is frequently used is that of a time-share condo...a condominium with multiple owners.  And this condo sits on valuable land.  As soon as the all the owners are done using the condo, it can be bulldozed to make way for other buildings.  The "retain count" of the condo would be the number of owners (i.e. those who are still wanting to use it). 

Adjusting the Retain Count  
    The rule of managing memory is to make sure that, by the time the program has finished executing, the number of "ownership methods" called on an object will equal the number of "loss-of-ownership" methods.

Ownership methods:
Don't release anything you don't own. You own anything that (any of the following):
  1. you call [myClass alloc] explicitly on
  2. you explicitly [myObject retain]
  3. you create an object with a method that has the word "copy" in it.
Note: you do not own objects created by convenience constructors...they are set to autorelease.  (An example of calling a convenience constructor: [NSString string];)

Loss-of-ownership methods:
To release something you own, you can either:
  1. call [myObject autorelease] - will release later
  2. call [myObject release]

Dealloc
Never directly call dealloc...you should always use release, and let the system decide when to deallocate.

You should override dealloc for each of your objects, and inside that method, release anything you have retained. And the end of the method, call [super dealloc]; (It's okay to call release on a null object).

Bookkeeping
When you implement a "set" method for encapsulation, you should do the following:

- (void) setSomeObject: (NSObject*) aNewObject
{
[aNewObject retain]; //#1
[myObject release]; //#2
myObject=aNewObject; //#3
}

#3 obviously sets the new object
#2 is called just in case you have a previous object...you should release it before you set it free. It is perfectly okay to call release on a null object.
#1 - always retain the new one first, just in case they are passing the same object in that is already assigned. Otherwise, you may end up deallocating the object.

And if you release a variable somewhere other than dealloc, you might want to set that variable to nil immediately afterwards.

Autorelease
    Suppose you add a convenience constructor to your own object.  Or you have a method that allocates and returns an object that you don't plan on "owning".  If you call "alloc", you will have to balance that out with a release of some sort.  But you can't call "release" before you return it--the object will cease to exist.  So instead, you call "autorelease".  This will cause it to be released at some point in the near future, but not immediately.

Example:

+ (id) createAnObjectForMe
{
return [[[MyClass alloc] init] autorelease];
}
  
Here is the settings menu from Garf.  Each row has a height of 48.0.  There are 3 groups and a total of 8 rows.  Groups 0 and 1 each have two rows, while Group 2 has one row.


GarfSettingsA.JPG







The arrow head (pointy-bracket) is achieved by calling "setShowDisclosure:YES" on the preference cell.

Most of the cells are of the type:
UIPreferencesTableCell






The "High scores" cell is of the following type: UIPreferencesControlTableCell.

The switch inside of it is of the type:
UISwitchControl






GarfSettingsB.jpg

Source Code and Packages

| | Comments (0) | TrackBacks (0)
Source Code
    When you start programming your first original program, you'll probably want to reuse the "main.m" source file that you find in another program (e.g. HelloWorld).   Just edit it and make sure it is importing your program's header.  And of course make sure it is specifying your program's name in the call to UIApplicationMain.  This should be self explanatory when you actually see it.
    So let's say you are creating a program called "Big Gulp".  Here is the layout of files I recommend:
  • main.m (a very small file...)
  • BigGulp.m (your primary application class...of type UIApplication)
  • BigGulp.h (the header file for your application class)
  • Makefile (the make file...recycle this from another program...be sure to edit it to make sure the filenames match your program)
Building Your Program
    When you want to compile and link your program, you will want to use a Makefile.  I'm no expert on this subject, and how they work is still somewhat of a mystery to me, but I do know that you it's best to copy the Makefile from another iPhone program you find and edit it to make sure it fits your program.  Be very careful...the file is very picky about white space (tabs, etc).  The Makefile easily deserves its own article, and so I won't go into too much detail here, but for now, you should be able to look at one and see what needs replacing.
    To compile, go to the prompt in your program's directory (e.g. Cygwin for Windows users).  If the Makefile is set up properly, the command "make clean" should delete any previously compiled binaries and give you a fresh plate...so go ahead and type that.  Then follow that by typing "make".  If it builds correctly, it should produce an executable file (whose name you specified in the Makefile).  Unlike Windows executables, this file will not have a special extension (.exe).  Or any extension, for that matter.  It will simply be plain extensionless file.

Package structure
    When you want get your program ready for installing on the iPhone springboard, you should create a directory on your computer called BigGulp.app (replacing "BigGulp" with the name of your app). 
    This folder should eventually contain the following files:
  • biggulp (or whatever your executable is called)
  • icon.png (the icon for your program)
  • Default.png (the splash screen for your program...should be 320 x 460)
  • PkgInfo (get this from another program and don't change it)
  • Info.plist (get this from another program but DO change it...)
  • (any other graphics file, etc. that your app needs)
Info.plist
    This is another file that you should borrow from some other iPhone program.  You will need to edit however.
    Specifically, you should edit the strings that come after the following labels (keys):
  • CFBundleExecutable - this should specify your app's executable file (e.g. biggulp)
  • CFBundleIdentifier - this should be some unique string...one that no other app in the world will have.  You may notice that it frequently looks like a web address in reverse.  This is merely a convention of coming up a unique identifier...since only one entity (company) will "own" a website name, two different companies won't accidentally use the same identifier.  Note that there is no requirement that this name have anything to do with a website, or that a website exists.  One place this identifier will be used is when you save user defaults, the iPhone will create a file by that name in the Preferences directory.
  • CFBundleVersion - this is whatever arbitrary version you have assigned to your app.
You should leave the other stuff alone, including the CFBundleInfoDictionaryVersion. 


Installing the app
    To install the app on your iPhone, you will simply need to copy the .app directory you just made into the /Applications directory on your phone (via SSH).  Make sure that both your app's directory (e.g. BigGulp.app) and the executable file inside it have full Execution permissions.  This is done by either your SSH program or by using an app like MobileFinder.
    After doing this, you will need to "respring" the phone...that is, restart SpringBoard.  There are a couple of apps that can do this for you, including SysInfo (from Robota Software - available on Installer).  Or you can simply reboot your phone (so it starts up again, showing the silver apple).  Your app should show up on the springboard.



Getting started (on Windows)

| | Comments (0) | TrackBacks (0)
Windows user?
    If you use Windows, you are at a slight disadvantage when it comes to developing for the iPhone.  Mac users have the (alleged) benefit of XCode.  Windows users, however, must make it on their own. 

Toolchain
    Obviously, the first step is to get the toolchain up and running with Cygwin.  Cygwin is a Linux shell (i.e. command prompt) emulator for Windows.  It is distributed with a setup file that makes it very easy to download most of the components you'll need (sort of like Installer on the iPhone).   To get everything setup, see the first entry of this blog.

Text editor
    You'll need a good text editor that is Unix friendly.  Windows (DOS) uses a different combination of invisible keys to mark the end of each line than Unix.  For this reason, I wouldn't recommend any Microsoft products. 
    The text editor I use is jEdit.  It's a free download and has a good number of easy-to-install plugins.  Fairly programmer friendly.

HelloWorld
    Once you have the toolchain up, you'll need to find a copy of the UIKit version of HelloWorld.  HelloWorld, in case you are new to programming, is the traditional name for the first program  you compile.  It's a fairly worthless program, but it serves to a) test the toolchain and b) provide a template for your future programs.
A brief Java-oriented glossary
If you are coming from Java (the language, not the place), then here's a very brief Java-to-UIKit guide.  (Note: some of these are only approximately equal)
  • interface (Java) = protocol (ObjC)
  • static method = class method
  • java.lang.Object = NSObject
  • javax.swing.JOptionsPane = UIAlertSheet
  • null = nil
  • System.out.println("my object=" + myObject); = NSLog(@"my object=%@", myObject);
  • "a literal string" = @"a literal string"
  • java.lang.String = NSString
  • boolean = BOOL
  • java.util.ArrayList = NSMutableArray
  • map = dictionary
  • this = self

For loop
One thing that sort of bugs me is that you aren't allowed to do this: "for (int i=0; i<10; i++)"...you can't declare the variable 'i' there.  You have to do this instead:
int i;
for (i=0; i<10; i++)


Methods in ObjC
(Note: the word "method" will be used interchangeably with the words "function" and "procedure".)

The most obvious difference between Java and Objective C is probably the ways methods are declared. 

A regular ole method declaration looks like this:
- (int) getSomeNumber: (int) aParameter withAnotherParameter: (NSString*) aStringParameter
This would be equivalent to:
private int getSomeNumber(int aParameter, String aStringParameter)

  • First, notice that it starts with a hypen (a minus sign).  If this were a plus sign (+), it would mean the function is a "class method" (known in Java as a static method).
  • Next, notice that Objective C puts the variable type in parentheses, sort of like you do when you cast from one type to another.  The parentheses do not enclose the entire parameter list.
  • Notice there are no commas...also note that ObjC uses labels for parameters (starting with the second one).  In the previous example, it is "withAnotherParameter".  This is just something that helps describe what the next variable is, and I could have just as easily used "fooMcLovin" or some other nonsense instead.
  • The asterisk means that ObjC uses the same pointer notation as C/C++.

When you call the function, it will look something like this:
int x = [myObject getSomeNumber: 32 withAnotherParameter: @"some string I made up"];
(Technically, in ObjC, you aren't calling methods...you are sending messages, but we'll ignore this distinction for the time being.)


Other differences
There are many more differences that I'm not going to cover in this article...e.g. header files, pointers, memory management (no more garbage collection).  These will hopefully be addressed in some form or another in later articles.

Find recent content on the main index or look in the archives to find all content.

Categories

Pages

Powered by Movable Type 4.1-en-release-26-r1120-20071224