diff musiccutter.py @ 4:9f4fa5f231e6

Rework to use mido iterator and paginate. Pagination doesn't handle notes extending over a page yet.
author Daniel O'Connor <darius@dons.net.au>
date Mon, 07 Mar 2016 21:23:59 +1030
parents 49a33c431b45
children af683606184e
line wrap: on
line diff
--- a/musiccutter.py	Mon Mar 07 14:46:37 2016 +1030
+++ b/musiccutter.py	Mon Mar 07 21:23:59 2016 +1030
@@ -1,18 +1,21 @@
 #!/usr/bin/env python
 
 import exceptions
+import itertools
 import mido
 import svgwrite
 import sys
 
 def test(filename = None):
     # http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf
-    conf = { 'notefile' : 'notes', 'pitch' : 5.5 , 'pagewidth' : 20, 'pageheight' : 15.5 }
+    conf = { 'notefile' : 'notes', 'pagewidth' : 20, 'pageheight' : 15.5,
+             'pitch' : 0.55, 'offset' : 0.60, 'timescale' : 1.0 }
     midi2note, note2midi = genmidi2note()
     note2slot = loadnote2slot(conf['notefile'], note2midi)
     if filename == None:
         filename = 'test.midi'
-    midi2svg(filename, 'test%d.svg', conf['pitch'], midi2note, note2midi, note2slot, conf['pagewidth'], conf['pageheight'])
+    midi2svg(filename, 'test%02d.svg', midi2note, note2midi, note2slot, conf['pagewidth'], conf['pageheight'],
+        conf['pitch'], conf['offset'], conf['timescale'])
 
 # http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/midi_note_numbers_for_octaves.htm
 def genmidi2note():
@@ -46,68 +49,71 @@
 
     return note2slot
 
-def emitnote(svg, slot, start, notelen, pageheight):
-    # Let us say 185 ticks for a quaver and that is 0.5cm long
-    tickscale = 185.0 / 0.5
-    pitch = 0.55
-    offset = 0.60
-    x = start / tickscale
+def emitnote(pages, outpat, slot, start, notelen, pagewidth, pageheight, pitch, offset, timescale):
+    x = start / timescale
+    pageidx = int(x / pagewidth)
+    x = x % pagewidth
     y = pageheight - (offset + slot * pitch)
-    w = notelen / tickscale
+    w = notelen / timescale
     h = pitch
-    print 'x = %.3f y = %.3f w = %.3f h = %.3f' % (x, y, w, h)
+    print 'pageidx = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pageidx, x, y, w, h)
+    if len(pages) <= pageidx:
+        pages.extend(itertools.repeat(None, len(pages) - pageidx + 1))
+    if not pages[pageidx]:
+        svg = svgwrite.Drawing(outpat % (pageidx), size = ('%.3fcm' % (pagewidth), '%.3fcm' % (pageheight)))
+        pages[pageidx] = svg
+    else:
+        svg = pages[pageidx]
     svg.add(svgwrite.shapes.Rect(insert = ('%.3fcm' % (x), '%.3fcm' % (y)),
                                  size = ('%.3fcm' % (w), '%.3fcm' % (h)),
                                 stroke = 'red', stroke_width = '1px', fill = 'red'))
 
-def midi2svg(inf, outpat, pitch, midi2note, note2midi, note2slot, pagewidth, pageheight):
+def midi2svg(inf, outpat, midi2note, note2midi, note2slot, pagewidth, pageheight, pitch, offset, timescale):
     playablecount = 0
     unplayablecount = 0
     midi = mido.MidiFile(inf)
-    svg = svgwrite.Drawing(outpat % (0), size = ('%.3fcm' % (pagewidth), '%.3fcm' % (pageheight)))
-    tnum = 0
+    ctime = 0
+    channels = []
+    for i in range(16):
+        channels.append({})
 
-    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
-            print ctime, ev
-            if ev.type == 'note_on' and ev.velocity > 0:
-                if ev.note in channels[ev.channel]:
-                    print "Duplicate channel on message"
+    pages = []
+    for ev in midi:
+        ctime += ev.time
+        #print ctime, ev
+        if ev.type == 'note_on' and ev.velocity > 0:
+            if ev.note in channels[ev.channel]:
+                print 'Duplicate note_on message %d (%s)' % (ev.note, midi2note[ev.note])
+            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 channel %d note %d' % (ev.channel, ev.note)
                 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)
+                    note = midi2note[ev.note]
+                    if note not in note2slot:
+                        print 'Skipping unplayable note %s' % (note)
+                        unplayablecount += 1
                     else:
-                        note = midi2note[ev.note]
-                        if note not in note2slot:
-                            print 'Skipping unplayable note %s' % (note)
-                            unplayablecount += 1
-                        else:
-                            start = channels[ev.channel][ev.note]
-                            notelen = ctime - start
-                            slot = note2slot[note]
-                            print 'Note %s (%d) at %d length %d' % (note, slot, start, notelen)
-                            emitnote(svg, slot, start, notelen, pageheight)
-                            print
-                            playablecount += 1
-                        del channels[ev.channel][ev.note]
-            elif ev.type == 'end_of_track':
-                print 'EOT, flushing'
-                for chan in channels:
-                    for ev in chan:
-                        print ev
-        print
-        print
-        print
-    svg.save()
+                        start = channels[ev.channel][ev.note]
+                        notelen = ctime - start
+                        slot = note2slot[note]
+                        #print 'Note %s (%d) at %d length %d' % (note, slot, start, notelen)
+                        emitnote(pages,outpat,  slot, start, notelen, pagewidth, pageheight, pitch, offset, timescale)
+                        playablecount += 1
+                    del channels[ev.channel][ev.note]
+        elif ev.type == 'end_of_track':
+            print 'EOT, not flushing, check for missed notes'
+            for chan in channels:
+                for ev in chan:
+                    print ev
+
+    for svg in pages:
+        if not svg:
+            asdasd
+            continue
+        svg.save()
+
     print 'Playable count:', playablecount
     print 'Unplayable count:', unplayablecount