diff musiccutter.py @ 11:9faad813e39e

switch to PDF to avoid dealing with CorelDraw crashing
author Daniel O'Connor <darius@dons.net.au>
date Thu, 07 Apr 2016 23:35:44 +0930
parents e9760224e992
children 6e46ceee57a7
line wrap: on
line diff
--- a/musiccutter.py	Thu Apr 07 23:35:19 2016 +0930
+++ b/musiccutter.py	Thu Apr 07 23:35:44 2016 +0930
@@ -4,25 +4,35 @@
 import itertools
 import math
 import mido
-import svgwrite
+import os
+import reportlab.lib.colors
+from reportlab.lib.units import mm
 import sys
 
+CUT_COLOUR = reportlab.lib.colors.red
+ENGRAVE_COLOUR = reportlab.lib.colors.black
+
+
+## XXX: PDF origin bottom left, SVG origin top left
 def test(filename = None):
     if filename == None:
         filename = 'test.midi'
     # Card layout from http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf
-    m = Midi2SVG('notes', 20, 15.5, 0.55, 0.6, 1.0)
-    m.processMidi(filename, 'test%02d.svg')
+    m = Midi2PDF('notes', 200, 155, 5.5, 6.0, 10, 'Helvetica', 12)
+    base, ext = os.path.splitext(filename)
+    m.processMidi(filename, base + '-%02d.pdf')
 
-class Midi2SVG(object):
-    def __init__(self, notefile, pagewidth, pageheight, pitch, offset, timescale):
-        self.midi2note, self.note2midi = Midi2SVG.genmidi2note()
-        self.note2slot = Midi2SVG.loadnote2slot(notefile, self.note2midi)
-        self.pagewidth = pagewidth
+class Midi2PDF(object):
+    def __init__(self, notefile, pagewidth, pageheight, pitch, offset, timescale, fontname, fontsize):
+        self.midi2note, self.note2midi = Midi2PDF.genmidi2note()
+        self.note2slot = Midi2PDF.loadnote2slot(notefile, self.note2midi)
+        self.pagewidth = pagewidth # Dimensions are in millimetres
         self.pageheight = pageheight
         self.pitch = pitch
         self.offset = offset
         self.timescale = timescale
+        self.fontname = fontname
+        self.fontsize = fontsize
 
     def processMidi(self, midifile, outpat):
         playablecount = 0
@@ -33,18 +43,22 @@
         for i in range(16):
             channels.append({})
 
-        npages = int(math.ceil(midi.length / self.timescale / self.pagewidth))
+        npages = int(math.ceil(midi.length * self.timescale / self.pagewidth))
         print 'npages', npages
-        svgs = []
+        pdfs = []
         for i in range(npages):
-            svg = svgwrite.Drawing(outpat % i, profile = 'full', size = ('%.3fcm' % (self.pagewidth), '%.3fcm' % (self.pageheight)))
-            svgs.append(svg)
+            pdf = reportlab.pdfgen.canvas.Canvas(file(outpat % (i + 1), 'w'), pagesize = (self.pagewidth * mm, self.pageheight * mm))
+            pdfs.append(pdf)
 
+        title = midifile
         for ev in midi:
+            if ev.type == 'text' and ctime == 0:
+                title = ev.text
+
             ctime += ev.time
             if ev.type == 'note_on' or ev.type == 'note_off':
                 note = self.midi2note[ev.note]
-            print ctime, ev
+            #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, note)
@@ -62,7 +76,7 @@
                             notelen = ctime - start
                             slot = self.note2slot[note]
                             print 'Note %s (%d) at %.2f length %.2f' % (note, slot, start, notelen)
-                            self.emitnote(svgs, slot, start, notelen)
+                            self.emitnote(pdfs, slot, start, notelen)
                             playablecount += 1
                         del channels[ev.channel][ev.note]
             elif ev.type == 'end_of_track':
@@ -74,11 +88,16 @@
         print 'Playable count:', playablecount
         print 'Unplayable count:', unplayablecount
 
-        for i in range(len(svgs)):
-            svg = svgs[i]
-            svg.add(svg.text('%s (%d / %d)' % (midifile, i + 1, npages), fill = 'black', stroke = 'black',
-                            insert = ('%.3fcm' % (0), '%.3fcm' % (self.pageheight))))
-            svg.save()
+        for i in range(len(pdfs)):
+            pdf = pdfs[i]
+            tobj = pdf.beginText()
+            tobj.setTextOrigin(2 * mm, 1 * mm)
+            tobj.setFont(self.fontname, self.fontsize)
+            tobj.setFillColor(ENGRAVE_COLOUR)
+            tobj.setStrokeColor(ENGRAVE_COLOUR)
+            tobj.textLine('%s (%d / %d)' % (title, i + 1, npages))
+            pdf.drawText(tobj)
+            pdf.save()
 
     # http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/midi_note_numbers_for_octaves.htm
     @staticmethod
@@ -112,26 +131,34 @@
 
         return note2slot
 
-    def emitnote(self, svgs, slot, start, notelen):
-        startx = start / self.timescale
-        startpageidx = int(startx / self.pagewidth)
-        endx = (start + notelen) / self.timescale
-        endpageidx = int(endx / self.pagewidth)
-        startx = startx % self.pagewidth
-        y = self.pageheight - (self.offset + slot * self.pitch)
-        w = notelen / self.timescale
+    def emitnote(self, pdfs, slot, start, notelen):
+        x = start * self.timescale
+        pageidx = int(x / self.pagewidth)
+        x = x % self.pagewidth
+        y = self.offset + slot * self.pitch
+        w = notelen * self.timescale
         h = self.pitch
 
-        if startpageidx != endpageidx:
-            print 'page crossed from %d to %d' % (startpageidx, endpageidx)
-        print 'page = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (startpageidx, startx, y, w, h)
-        Midi2SVG._emitnote(svgs[startpageidx], startx, y, w, h)
+        print 'page = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pageidx, x, y, w, h)
+        w1 = w
+        # Check if the note crosses a page
+        if x + w > self.pagewidth:
+            w1 = x - self.pagewidth # Crop first note
+            w2 = w - w1
+            assert w1 <= self.pagewidth, 'note extends for more than a page'
+            # Emit second half of note
+            print 'split note, page %d w2 = %.3f' % (pageidx + 1, w2)
+            Midi2PDF._emitnote(pdfs[pageidx + 1], 0, y, w2, h)
+
+        Midi2PDF._emitnote(pdfs[pageidx], x, y, w1, h)
 
     @staticmethod
-    def _emitnote(svg, x, y, w, h):
-        svg.add(svgwrite.shapes.Rect(insert = ('%.3fcm' % (x), '%.3fcm' % (y)),
-                                     size = ('%.3fcm' % (w), '%.3fcm' % (h)),
-                                     stroke = 'red', stroke_width = '1px', fill = 'none'))
+    def _emitnote(pdf, x, y, w, h):
+        pdf.saveState()
+        pdf.setStrokeColor(CUT_COLOUR)
+        pdf.setLineWidth(0)
+        pdf.rect(x * mm, y * mm, w * mm, h * mm, fill = False, stroke = True)
+        pdf.restoreState()
 
 if __name__ == '__main__':
     main()