Mercurial > ~darius > hgwebdir.cgi > musiccutter
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) |