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:
Prerequisite - Download library
Please keep in mind that the answer was written using the OggSharp library, which is now deprecated and replaced by the NVorbis library.
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();