view testdata/scripts/convert_midifileout.yeti @ 372:af71cbdab621 tip

Update bqvec code
author Chris Cannam
date Tue, 19 Nov 2019 10:13:32 +0000
parents 51c18a17404a
children
line wrap: on
line source

// Convert the output of the midifile program
// (https://code.soundsoftware.ac.uk/projects/midifile)
// into lab file like those output from the test scripts.
//
// The midifile format contains lines looking like:
//
// <t>: Note: channel <c> duration <d> pitch <p> velocity <v>
//
// where t and d are defined in terms of the timebase and
// tempo, which are (usually) given in earlier lines:
//
// Timing division: <n> ppq
// <t>: Tempo: <n>
//
// Note that we assume 4 quarter-notes per bar, we don't parse time
// signatures, and we only handle the first tempo event.
// 
// The output file format looks like:
//
// onset offset frequency
//
// with times in seconds.

program convert_midifileout;

usage () =
    eprintln "\nUsage: convert_midifileout file.txt\n";

toFrequency m = 
    440 * Math#pow(2.0, (m - 69) / 12.0);

toTime timebase tempo t =
    (t / timebase) * (60 / tempo);

convert f =
   (str = openInFile f "UTF-8";
    var timebase = 480;
    var tempo = 120;
    var tempoNowFixed = false;
    for (str.lines ()) do line:
        bits = strSplit ": " line;
        if length bits > 1 then
            if bits[0] == "Timing division" then
                timebase := number (strReplace " ppq" "" bits[1]);
                eprintln "Set timing division to \(timebase)";
            elif bits[1] == "Tempo" then
                if tempoNowFixed then
                    failWith "Can't handle variable-tempo file";
                fi;
                if length bits < 3 then 
                    failWith "Too few bits in tempo line: \(line)";
                fi;
                tempo := number (bits[2]);
                tempoNowFixed := true;
                eprintln "Set tempo to \(tempo)";
            elif bits[1] == "Note" then
                tempoNowFixed := true;
                if length bits < 3 then
                    failWith "Too few bits in note line: \(line)";
                fi;
                noteparts = strSplit " " bits[2];
                if length noteparts < 8 then
                    failWith "Too few note parameters in line: \(line)";
                fi;
                onset = toTime timebase tempo (number bits[0]);
                duration = toTime timebase tempo (number noteparts[3]);
                frequency = toFrequency (number noteparts[5]);
                println "\(onset)\t\(onset + duration)\t\(frequency)";
            fi;
        fi;
    done;
    str.close ());

case (list _argv) of
file::[]: convert file;
_: usage ();
esac;