Module Music on iPhone/iPad with ChibiXM 

Module music was once a staple in video game development but has since been mostly replaced by streamed audio formats like MP3 and OGG.  The reason is straightforward – streamed audio is production environment agnostic whereas module music requires specific production tools and constraints.  However, one of the interesting things about creating games for limited profiles like iOS devices is that many game development practises in use ten to fifteen years ago become relevant again.  There are some big advantages to using mod music for an iOS game.

module music ios milky Module Music on iPhone/iPad with ChibiXM

Milky Tracker is software used to make module music

In a module file each instrument is stored as a single sample while the music is stored as sequenced data. You can think of it as a piano roll, where each notch plays back one of the samples at a specified frequency. Mod’s have many advantages over MP3 audio for games on iOS:

  • File size does not scale directly with the play time of the music.  Each instrument in the song is stored once as a sample and that data can be reused over and over as the note sequence is processed.
  • Seamless looping and loop points.  Many songs benefit from a loop back to just after the intro or  infinitely looping an ominous ending phrase.
  • More control over playback.  If the timer is low, you can increase the speed of playback without affecting pitch to create an organic frenzied feeling.  If Mario gets on a Yoshi you can dynamically mute/un-mute an extra drum channel.
  • Fine grained optimisation.  With an MP3 you have to deal with the quality of the entire audio clip as a whole when adjusting for quality.  With a module file you can decrease or increase the fidelity of specific samples to get the best quality for your memory and size constraints.

Unfortunately, many existing and popular solutions for playback on iOS come with licensing fees, but there are alternatives that will do the job. Chibi XMPlay, a lightweight module playback library created by Juan Linietsky and myself, is one of them.

It’s used for playing a type of module file known as XM (eXtended Module) on limited platforms like the iOS or Nintendo DS. Best of all it’s completely open source, licensed under new BSD, and free to use for both commercial and non-commercial applications.

Using ChibiXM Play

Chibi XMPlay was written with portability in mind.  As a client it’s your responsibility to populate memory, file i/o, and mixer function tables for the OS you’re using.  Lucky for you, I’ve done this work already for Trudy’s Mechanicals.  I’ve also added functionality to the wrapper to play sound effects which is possibly useful as a low latency alternative to Apple’s numerous methods.  To use ChibiXM do the following:

  1. Make sure you have the “AudioToolbox” framework imported
  2. Download the ChibiXM source w/Objective C wrapper and include it
  3. The following code will play an XM file (provided ‘song.xm’ is part of your project):
XMFile* song = [XMFile songWithName:@"song.xm"];
[[ChibiXM sharedInstance] play:song]; // Starts playback and retains the XMFile

player.song = nil; // Releases the XMFile
...
XMSample* sample = [[XMSample alloc] initWithName:@"cacodemon.wav"];
[[ChibiXM sharedInstance] playSample:sample onChannel:0 withVolume:1.0 andPanning:0.0 andPitch:1.0];
....
[sample release];

If you just want to see ChibiXM in action here’s a complete XCode project you can compile and deploy that shows some graphics and let’s you switch between some songs with a touch.  Note that the project was created in XCode version 3.13 of the SDK.  To compile under the latest just go to Project Info and change Base SDK to 4.x

collage Module Music on iPhone/iPad with ChibiXM

Just a few games that used module based music solutions! Ah, memories.

Where To Go From Here?

The wrapper is a simple way to play module music and sound effects, but I encourage you to look further into ChibiXM’s capabilities.  It has functionality to share a pack of samples among many songs and other goodies that allow easy programmability and control over music playback and mixing.  It’s also pretty easy to extend if you wish to add DSP effects to playback.

Here are some links to help you explore this approach to putting music into your game:

Chibi XmPlay – Berlios project page for Chibi XmPlay
Milky Tracker – Multi-platform tracking software that can create XM modules
ModArchive – Massive repository of tracked music
IndieGameMusic.com – Website designed to connect game developers to game musicians

Follow me on twitter! @infey

** UPDATE **
Dave Rempel points out that you may run into an issue if you load many songs into memory at once using xm_song_alloc() as the ChibiXM built-in software mixer (which the iOS code uses) has a statically sized pool of memory for storing samples for all songs currently loaded. The easiest way to avoid running into the issue is to only load the songs into memory you absolutely need for playback. However, if you wish to change the size of this buffer based on the requirements of your app you can modify the size of this buffer in xmplay.c, line 113:

/* change this limit as you see fit */
#define _XM_SW_MAX_SAMPLES 256
...
Posted by Dave Churchill
@infey, programmer at Incubator Games
Playing MOD/XM/S3M Sound through iPhone - iPhone Dev SDK Forum Says:

[...] Originally Posted by fulvio Hello all, I was wondering whether there was any way of playing XM/MOD/S3M music files through the iPhone. I am developing a game where I don't want to have to convert existing XM music files to WAV (since they end up being over 20mb). XM/MOD files are so small and there are so many that can be used for free. Is there some sort of library/framework that I can use to play these sounds? Thanks, Fulvio You can use ChibiXM .. it's open source and free of restrictive licensing. There's a library here that lets you load and play xm files in two lines of objective c … Module Music on iPhone/iPad with ChibiXM | Incubator Games [...]

Galen Wollenberg Says:

Hi, your code example is awesome. However I cannot seem to expand the file list array to more than 32 .xm files. The app freezes then loops back to the first file in the list. Is there a way to add more than 32 files? thx

Dave Says:

Hey Galen!

It’s hard to diagnose without looking at code but I can take a guess.

Shouldn’t be anything stopping more than 32 files being added to the NSMutableArray other than the memory of the device. In the code example I provided the array was initialised this way:


[m_file_list addObject:[XMFile songWithName:@"Forsaken.xm"]];
[m_file_list addObject:[XMFile songWithName:@"i_realize.xm"]];
[m_file_list addObject:[XMFile songWithName:@"loner's lie.xm"]];

Each element is created using the autorelease constructor of the XMFile class – which allocates memory for all the samples and the sequencer data in the module file. This allows really quick switching between songs in the demo because all the files are already loaded. However, if you’re creating an array of 32 XMFile objects you will have all 32 XMFile’s in memory at one time which may be leading to memory issues on the device.

In this case it’d be better to keep an array of filenames (NSString) instead and load and unload XMFile objects as required. ie; Only have the song you currently require loaded into memory.

If you are running out of memory (or another error occured) the autorelease constructor songWithName: will return nil which should raise an NSInvalidArgumentException with NSMutableArray’s addObject method.

I hope that helps. If it doesn’t, feel free to upload a sample of your code and sending the link to me through the ‘About’ page Contact section and I’ll be happy to take a closer look.

Thanks!

Dr. Bit Says:

Any possibility of you releasing a version of your template that will work with a native OS x app? I tried to manipulate your source in Xcode, although I’m not that familiar with porting apps from iOS to Mac OS X — If you could I would be very grateful.

david Says:

What are the intervalls used for sound sample volume pitch and panning (and channels)

Dave Says:

Dr. Bit:

Got it working in MacOSX pretty easy, mainly just a one line change ChibiXM.m’s init.

#if TARGET_OS_IPHONE
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif

I also commented out the call to AudioUnitSetProperty right after it. It seems unnecessary and throws an error (although playback still works with the error being thrown)

Dave Says:

Thanks for the answer, Dave.
I’m glad it’s easy to get up and running on MacOSX as well.

Dr. Bit Says:

Hi Dave,

Thanks for the tips. I’m not sure where/how to add this to ChibiXM.m (I think that it’s supposed to go here?):

// Describe audio component

I can build your project just fine and run on the iOS simulator, although when I try and build it for Cocoa, UIKit seems to break everything, and the build fails. How exactly did you get it to run on OSX? If you could post an XCode template for it that would be incredibly awesome of you. Thanks very much for answering my questions; I had all but given up hope for including .xm/mod music in my app.

Dave Says:

Yes, you make the change there at the describe audio component. As for UI Kit, Chibi XM doesn’t have any dependencies on UIKit, just Core Audio, so I’m assuming that you are trying to run the sample IOS app that shows a visualizer. That will not work on Mac OSX since UIKit doesn’t exist there, AppKit is the MacOSX equivalent and they are similar but not the same thing.

What you want to do is create a Mac App project from Xcode’s wizard, copy xmplay.(c/h), ChibiXM.(m/h), XMFile.(m/h) and XMSample.(m/h). Make the change I suggested in ChibiXM.m, and add AudioUnit.framework to your link stage, and write some simple code to instantiate a XMFile and play it through the ChibiXM singleton. Should be pretty straight forward.

For bonus points you can fix the warnings that it will spew (at least with Xcode 4.2 and Lion), the xmplay.h doesn’t conform to the latest C standard so when xmplay.c is compiled any functions that have no parameters don’t get prototyped properly. Fix to bring it up to the modern C standard is to change the header to stick void in each declaration.

eg:
XM_AudioLock * xm_get_audio_lock()
becomes
XM_AudioLock * xm_get_audio_lock(void);

One more thing is that you unless you compile for 32bit (latest Xcode’s and Mac OSX defaults to 64bit) you’ll probably have to change the integer types in xmplay.h

I changed this code:
typedef unsigned char xm_u8;
typedef unsigned short xm_u16;
typedef unsigned int xm_u32;

typedef signed char xm_s8;
typedef signed short xm_s16;
typedef signed int xm_s32;

/* override with wathever makes your compiler happy */
#ifndef xm_s64
typedef long long xm_s64;
#endif

#endif

typedef xm_u8 xm_bool;

to………………….:

#include

typedef uint8_t xm_u8;
typedef uint16_t xm_u16;
typedef uint32_t xm_u32;

typedef int8_t xm_s8;
typedef int16_t xm_s16;
typedef int32_t xm_s32;

typedef int64_t xm_s64;

typedef xm_u8 xm_bool;

Using these typedefs keep it correct for 64bit (OSX) and 32bit (OSX/IOS) platforms

Dave Says:

Looks like my #include directive got eaten by the form when I posted. the include you need is inttypes.h in case you didn’t know where those where declared.

Dr. Bit Says:

Woohoo! It’s working now, and I’m very happy about it. Your advice was incredibly helpful and insightful. Now I just need to figure out all the other good stuff to go with it. ;-) Thanks once again, I truly appreciated the help, you’re a gentlemen and a scholar.

Dr. Bit Says:

** The only problem now is that if I take focus away from the app the music stops, then upon returning to the app the song starts over. Ideally the music would keep playing, or if it stops it would be better to have it resume from where it left off.

the dude Says:

how does one improve the quality of output? i know it’s meant for a streaming environment, but the quality of anything i try is downgraded and sounds bad.

Dave Says:

In ChibiXM.m there’s a SAMPLE_RATE define at line 6 or so. Increasing that should make the quality better (I’ve only tested this on my OSX build at this point, haven’t tried IOS yet). 44100 instead of 11025 shows a very noticeable increase in sound quality. There’s likely a cost to the software mixer in terms of processing speed however, something to keep in mind.

Tommy Says:

Great work!

I was just wondering if there is a simple way of setting the song volume to something and then the sound effect volume to something else?

It seems I can set the overall volume, but then the sound effect volume is also affected…

…but again, I’m awestruck how easy it was to get my old Amiga mod song from 1988 to play on my iPod! – I just used Milky Tracker to convert it to xm. Thanks a million!

Tommy Says:

Never mind,

I found that the _XM_PlayerData struct has a global_volume that is only used by songs, not sound effects. I exposed changing it from xmplay.c / xmlplay.h.

void xm_player_set_vol(xm_u8 vol)
{
if (!_xm_player) {
_XM_ERROR_PRINTF(“NO PLAYER CONFIGURED”);
return;
}

_XM_AUDIO_LOCK
_xm_player->global_volume = vol;
_XM_AUDIO_UNLOCK
}

How we built a Super Nintendo out of a wireless keyboard @Sifteo #Sifteo « adafruit industries blog Says:

[...] were existing tracker playback engines that almost fit the bill. ChibiXM, a tracker designed for iOS games, was close. But it turned out that our tracker needed to be [...]

zumph Says:

Great stuff!!

I wonder if there´s a way to change the pitch of a sample without it going faster? Like in audacity you can change the pitch but the sample takes the same time to play.

Huse Says:

How can we achieve this with ChibiXM???
I mean how to raise the pitch or speed of an xm in realtime… or mute a single channel… or play specific pattern?

“More control over playback. If the timer is low, you can increase the speed of playback without affecting pitch to create an organic frenzied feeling. If Mario gets on a Yoshi you can dynamically mute/un-mute an extra drum channel.”

Comment: