I'm working on a "Game Maker"-type of application for Windows where the user imports his own assets to be used in the game. I need to be able to load this content at runtime on the engine side.

However I don't want the user to have to install anything more than the XNA runtime, so calling the content pipeline at runtime is out.

For images I'm doing fine using Texture2D.FromStream.

I've also noticed that XNA 4.0 added a FromStream method to the SoundEffect class but it only accepts PCM wave files.

I'd like to support more than wave files though, at least MP3.

Any recommendations? Perhaps some C# library that would do the decoding to PCM wave format.

• It's worth noting that the XNA runtime itself has no way to decode MP3s for sound effects. XNA Game Studio handles the conversion to PCM (uncompressed) or a compressed format (not MP3, but I forget exactly what the format is right now) at content build time. (Music, on the other hand, gets handed off to Media Player, which can play MP3s.) – Andrew Russell Dec 9 '11 at 2:12

I hadn't noticed the FromStream method, that's a good one to remember. I'm now curious about using FromStream vs DynamicSoundEffectInstance, since you could theoretically accomplish a lot of the same work with both methods.

Google turned up this library: http://robburke.net/mle/mp3sharp/ And you could also conceivably call lame.exe from within your app to decode the sound.

I'd be wary of MP3 though, it's a legal can of worms. Adobe pays a hefty fee to Fraunhofer for the right to use it in Flash. Ogg Vorbis is a superior format, easily converted from mp3 using free and open-source software, and there seem to be many more C#/.Net decoder libraries for it.

• Thanks! I was vaguely aware of the legal implications of using MP3 but that cleared it up for me. I'll give those libraries a try to see if they can really decode from MP3 into OGG (which will be done when the user imports the assets) and then from OGG to PCM (at runtime/load time). I'll report back with my results. – David Gouveia Dec 8 '11 at 18:29
• The ogg libraries won't be able to, but you could decode with lame then convert to Ogg Vorbis. Just don't include LAME and do as Audacity does, "You need an mp3 decoder, we don't care where you get it, we may know a place to get it, but it's your responsibility to acquire it". And disclaimer: I am not a lawyer. – michael.bartnett Dec 8 '11 at 18:39
• I've just noticed that SoundEffect.FromStream expects a PCM wave file (incluiding headers) and a very specific format. I've decoded the OGG file using one of those libraries above but I just can't seem to get FromStream to accept it without throwing an exception. I'm starting to wonder whether I should use an external Sound API such as FMOD instead. – David Gouveia Dec 8 '11 at 19:15
• That might be worth it if you don't care about targeting Windows Phone or XBLIG. You could also give DynamicSoundEffectInstance a try, it just throws up an event asking for samples every time it runs low. – michael.bartnett Dec 8 '11 at 19:46
• I've actually used DynamicSoundEffectInstance to make some other stuff before (such as this and this). :) But it also requires you to submit your samples in a very specific format (interleaved 16-bit samples), which I don't know if that's the format of my decoded OGG stream. Since I only need Windows support I went the FMOD route. I'll post my solution using FMOD in a minute. – David Gouveia Dec 8 '11 at 20:14

I decided to give this problem another try today and finally managed to load an OGG file at runtime into a SoundEffect object. Here's what I did! First download the library below which contains a class capable of decoding OGG files:

The library already has an example, but it uses DynamicSoundEffectInstance and streams the audio. But I wanted to load it all at once into a regular SoundEffect object so the process was a bit different.

Step 1 - Decode file

First create an instance of OggDecoder and initialize it with your file:

decoder = new OggDecoder();
decoder.Initialize(TitleContainer.OpenStream(@"sound.ogg"));


Step 2 - Get decoded data

Read all of the data into a buffer. This is the decoded raw PCM data of the file:

byte[] data = decoder.SelectMany(chunk => chunk.Bytes.Take(chunk.Length)).ToArray();


Step 3 - Create SoundEffect from stream containing complete wave file header

However, the stream that SoundEffect requires must contain not only the raw data, but also the complete wave file header. You can use this helper method to write the header plus the data:

private static void WriteWave(BinaryWriter writer, int channels, int rate, byte[] data)
{
writer.Write(new char[4] { 'R', 'I', 'F', 'F' });
writer.Write((int)(36 + data.Length));
writer.Write(new char[4] { 'W', 'A', 'V', 'E' });

writer.Write(new char[4] { 'f', 'm', 't', ' ' });
writer.Write((int)16);
writer.Write((short)1);
writer.Write((short)channels);
writer.Write((int)rate);
writer.Write((int)(rate * ((16 * channels) / 8)));
writer.Write((short)((16 * channels) / 8));
writer.Write((short)16);

writer.Write(new char[4] { 'd', 'a', 't', 'a' });
writer.Write((int)data.Length);
writer.Write(data);
}


Use that method to write the data to a stream that can be fed to SoundEffect.FromStream:

using (MemoryStream stream = new MemoryStream())
using(BinaryWriter writer = new BinaryWriter(stream))
{
WriteWave(writer, decoder.Stereo ? 2 : 1, decoder.SampleRate, data);
stream.Position = 0;
soundEffect = SoundEffect.FromStream(stream);
}


Step 4 - Use the SoundEffect as normal

Your OGG file is now loaded and can be used like any other SoundEffect loaded through the content pipeline:

soundEffect.Play();

• Perfect! The example of the site don't work correctly. I tried one song with 3:30 min and it don't play the entire file. But your solution worked perfectly! It's slower to load than the orinal example, but it can be tolerated. – Emir Lima Dec 29 '14 at 0:44
• The oggsharp link says that it's deprecated and "use NVorbis" instead. Has anyone tried that? Also, it would be good if you explicitly listed the "usings" (references) needed to make it work. – DrZ214 Jun 19 '15 at 5:13
• @EmirLima can you confirm if you used the same version of oggsharp as in the link? Or if it was the new NVorbis version? – DrZ214 Jun 22 '15 at 21:42
• @DrZ214 I used the same version of the site. But the solution of the David Gouveia works very well (instantiating a SoundEffect with the Ogg decoded). – Emir Lima Jun 23 '15 at 11:55
• @EmirLima Yea that's exactly what I want too: SoundEffect loaded with a .ogg music file. Where did you get the OggSharp dlls? At oggsharp.codeplex.com , the only download buttons I can find give a zip file with an example .exe, no dlls :/ – DrZ214 Jun 23 '15 at 23:13

If you're only targeting Windows, I've found that the easiest way is to bypass the XNA Audio API altogether and use something else.

I found the FMOD API to be great for this, and it even comes bundled with a C# wrapper already. I've added my own wrapper around theirs and here's the bare minimum you need to load a sound from a file and play it:

The wrapper:

namespace YourNamespace
{
using System;
using FMOD;

public class SoundSystem
{
public SoundSystem()
{
RESULT result = Factory.System_Create(ref _system);
if(result != RESULT.OK)
throw new Exception("Create SoundSystem Failed");

uint version = 0;
result = System.getVersion(ref version);
if (result != RESULT.OK || version < VERSION.number)
throw new Exception("Create SoundSystem Failed");

result = System.init(32, INITFLAGS.NORMAL, (IntPtr)null);
if (result != RESULT.OK)
throw new Exception("Create SoundSystem Failed");
}

public System System
{
get { return _system; }
}

}

public class Sound
{
public Sound(SoundSystem system, string path)
{
_system = system;
RESULT result = system.System.createSound(path, MODE.HARDWARE, ref _sound);
if (result != RESULT.OK)
throw new Exception("Create Sound Failed");
}

public void Play()
{
Channel channel = null;
RESULT result = _system.System.playSound(CHANNELINDEX.FREE, _sound, false, ref channel);
if (result != RESULT.OK)
throw new Exception("Play Sound Failed");
}

}
}


And how to use it:

SoundSystem system = new SoundSystem();
Sound sound = new Sound(system, "song.mp3");
sound.Play();


Of course that wrapper is just the bare minimum, but should be simple to expand in order to expose any other features you need.