Audio Queues
A BIG thank you to AnotherJake - your code works like a charm.
A heads-up for those having problems playing mp3s - iPhone/iTouch seems to only like mp3 encoded at 44.1kHz and above.
Thanks again AnotherJake!
---------------
EDIT (25 August): Actually you can play less then 44.1kHz MP3s. The problem I was having was that I was trying unsuccessfully to play very short sound-effect MP3s. The solution was to decrease the size of the buffer:
static UInt32 gBufferSizeBytes = 0x10000;
---------------
I set it to 0x01000 and it worked fine even for 20kHz encoded MP3 files.
A heads-up for those having problems playing mp3s - iPhone/iTouch seems to only like mp3 encoded at 44.1kHz and above.
Thanks again AnotherJake!
---------------
EDIT (25 August): Actually you can play less then 44.1kHz MP3s. The problem I was having was that I was trying unsuccessfully to play very short sound-effect MP3s. The solution was to decrease the size of the buffer:
static UInt32 gBufferSizeBytes = 0x10000;
---------------
I set it to 0x01000 and it worked fine even for 20kHz encoded MP3 files.
Oops! I can't say that it works on iPhone because of the NDA (so I don't know what you are talking about
), this is for Mac OS X. But I'm glad to hear from you ethan! 
I have received many kind emails about GBMusicTrack over the last several weeks. Again, for anyone who has comments or suggestions or improvements for it, you can contact me about it at anotherjake +AT_ mac.com
Whilst on that subject, Bence, from Hungary, added some small modifications to show an example of how to do seeking. I haven't had a chance to try it myself yet, but it looks pretty handy, and Bence said it'd be okay to share, so here it is for anyone who's interested:
BenceGBMusicTrack.h
BenceGBMusicTrack.m
And here's another little code snippet I put together from one of Bence's emails, which will get the artist string from some files. It doesn't seem to work with everything for some reason. More AudioToolbox bugs I suspect.
), this is for Mac OS X. But I'm glad to hear from you ethan! 
I have received many kind emails about GBMusicTrack over the last several weeks. Again, for anyone who has comments or suggestions or improvements for it, you can contact me about it at anotherjake +AT_ mac.com
Whilst on that subject, Bence, from Hungary, added some small modifications to show an example of how to do seeking. I haven't had a chance to try it myself yet, but it looks pretty handy, and Bence said it'd be okay to share, so here it is for anyone who's interested:
BenceGBMusicTrack.h
Code:
#ifndef __GBMUSICTRACK_H
#define __GBMUSICTRACK_H
#import <Cocoa/Cocoa.h>
#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#include "Utility.h"
#define NUM_QUEUE_BUFFERS 3
typedef enum {
EAudioStateClosed,
EAudioStateStopped,
EAudioStatePlaying,
EAudioStatePaused,
EAudioStateSeeking
} EAudioState;
@interface GBMusicTrack : NSObject
{
AudioFileID audioFile;
AudioStreamBasicDescription dataFormat;
AudioQueueRef queue;
UInt64 packetIndex;
UInt32 numPacketsToRead;
AudioStreamPacketDescription* packetDescs;
BOOL repeat;
BOOL trackClosed;
AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];
EAudioState audioState;
}
- (id)initWithPath:(NSString *)path;
- (void)setGain:(Float32)gain;
- (void)setRepeat:(BOOL)yn;
- (void)play;
- (void)pause;
- (void)stop;
- (void)seek:(UInt64)packetOffset;
- (EAudioState)getState;
// close is called automatically in GBMusicTrack's dealloc method, but it is recommended
// to call close first, so that the associated Audio Queue is released immediately, instead
// of having to wait for a possible autorelease, which may cause some conflict
- (void)close;
extern NSString* GBMusicTrackFinishedPlayingNotification;
@end
#endifBenceGBMusicTrack.m
Code:
#import "GBMusicTrack.h"
#include "SoundEngine.h" // for callback function
static UInt32 gBufferSizeBytes = 0x10000; // 64k
NSString* GBMusicTrackFinishedPlayingNotification = @"GBMusicTrackFinishedPlayingNotification";
@interface GBMusicTrack (InternalMethods)
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer);
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer;
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer;
- (void)prefetchBuffers;
@end
@implementation GBMusicTrack
#pragma mark -
#pragma mark GBMusicTrack
- (void)dealloc
{
[self close];
[super dealloc];
}
- (void)close
{
// it is preferrable to call close first, before dealloc if there is a problem waiting for an autorelease
audioState = EAudioStateClosed;
if (trackClosed) return;
trackClosed = YES;
AudioQueueStop(queue, YES);
AudioQueueDispose(queue, YES);
AudioFileClose(audioFile);
free(packetDescs);
packetDescs = nil;
}
- (id)initWithPath:(NSString *)path
{
UInt32 size, maxPacketSize;
char *cookie;
audioState = EAudioStateStopped;
if (!(self = [super init])) return nil;
if (path == nil) return nil;
// try to open up the file using the specified path
if (noErr != AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:path], 0x01, kAudioFileCAFType, &audioFile))
{
LOGPAR("GBMusicTrack Error - initWithPath: could not open audio file. Path given was: %@", path);
return nil;
}
// get the data format of the file
size = sizeof(dataFormat);
AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat, &size, &dataFormat);
// create a new playback queue using the specified data format and buffer callback
AudioQueueNewOutput(&dataFormat, BufferCallback, self, nil, nil, 0, &queue);
// calculate number of packets to read and allocate space for packet descriptions if needed
if (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0)
{
// since we didn't get sizes to work with, then this must be VBR data (Variable BitRate), so
// we'll have to ask Core Audio to give us a conservative estimate of the largest packet we are
// likely to read with kAudioFilePropertyPacketSizeUpperBound
size = sizeof(maxPacketSize);
AudioFileGetProperty(audioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
if (maxPacketSize > gBufferSizeBytes)
{
// hmm... well, we don't want to go over our buffer size, so we'll have to limit it I guess
maxPacketSize = gBufferSizeBytes;
LOGPAR("GBMusicTrack Warning - initWithPath: had to limit packet size requested for file path: %@", path);
}
numPacketsToRead = gBufferSizeBytes / maxPacketSize;
// will need a packet description for each packet since this is VBR data, so allocate space accordingly
packetDescs = malloc(sizeof(AudioStreamPacketDescription) * numPacketsToRead);
}
else
{
// for CBR data (Constant BitRate), we can simply fill each buffer with as many packets as will fit
numPacketsToRead = gBufferSizeBytes / dataFormat.mBytesPerPacket;
// don't need packet descriptsions for CBR data
packetDescs = nil;
}
// see if file uses a magic cookie (a magic cookie is meta data which some formats use)
AudioFileGetPropertyInfo(audioFile, kAudioFilePropertyMagicCookieData, &size, nil);
if (size > 0)
{
// copy the cookie data from the file into the audio queue
cookie = malloc(sizeof(char) * size);
AudioFileGetProperty(audioFile, kAudioFilePropertyMagicCookieData, &size, cookie);
AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, cookie, size);
free(cookie);
}
// allocate buffers
for (int i = 0; i < NUM_QUEUE_BUFFERS; i++)
AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]);
packetIndex = 0;
repeat = NO;
trackClosed = NO;
return self;
}
- (void)prefetchBuffers
{
// prime buffers with some data
for (int i = 0; i < NUM_QUEUE_BUFFERS; i++)
{
//AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]);
if ([self readPacketsIntoBuffer:buffers[i]] == 0)
{
// this might happen if the file was so short that it needed less buffers than we planned on using
break;
}
}
}
- (void)seek:(UInt64)packetOffset
{
BOOL playbeforeseek = ([self getState] == EAudioStatePlaying);
audioState = EAudioStateSeeking;
[self stop];
packetIndex = packetOffset;
if (playbeforeseek) [self play];
}
- (void)stop
{
if (audioState != EAudioStateSeeking)
audioState = EAudioStateStopped;
AudioQueueStop(queue, YES);
}
- (void)setGain:(Float32)gain
{
AudioQueueSetParameter(queue, kAudioQueueParam_Volume, gain);
}
- (void)setRepeat:(BOOL)yn
{
repeat = yn;
}
- (void)play
{
if (audioState == EAudioStateStopped || audioState == EAudioStateSeeking)
[self prefetchBuffers];
audioState = EAudioStatePlaying;
OSStatus err = AudioQueueStart(queue, nil);
if (err) LOGPAR("GBMusicTrack AudioQueueStart ERROR %d", err);
}
- (void)pause
{
audioState = EAudioStatePaused;
AudioQueuePause(queue);
}
#pragma mark -
#pragma mark Callback
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer)
{
// redirect back to the class to handle it there instead, so we have direct access to the instance variables
[(GBMusicTrack *)inUserData callbackForBuffer:buffer];
}
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer
{
if (audioState == EAudioStateSeeking) return;
if ([self readPacketsIntoBuffer:buffer] == 0)
{
// End Of File reached, so rewind and refill the buffer using the beginning of the file instead
packetIndex = 0;
[self readPacketsIntoBuffer:buffer];
// if not repeating then we'll pause it so it's ready to play again immediately if needed
if (!repeat)
{
//[self pause];
[self stop];
// we're not in the main thread during this callback, so enqueue a message on the main thread to post notification
// that we're done, or else the notification will have to be handled in this thread, making things more difficult
[self performSelectorOnMainThread:@selector(postTrackFinishedPlayingNotification:) withObject:nil waitUntilDone:NO];
}
}
}
- (EAudioState)getState
{
return audioState;
}
- (void)postTrackFinishedPlayingNotification:(id)object
{
// if we're here then we're in the main thread as specified by the callback, so now we can post notification that
// the track is done without the notification observer(s) having to worry about thread safety and autorelease pools
[[NSNotificationCenter defaultCenter] postNotificationName:GBMusicTrackFinishedPlayingNotification object:self];
if (audioState != EAudioStatePlaying) {
[self close];
callbackPlayFinished();
}
}
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer
{
UInt32 numBytes, numPackets;
// read packets into buffer from file
numPackets = numPacketsToRead;
AudioFileReadPackets(audioFile, NO, &numBytes, packetDescs, packetIndex, &numPackets, buffer->mAudioData);
if (numPackets > 0)
{
// - End Of File has not been reached yet since we read some packets, so enqueue the buffer we just read into
// the audio queue, to be played next
// - (packetDescs ? numPackets : 0) means that if there are packet descriptions (which are used only for Variable
// BitRate data (VBR)) we'll have to send one for each packet, otherwise zero
buffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer(queue, buffer, (packetDescs ? numPackets : 0), packetDescs);
// move ahead to be ready for next time we need to read from the file
packetIndex += numPackets;
}
return numPackets;
}
@endAnd here's another little code snippet I put together from one of Bence's emails, which will get the artist string from some files. It doesn't seem to work with everything for some reason. More AudioToolbox bugs I suspect.

Code:
if (noErr == AudioFileGetPropertyInfo (audioFile, kAudioFilePropertyInfoDictionary, &size, NULL))
{
NSMutableDictionary *myDictionary;
if (noErr == AudioFileGetProperty(audioFile, kAudioFilePropertyInfoDictionary, &size, &myDictionary))
{
NSString *artistString;
if (CFDictionaryGetValueIfPresent((CFMutableDictionaryRef)myDictionary, CFSTR(kAFInfoDictionary_Artist), (const void **) &artistString))
NSLog(@"Artist : %@", artistString);
else
NSLog(@"Unknown Artist");
[myDictionary release];
}
}
Jake, thanks for all this! It works like a charm.
And I know you're using it to only play one sound file at a time. But I want to do some cross fading between sound files. So that requires 2 to play at the same time.
And I edited your code to allow this. And it works great in the sim. But on the device it plays the first song. Doesn't play the second and lags he whole app like crazy. Any ideas? Is it possible?
And I know you're using it to only play one sound file at a time. But I want to do some cross fading between sound files. So that requires 2 to play at the same time.
And I edited your code to allow this. And it works great in the sim. But on the device it plays the first song. Doesn't play the second and lags he whole app like crazy. Any ideas? Is it possible?
Thanks OdieGiblet!
Unfortunately, iPhone is under NDA so I cannot say that I know anything about GBMusicTrack working on iPhone.
+++++ GBMusicTrack was written exclusively for Mac OS X. +++++
That said, you are correct, it is only designed to play one track at a time. I cannot possibly stress that detail enough. If you would like to further discuss why I designed it this way, please contact me at anotherjake +AT+ mac.com
Unfortunately, iPhone is under NDA so I cannot say that I know anything about GBMusicTrack working on iPhone.
+++++ GBMusicTrack was written exclusively for Mac OS X. +++++
That said, you are correct, it is only designed to play one track at a time. I cannot possibly stress that detail enough. If you would like to further discuss why I designed it this way, please contact me at anotherjake +AT+ mac.com
OpenAL is a perfectly good alternative for playing multiple files at the same time, and works on a variety of platforms.
AnotherJake Wrote:Heck, well for Mac, all you had to do was ask!
Here is what I've been doing so far. This isn't well tested and I can't guarantee any accuracy or that there isn't a memory leak [edit]found a memory leak, studied it, bug report filed[/edit] or some other dreadful bug, but I commented the bejeezus out of it -- against my desire not to comment anything. I am tentatively planning on adding it to the GameBase framework project, but I haven't tested it enough to be satisfied with it. Plus, I am in the middle of writing a musicPlayer class to automatically handle these tracks in playlists, like a behind-the-scenes iTunes for game developers. This is just the raw class to play a tune. You'd use it by alloc-initing a new track every time you want to play a new one, and you release it as soon as it's done playing, and subsequently alloc-init a new one to play (if you are going to play a different track, otherwise set it to repeat). Even if you plan on playing the same track again later, you need to release it first if you want to play a different one in the meantime. It is designed this way to conserve resources. The musicPlayer class I was talking about will manage this automatically. I'd post that too, but it's not *quite* finished (as in, it's feature complete, but it's being tested and bugs are being found and squashed). So for now, maybe you can figure out how to get stuff rolling with Audio Queues using this.
*** NOTE *** Use this for background music only, and only one track at a time. If you are looping it, it is better to set the track to repeat rather than releasing it and reallocating it again.
You'd use it like this:
Code:
GBMusicTrack *song = [[GBMusicTrack alloc] initWithPath:[[NSBundle mainBundle] pathForResource:@"BackgroundMusic" ofType:@"mp3"]];
[song setRepeat:YES];
[song play];
GBMusicTrack.h
Code:
//
// GBMusicTrack.h
// GameBase
//
// Created by Jake Peterson (AnotherJake) on 7/6/08.
// Copyright 2008 Jake Peterson. All rights reserved.
//
#import <AppKit/AppKit.h>
#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#define NUM_QUEUE_BUFFERS 3
@interface GBMusicTrack : NSObject
{
AudioFileID audioFile;
AudioStreamBasicDescription dataFormat;
AudioQueueRef queue;
UInt64 packetIndex;
UInt32 numPacketsToRead;
AudioStreamPacketDescription *packetDescs;
BOOL repeat;
BOOL trackClosed;
BOOL trackEnded;
AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];
}
- (id)initWithPath:(NSString *)path;
- (void)setGain:(Float32)gain;
- (void)setRepeat:(BOOL)yn;
- (void)play;
- (void)pause;
// close is called automatically in GBMusicTrack's dealloc method, but it is recommended
// to call close first, so that the associated Audio Queue is released immediately, instead
// of having to wait for a possible autorelease, which may cause some conflict
- (void)close;
extern NSString *GBMusicTrackFinishedPlayingNotification;
@end
GBMusicTrack.m
Code:
//
// GBMusicTrack.m
// GameBase
//
// Created by Jake Peterson (AnotherJake) on 7/6/08.
// Copyright 2008 Jake Peterson. All rights reserved.
//
// last modified 8/7/08
#import "GBMusicTrack.h"
static UInt32 gBufferSizeBytes = 0x10000; // 64k
// *** NOTE *** GBMusicTrack is only designed to play one track at a time, as background music, so gThereIsAnActiveTrack
// is used to prevent multiple tracks from playing. Use something else like OpenAL to play sounds like lasers and explosions
static BOOL gThereIsAnActiveTrack = NO;
NSString *GBMusicTrackFinishedPlayingNotification = @"GBMusicTrackFinishedPlayingNotification";
@interface GBMusicTrack (InternalMethods)
static void propertyListenerCallback(void *inUserData, AudioQueueRef queueObject, AudioQueuePropertyID propertyID);
- (void)playBackIsRunningStateChanged;
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer);
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer;
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer;
@end
@implementation GBMusicTrack
#pragma mark -
#pragma mark GBMusicTrack
- (void)dealloc
{
[self close];
if (packetDescs != nil)
free(packetDescs);
[super dealloc];
}
- (void)close
{
// it is preferrable to call close first, if there is a problem waiting for an autorelease
if (trackClosed)
return;
trackClosed = YES;
AudioQueueStop(queue, YES); // <-- YES means stop immediately
AudioQueueDispose(queue, YES);
AudioFileClose(audioFile);
gThereIsAnActiveTrack = NO;
}
- (id)initWithPath:(NSString *)path
{
UInt32 size, maxPacketSize;
char *cookie;
int i;
if (gThereIsAnActiveTrack)
{
NSLog(@"*** WARNING *** GBMusicTrack only plays one track at a time! You must close the previously running track"
" before you can play another. Requested track was: %@", [path lastPathComponent]);
return nil;
}
if(!(self = [super init])) return nil;
if (path == nil) return nil;
// try to open up the file using the specified path
if (noErr != AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:path], 0x01, 0, &audioFile))
{
NSLog(@"*** Error *** GBMusicTrack - initWithPath: could not open audio file. Path given was: %@", path);
return nil;
}
// get the data format of the file
size = sizeof(dataFormat);
AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat, &size, &dataFormat);
// create a new playback queue using the specified data format and buffer callback
AudioQueueNewOutput(&dataFormat, BufferCallback, self, nil, nil, 0, &queue);
// calculate number of packets to read and allocate space for packet descriptions if needed
if (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0)
{
// since we didn't get sizes to work with, then this must be VBR data (Variable BitRate), so
// we'll have to ask Core Audio to give us a conservative estimate of the largest packet we are
// likely to read with kAudioFilePropertyPacketSizeUpperBound
size = sizeof(maxPacketSize);
AudioFileGetProperty(audioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
if (maxPacketSize > gBufferSizeBytes)
{
// hmm... well, we don't want to go over our buffer size, so we'll have to limit it I guess
maxPacketSize = gBufferSizeBytes;
NSLog(@"*** Warning *** GBMusicTrack - initWithPath: had to limit packet size requested for file: %@", [path lastPathComponent]);
}
numPacketsToRead = gBufferSizeBytes / maxPacketSize;
// will need a packet description for each packet since this is VBR data, so allocate space accordingly
packetDescs = malloc(sizeof(AudioStreamPacketDescription) * numPacketsToRead);
}
else
{
// for CBR data (Constant BitRate), we can simply fill each buffer with as many packets as will fit
numPacketsToRead = gBufferSizeBytes / dataFormat.mBytesPerPacket;
// don't need packet descriptions for CBR data
packetDescs = nil;
}
// see if file uses a magic cookie (a magic cookie is meta data which some formats use)
AudioFileGetPropertyInfo(audioFile, kAudioFilePropertyMagicCookieData, &size, nil);
if (size > 0)
{
// copy the cookie data from the file into the audio queue
cookie = malloc(sizeof(char) * size);
AudioFileGetProperty(audioFile, kAudioFilePropertyMagicCookieData, &size, cookie);
AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, cookie, size);
free(cookie);
}
// we want to know when the playing state changes so we can properly dispose of the audio queue when it's done
AudioQueueAddPropertyListener(queue, kAudioQueueProperty_IsRunning, propertyListenerCallback, self);
// allocate and prime buffers with some data
packetIndex = 0;
for (i = 0; i < NUM_QUEUE_BUFFERS; i++)
{
AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]);
if ([self readPacketsIntoBuffer:buffers[i]] == 0)
{
// this might happen if the file was so short that it needed less buffers than we planned on using
break;
}
}
repeat = NO;
trackClosed = NO;
trackEnded = NO;
gThereIsAnActiveTrack = YES;
return self;
}
- (void)setGain:(Float32)gain
{
if (trackClosed)
return;
AudioQueueSetParameter(queue, kAudioQueueParam_Volume, gain);
}
- (void)setRepeat:(BOOL)yn
{
repeat = yn;
}
- (void)play
{
if (trackClosed)
return;
AudioQueueStart(queue, nil);
}
- (void)pause
{
if (trackClosed)
return;
AudioQueuePause(queue);
}
#pragma mark -
#pragma mark Callback
static void propertyListenerCallback(void *inUserData, AudioQueueRef queueObject, AudioQueuePropertyID propertyID)
{
// redirect back to the class to handle it there instead, so we have direct access to the instance variables
if (propertyID == kAudioQueueProperty_IsRunning)
[(GBMusicTrack *)inUserData playBackIsRunningStateChanged];
}
- (void)playBackIsRunningStateChanged
{
if (trackEnded)
{
// go ahead and close the track now
trackClosed = YES;
AudioQueueDispose(queue, YES);
AudioFileClose(audioFile);
gThereIsAnActiveTrack = NO;
// we're not in the main thread during this callback, so enqueue a message on the main thread to post notification
// that we're done, or else the notification will have to be handled in this thread, making things more difficult
[self performSelectorOnMainThread:@selector(postTrackFinishedPlayingNotification:) withObject:nil waitUntilDone:NO];
}
}
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer)
{
// redirect back to the class to handle it there instead, so we have direct access to the instance variables
[(GBMusicTrack *)inUserData callbackForBuffer:buffer];
}
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer
{
// I guess it's possible for the callback to continue to be called since this is in another thread, so to be safe,
// don't do anything else if the track is closed, and also don't bother reading anymore packets if the track ended
if (trackClosed || trackEnded)
return;
if ([self readPacketsIntoBuffer:buffer] == 0)
{
if (repeat)
{
// End Of File reached, so rewind and refill the buffer using the beginning of the file instead
packetIndex = 0;
[self readPacketsIntoBuffer:buffer];
}
else
{
// set it to stop, but let it play to the end, where the property listener will pick up that it actually finished
AudioQueueStop(queue, NO);
trackEnded = YES;
}
}
}
- (void)postTrackFinishedPlayingNotification:(id)object
{
// if we're here then we're in the main thread as specified by the callback, so now we can post notification that
// the track is done without the notification observer(s) having to worry about thread safety and autorelease pools
[[NSNotificationCenter defaultCenter] postNotificationName:GBMusicTrackFinishedPlayingNotification object:self];
}
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer
{
UInt32 numBytes, numPackets;
// read packets into buffer from file
numPackets = numPacketsToRead;
AudioFileReadPackets(audioFile, NO, &numBytes, packetDescs, packetIndex, &numPackets, buffer->mAudioData);
if (numPackets > 0)
{
// - End Of File has not been reached yet since we read some packets, so enqueue the buffer we just read into
// the audio queue, to be played next
// - (packetDescs ? numPackets : 0) means that if there are packet descriptions (which are used only for Variable
// BitRate data (VBR)) we'll have to send one for each packet, otherwise zero
buffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer(queue, buffer, (packetDescs ? numPackets : 0), packetDescs);
// move ahead to be ready for next time we need to read from the file
packetIndex += numPackets;
}
return numPackets;
}
@end
Hi AnotherJake.
What a delicious code for Mac OSX!!!!!
I just tried it on my portable Mac OSX device... works like a charme...
Questions I may have:
Would it be difficult for you to manage a "song" buffer?
How would you manage a "resume" method? Let me explain.
I'd like to read web hosted mp3, and I could have connection prob. therefore, while checking the connection, if it is ok, I could resume the track from its last known position.
Many thanks for answering.
Pat
Thanks so much for this code.
Would it work to play an audio file (mp3) located on a web server?
If not, would it be possible for you to explain what modification would be necessary?
Thank you
PM
Would it work to play an audio file (mp3) located on a web server?
If not, would it be possible for you to explain what modification would be necessary?
Thank you
PM
Thanks pm7_7! 
As far as streaming from a web server I have not even the slightest clue how that could be done. My guess is that you would have to probably use lower level stuff in Core Audio to intercept the decoded stream on the fly.
For the "resume" thing, I haven't tried it, but Bence's code (just a few posts above) might work for that. He added some handy seeking stuff.

As far as streaming from a web server I have not even the slightest clue how that could be done. My guess is that you would have to probably use lower level stuff in Core Audio to intercept the decoded stream on the fly.
For the "resume" thing, I haven't tried it, but Bence's code (just a few posts above) might work for that. He added some handy seeking stuff.
works great, thanks for the code for Mac OSX
I'll be looking forward more excellent code for Mac OSX from you =)
I'll be looking forward more excellent code for Mac OSX from you =)
Thanks ishaq!
I finaly got it to work with one mp3
works great. But anyone know how would i make this file work with 2 mp3s.. to switch between them? I tried [song close] but it didnt work ...before playing the second song
Please help.
works great. But anyone know how would i make this file work with 2 mp3s.. to switch between them? I tried [song close] but it didnt work ...before playing the second songPlease help.
Simply calling the close method is not enough, in addition to close you need to actually release it before playing the next one.
If that still doesn't work then try a different mp3 or maybe making the buffer size a little smaller. There have been reports of some tracks mysteriously causing problems.
If that still doesn't work then try a different mp3 or maybe making the buffer size a little smaller. There have been reports of some tracks mysteriously causing problems.
Hey thanks, i got it to work. .But i am having issue with looping mp3s. i tried m4a too and i still hear a GAP between the loops.. if i use aif or wav its perfect but because of the sizes i have .. i cant use the uncompressed format
anyone got any suggestions?
anyone got any suggestions?
Yes, there is a gap between loops of compressed audio, and there doesn't appear to be any way around it (that I know of). What I and others have been doing is fading songs out at the ends. If someone comes up with a better workaround for this, I'd definitely like to hear from you!
The only two ideas I can suggest is to uncompress the sound into memory on load and use OpenAL instead, or perhaps uncompress the sound to disk once it's reached the customer and load it as a WAV.
The only two ideas I can suggest is to uncompress the sound into memory on load and use OpenAL instead, or perhaps uncompress the sound to disk once it's reached the customer and load it as a WAV.
I did notice that when i encoded the mp3s they had a slight silence in the beginning and end of the file. but when i removed those it still didn't loop perfectly. the m4a format zoomed in didnt have those either.
there must be a way to fix it.. anyone tried the BenceGBMusicTrack to loop mp3s? i havent tried that version .. could that fix it?
there must be a way to fix it.. anyone tried the BenceGBMusicTrack to loop mp3s? i havent tried that version .. could that fix it?
Possibly Related Threads...
| Thread: | Author | Replies: | Views: | Last Post | |
| Core Audio mutlichannel audio | tachyon | 2 | 5,376 |
Mar 18, 2005 01:04 AM Last Post: tachyon |
|

