Code Snippet: Decompressing a gzipped buffer.

The other day I was working with a web service that gzipped some of its replies. I needed something simple and robust that would gunzip an NSData without writing a file. When a little googling did not turn up a palatable solution, I implemented one of my own. I have made the code available on GitHub, but I also wanted to write a blog post about it, as there does seem to be a lot of confusion about how to use zlib to gunzip data.

Since the data I needed to gunzip would be held in an NSData I decided that using a category on NSData was “the way to go”. The header file NSData+IDZGunzip.h looks like this:

#import <Foundation/Foundation.h>

extern NSString* const IDZGunzipErrorDomain;

@interface NSData (IDZGunzip)

- (NSData*)gunzip:(NSError**)error;

@end

There’s not much to comment on here. This file adds a new method to NSData, gunzip and defines a constant IDZGunzipErrorDomain that will be used in error handling.

Ignoring error handling for the moment, the implementation file NSData+IDZGunzip.m looks like this:

#import "NSData+IDZGunzip.h"
#import <zlib.h>

NSString* const IDZGunzipErrorDomain = @"com.iosdeveloperzone.IDZGunzip";

@implementation NSData (IDZGunzip)

- (NSData*)gunzip:(NSError *__autoreleasing *)error
{
    z_stream zStream;
    memset(&zStream, 0, sizeof(zStream));
    inflateInit2(&zStream, 16);

    UInt32 nUncompressedBytes = *(UInt32*)(self.bytes + self.length - 4);
    NSMutableData* gunzippedData = [NSMutableData dataWithLength:nUncompressedBytes];
    
    zStream.next_in = (Bytef*)self.bytes;
    zStream.avail_in = self.length;
    zStream.next_out = (Bytef*)gunzippedData.bytes;
    zStream.avail_out = gunzippedData.length;
    
    inflate(&zStream, Z_FINISH);

    inflateEnd(&zStream);

    return gunzippedData;
}

@end

Let’s take a closer look at this code. Lines 10-12 initialize the zlib stream structure and library for decompression. The 16 is a “magic” value that instructs zlib to expect gzip compressed data.

Lines 14-15 create an appropriately sized output buffer to receive the output data. The last 4 bytes of gzipped data contain the length of the uncompressed data as a little-endian 32-bit unsigned integer.

Lines 17-20 connect the input and output buffers to the stream.

Line 22 does the actual decompression. The Z_FINISH argument tells the library that this is the final (and in our case only) input buffer to force it flush all its output.

Line 24 releases any memory used by the library.

Finally, line 26 returns the output to the caller.

When we add in some error handling things are a little bit more complex, but not too much:

#import "NSData+IDZGunzip.h"
#import <zlib.h>

NSString* const IDZGunzipErrorDomain = @"com.iosdeveloperzone.IDZGunzip";

@implementation NSData (IDZGunzip)

- (NSData*)gunzip:(NSError *__autoreleasing *)error
{
    /*
     * A minimal gzip header/trailer is 18 bytes long.
     * See: RFC 1952 http://www.gzip.org/zlib/rfc-gzip.html
     */
    if(self.length < 18)
    {
        if(error)
            *error = [NSError errorWithDomain:IDZGunzipErrorDomain code:Z_DATA_ERROR userInfo:nil];
        return nil;
    }
    z_stream zStream;
    memset(&zStream, 0, sizeof(zStream));
    /* 
     * 16 is a magic number that allows inflate to handle gzip 
     * headers.
     */
    int iResult = inflateInit2(&zStream, 16);
    if(iResult != Z_OK)
    {
        if(error)
            *error = [NSError errorWithDomain:IDZGunzipErrorDomain code:iResult userInfo:nil];
        return nil;
    }
    /*
     * The last four bytes of a gzipped file/buffer contain the the number 
     * of uncompressed bytes expressed as a 32-bit little endian unsigned integer.
     * See: RFC 1952 http://www.gzip.org/zlib/rfc-gzip.html
     */
    UInt32 nUncompressedBytes = *(UInt32*)(self.bytes + self.length - 4);
    NSMutableData* gunzippedData = [NSMutableData dataWithLength:nUncompressedBytes];
    
    zStream.next_in = (Bytef*)self.bytes;
    zStream.avail_in = self.length;
    zStream.next_out = (Bytef*)gunzippedData.bytes;
    zStream.avail_out = gunzippedData.length;
    
    iResult = inflate(&zStream, Z_FINISH);
    if(iResult != Z_STREAM_END)
    {
        if(error)
            *error = [NSError errorWithDomain:IDZGunzipErrorDomain code:iResult userInfo:nil];
        gunzippedData = nil;
    }
    inflateEnd(&zStream);
    return gunzippedData;
}

@end

The finished code can be used as follows:

#import "NSData+IDZGunzip.h"

...

// Assuming data holds valid gzipped data
NSError* error = nil;
// gunzip the data
NSData* gunzippedData = [data gunzip:&error];
if(!gunzippedData)
{
  // Handle error
}
else
{
  // Success use gunzippedData
}

Get the code from GitHub at this URL: https://github.com/iosdevzone/IDZGunzip


Posted in Code Snippets | 1 Comment

Tutorial: Playing Audio with AVAudioPlayer

Screenshot of IDZAudioPlayerTutorial App

Screenshot of IDZAudioPlayerTutorial App


The past few tutorials dealt with compiling the open source Ogg and Vorbis libraries. In the next few tutorials I will demonstrate how to play Ogg Vorbis files using these libraries.

Before attempting to play an unsupported file format like Ogg Vorbis, it make sense to first understand how a supported file format can be played, so I will start off by showing how to play supported audio formats using AVAudioPlayer.

Since the UI for this tutorial is a little busy and this is not, after all, a tutorial about using Interface Builder I will provide a starting point project with the UI already constructed: IDZAudioPlayerTutorial0.zip

Creating an instance of AVAudioPlayer

To use AVAudioPlayer you need to import AVFoundation.h. Edit IDZViewController.h and add the following line:

#import <AVFoundation/AVFoundation.h>

Next open IDZViewController.m.

Add a property to the class extension to hold your a reference to your player instance:

@interface IDZViewController ()
@property (nonatomic, strong) AVAudioPlayer* player;
@end

Keeping private properties and methods avoids the interface in your header file become cluttered with details a user of the class need not know.

Add a synthesize statement for this property:

@synthesize player = mPlayer;

In Xcode 4.5 you do not need to add a synthesize statement, but I still like to do it to explicitly give the property and instance variable distinct names.

The downloaded project contains a file Rondo_Alla_Turka_Short.aiff. Add the following code to create a player loaded with this file.

- (void)viewDidLoad
{
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib.  
    NSURL* url = [[NSBundle mainBundle] URLForResource:@"Rondo_Alla_Turka_Short" withExtension:@"aiff"];
    NSAssert(url, @"URL is valid."); 
    NSError* error = nil;
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&amp;error];
    if(!self.player)
    {
        NSLog(@"Error creating player: %@", error);
    }
}

Also add the following line somewhere in viewDidUnload:

    self.player = nil;

Finally update the play, pause and stop methods as shown:

- (IBAction)play:(id)sender {
    IDZTrace();
    [self.player play];
}

- (IBAction)pause:(id)sender {
    IDZTrace();
    [self.player pause];
}

- (IBAction)stop:(id)sender {
    IDZTrace();
    [self.player stop];
}

If you compile and run the project you should not be able to tap the Play button and the music will start playing.

Initializing the UI Controls

The duration and numberOfChannels will not change during playback so they can be set just after the player has been created. This is also a good time to set the minimumValue and maximumValue of the currentTimeSlider. To do this add the following code to the end of viewDidLoad:

    self.durationLabel.text = [NSString
        stringWithFormat:@&quot;%.02fs&quot;,self.player.duration];
    self.numberOfChannelsLabel.text = [NSString 
        stringWithFormat:@&quot;%d&quot;, self.player.numberOfChannels];
    self.currentTimeSlider.minimumValue = 0.0f;
    self.currentTimeSlider.maximumValue = self.player.duration;

Updating the Controls During Playback

A timer can be used to periodically update the display when the audio is playing. The timer should be started when the user taps the Play button.

Add a property to the class extension for the timer:

@interface IDZViewController ()
@property (nonatomic, strong) AVAudioPlayer* player;
@property (nonatomic, strong) NSTimer* timer;
@end 

Also add a synthesize statement near the top of the implementation (you can skip this on Xcode 4.5):

@synthesize timer = mTimer;

Now modify your play method:

- (IBAction)play:(id)sender 
{
    IDZTrace();
    [self.player play];
    self.timer = [NSTimer 
        scheduledTimerWithTimeInterval:0.1 
        target:self selector:@selector(timerFired:) 
        userInfo:nil repeats:YES];               
}

Lines 5-8 create a timer that will call a method timerFired every 0.1 seconds.

Implement timerFired:

- (void)timerFired:(NSTimer*)timer
{
    [self updateDisplay];
}

You’ll implement updateDisplay a little later. Let’s finish with the timer first.

Declare a stopTimer method in the class extension:

- (void)stopTimer;

Add the method to the implementation:

- (void)stopTimer
{
    [self.timer invalidate];
    self.timer = nil;
}

Line 3 stops the timer from firing and line 4 signals to ARC that the timer is no longer needed.

The timer must be stopped when:

  • the user presses pause,
  • the user presses stop,
  • playback stops at the end of file, or
  • playback stops due to an error.

The first two cases can easily dealt with by modifying the pause and stop methods:

- (IBAction)pause:(id)sender {
    IDZTrace();
    [self.player pause];
    [self stopTimer];
    [self updateDisplay];
}

- (IBAction)stop:(id)sender {
    IDZTrace();
    [self.player stop];
    [self stopTimer];
    [self updateDisplay];
}

The latter two cases can be dealt with using delegation and the AVAudioPlayerDelegate protocol.

The AVAudioPlayerDelegate Protocol.

To begin signal to the compiler that your view controller will be implementing the AVAudioPlayerDelegate protocol, open IDZViewController.h and add the protocol to the interface definition.

@interface IDZViewController : UIViewController&lt;AVAudioPlayerDelegate&gt;

Back in IDZViewController.m add the following method declarations to the class extension:

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player 
        successfully:(BOOL)flag;
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player 
        error:(NSError *)error;

And add implementations:

#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    NSLog(@&quot;%s successfully=%@&quot;, __PRETTY_FUNCTION__, flag ? @&quot;YES&quot;  : @&quot;NO&quot;);
    [self stopTimer];
    [self updateDisplay];
}

- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
    NSLog(@&quot;%s error=%@&quot;, __PRETTY_FUNCTION__, error);
    [self stopTimer];
    [self updateDisplay];
}

We’re now finished with the timer, time to turn our attention back the updateDisplay method.

Updating the UI Controls

Add a declaration to the class extension:

- (void)updateDisplay
- (void)updateSliderLabels

The implementation is straightforward:

#pragma mark - Display Update
- (void)updateDisplay
{
    NSTimeInterval currentTime = self.player.currentTime;
    NSString* currentTimeString = [NSString stringWithFormat:@"%.02f", currentTime];
    
    self.currentTimeSlider.value = currentTime;
    [self updateSliderLabels];
    
    self.currentTimeLabel.text = currentTimeString;
    self.playingLabel.text = self.player.playing ? @"YES" : @"NO";
    self.deviceCurrentTimeLabel.text = [NSString stringWithFormat:@"%.02f", self.player.deviceCurrentTime];
}

- (void)updateSliderLabels
{
    NSTimeInterval currentTime = self.currentTimeSlider.value;
    NSString* currentTimeString = [NSString stringWithFormat:@"%.02f", currentTime];
    
    self.elapsedTimeLabel.text =  currentTimeString;
    self.remainingTimeLabel.text = [NSString stringWithFormat:@"%.02f", self.player.duration - currentTime];
}

The reason for using a separate method to update the slider labels is that when you implement seeking you will want to be able to update them separately from other display elements.

You should now be able to compile and run the project, and all the UI controls should update as the audio is playing.

Implementing Seeking

A seek feature can be added by completing the implementation of two methods that were included in the skeleton project.

The currentTimeSliderValueChanged method is connected in the XIB file to the Value Changed action for the horizontal slider. Add the following lines:

- (IBAction)currentTimeSliderValueChanged:(id)sender
{
    if(self.timer)
        [self stopTimer];
    [self updateSliderLabels];
}

This code stops the display update timer so that the user’s dragging is not being contradicted by the current position and also updates the slider labels so the user knows where they are dragging. You could also stop playback if you wanted to, but I prefer letting it continue until the user releases the slider.

When the user releases the slider, a Touch Up Inside action is generated. In the skeleton project this action is connected to the currentTimeSliderTouchUpInside: method. The following shows the completed implementation:

- (IBAction)currentTimeSliderTouchUpInside:(id)sender
{
    [self.player stop];
    self.player.currentTime = self.currentTimeSlider.value;
    [self.player prepareToPlay];
    [self play:self];
}

This stops playback, sets the player’s currentTime property to the value of the slider, and restarts playback from that position.

Download the Source Code

You can download the complete source code for the project here: IDZAudioPlayerTutorial1.zip


Posted in Tutorial | Tagged , | 13 Comments

Tutorial: Open Source on iOS (Part 4): Compiling libvorbis

This is the forth in a series of posts on building open source libraries for iOS. This tutorial uses the scripts and directory structure developed in the first three posts and applies them to compiling the libvorbis open source audio codec. Please make sure you have completed the previous tutorials before attempting this one.

Obtaining the Source Code

Create a directory for your build (in $IDZ_BUILD_ROOT).

cd $IDZ_BUILD_ROOT
mkdir -p libvorbis/1.3.3
pushd libvorbis/1.3.3

Download and decompress the source

curl -O http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.3.tar.gz
tar xvfz libvorbis-1.3.3.tar.gz

You now need to change the -O4 optimization in the configure script. Open the file libvorbis-1.3.3/configure file and locate the following section:

        *-*-darwin*)
                DEBUG="-DDARWIN -fno-common -force_cpusubtype_ALL -Wall -g -O0 -fsigned-char"
                CFLAGS="-DDARWIN -fno-common -force_cpusubtype_ALL -Wall -g -O4 -ffast-math -fsigned-char"
                PROFILE="-DDARWIN -fno-common -force_cpusubtype_ALL -Wall -g -pg -O4 -ffast-math -fsigned-char";;

Change the -O4 to -O3 and remove all the -force_cpusubtype_ALL flags:

        *-*-darwin*)
                DEBUG="-DDARWIN -fno-common  -Wall -g -O0 -fsigned-char"
                CFLAGS="-DDARWIN -fno-common  -Wall -g -O3 -ffast-math -fsigned-char"
                PROFILE="-DDARWIN -fno-common  -Wall -g -pg -O3 -ffast-math -fsigned-char";;

You will also need to edit libvorbis-1.3.3/lib/os.h. Locate the following section, near line 84:

/* Special i386 GCC implementation */
#if defined(__i386__) && defined(__GNUC__) && !defined(__BEOS__)
#  define VORBIS_FPU_CONTROL

And change it to:

/* Special i386 GCC implementation */
#if 0 && defined(__i386__) && defined(__GNUC__) && !defined(__BEOS__)
#  define VORBIS_FPU_CONTROL

This disables a special rounding macro used for the i386 architecture that does not seem to work correctly on the simulator.

Building the Static Libraries

You are now ready to attempt a build.

idz_configure armv7 6.0 libvorbis-1.3.3/configure

Lots of messages will print on your terminal, but the last few lines will be:

configure: error: Ogg >= 1.0 required !
make: *** No rule to make target `clean'.  Stop.
make: *** No targets specified and no makefile found.  Stop.
make: *** No rule to make target `install'.  Stop.

It would appear that the build is looking for libogg but does not know where to look for it.

To find information about the command line options to a configure script you use the --help argument.
Type:

libvorbis-1.3.3/configure --help

The relevant line is

  --with-ogg=PFX          Prefix where libogg is installed (optional)

The idz_configure script will append the contents of the environment variable IDZ_EXTRA_CONFIGURE_FLAGS to the command line arguments. Therefore you can retry the build as follows:

export IDZ_EXTRA_CONFIGURE_FLAGS=--with-ogg=/Users/idz/idz_builds/libogg/1.3.0/install-iPhoneOS-armv7
idz_configure armv7 6.0 libvorbis-1.3.3/configure

The build should now succeed.

You can repeat the build for any other architectures you may need. For example, to build for the simulator use:

export IDZ_EXTRA_CONFIGURE_FLAGS=--with-ogg=/Users/idz/idz_builds/libogg/1.3.0/install-iPhoneSimulator-i386
idz_configure i386 6.0 libvorbis-1.3.3/configure

Creating the Pseudo-Framework

Building libvorbis creates three static libraries, libvorbis.a, libvorbisenc.a and libvorbisfile.a, however a pseudo-framework can only have one static library, so we need a method to combine these libraries into a single .a file. Luckily static libraries are only object file archives, so this is not hard to do.

Change into the lib directory of one the install directories:

pushd install-iPhoneSimulator-i386/lib

Make a temporary directory to work in and change into it:

mkdir tmp
pushd tmp

Extract the object files from each of the libraries in turn:

ar -x ../libvorbis.a
ar -x ../libvorbisenc.a
ar -x ../libvorbisfile.a

Combine all the object files into a new library called libvorbisall.a.

ar -rs ../libvorbisall.a *.o

Pop out of the tmp directory and remove it

popd
rm -rf tmp

Since you will need to repeat this process for each architecture it makes sense to generalize this process and create a script automate it. Create the following script file in $IDZ_BUILD_ROOT/bin/idz_combine:

#!/bin/bash
#
# This script combines a number of static library files (.a files) into
# a single (new) output library
#
# If the output file exists it will be overwritten
#

usage()
{
  echo "Usage: $0 output_lib input_lib1 ..."
  exit 1
}

if [ "$#" == "0" ]; then
  usage
fi

#
# Determine the absolute path of the output file
#
if [[ "$1" = /* ]]; then
  ABS_OUTPUT_FILE=$1
else
  ABS_OUTPUT_FILE=$(pwd)/$1
fi
#
# Remember output file as it appeared on command line too
#
OUTPUT_FILE=$1
#
# Determine the input files
#
shift
INPUT_FILES=$@
#
# Print what we are going to do
#
echo "$OUTPUT_FILE <= $@"
#
# We'll do all the work in a temp dir
#
TMP_DIR=$(mktemp -d tmp.XXXXXX)
mkdir -p $TMP_DIR
pushd $TMP_DIR > /dev/null
#
# Extract the .o files from all the libs
#
for lib in $@
do
  if [[ "$lib" = /* ]]; then
    ar -x $lib
  else
    ar -x ../$lib
  fi
done
#
# Create a new output archive by combining all the *.o files
#
ar -rs $ABS_OUTPUT_FILE *.o
#
# Return to the original directory and clean up
#
popd > /dev/null
rm -rf $TMP_DIR
exit 0

When I have more time I will come back and describe what is going on here line by line.

Using this script, all you need to do to perform the combination step is:

pushd install-iPhoneOS-armv7/lib
idz_combine libvorbisall.a *.a
popd

and repeat this for each of your architectures.

These per-architecture combined static libraries can then be combined using the idz_fw script from the previous tutorial:

idz_fw Vorbis libvorbisall.a install-iPhoneSimulator-i386/include/vorbis

In coming posts, I’ll look at how you can use Ogg.framework and Vorbis.framework to play audio on iOS.


Posted in Tutorial | 2 Comments