View Full Version : Music formats that loop without gaps?
MattDiamond
2004.10.24, 12:21 AM
It's been pointed out in these forums that the MP3 format introduces a slight gap in playback at the end of the track. This bit me last year with my game Asteroid Rally. Pity, because MP3 files sound great and are so much smaller than AIFF files.
This year I started with AIFF files and the music looping is seamless. But now my game is larger than contest rules permit. I could simply remove one of my tracks (I have three different tunes in the game just for variety.) Or I could downsample the tracks and hope it doesn't sound too crappy. But I was wondering if there was a format that Quicktime understands that is both compressed and does not introduce a gap at the end of the track. For example, does AAC have the same problem as MP3?
Thanks!
skyhawk
2004.10.24, 12:52 AM
For example, does AAC have the same problem as MP3?
Thanks!
not that I am aware of, I have noticed no problems with gaps between songs (though technically they are both the same song. I might have to listen more closely, but if you have a copy, check out Microbian: Fighter (with music). It has seamless music.
igame3d
2004.10.24, 02:29 AM
On technique I have been considering is fade the song on one channel at the end of the track, and fade in on another channel , repeat as necessary.
AnotherJake
2004.10.24, 02:30 AM
This year I started with AIFF files and the music looping is seamless.
Using Quicktime for playback?
aarku
2004.10.24, 05:32 AM
It's not the format the sounds are stored in, but the manner in which you play them. .. I get seamless looping with the Sound Manager.
-Jon
MattDiamond
2004.10.24, 01:58 PM
Using Quicktime for playback?
Yes. (Specifically, Kelvin's Cocoa wrapper from his CocoaBlitz framework.)
It's not the format the sounds are stored in, but the manner in which you play them. .. I get seamless looping with the Sound Manager.
With mp3's? If so, that's interesting. I was told that MP3's quantize the time slices to something like 1/40th of a second. This leads to a discernable break unless (a) you r playback code works around it, (b) your music loop is in a style that makes breaks hard to detect (i.e. no sustained note where the loop occurs) or (c) the end of your track happens to align with the time slices. (People are saying that this is why iPod's don't play seamlessly between tracks.)
AnotherJake
2004.10.24, 03:46 PM
Yeah, okay, that's what I thought you meant. Just wanted to be sure. I thought Quicktime introduced a gap with any format.
At any rate, the format doesn't cause the gaps, Quicktime does. You need to uncompress your mp3 or whatever into memory and then loop it through OpenAL or CoreAudio or the Sound Manager or something else besides Quicktime to avoid those gaps. I guess you could still use Quicktime after it's been decompressed if it indeed does not gap with raw audio.
aarku
2004.10.24, 04:59 PM
With mp3's? If so, that's interesting. I was told that MP3's quantize the time slices to something like 1/40th of a second. This leads to a discernable break unless (a) you r playback code works around it, (b) your music loop is in a style that makes breaks hard to detect (i.e. no sustained note where the loop occurs) or (c) the end of your track happens to align with the time slices. (People are saying that this is why iPod's don't play seamlessly between tracks.)
Humph you're right! I made a little test sound, and the only one it loops absolutely flawlessly is with aiff. Both mp3 and m4a have a little jump. Crummy! Must not have noticed it before. I'll have to write something to look at the last 1/30th of the decompressed audio, and delete it the ending if there is no sound or something.
-Jon
FCCovett
2004.10.25, 11:59 AM
You could use FLAC, which is about 50% smaller than AIFF and lossless, but it's a bit of pain to implement. I stopped at 80% done. Gotta try again.
_Kevlar
2004.10.25, 12:48 PM
Is this the problem?
http://developer.apple.com/qa/qa2004/qa1371.html
MattDiamond
2004.10.25, 02:34 PM
Looks interesting. I don't think I'll get a chance to try it before the contest deadline, but it's probably worth investigating Apple's extremely kludgey workaround.
The reason I think it might not be the same issue is that I believe the CocoaBlitz code I'm using uses an NSTimer to decide when to start playing the track again. It's not using Quicktime's native looping. And I believe people do that specifically because they get better playback. So I'm not sure this trick applies.
ThemsAllTook
2004.10.25, 02:45 PM
I was able to get around this problem in Water Tower by decompressing the MP3 file into memory, and playing it with the Sound Manager. If you have a lot of music, this will eat up a significant portion of memory, but if it's only a couple of minutes or so, you could probably get away with it.
Sorry for the ugly code:
static SndListHandle music = NULL;
static SndChannelPtr musicChannel = NULL;
static Boolean musicAvailable = 0, musicPlaying = 0, musicSuspended = 0;
void initWaterTowerMusic() {
OSErr error;
FSSpec file;
short refNum;
Track track;
Movie movie;
FSRef bundleFSRef;
FSSpec appFSSpec;
musicAvailable = 0;
musicChannel = (SndChannelPtr) NewPtr(sizeof(SndChannel));
musicChannel->qLength = 6;
SndNewChannel(&musicChannel, sampledSynth, initMono, NULL);
error = EnterMovies();
if (error == noErr) {
CFURLGetFSRef(CFBundleCopyBundleURL(CFBundleGetMai nBundle()), &bundleFSRef);
FSGetCatalogInfo(&bundleFSRef, kFSCatInfoNone, NULL, NULL, &appFSSpec, NULL);
error = FSMakeFSSpec(appFSSpec.vRefNum, appFSSpec.parID, "\p:Music.mp3", &file);
if (error == noErr) error = OpenMovieFile(&file, &refNum, fsRdPerm);
if (error == noErr) {
error = NewMovieFromFile(&movie, refNum, NULL, NULL, newMovieActive, NULL);
if (error == noErr) track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic, (movieTrackCharacteristic | movieTrackEnabledOnly));
if (track != NULL) {
music = (SndListHandle) NewHandle(0);
if (music != NULL) {
error = PutMovieIntoTypedHandle(movie, track, soundListRsrc, (Handle) music, 0, GetTrackDuration(track), 0, NULL);
if (error == noErr) {
musicAvailable = 1;
} else {
DisposeHandle((Handle) music);
}
}
DisposeMovieTrack(track);
}
CloseMovieFile(refNum);
}
DisposeMovie(movie);
ExitMovies();
}
}
void runWaterTowerMusic() {
SCStatus status;
if (!musicPlaying) return;
SndChannelStatus(musicChannel, sizeof(SCStatus), &status);
if (!status.scChannelBusy) startBGMusic(1);
}
void shutDownWaterTowerMusic() {
stopBGMusic();
if (musicChannel != NULL) {
SndCommand cmd;
cmd.cmd = flushCmd;
cmd.param1 = 0;
cmd.param2 = 0;
SndDoImmediate(musicChannel, &cmd);
cmd.cmd = quietCmd;
SndDoImmediate(musicChannel, &cmd);
SndDisposeChannel(musicChannel, NULL);
DisposePtr((Ptr) musicChannel);
musicChannel = NULL;
}
if (music != NULL) {
DisposeHandle((Handle) music);
music = NULL;
}
}
void suspendWaterTowerMusic() {
if (musicPlaying) {
stopBGMusic();
musicSuspended = 1;
}
}
void resumeWaterTowerMusic() {
if (musicSuspended) {
startBGMusic(0);
musicSuspended = 0;
}
}
void startBGMusic(Boolean fromBeginning) {
SndCommand cmd;
long offset;
SCStatus status;
if (!musicAvailable || musicChannel == NULL) return;
SndChannelStatus(musicChannel, sizeof(SCStatus), &status);
if (fromBeginning || !status.scChannelBusy) {
cmd.cmd = flushCmd;
cmd.param1 = 0;
cmd.param2 = 0;
SndDoImmediate(musicChannel, &cmd);
cmd.cmd = quietCmd;
SndDoImmediate(musicChannel, &cmd);
cmd.cmd = rateMultiplierCmd;
cmd.param1 = 0;
cmd.param2 = 0x00010000;
SndDoImmediate(musicChannel, &cmd);
GetSoundHeaderOffset(music, &offset);
cmd.cmd = bufferCmd;
cmd.param1 = 0;
cmd.param2 = (long) ((Ptr) *music + offset);
SndDoCommand(musicChannel, &cmd, 0);
} else {
cmd.cmd = rateMultiplierCmd;
cmd.param1 = 0;
cmd.param2 = 0x00010000;
SndDoImmediate(musicChannel, &cmd);
}
musicPlaying = 1;
}
void stopBGMusic() {
SndCommand cmd;
if (!musicAvailable) return;
if (musicPlaying && musicChannel != NULL) {
cmd.cmd = rateMultiplierCmd;
cmd.param1 = 0;
cmd.param2 = 0x00000000;
SndDoImmediate(musicChannel, &cmd);
musicPlaying = 0;
}
}
Alex Diener
MattDiamond
2004.10.25, 08:03 PM
Thanks for the code. I'm fond of the nifty CocoaBlitz wrapper, but I'm also fond of having my game be smaller and download-friendly. If I have time I'll revisit my looping code. If you see your name in my credits then you'll know I did, and that it worked. :-)
kelvin
2004.10.26, 02:21 AM
The CBMovie class in CocoaBlitz can be used by itself without the entire framework. Just import CBMovie.h/m into your project and change #import <CBMovie.h/CocoaBlitz> to #import "CBMovie.h"
MattDiamond
2004.10.26, 08:12 AM
The CBMovie class in CocoaBlitz can be used by itself without the entire framework.
Yep, that's what I'm using.
Here's my little testimonial about CBMusic: to anyone who is already using Obj-C on their project, this class is a very painless way to add music to your project. It drops in easily, and the methods are very simple to use. I'm grateful to kelvin for lending it to me even before he'd Open Sourced CocoaBlitz.
The only downside is this gap-looping issue, apparently inherited from Quicktime. (Worth pointing out that I didn't get a single complaint about the "seams" in my loops last year, they seemed to bother me more than anyone else.) Since my game for uDG this year is ever-so-slightly above the size limit I have to take action on this, unfortunately. I'm going to have to switch from AIFF to MP3, and if I can hear gaps in these particular tracks (by no means certain due to their style) then I will have to replace CBMusic. I may try downsampling the tracks first.
Don'tcha just love last minute issues like this? :-)
aarku
2004.10.26, 11:01 AM
Well, I'm using the SpriteWorld Sounds utility for my sound/music both. It works quite well take a listen to my uDG:
http://www.tc.umn.edu/~czec0035/files/BugThug%20build%202004102601.app.tar.gz
SpriteWorld of course is free source to look at. I'll mention again that SWSounds.c can trivially be taken out and used separately. My in-game music file needs to be looped a tad better, but the jump isn't too bad at all. I _think_ it's a lot less than other methods, especially QuickTime.
-Jon
vBulletin® v3.6.8, Copyright ©2000-2008, Jelsoft Enterprises Ltd.