UNPKG

abcjs

Version:

Renderer for abc music notation

116 lines (97 loc) 3.42 kB
// abc_midi_create.js: Turn a linear series of events into a midi file. var rendererFactory = require('../synth/abc_midi_renderer'); var create; (function() { "use strict"; var baseDuration = 480*4; // nice and divisible, equals 1 whole note create = function create(abcTune, options) { if (options === undefined) options = {}; var commands = abcTune.setUpAudio(options); var midi = rendererFactory(); var title = abcTune.metaText ? abcTune.metaText.title : undefined; if (title && title.length > 128) title = title.substring(0, 124) + '...'; var key = abcTune.getKeySignature(); var time = abcTune.getMeterFraction(); // MAE 7 July 2024 - Fix for */8 meter tempos var tempo = commands.tempo; var beatsPerSecond = tempo / 60; // Fix tempo for */8 meters if (time.den == 8){ // Compute the tempo based on the actual milliseconds per measure, scaled by the number of eight notes and halved to get tempo in bpm. var msPerMeasure = abcTune.millisecondsPerMeasure(); tempo = (60000 / (msPerMeasure/time.num)) / 2; beatsPerSecond = tempo/60; } //var beatLength = abcTune.getBeatLength(); midi.setGlobalInfo(tempo, title, key, time); for (var i = 0; i < commands.tracks.length; i++) { midi.startTrack(); var notePlacement = {}; for (var j = 0; j < commands.tracks[i].length; j++) { var event = commands.tracks[i][j]; switch (event.cmd) { case 'text': midi.setText(event.type, event.text); break; case 'program': var pan = 0; if (options.pan && options.pan.length > i) pan = options.pan[i]; if (event.instrument === 128) { // If we're using the percussion voice, change to Channel 10 midi.setChannel(9, pan); midi.setInstrument(0); } else { midi.setChannel(event.channel, pan); midi.setInstrument(event.instrument); } break; case 'note': var gapLengthInBeats = event.gap * beatsPerSecond; var start = event.start; // The staccato and legato are indicated by event.gap. // event.gap is in seconds but the durations are in whole notes. var end = start + event.duration - gapLengthInBeats; if (!notePlacement[start]) notePlacement[start] = []; notePlacement[start].push({ pitch: event.pitch, volume: event.volume, cents: event.cents }); if (!notePlacement[end]) notePlacement[end] = []; notePlacement[end].push({ pitch: event.pitch, volume: 0 }); break; default: console.log("MIDI create Unknown: " + event.cmd); } } addNotes(midi, notePlacement, baseDuration); midi.endTrack(); } return midi.getData(); }; function addNotes(midi, notePlacement, baseDuration) { var times = Object.keys(notePlacement); for (var h = 0; h < times.length; h++) times[h] = parseFloat(times[h]); times.sort(function(a,b) { return a - b; }); var lastTime = 0; for (var i = 0; i < times.length; i++) { var events = notePlacement[times[i]]; if (times[i] > lastTime) { var distance = (times[i] - lastTime) * baseDuration; midi.addRest(distance); lastTime = times[i]; } for (var j = 0; j < events.length; j++) { var event = events[j]; if (event.volume) { midi.startNote(event.pitch, event.volume, event.cents); } else { midi.endNote(event.pitch); } } } } })(); module.exports = create;