comparison musiccutter.py @ 19:ffaf97818ce3

- Music needs to run right to left. - Rename margin to leadin to be clearer (although too much leadin makes it barf) - Make time marks and rectangle drawing options
author Daniel O'Connor <darius@dons.net.au>
date Tue, 26 Apr 2016 18:10:18 +0930
parents cf93a76eda92
children 9bb72ae63651
comparison
equal deleted inserted replaced
18:cf93a76eda92 19:ffaf97818ce3
15 15
16 def test(filename = None): 16 def test(filename = None):
17 if filename == None: 17 if filename == None:
18 filename = 'test.midi' 18 filename = 'test.midi'
19 # Card layout from http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf 19 # Card layout from http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf
20 m = Midi2PDF('notes', 200, 155, 5.5, 3.0, 6.0, 20, 10, 'Helvetica', 12) 20 # Notes are read from right to left
21 m = Midi2PDF('notes', 130, 155, 5.5, 3.0, 6.0, 0, False, False, 10, 'Helvetica', 12)
21 base, ext = os.path.splitext(filename) 22 base, ext = os.path.splitext(filename)
22 m.processMidi(filename, base + '-%02d.pdf') 23 m.processMidi(filename, base + '-%02d.pdf')
23 24
24 class Midi2PDF(object): 25 class Midi2PDF(object):
25 def __init__(self, notefile, pagewidth, pageheight, pitch, slotsize, offset, lmargin, timescale, fontname, fontsize): 26 def __init__(self, notefile, pagewidth, pageheight, pitch, slotsize, offset, leadin, timemarks, drawrect, timescale, fontname, fontsize):
26 self.midi2note, self.note2midi = Midi2PDF.genmidi2note() 27 self.midi2note, self.note2midi = Midi2PDF.genmidi2note()
27 self.note2slot, self.slot2note = Midi2PDF.loadnote2slot(notefile, self.note2midi) 28 self.note2slot, self.slot2note = Midi2PDF.loadnote2slot(notefile, self.note2midi)
28 self.pagewidth = pagewidth # Dimensions are in millimetres 29 self.pagewidth = pagewidth # Dimensions are in millimetres
29 self.pageheight = pageheight 30 self.pageheight = pageheight
30 self.pitch = pitch # Distance between each slot 31 self.pitch = pitch # Distance between each slot
31 self.slotsize = slotsize # Size of each slot cut out 32 self.slotsize = slotsize # Size of each slot cut out
32 self.offset = offset # Bottom margin 33 self.offset = offset # Bottom margin
33 self.lmargin = lmargin # Left margin 34 self.leadin = leadin # Extra at the start
35 self.timemarks = timemarks # Draw vertical time lines
36 self.drawrect = drawrect # Draw rectangle around each page
34 self.timescale = timescale # Width per second 37 self.timescale = timescale # Width per second
35 self.fontname = fontname 38 self.fontname = fontname
36 self.fontsize = fontsize # Points 39 self.fontsize = fontsize # Points
37 40
38 def processMidi(self, midifile, outpat): 41 def processMidi(self, midifile, outpat):
42 ctime = 0 45 ctime = 0
43 channels = [] 46 channels = []
44 for i in range(16): 47 for i in range(16):
45 channels.append({}) 48 channels.append({})
46 49
47 npages = int(math.ceil(((midi.length * self.timescale) + self.lmargin) / self.pagewidth)) 50 npages = int(math.ceil(((midi.length * self.timescale) + self.leadin) / self.pagewidth))
48 print 'npages', npages 51 print 'npages', npages
49 pdfs = [] 52 pdfs = []
50 for i in range(npages): 53 for i in range(npages):
51 pdf = reportlab.pdfgen.canvas.Canvas(file(outpat % (i + 1), 'w'), pagesize = (self.pagewidth * mm, self.pageheight * mm)) 54 pdf = reportlab.pdfgen.canvas.Canvas(file(outpat % (i + 1), 'w'), pagesize = (self.pagewidth * mm, self.pageheight * mm))
52 pdfs.append(pdf) 55 pdfs.append(pdf)
90 print 'Unplayable count:', unplayablecount 93 print 'Unplayable count:', unplayablecount
91 94
92 for pindx in range(len(pdfs)): 95 for pindx in range(len(pdfs)):
93 pdf = pdfs[pindx] 96 pdf = pdfs[pindx]
94 # Add title and page number 97 # Add title and page number
95 Midi2PDF.textHelper(pdf, 25 * mm, 1 * mm, ENGRAVE_COLOUR, True, self.fontname, self.fontsize, '%s (%d / %d)' % (title, pindx + 1, npages)) 98 Midi2PDF.textHelper(pdf, 0 * mm, 1 * mm, ENGRAVE_COLOUR, True, self.fontname, self.fontsize, '%s (%d / %d)' % (title, pindx + 1, npages))
96 99
97 pdf.saveState() # Not really necessary since this is last thing we do 100 pdf.saveState() # Not really necessary since this is last thing we do
98 pdf.setLineWidth(0) 101 pdf.setLineWidth(0)
99 102
100 # Draw time marks 103 # Draw time marks
101 if False: 104 if self.timemarks:
102 tstart = 0 105 tstart = self.leadin / self.timescale
103 tend = (self.pagewidth - self.lmargin) / self.timescale 106 tend = self.pagewidth / self.timescale
104 if pindx > 0: 107 if pindx > 0:
105 tsize = self.pagewidth / self.timescale 108 tsize = self.pagewidth / self.timescale
106 tstart = tend + tsize * pindx 109 tstart = tend + tsize * pindx
107 tend = tend + tsize * (pindx + 1) 110 tend = tend + tsize * (pindx + 1)
108 for s in range(tstart, tend, 5): 111 for s in range(tstart, tend, 5):
109 x = float(s * self.timescale + self.lmargin) % self.pagewidth 112 x = self.pagewidth - (float(s * self.timescale + self.leadin) % self.pagewidth)
110 pdf.line(x * mm, self.offset, x * mm, self.pageheight * mm) 113 pdf.line(x * mm, self.offset, x * mm, self.pageheight * mm)
111 Midi2PDF.textHelper(pdf, x * mm, 1 * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, str(s) + 's') 114 Midi2PDF.textHelper(pdf, x * mm, 1 * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, str(s) + 's')
112 115
113 # Draw rectangle around page 116 # Draw rectangle around page
114 if False: 117 if self.drawrect:
115 pdf.rect(0, 0, self.pagewidth * mm, self.pageheight * mm, fill = False, stroke = True) 118 pdf.rect(0, 0, self.pagewidth * mm, self.pageheight * mm, fill = False, stroke = True)
116 pdf.line(0, 0, 0, self.pagewidth * mm) 119 pdf.line(0, 0, 0, self.pagewidth * mm)
117 pdf.line(0, self.pagewidth * mm, self.pagewidth * mm, self.pageheight * mm) 120 pdf.line(0, self.pagewidth * mm, self.pagewidth * mm, self.pageheight * mm)
118 pdf.line(self.pagewidth * mm, self.pageheight * mm, 0, self.pageheight * mm) 121 pdf.line(self.pagewidth * mm, self.pageheight * mm, 0, self.pageheight * mm)
119 pdf.line(0, self.pageheight * mm, 0, 0) 122 pdf.line(0, self.pageheight * mm, 0, 0)
121 # Draw lines per note 124 # Draw lines per note
122 for slot in sorted(self.slot2note.keys()): 125 for slot in sorted(self.slot2note.keys()):
123 ofs = (self.offset + slot * self.pitch) * mm 126 ofs = (self.offset + slot * self.pitch) * mm
124 pdf.line(0, ofs, self.pagewidth * mm, ofs) 127 pdf.line(0, ofs, self.pagewidth * mm, ofs)
125 # Note name 128 # Note name
126 Midi2PDF.textHelper(pdf, 0 * mm, ofs + 1 * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, self.slot2note[slot]) 129 Midi2PDF.textHelper(pdf, (self.pagewidth - 10) * mm, ofs + 1 * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, self.slot2note[slot])
127 pdf.restoreState() 130 pdf.restoreState()
128 pdf.save() 131 pdf.save()
129 132
130 # http://newt.phys.unsw.edu.au/jw/notes.html 133 # http://newt.phys.unsw.edu.au/jw/notes.html
131 @staticmethod 134 @staticmethod
160 index += 1 163 index += 1
161 164
162 return note2slot, slot2note 165 return note2slot, slot2note
163 166
164 def emitnote(self, pdfs, slot, start, notelen): 167 def emitnote(self, pdfs, slot, start, notelen):
165 x = start * self.timescale + self.lmargin 168 x = start * self.timescale + self.leadin # Convert start time to distance
166 pageidx = int(x / self.pagewidth) 169 pageidx = int(x / self.pagewidth) # Determine which page
167 x = x % self.pagewidth 170 x = x % self.pagewidth # and where on that page
168 y = self.offset + slot * self.pitch + (self.pitch - self.slotsize) / 2
169 w = notelen * self.timescale
170 h = self.slotsize 171 h = self.slotsize
172 y = self.offset + slot * self.pitch + (self.pitch - h) / 2
173 w = notelen * self.timescale # Convert note length in time to distance
171 174
172 print 'page = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pageidx, x, y, w, h) 175 print 'page = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pageidx, x, y, w, h)
173 w1 = w 176 w1 = w
174 # Check if the note crosses a page 177 # Check if the note crosses a page
175 if x + w > self.pagewidth: 178 if x + w > self.pagewidth:
176 w1 = x - self.pagewidth # Crop first note 179 w1 = self.pagewidth - x # Crop first note
177 w2 = w - w1 180 w2 = w - w1 # Calculate length of second note
178 assert w1 <= self.pagewidth, 'note extends for more than a page' 181 assert w2 <= self.pagewidth, 'note extends for more than a page'
179 # Emit second half of note 182 # Emit second half of note
180 print 'split note, page %d w2 = %.3f' % (pageidx + 1, w2) 183 print 'split note, page %d w2 = %.3f' % (pageidx + 1, w2)
181 Midi2PDF._emitnote(pdfs[pageidx + 1], 0, y, w2, h) 184 Midi2PDF._emitnote(pdfs[pageidx + 1], self.pagewidth - w2, y, w2, h)
182 185
183 Midi2PDF._emitnote(pdfs[pageidx], x, y, w1, h) 186 Midi2PDF._emitnote(pdfs[pageidx], self.pagewidth - x - w1, y, w1, h)
184 187
185 @staticmethod 188 @staticmethod
186 def _emitnote(pdf, x, y, w, h): 189 def _emitnote(pdf, x, y, w, h):
187 pdf.saveState() 190 pdf.saveState()
188 pdf.setStrokeColor(CUT_COLOUR) 191 pdf.setStrokeColor(CUT_COLOUR)