Chris@137
|
1
|
Chris@193
|
2 // Convert the output of the midifile program
|
Chris@193
|
3 // (https://code.soundsoftware.ac.uk/projects/midifile)
|
Chris@193
|
4 // into lab file like those output from the test scripts.
|
Chris@193
|
5 //
|
Chris@193
|
6 // The midifile format contains lines looking like:
|
Chris@193
|
7 //
|
Chris@193
|
8 // <t>: Note: channel <c> duration <d> pitch <p> velocity <v>
|
Chris@193
|
9 //
|
Chris@193
|
10 // where t and d are defined in terms of the timebase and
|
Chris@193
|
11 // tempo, which are (usually) given in earlier lines:
|
Chris@193
|
12 //
|
Chris@193
|
13 // Timing division: <n> ppq
|
Chris@193
|
14 // <t>: Tempo: <n>
|
Chris@193
|
15 //
|
Chris@222
|
16 // Note that we assume 4 quarter-notes per bar, we don't parse time
|
Chris@222
|
17 // signatures, and we only handle the first tempo event.
|
Chris@137
|
18 //
|
Chris@193
|
19 // The output file format looks like:
|
Chris@137
|
20 //
|
Chris@137
|
21 // onset offset frequency
|
Chris@193
|
22 //
|
Chris@193
|
23 // with times in seconds.
|
Chris@137
|
24
|
Chris@193
|
25 program convert_midifileout;
|
Chris@137
|
26
|
Chris@137
|
27 usage () =
|
Chris@193
|
28 eprintln "\nUsage: convert_midifileout file.txt\n";
|
Chris@137
|
29
|
Chris@137
|
30 toFrequency m =
|
Chris@137
|
31 440 * Math#pow(2.0, (m - 69) / 12.0);
|
Chris@137
|
32
|
Chris@193
|
33 toTime timebase tempo t =
|
Chris@193
|
34 (t / timebase) * (60 / tempo);
|
Chris@193
|
35
|
Chris@137
|
36 convert f =
|
Chris@137
|
37 (str = openInFile f "UTF-8";
|
Chris@193
|
38 var timebase = 480;
|
Chris@193
|
39 var tempo = 120;
|
Chris@222
|
40 var tempoNowFixed = false;
|
Chris@137
|
41 for (str.lines ()) do line:
|
Chris@193
|
42 bits = strSplit ": " line;
|
Chris@193
|
43 if length bits > 1 then
|
Chris@193
|
44 if bits[0] == "Timing division" then
|
Chris@193
|
45 timebase := number (strReplace " ppq" "" bits[1]);
|
Chris@193
|
46 eprintln "Set timing division to \(timebase)";
|
Chris@193
|
47 elif bits[1] == "Tempo" then
|
Chris@222
|
48 if tempoNowFixed then
|
Chris@222
|
49 failWith "Can't handle variable-tempo file";
|
Chris@222
|
50 fi;
|
Chris@193
|
51 if length bits < 3 then
|
Chris@193
|
52 failWith "Too few bits in tempo line: \(line)";
|
Chris@193
|
53 fi;
|
Chris@193
|
54 tempo := number (bits[2]);
|
Chris@222
|
55 tempoNowFixed := true;
|
Chris@193
|
56 eprintln "Set tempo to \(tempo)";
|
Chris@193
|
57 elif bits[1] == "Note" then
|
Chris@222
|
58 tempoNowFixed := true;
|
Chris@193
|
59 if length bits < 3 then
|
Chris@193
|
60 failWith "Too few bits in note line: \(line)";
|
Chris@193
|
61 fi;
|
Chris@193
|
62 noteparts = strSplit " " bits[2];
|
Chris@193
|
63 if length noteparts < 8 then
|
Chris@193
|
64 failWith "Too few note parameters in line: \(line)";
|
Chris@193
|
65 fi;
|
Chris@193
|
66 onset = toTime timebase tempo (number bits[0]);
|
Chris@193
|
67 duration = toTime timebase tempo (number noteparts[3]);
|
Chris@193
|
68 frequency = toFrequency (number noteparts[5]);
|
Chris@193
|
69 println "\(onset)\t\(onset + duration)\t\(frequency)";
|
Chris@193
|
70 fi;
|
Chris@193
|
71 fi;
|
Chris@137
|
72 done;
|
Chris@137
|
73 str.close ());
|
Chris@137
|
74
|
Chris@137
|
75 case (list _argv) of
|
Chris@137
|
76 file::[]: convert file;
|
Chris@137
|
77 _: usage ();
|
Chris@137
|
78 esac;
|
Chris@137
|
79
|