diff options
Diffstat (limited to 'midi/midi.ha')
| -rw-r--r-- | midi/midi.ha | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/midi/midi.ha b/midi/midi.ha new file mode 100644 index 0000000..7da6689 --- /dev/null +++ b/midi/midi.ha @@ -0,0 +1,205 @@ +use fmt; +use endian; +use strings; +use io; +use os; +use strconv; + +def HEADER: [4]u8 = ['M', 'T', 'h', 'd']; +def TRACK: [4]u8 = ['M', 'T', 'r', 'k']; + +def SOUNDOFF: [2]u8 = [0x78, 0x00]; + +export type chunk = struct { + chktype: [4]u8, + length: u32, +}; + +export type hchunk = struct { + chk: chunk, + format: u16, + ntrks: u16, + division: u16, +}; + +export type trchunk = struct { + chk: chunk, + ev: []event, +}; + +export type vlen = u32; + +export type event = struct { + deltatime: vlen, + ev: (midi | sysex | meta), +}; + +export type midi = struct { + status: u8, + data: [2]u8, +}; + +export type sysex = void; +export type meta = void; + +export def HEADERCHUNK = chunk { + chktype = HEADER, + // MIDI 1.0 + length = 6, +}; + +def TRACKCHUNK = chunk { + chktype = TRACK, + ... +}; + +export type note = enum u8 { + A = 0x39, + AS = 0x3A, + B = 0x3B, + BS = 0x3C, + C = 0x3C, + CS = 0x3D, + D = 0x3E, + DS = 0x3F, + E = 0x40, + ES = 0x41, + F = 0x41, + FS = 0x42, + G = 0x43, + GS = 0x44, +}; + +export fn finishtrack(c: trchunk) void = { + free(c.ev); +}; + +export fn newtrack() trchunk = { + return trchunk { + chk = TRACKCHUNK, + ev = [], + }; +}; + +export fn addevent(c: *trchunk, e: event) void = { + append(c.ev, e); +}; + +export fn noteon(c: *trchunk, d: vlen, n: note, vel: u8 = 0x40, oct: uint = 0) void = { + const np = n + (oct * 12): u8; + const e = event { + deltatime = d, + ev = midi { + status = 0x90, + data = [n, vel], + }, + }; + append(c.ev, e); +}; + +export fn noteoff(c: *trchunk, d: vlen, n: note, vel: u8 = 0x40, oct: uint = 0) void = { + const np = n + (oct * 12): u8; + const e = event { + deltatime = d, + ev = midi { + status = 0x80, + data = [n, vel], + }, + }; + append(c.ev, e); +}; + +export fn writeheader(c: hchunk) void = { + io::writeall(os::stdout, c.chk.chktype)!; + let buf: [4]u8 = [0...]; + let l = c.chk.length; + endian::beputu32(buf, l); + io::writeall(os::stdout, buf)!; + + let bf: [2]u8 = [0...]; + endian::beputu16(bf, c.format); + io::writeall(os::stdout, bf)!; + let bf: [2]u8 = [0...]; + endian::beputu16(bf, c.ntrks); + io::writeall(os::stdout, bf)!; + let bf: [2]u8 = [0...]; + endian::beputu16(bf, c.division); + io::writeall(os::stdout, bf)!; +}; + +export fn writechunk(c: trchunk) void = { + io::writeall(os::stdout, c.chk.chktype)!; + let buf: [4]u8 = [0...]; + let l = calclength(c); + //let l = c.chk.length; + endian::beputu32(buf, l); + io::writeall(os::stdout, buf)!; + + for (const ev .. c.ev) { + let dt = encodevlen(ev.deltatime); + endian::beputu32(buf, dt); + if (dt <= 127) { + io::writeall(os::stdout, [buf[3]])!; + } else if (dt <= 0xFF7F) { + io::writeall(os::stdout, buf[2..])!; + } else if (dt <= 0xFFFF7F) { + io::writeall(os::stdout, buf[1..])!; + } else { + io::writeall(os::stdout, buf)!; + }; + match (ev.ev) { + case let m: midi => + io::writeall(os::stdout, [m.status])!; + io::writeall(os::stdout, m.data)!; + case => + abort(); + }; + }; +}; + +fn calclength(c: trchunk) u32 = { + let length = 0u32; + for (const ev .. c.ev) { + let dt = encodevlen(ev.deltatime); + if (dt <= 127) { + length += 1; + } else if (dt <= 0xFF7F) { + length += 2; + } else if (dt <= 0xFFFF7F) { + length += 3; + } else { + length += 4; + }; + match (ev.ev) { + case let m: midi => + length += 3; + case => + abort(); + }; + }; + return length; +}; + +fn encodevlen(v: vlen) u32 = { + if (v < 128) { + return v; + }; + + let n = v: u32; + let b: [4]u8 = [0...]; + endian::beputu32(b, v); + for (let i = 3z; i >= 0; i -= 1) { + b[i] = (n & 0x7F): u8; + + if (i < 3) { + b[i] |= 0x80; + }; + + n >>= 7; + + if (n < 1) { + break; + }; + }; + return endian::begetu32(b); +}; |
