Writing Wave files
SpessaSynth has a helper function for writing wave files.
audioBufferToWav
Converts an audio buffer into a fully valid wave file.
const file = audioBufferToWav(audioBuffer, normalizeAudio = true, channelOffset = 0, metadata = {}, loop = undefined, channelCount = all);
audioBuffer
- AudioBuffer
- the buffer to write. Multiple channels are allowed. - normalizeAudio
- optional boolean
- if true, the gain of the entire song will be adjusted, so the max sample is always 32,767 or min is always -32,768 (whichever is greater). Recommended. - channelOffset
- optional number
- if the buffer has more than two channels, you can specify the channel offset to use. This is especially useful in one output mode - metadata
- optional Object
described below. All options are string and are optional: - title
- the song’s title - artist
- the song’s artist - album
- the song’s album - genre
- the song’s genre - loop
- optional Object
that will write loop points to the file (using the cue
chunk) - start
- start time in seconds - end
- end time in seconds - channelCount
- optional number
that limits the channel count to a given number. Otherwise, all channels from channelOffset
to the last channel are used. The metadata uses the INFO
chunk to write the information. It is encoded with utf-8
Example code
This example code shows how to save MIDI to a wav file with loop points.
const parsedMid = new MIDI(midiBinary, "unnamed.mid");
const sampleRate = 44100; // hz
const durationInSamples = sampleRate * parsedMid.duration;
const context = new OfflineAudioContext({
numberOfChannels: 2,
samplerate: sampleRate,
length: durationInSamples
});
// remember to add the module!
await context.audioWorklet.addModule("worklet_processor.min.js");
const synth = new Synthetizer(
context.destination, // play directly to output
soundfontBinary,
false,
{
parsedMIDI: parsedMid,
oneOutput: false,
snapshot: undefined,
loopCount: 0,
},
/*
use the default effects.
NOTE: it is HIGHLY recommended that you provide the impulse response here as mentioned in Synthetizer page,
but it's omitted for simplicity
*/
undefined
);
// start rendering
const buffer = await context.startRendering();
// Calculate loop points
// the file skips to the first note on event,
// but the loop points are absolute.
// So we need to adjust them
const startOffset = MIDIticksToSeconds(parsedMid.firstNoteOn, parsedMid);
const loopStart = MIDIticksToSeconds(parsedMid.loop.start, parsedMid) - startOffset;
const loopEnd = MIDIticksToSeconds(parsedMid.loop.end, parsedMid) - startOffset;
// create the WAV file
const wav = audioBufferToWav(
buffer,
true, // normalize audio
0, // channel offset
{ title: parsedMid.midiName }, // add some metadata
{ start: loopStart, end: loopEnd}
);
// save the file
const a = document.createElement("a");
a.href = URL.createObjectURL(wav);
a.download = parsedMid.midiName + ".wav";
a.click();
For a real use-case, see src/website/manager/export_audio.js
.