Mercurial > ~darius > hgwebdir.cgi > musiccutter
diff musiccutter.py @ 0:0773354c7428
Initial commit, WIP.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sat, 05 Mar 2016 21:59:44 +1030 |
parents | |
children | 123b42d95ab8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/musiccutter.py Sat Mar 05 21:59:44 2016 +1030 @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +import exceptions +import mido +import svgwrite +import sys + +def test(): + # http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf + conf = { 'notefile' : 'notes', 'pitch' : 5.5 } + midi2note, note2midi = genmidi2note() + note2slot = loadnote2slot(conf['notefile'], note2midi) + midi2svg('test.midi', 'test%d.svg', conf['pitch'], midi2note, note2midi, note2slot) + +# http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/midi_note_numbers_for_octaves.htm +def genmidi2note(): + '''Create forward & reverse tables for midi number to note name (assuming 69 == A440)''' + names = ['C%d', 'C%d#', 'D%d', 'D%d#', 'E%d', 'F%d', 'F%d#', 'G%d', 'G%d#', 'A%d', 'A%d#', 'B%d'] + midi2note = {} + note2midi = {} + for midi in range(128): + octave = midi / len(names) + index = midi % len(names) + name = names[index] % (octave) + midi2note[midi] = name + note2midi[name] = midi + + return midi2note, note2midi + +def loadnote2slot(fname, note2midi): + svg = svgwrite.Drawing(fname) + + note2slot = {} + index = 0 + + for note in file(fname): + note = note.strip() + if note[0] == '#': + continue + if note not in note2midi: + raise exceptions.ValueError('Note \'%s\' not valid' % note) + note2slot[note] = index + index += 1 + + return note2slot + +def emitnote(svg, slot, start, notelen): + # Let us say 185 ticks for a quaver and that is 5mm long + tickscale = 185.0 / 5.0 + pitch = 5.5 + offset = 6 + x = start / tickscale + y = offset + slot * pitch + w = notelen / tickscale + h = pitch + print x, y, w, h + svg.add(svgwrite.shapes.Rect(insert = ('%.3fcm' % (x / 10, '%.3fcm' % (y / 10)), size = (w, h), stroke = 'red', stroke_width = '1px', fill = 'red')) + +def midi2svg(inf, outpat, pitch, midi2note, note2midi, note2slot): + midi = mido.MidiFile(inf) + svg = svgwrite.Drawing(outpat % (0)) + tnum = 0 + for t in midi.tracks: + print "Track", tnum + tnum += 1 + channels = [] + for i in range(16): + channels.append({}) + ctime = 0 + for ev in t: + ctime += ev.time + if ev.type == 'note_on' and ev.velocity > 0: + if ev.note in channels[ev.channel]: + print "Duplicate channel on message" + else: + channels[ev.channel][ev.note] = ctime + elif ev.type == 'note_off' or (ev.type == 'note_on' and ev.velocity == 0): + if ev.note not in channels[ev.channel]: + print 'Note_off with no corresponding note_on for track %d channel %d note %d' % (tnum, ev.channel, ev.note) + else: + note = midi2note[ev.note] + if note not in note2slot: + print 'Skipping unplayable note %s' % (note) + else: + notelen = ctime - channels[ev.channel][ev.note] + slot = note2slot[note] + print 'Note %s (%d) at %d length %d' % (note, slot, ctime, notelen) + emitnote(svg, slot, ctime, notelen) + del channels[ev.channel][ev.note] + svg.save() + +if __name__ == '__main__': + main()