comparison musiccutter.py @ 13:5f4c21bb5140

Add lines and notes engraved on the paper. Add left margin for page one. Add comments about what each thing does.
author Daniel O'Connor <darius@dons.net.au>
date Tue, 12 Apr 2016 21:54:30 +0930
parents 6e46ceee57a7
children 43b5d8d62df6
comparison
equal deleted inserted replaced
12:6e46ceee57a7 13:5f4c21bb5140
17 ## XXX: PDF origin bottom left, SVG origin top left 17 ## XXX: PDF origin bottom left, SVG origin top left
18 def test(filename = None): 18 def test(filename = None):
19 if filename == None: 19 if filename == None:
20 filename = 'test.midi' 20 filename = 'test.midi'
21 # Card layout from http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf 21 # Card layout from http://www.orgues-de-barbarie.com/wp-content/uploads/2014/09/format-cartons.pdf
22 m = Midi2PDF('notes', 200, 155, 5.5, 6.0, 10, 'Helvetica', 12) 22 m = Midi2PDF('notes', 200, 155, 5.5, 6.0, 20, 10, 'Helvetica', 12)
23 base, ext = os.path.splitext(filename) 23 base, ext = os.path.splitext(filename)
24 m.processMidi(filename, base + '-%02d.pdf') 24 m.processMidi(filename, base + '-%02d.pdf')
25 25
26 class Midi2PDF(object): 26 class Midi2PDF(object):
27 def __init__(self, notefile, pagewidth, pageheight, pitch, offset, timescale, fontname, fontsize): 27 def __init__(self, notefile, pagewidth, pageheight, pitch, offset, margin, timescale, fontname, fontsize):
28 self.midi2note, self.note2midi = Midi2PDF.genmidi2note() 28 self.midi2note, self.note2midi = Midi2PDF.genmidi2note()
29 self.note2slot = Midi2PDF.loadnote2slot(notefile, self.note2midi) 29 self.note2slot, self.slot2note = Midi2PDF.loadnote2slot(notefile, self.note2midi)
30 self.pagewidth = pagewidth # Dimensions are in millimetres 30 self.pagewidth = pagewidth # Dimensions are in millimetres
31 self.pageheight = pageheight 31 self.pageheight = pageheight
32 self.pitch = pitch 32 self.pitch = pitch # Distance between each slot
33 self.offset = offset 33 self.offset = offset # Bottom margin
34 self.margin = margin # Left margin
34 self.timescale = timescale 35 self.timescale = timescale
35 self.fontname = fontname 36 self.fontname = fontname
36 self.fontsize = fontsize 37 self.fontsize = fontsize
37 38
38 def processMidi(self, midifile, outpat): 39 def processMidi(self, midifile, outpat):
89 print 'Playable count:', playablecount 90 print 'Playable count:', playablecount
90 print 'Unplayable count:', unplayablecount 91 print 'Unplayable count:', unplayablecount
91 92
92 for i in range(len(pdfs)): 93 for i in range(len(pdfs)):
93 pdf = pdfs[i] 94 pdf = pdfs[i]
95 # Add title and page number
94 tobj = pdf.beginText() 96 tobj = pdf.beginText()
95 tobj.setTextOrigin(2 * mm, 1 * mm) 97 tobj.setTextOrigin(50 * mm, 1 * mm)
96 tobj.setFont(self.fontname, self.fontsize) 98 tobj.setFont(self.fontname, self.fontsize)
97 tobj.setFillColor(ENGRAVE_COLOUR) 99 tobj.setFillColor(ENGRAVE_COLOUR)
98 tobj.setStrokeColor(ENGRAVE_COLOUR) 100 tobj.setStrokeColor(ENGRAVE_COLOUR)
99 tobj.textLine('%s (%d / %d)' % (title, i + 1, npages)) 101 tobj.textLine('%s (%d / %d)' % (title, i + 1, npages))
100 pdf.drawText(tobj) 102 pdf.drawText(tobj)
103
104 # Draw rect around page
105 pdf.saveState()
106 pdf.setLineWidth(0)
107 #pdf.rect(0, 0, self.pagewidth * mm, self.pageheight * mm, fill = False, stroke = True)
108 # Draw lines per note
109 for slot in sorted(self.slot2note.keys()):
110 ofs = (self.offset + slot * self.pitch) * mm
111 pdf.line(0, ofs, self.pagewidth * mm, ofs)
112 # Note name
113 tobj = pdf.beginText()
114 tobj.setTextOrigin(0 * mm, ofs + 1 * mm)
115 tobj.setFont(self.fontname, self.fontsize)
116 tobj.setFillColor(ENGRAVE_COLOUR)
117 tobj.setStrokeColor(ENGRAVE_COLOUR)
118 tobj.textLine('%s' % (self.slot2note[slot]))
119 pdf.drawText(tobj)
120 pdf.restoreState()
101 pdf.save() 121 pdf.save()
102 122
103 # http://newt.phys.unsw.edu.au/jw/notes.html 123 # http://newt.phys.unsw.edu.au/jw/notes.html
104 @staticmethod 124 @staticmethod
105 def genmidi2note(): 125 def genmidi2note():
106 '''Create forward & reverse tables for midi number to note name (assuming 69 = A4 = A440)''' 126 '''Create forward & reverse tables for midi number to note name (assuming 69 = A4 = A440)'''
107 names = ['C%d', 'C%d#', 'D%d', 'D%d#', 'E%d', 'F%d', 'F%d#', 'G%d', 'G%d#', 'A%d', 'A%d#', 'B%d'] 127 names = ['C%d', 'C%d#', 'D%d', 'D%d#', 'E%d', 'F%d', 'F%d#', 'G%d', 'G%d#', 'A%d', 'A%d#', 'B%d']
108 midi2note = {} 128 midi2note = {}
109 note2midi = {} 129 note2midi = {}
110 for midi in range(128): 130 for midi in range(21, 128):
111 octave = midi / len(names) - 1 131 octave = midi / len(names) - 1
112 index = midi % len(names) 132 index = midi % len(names)
113 name = names[index] % (octave) 133 name = names[index] % (octave)
114 midi2note[midi] = name 134 midi2note[midi] = name
115 note2midi[name] = midi 135 note2midi[name] = midi
117 return midi2note, note2midi 137 return midi2note, note2midi
118 138
119 @staticmethod 139 @staticmethod
120 def loadnote2slot(fname, note2midi): 140 def loadnote2slot(fname, note2midi):
121 note2slot = {} 141 note2slot = {}
142 slot2note = {}
122 index = 0 143 index = 0
123 144
124 for note in file(fname): 145 for note in file(fname):
125 note = note.strip() 146 note = note.strip()
126 if note[0] == '#': 147 if note[0] == '#':
127 continue 148 continue
128 if note not in note2midi: 149 if note not in note2midi:
129 raise exceptions.ValueError('Note \'%s\' not valid' % note) 150 raise exceptions.ValueError('Note \'%s\' not valid' % note)
130 note2slot[note] = index 151 note2slot[note] = index
152 slot2note[index] = note
131 index += 1 153 index += 1
132 154
133 return note2slot 155 return note2slot, slot2note
134 156
135 def emitnote(self, pdfs, slot, start, notelen): 157 def emitnote(self, pdfs, slot, start, notelen):
136 x = start * self.timescale 158 x = start * self.timescale + self.margin
137 pageidx = int(x / self.pagewidth) 159 pageidx = int(x / self.pagewidth)
138 x = x % self.pagewidth 160 x = x % self.pagewidth
139 y = self.offset + slot * self.pitch 161 y = self.offset + slot * self.pitch
140 w = notelen * self.timescale 162 w = notelen * self.timescale
141 h = self.pitch 163 h = self.pitch