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