The Great Porting Adventure: Day 6

Day 5 ended with me realizing that an XACT-based audio system was probably going to be a bit of a bust using MonoGame. Sure, I had gotten the very basic “play a sound” functionality to work, but all the features I make use of in XACT are not likely to show up in the near future.

Speaking of which, I was asked why I use XACT at all. My main reason for trying it was its handling of music. XACT gives you nice crossfade options, as well as Category support (which in turn grants you Custom Soundtrack support on Xbox for free). It also gives you a nice layer of abstraction between sounds and the waves they use, meaning I can tweak the underlying sound of the game without changing anything in code. These sounds can also have nifty features like distance attenuation, instance limiting and automatic volume/pitch variance for giving your audio a bit of randomness. All of this is managed in reasonable functional application that allows me to keep my audio content separate from the game. To add new sounds (cues), I just update the XACT project and then call the corresponding cue from within code. I don’t need to update my game’s content project or loading, since it’s all handled through the “.xap” asset (which builds the associated wavebanks and soundbanks).

So XACT is great and now I can’t use it. But you can’t use it on Windows Phone either, so really it’s just Windows and Xbox. Maybe moving away from XACT is a prudent move, especially if I can get the same functionality out of SoundEffect.

It was time for a musical interlude!

Disclaimer: This is basically unnecessary over-engineering that I did for the heck of it. Don’t think for a second that this is required for porting to MonoGame, though it is part of my approach to porting.

I already added a layer of indirection to my audio system, so now it looked a little like this:

Pseudo-UML. Thanks Word art!

Coming up with an equivalent SoundEffect-based implementation was relatively straightforward, given that the features I use in XACT aren’t that advanced. I had to do a bit of work to keep track of instances (in categories) as well as music cross-fading, but it was all pretty simple to do. And my game didn’t need to change at all (other than to say whether it wanted to use XACT or SoundEffect).

The big wrinkle was how I was going to tell my game what sounds to load and what sort of features they would have (Category, volume/pitch variance, cue names rather than file names). After all, I want to just be able to play a sound by its cue name from the game, regardless of what the audio implementation is underneath.

One of the overarching goals of this whole project is to avoid maintaining different versions of anything just for the sake of porting. “Write once, play everywhere” right? If I need to maintain a separate list of sound effects to load (or have to add them to my project manually), it’s going to get out of sync quickly and be a huge headache.

But I already have a description of all the waves, cues, categories and events: the .xap file.

Here’s a peek at a small part of the .xap file for DLC Quest:

    Name = Music;
    Public = 1;
    Background Music = 1;
    Volume = -260;

    Category Entry
        Name = Global;

    Instance Limit
        Max Instances = 1;
        Behavior = 2;

           Fade In = 2000;
           Fade Out = 2000;
           Crossfade Type = 1;

Aha! A plain-text description of all the meta-data I’d need to play my sounds! Not in an easily paresable format though. It looked simple enough that I was going to try writing a quick and dirty parser. Then I found XapParse. From the Codeplex page:

A C#-based XACT project (XAP) file parser, intended for use as part of the custom content pipeline in XNA.

Hot dog, just what I needed! Here was the plan:

Add a new SoundEffectMetaData class to my audio system. Whenever a cue needs to be played by the SoundEffect-based system, it looks up the associated meta data and uses that to play the appropriate wave with the correct category/volume variance/pitch variance, etc.

Create a XAP Pipeline Extension project to:

  1. Read in the .xap file.
  2. Parse the .xap file into objects using the xapparse project.
  3. Construct a list of SoundEffectMetaData objects from the in-memory representation of the XAP project in Step 2.
  4. Write out the list of meta data to an .xnb file (automatic .xnb serialization is awesome – no work here!)
  5. Also, call context.BuildAsset to find and build the corresponding .wav file.

Step 5 is important, because it means that I can just build the .xap file and it will build all the required wave assets without me needing to add them to my content project.

Lastly, in my game itself:

  1. Reference the XAP Pipeline Extension in the Content project.
  2. Switch the Importer/Processor on the .xap file to use my new extensions and build.
  3. Copy the built list of metadata and all the built sounds to the Mac project.
  4. Switch the Importer/Processor back to the default XACT project to continue using it with the Windows/Xbox builds.

Step 4 here is simply because all content builds are performed on Windows and I need to build things a different way for Mac. I still need to come up with a nicer way of syncing content to my Mac build, but for now I can just switch the processor and I get everything I need for the SoundEffect-based implementation.

So now I can continue to manage all my audio inside XACT but still end up with the metadata I need for using SoundEffect when I choose. No duplicating effort, no managing two lists of sounds and their properties. Pretty slick, if a bit convoluted. But that’s why we program, right? 🙂

This is running a bit long, so I’ll take a quick look at what remains to be done:

  • Handle music properly. Right now it’s just a normal .wav SoundEffect, so the filesize is huge. Should probably be using the Song API.
  • Fix multiple sound effect instances (currently bugged in MonoGame, but there’s a promising pull request for OpenAL support that I’ll try out)
  • Fix vsync (still no idea where to start)
  • Investigate key binds
  • Investigate controller support
  • Bring back my loading screen
  • Supported resolutions list
  • Alt-tabbing? Minimizing? Losing focus? Etc.
  • App bundling
  • Test test test

Not going to finish within 7 days, but the game is looking pretty good right now.

Running total: 6 days, $112.87


About Ben Kane
I'm a game developer with a heart of gold.

3 Responses to The Great Porting Adventure: Day 6

  1. Awesome work man! Keep it up. I’m not too far behind you now 😀

  2. Dennis says:

    One major problem I encountered with the Song API when porting my game to WP7 was that I could not get the music to loop seamlessly. The tiny hiccup that occurs everytime the song loops makes it sound incredibly sloppy. I wonder if you are going to run into that problem too.

  3. Oh, man… I wrote that XapParse code waaaaaaaaaay back when. I too have discovered MonoGame and that reminded me of XapParse and I took one look at it, and, WOW, that’s some seriously bad C#. 🙂 I was a C++ programmer at the time… and you can tell! I’d write it completely differently now.

    Still, it’s great to hear that people are getting use out of it!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: