Mercurial > ~darius > hgwebdir.cgi > musiccutter
comparison musiccutter.py @ 31:ea98c9507f47
Preliminary multi-page support. Breaks time marks though.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Tue, 03 May 2016 18:04:41 +0930 |
parents | f46cc9401e79 |
children | 1d5dcaa3b07d |
comparison
equal
deleted
inserted
replaced
30:f46cc9401e79 | 31:ea98c9507f47 |
---|---|
23 base, ext = os.path.splitext(filename) | 23 base, ext = os.path.splitext(filename) |
24 base = os.path.basename(base) | 24 base = os.path.basename(base) |
25 m.processMidi(filename, base + '-%02d.pdf') | 25 m.processMidi(filename, base + '-%02d.pdf') |
26 | 26 |
27 class Midi2PDF(object): | 27 class Midi2PDF(object): |
28 def __init__(self, notefile, pagewidth, pageheight, pitch, slotsize, heel, leadin, timemarks, trytranspose, drawrect, notenames, notelines, noteoffset, timescale, notescale, fontname, fontsize): | 28 def __init__(self, notefile, pagewidth, pageheight, pitch, slotsize, heel, leadin, timemarks, trytranspose, drawrect, notenames, notelines, noteoffset, pagesperpdf, timescale, notescale, fontname, fontsize): |
29 self.midi2note, self.note2midi = Midi2PDF.genmidi2note() | 29 self.midi2note, self.note2midi = Midi2PDF.genmidi2note() |
30 self.note2slot, self.slot2note = Midi2PDF.loadnote2slot(notefile, self.note2midi) | 30 self.note2slot, self.slot2note = Midi2PDF.loadnote2slot(notefile, self.note2midi) |
31 self.pagewidth = pagewidth # Dimensions are in millimetres | 31 self.pagewidth = pagewidth # Dimensions are in millimetres |
32 self.pageheight = pageheight | 32 self.pageheight = pageheight |
33 self.pitch = pitch # Distance between each slot | 33 self.pitch = pitch # Distance between each slot |
38 self.trytranspose = trytranspose # Attempt to tranpose unplayable notes | 38 self.trytranspose = trytranspose # Attempt to tranpose unplayable notes |
39 self.drawrect = drawrect # Draw rectangle around each page | 39 self.drawrect = drawrect # Draw rectangle around each page |
40 self.notenames = notenames # Draw note names on the right edge | 40 self.notenames = notenames # Draw note names on the right edge |
41 self.notelines = notelines # Draw line rulers | 41 self.notelines = notelines # Draw line rulers |
42 self.noteoffset = noteoffset # Amount to adjust note pitches by (+12 = 1 octave) | 42 self.noteoffset = noteoffset # Amount to adjust note pitches by (+12 = 1 octave) |
43 self.pagesperpdf = pagesperpdf # Number of pages to emit per PDF | |
43 self.timescale = timescale # Width per second | 44 self.timescale = timescale # Width per second |
44 self.notescale = notescale # Multiply all note lengths by this (to get rearticulation) | 45 self.notescale = notescale # Multiply all note lengths by this (to get rearticulation) |
45 self.fontname = fontname | 46 self.fontname = fontname |
46 self.fontsize = fontsize # Points | 47 self.fontsize = fontsize # Points |
48 | |
49 self.pdfwidth = self.pagewidth * self.pagesperpdf | |
47 | 50 |
48 def processMidi(self, midifile, outpat): | 51 def processMidi(self, midifile, outpat): |
49 playablecount = 0 | 52 playablecount = 0 |
50 unplayablecount = 0 | 53 unplayablecount = 0 |
51 transposeupcount = 0 | 54 transposeupcount = 0 |
55 channels = [] | 58 channels = [] |
56 for i in range(16): | 59 for i in range(16): |
57 channels.append({}) | 60 channels.append({}) |
58 | 61 |
59 npages = int(math.ceil(((midi.length * self.timescale) + self.leadin) / self.pagewidth)) | 62 npages = int(math.ceil(((midi.length * self.timescale) + self.leadin) / self.pagewidth)) |
60 print 'npages', npages | 63 npdfs = int(math.ceil(float(npages) / self.pagesperpdf)) |
64 print 'npages %d, npdfs %d' % (npages, npdfs) | |
65 | |
61 pdfs = [] | 66 pdfs = [] |
62 for i in range(npages): | 67 for i in range(npdfs): |
63 pdf = reportlab.pdfgen.canvas.Canvas(file(outpat % (i + 1), 'w'), pagesize = (self.pagewidth * mm, self.pageheight * mm)) | 68 pdf = reportlab.pdfgen.canvas.Canvas(file(outpat % (i + 1), 'w'), |
69 pagesize = (self.pdfwidth * mm, self.pageheight * mm)) | |
64 pdfs.append(pdf) | 70 pdfs.append(pdf) |
65 | 71 |
66 title = os.path.basename(midifile) | 72 title = os.path.basename(midifile) |
67 title, ext = os.path.splitext(title) | 73 title, ext = os.path.splitext(title) |
68 for ev in midi: | 74 for ev in midi: |
124 print 'Unplayable count:', unplayablecount | 130 print 'Unplayable count:', unplayablecount |
125 if self.trytranspose: | 131 if self.trytranspose: |
126 print 'Transpose down:', transposedowncount | 132 print 'Transpose down:', transposedowncount |
127 print 'Transpose up:', transposeupcount | 133 print 'Transpose up:', transposeupcount |
128 | 134 |
129 for pindx in range(len(pdfs)): | 135 for pindx in range(npages): |
130 pdf = pdfs[pindx] | 136 pdf = pdfs[pindx / self.pagesperpdf] # PDF for this page |
137 # Offset into PDF where the page starts | |
138 pageofs = self.pagewidth * (self.pagesperpdf - (pindx % self.pagesperpdf) - 1) | |
131 # Add title and page number | 139 # Add title and page number |
132 Midi2PDF.textHelper(pdf, 0 * mm, 1 * mm, ENGRAVE_COLOUR, True, self.fontname, self.fontsize, '%s (%d / %d)' % (title, pindx + 1, npages)) | 140 Midi2PDF.textHelper(pdf, pageofs * mm, 1 * mm, |
133 | 141 ENGRAVE_COLOUR, True, self.fontname, self.fontsize, |
142 '%s (%d / %d)' % (title, pindx + 1, npages)) | |
143 pdf.saveState() | |
134 pdf.setLineWidth(0) | 144 pdf.setLineWidth(0) |
135 | 145 |
136 # Draw time marks | 146 # Draw time marks every 5 seconds |
137 if self.timemarks: | 147 if self.timemarks: |
148 pdfidx = pindx / self.pagesperpdf | |
149 # Work out start and end times (pdf 1 is special due to leadin) | |
138 tstart = self.leadin / self.timescale | 150 tstart = self.leadin / self.timescale |
139 tend = self.pagewidth / self.timescale | 151 tend = (self.pagewidth * self.pagesperpdf) / self.timescale |
140 if pindx > 0: | 152 if pindx > 0: |
141 tsize = self.pagewidth / self.timescale | 153 tsize = self.pagewidth / self.timescale # Amount of time per pdf |
142 tstart = tend + tsize * pindx | 154 tstart = tend + tsize * pdfidx |
143 tend = tend + tsize * (pindx + 1) | 155 tend = tend + tsize * (pdfidx + 1) |
144 for s in range(tstart, tend, 5): | 156 for s in range(tstart, tend, 5): |
145 x = self.pagewidth - (float(s * self.timescale + self.leadin) % self.pagewidth) | 157 x = self.pagewidth - (float(s * self.timescale + self.leadin) % self.pagewidth) |
146 pdf.line(x * mm, self.heel, x * mm, self.pageheight * mm) | 158 pdf.line(x * mm, self.heel, x * mm, self.pageheight * mm) |
147 Midi2PDF.textHelper(pdf, x * mm, 1 * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, str(s) + 's') | 159 Midi2PDF.textHelper(pdf, x * mm, 1 * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, str(s) + 's') |
148 | 160 |
149 # Draw rectangle around page (upper and right hand ends don't seem to render though) | 161 # Draw rectangle around page (upper and right hand ends don't seem to render though) |
150 if self.drawrect: | 162 if self.drawrect: |
151 pdf.rect(0, 0, self.pagewidth * mm, self.pageheight * mm, fill = False, stroke = True) | 163 pdf.rect((pindx % self.pagesperpdf) * self.pagewidth * mm, 0, |
164 self.pagewidth * mm, self.pageheight * mm, fill = False, stroke = True) | |
152 | 165 |
153 # Draw lines per note | 166 # Draw lines per note |
154 for slot in sorted(self.slot2note.keys()): | 167 for slot in sorted(self.slot2note.keys()): |
155 ofs = self.pageheight - (self.heel + slot * self.pitch) - self.slotsize / 2 | 168 ofs = self.pageheight - (self.heel + slot * self.pitch) - self.slotsize / 2 |
156 if self.notelines: | 169 if self.notelines: |
157 pdf.line(0, ofs * mm, self.pagewidth * mm, ofs * mm) | 170 pdf.line(0, ofs * mm, self.pdfwidth * mm, ofs * mm) |
158 # Note name | 171 # Note name |
159 if self.notenames: | 172 if self.notenames: |
160 Midi2PDF.textHelper(pdf, (self.pagewidth - 10) * mm, (ofs + 0.5) * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, self.slot2note[slot]) | 173 Midi2PDF.textHelper(pdf, (self.pdfwidth - 10) * mm, (ofs + 0.5) * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, self.slot2note[slot]) |
161 | 174 pdf.restoreState() |
162 # Save PDF | 175 for pdf in pdfs: |
163 pdf.save() | 176 pdf.save() |
164 | 177 |
165 # http://newt.phys.unsw.edu.au/jw/notes.html | 178 # http://newt.phys.unsw.edu.au/jw/notes.html |
166 @staticmethod | 179 @staticmethod |
167 def genmidi2note(): | 180 def genmidi2note(): |
196 | 209 |
197 return note2slot, slot2note | 210 return note2slot, slot2note |
198 | 211 |
199 def emitnote(self, pdfs, slot, start, notelen): | 212 def emitnote(self, pdfs, slot, start, notelen): |
200 x = start * self.timescale + self.leadin # Convert start time to distance | 213 x = start * self.timescale + self.leadin # Convert start time to distance |
201 pageidx = int(x / self.pagewidth) # Determine which page | 214 pdfidx = int(x / self.pdfwidth) # Determine which pdf |
202 x = x % self.pagewidth # and where on that page | 215 x = x % self.pdfwidth # and where on that pdf |
203 h = self.slotsize | 216 h = self.slotsize |
204 y = self.pageheight - (self.heel + slot * self.pitch - self.slotsize / 2) - self.slotsize | 217 y = self.pageheight - (self.heel + slot * self.pitch - self.slotsize / 2) - self.slotsize |
205 w = notelen * self.timescale # Convert note length in time to distance | 218 w = notelen * self.timescale # Convert note length in time to distance |
206 | 219 |
207 print 'page = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pageidx, x, y, w, h) | 220 print 'pdf = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pdfidx, x, y, w, h) |
208 w1 = w | 221 w1 = w |
209 # Check if the note crosses a page | 222 # Check if the note crosses a pdf |
210 if x + w > self.pagewidth: | 223 if x + w > self.pdfwidth: |
211 w1 = self.pagewidth - x # Crop first note | 224 w1 = self.pdfwidth - x # Crop first note |
212 w2 = w - w1 # Calculate length of second note | 225 w2 = w - w1 # Calculate length of second note |
213 assert w2 <= self.pagewidth, 'note extends for more than a page' | 226 assert w2 <= self.pdfwidth, 'note extends for more than a pdf' |
214 # Emit second half of note | 227 # Emit second half of note |
215 print 'split note, page %d w2 = %.3f' % (pageidx + 1, w2) | 228 print 'split note, pdf %d w2 = %.3f' % (pdfidx + 1, w2) |
216 Midi2PDF._emitnote(pdfs[pageidx + 1], self.pagewidth - w2, y, w2, h) | 229 Midi2PDF._emitnote(pdfs[pdfidx + 1], self.pdfwidth - w2, y, w2, h) |
217 | 230 |
218 Midi2PDF._emitnote(pdfs[pageidx], self.pagewidth - x - w1, y, w1, h) | 231 Midi2PDF._emitnote(pdfs[pdfidx], self.pdfwidth - x - w1, y, w1, h) |
219 | 232 |
220 @staticmethod | 233 @staticmethod |
221 def _emitnote(pdf, x, y, w, h): | 234 def _emitnote(pdf, x, y, w, h): |
222 pdf.saveState() | 235 pdf.saveState() |
223 pdf.setStrokeColor(CUT_COLOUR) | 236 pdf.setStrokeColor(CUT_COLOUR) |