- commit
- 89439b0bd26b64261456a9a71b76087845b486ba
- parent
- 365fdeedb85413c1eb0d28a0a20160dd3feb55d1
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2023-09-19 09:38
rewrite renderer
Diffstat
| M | renderer.py | 119 | +++++++++++++++++++++++-------------------------------------- |
| M | xi2.py | 4 | ++-- |
2 files changed, 47 insertions, 76 deletions
diff --git a/renderer.py b/renderer.py
@@ -1,100 +1,71 @@ 1 1 import midi 2 23 -1 # The tricky part here is the time conversion.4 -1 # and noteOff events5 -16 3 7 4 class Renderer: 8 5 """Convert intermediate code to MIDI bytecode.""" 9 610 -1 def __init__(self, midi, seq, ch=0, offset=60):-1 7 def __init__(self, midi, ch=0, offset=60): 11 8 self.midi = midi 12 9 self.ch = ch 13 10 self.offset = offset 14 11 self.dt = 015 -1 self.dt_stack = []16 -1 self.stack = []17 -1 self.render_seq(seq)18 -119 -1 def dt_step(self):20 -1 a = 1.021 -1 for i in self.dt_stack[1:]:22 -1 a /= i23 -1 return a-1 12 self.active_notes = set() 24 1325 -1 def render_element(self, e):26 -1 if e.isdigit(): # note27 -1 self.midi.note_on(self.dt, self.ch, self.offset + int(e), 1)28 -1 self.dt = self.dt_step()29 -1 elif e == '-': # hold30 -1 self.dt += self.dt_step()31 -1 elif e == '': # break32 -1 self.dt += self.dt_step()33 -1 else: # lyrics34 -1 self.midi.lyrics(self.dt, e.replace('_', ' '))35 -1 self.dt = self.dt_step()-1 14 def note_on(self, s): -1 15 note = int(s, 10) + self.offset -1 16 self.midi.note_on(self.dt, self.ch, note, 1) -1 17 self.active_notes.add(note) -1 18 self.dt = 0 36 1937 -1 def render_seq(self, seq):38 -1 self.dt_stack.append(len(seq))39 -1 for e in seq:40 -1 if isinstance(e, str):41 -1 if e != '-':42 -1 self.stop()43 -1 self.stack.append(e)44 -1 self.render_element(e)45 -1 elif isinstance(e, set):46 -1 if '-' not in e:47 -1 self.stop()48 -1 self.stack.append(e)49 -1 self.render_chord(e)50 -1 elif isinstance(e, list):51 -1 self.render_seq(e)52 -1 else:53 -1 raise ValueError("unknown element: " + e)54 -1 self.dt_stack.pop()-1 20 def lyrics(self, s): -1 21 self.midi.lyrics(self.dt, s.replace('_', ' ')) -1 22 self.dt = 0 55 2356 -1 def render_chord(self, s):57 -1 for e in s:58 -1 if not isinstance(e, str):59 -1 raise ValueError("only elements are allowed inside chords: " + e)60 -1 elif e == '':61 -1 raise ValueError("Breaks are not allowed inside sets!")62 -1 else:63 -1 self.render_element(e)-1 24 def stop(self): -1 25 while self.active_notes: -1 26 self.midi.note_off(self.dt, self.ch, self.active_notes.pop(), 1) 64 27 self.dt = 065 -1 self.dt = self.dt_step()66 2867 -1 def stop(self):68 -1 if len(self.stack) == 0:69 -1 return70 -1 e = self.stack.pop()71 -1 if isinstance(e, str):72 -1 if e == '-':-1 29 def render_seq(self, seq, step=1.0): -1 30 for item in seq: -1 31 if isinstance(item, set): -1 32 if '-' not in item: -1 33 self.stop() -1 34 for subitem in item: -1 35 if subitem == '-': -1 36 continue -1 37 elif subitem.isdigit(): -1 38 self.note_on(subitem) -1 39 else: -1 40 self.lyrics(subitem) -1 41 self.dt = step -1 42 elif isinstance(item, list): -1 43 self.render_seq(item, step / len(item)) -1 44 elif item == '-': -1 45 self.dt += step -1 46 elif item == '': 73 47 self.stop()74 -1 elif e == '':75 -1 pass # already stopped76 -1 elif e.isdigit():77 -1 self.midi.note_off(self.dt, self.ch, self.offset + int(e), 1)78 -1 self.dt = 0-1 48 self.dt += step -1 49 elif item.isdigit(): -1 50 self.stop() -1 51 self.note_on(item) -1 52 self.dt = step 79 53 else:80 -1 pass81 -1 elif isinstance(e, set):82 -1 if '-' in e:83 54 self.stop()84 -1 for ee in e:85 -1 # we already checked the validity of the set when parsing.86 -1 # we only need to check if this is a note, lyrics or '-'87 -1 if ee.isdigit():88 -1 self.midi.note_off(self.dt, self.ch, self.offset + int(ee), 1)89 -1 self.dt = 090 -1 else:91 -1 assert False, "Unexpected object on stack: " + e-1 55 self.lyrics(item) -1 56 self.dt = step -1 57 -1 58 -1 59 def render(seq, midi, ch=0, offset=60): -1 60 r = Renderer(midi, ch, offset) -1 61 r.render_seq(seq) -1 62 r.stop() 92 63 93 64 94 65 if __name__ == '__main__': 95 66 a = [[['0', '1'], '2'], '4', '5', '-', '', {'0', '4', '7'}, '', '', '0', {'3', '-'}] 96 67 t = midi.Midi()97 -1 ip = Renderer(t, a, 0, 60)-1 68 render(a, t) 98 69 99 70 with open('test.mid', 'wb') as fh: 100 71 midi.write_file(fh, [t])
diff --git a/xi2.py b/xi2.py
@@ -4,7 +4,7 @@ import argparse 4 4 import re 5 5 6 6 import midi7 -1 from renderer import Renderer-1 7 from renderer import render 8 8 9 9 10 10 def parse(t): @@ -115,7 +115,7 @@ if __name__ == '__main__': 115 115 prog = 0 116 116 m.prog_ch(0, ch, prog) 117 117 # write data118 -1 Renderer(m, track, ch=ch, offset=args.offset)-1 118 render(track, m, ch=ch, offset=args.offset) 119 119 # write 120 120 midi_tracks.append(m) 121 121 ch += 1