Chris@137: Chris@193: // Convert the output of the midifile program Chris@193: // (https://code.soundsoftware.ac.uk/projects/midifile) Chris@193: // into lab file like those output from the test scripts. Chris@193: // Chris@193: // The midifile format contains lines looking like: Chris@193: // Chris@193: // : Note: channel duration pitch

velocity Chris@193: // Chris@193: // where t and d are defined in terms of the timebase and Chris@193: // tempo, which are (usually) given in earlier lines: Chris@193: // Chris@193: // Timing division: ppq Chris@193: // : Tempo: Chris@193: // Chris@222: // Note that we assume 4 quarter-notes per bar, we don't parse time Chris@222: // signatures, and we only handle the first tempo event. Chris@137: // Chris@193: // The output file format looks like: Chris@137: // Chris@137: // onset offset frequency Chris@193: // Chris@193: // with times in seconds. Chris@137: Chris@193: program convert_midifileout; Chris@137: Chris@137: usage () = Chris@193: eprintln "\nUsage: convert_midifileout file.txt\n"; Chris@137: Chris@137: toFrequency m = Chris@137: 440 * Math#pow(2.0, (m - 69) / 12.0); Chris@137: Chris@193: toTime timebase tempo t = Chris@193: (t / timebase) * (60 / tempo); Chris@193: Chris@137: convert f = Chris@137: (str = openInFile f "UTF-8"; Chris@193: var timebase = 480; Chris@193: var tempo = 120; Chris@222: var tempoNowFixed = false; Chris@137: for (str.lines ()) do line: Chris@193: bits = strSplit ": " line; Chris@193: if length bits > 1 then Chris@193: if bits[0] == "Timing division" then Chris@193: timebase := number (strReplace " ppq" "" bits[1]); Chris@193: eprintln "Set timing division to \(timebase)"; Chris@193: elif bits[1] == "Tempo" then Chris@222: if tempoNowFixed then Chris@222: failWith "Can't handle variable-tempo file"; Chris@222: fi; Chris@193: if length bits < 3 then Chris@193: failWith "Too few bits in tempo line: \(line)"; Chris@193: fi; Chris@193: tempo := number (bits[2]); Chris@222: tempoNowFixed := true; Chris@193: eprintln "Set tempo to \(tempo)"; Chris@193: elif bits[1] == "Note" then Chris@222: tempoNowFixed := true; Chris@193: if length bits < 3 then Chris@193: failWith "Too few bits in note line: \(line)"; Chris@193: fi; Chris@193: noteparts = strSplit " " bits[2]; Chris@193: if length noteparts < 8 then Chris@193: failWith "Too few note parameters in line: \(line)"; Chris@193: fi; Chris@193: onset = toTime timebase tempo (number bits[0]); Chris@193: duration = toTime timebase tempo (number noteparts[3]); Chris@193: frequency = toFrequency (number noteparts[5]); Chris@193: println "\(onset)\t\(onset + duration)\t\(frequency)"; Chris@193: fi; Chris@193: fi; Chris@137: done; Chris@137: str.close ()); Chris@137: Chris@137: case (list _argv) of Chris@137: file::[]: convert file; Chris@137: _: usage (); Chris@137: esac; Chris@137: