Midi Rendering and Sound Library

I wrote this library a number of years ago. This code was used in some homebrew code on portable gaming platforms like the GP2X.

Just like with my image library, this one is supposed to be self contained, and compile without any dependencies, on small or embeded systems. All sound generation, file format processing, sound mixing and event management is handled by this code without the use of any external libraries.

Here is the library source code:

This library can read and write most basic MIDI and WAV files. It can also talk to sound hardware on Linux platforms. For MIDI, it uses wavetable synthesis, and reads SF2 Soundfont data for its instrument definitions.

The main component is the wave table synthesizer that is responsible for reading instruments sample data and the MIDI events file and for generating raw sound data that can then be sent directly to a DAC, standard sound hardware, or written out to a WAV file.

Here’s some sample code to play a MIDI file in a loop. It’s pretty straight forward as you can see:

sound = R_InitSound(1, 16, 44100, 8196); // 8K buffer, 16bit samples at 44100 kHz
mixer = R_InitMixer();
bankset = readSF2("MINI.SF2"); // read instruments
buffersize = R_GetSoundDeviceBufferSize(sound);
rendered = R_CreateBlankSample(1, 16, 44100, buffersize);
R_InitSequence(sequence); // init sequencer

while(1){
ready = R_SendSoundBuffers(sound);
if (ready && R_IsMixerChannelReady(mixer, 0)){
R_SilenceSample(rendered);
R_RenderSequencetoSample(rendered, sequence, bankset, buffersize);
R_SetMixerChannelSample(mixer, 0, rendered);
}
R_MixSoundVoices(sound, mixer);
}

Here’s how you can convert a MIDI file to a WAV file without using any sound hardware.

bankset = readSF2("MINI.SF2"); 
sequence = readMIDI("input.mid");
R_InitSequence(sequence);
lastevent = sequence->lastevent->time;
buffersize = (lastevent + 1.0) * 2 * 44100; // 44100 kHz 16-bit samples

rendered = R_CreateBlankSample(1, 16, 44100, buffersize);
R_SilenceSample(rendered);
R_RenderSequencetoSample(rendered, sequence, bankset, buffersize);
writeWAV("output.wav", rendered);

Here are some sample MIDI renderings from Final Fantasy game MIDI files. This conversion was done using a 3MB sound font file. This would be approximately the quality of sound that would be possible on a PC from the early 90s running this software.

Using a larger soundfont file (512MB for example) would greatly improve the quality of the sound.

FF3 – Locke Theme
FF2 – Zeromus Battle
FF3 – Airship Blackjack
FF2 – Toroia
FF2 – Big Whale

The original MIDI files are here: