Wherein we present an example of Hz support for performing MIDI files.
Right-click to copy examples to your workspace
Here's a simple driver script to perform a MIDI file on a single
instrument node (here FluidSynth). Of note is the configuration
object which is pass to the Perform
method. The programMapper
field supports a callback function that allows your code to assign
a MIDI program number to each track in the file.
let thisDir = path.dirname(this.GetFilePath());
// select one of our example files by setting the array index to 0, 1 or 2.
let f = ["bwv772.mid", "bach_846.mid", "lotus.midi"][2];
let uri = path.join(thisDir, f);
// instantiate a MidiFile object.
let midiFile = new MidiFile();
// asyncrhonously load th file
let err = await midiFile.Load(uri);
if(err) return;
// start up audio engine with a FluidSynth since it supports
// General MIDI voice assignments.
let scene = await Ascene.BeginFiber(this);
let fluid = scene.NewAnode("FluidSynth");
let gain = scene.NewAnode("Hz.Mix", {cfg:"stereo", preset:{Gain:-8}});
let dac = scene.GetDAC();
scene.Chain(fluid, gain, dac);
await scene.Sync();
// fluid.Show();
gain.Show();
// establish midi file configurations
// nb: we can also perform a single track
let cfg =
{
speed: 1,
setTempo: "once", // "once" | "never" | "always" (busted for bach)
programChange: "allow",
programMapper: function(oldProg, trk)
{
if(f.startsWith("lotus"))
{
let newProg = oldProg;
console.log(`prog ${trk} ${oldProg} -> ${newProg}`);
return newProg;
}
else
if(f.startsWith("bach"))
{
let newProg = 24 + trk;
console.log(`prog ${trk} ${oldProg} -> ${newProg}`);
return newProg;
}
else
return oldProg;
},
dumpTrack: null, // -1 means all
dumpJoin: true
};
if(cfg.dumpTrack != null)
midiFile.Dump(cfg);
// This is where it all happens -------------------------------------
console.log(`${f} has ${midiFile.GetNumTracks()} tracks, format ${midiFile.GetFormat()}`);
for await (const val of midiFile.Perform(fluid, .5, cfg))
{
yield;
}