xi2

a plain text language that compiles to MIDI
git clone https://git.ce9e.org/xi2.git

commit
9a3208982019ab92245d3287ba18fe3911dec265
parent
4b2ce16ce4b281c4f9c86bc83f2862539a2e7e7a
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-09-18 15:32
style: use snake_case

Diffstat

M iparser.py 44 ++++++++++++++++++++++----------------------
M midi.py 112 ++++++++++++++++++++++++++++++------------------------------
M xi2.py 10 +++++-----

3 files changed, 83 insertions, 83 deletions


diff --git a/iparser.py b/iparser.py

@@ -12,55 +12,55 @@ class IParser:
   12    12         self.ch = ch
   13    13         self.offset = offset
   14    14         self.dt = 0
   15    -1         self.dtStack = []
   -1    15         self.dt_stack = []
   16    16         self.stack = []
   17    -1         self.parseSeq(seq)
   -1    17         self.parse_seq(seq)
   18    18 
   19    -1     def dtStep(self):
   -1    19     def dt_step(self):
   20    20         a = 1.0
   21    -1         for i in self.dtStack[1:]:
   -1    21         for i in self.dt_stack[1:]:
   22    22             a /= i
   23    23         return a
   24    24 
   25    -1     def parseEl(self, e):
   -1    25     def parse_el(self, e):
   26    26         if e.isdigit(): # note
   27    -1             self.midi.noteOn(self.dt, self.ch, self.offset+int(e), 1)
   28    -1             self.dt = self.dtStep()
   -1    27             self.midi.note_on(self.dt, self.ch, self.offset+int(e), 1)
   -1    28             self.dt = self.dt_step()
   29    29         elif e == '-': # continue
   30    -1             self.dt += self.dtStep()
   -1    30             self.dt += self.dt_step()
   31    31         elif e == '': # break
   32    -1             self.dt += self.dtStep()
   -1    32             self.dt += self.dt_step()
   33    33         else: # lyrics
   34    34             self.midi.lyrics(self.dt, e.replace('_', ' '))
   35    -1             self.dt = self.dtStep()
   -1    35             self.dt = self.dt_step()
   36    36 
   37    -1     def parseSeq(self, seq):
   38    -1         self.dtStack.append(len(seq))
   -1    37     def parse_seq(self, seq):
   -1    38         self.dt_stack.append(len(seq))
   39    39         for e in seq:
   40    40             if type(e) == type(''):
   41    41                 if e != '-': self.stop()
   42    42                 self.stack.append(e)
   43    -1                 self.parseEl(e)
   -1    43                 self.parse_el(e)
   44    44             elif type(e) == type([]):
   45    45                 if not '-' in e: self.stop()
   46    46                 self.stack.append(e)
   47    -1                 self.parseSet(e)
   -1    47                 self.parse_set(e)
   48    48             elif type(e) == type(()):
   49    -1                 self.parseSeq(e)
   -1    49                 self.parse_seq(e)
   50    50             else:
   51    51                 raise Exception("unknown element: " + e)
   52    -1         self.dtStack.pop()
   -1    52         self.dt_stack.pop()
   53    53 
   54    -1     def parseSet(self, s):
   -1    54     def parse_set(self, s):
   55    55         for e in s:
   56    56             if type(e) != type(''):
   57    57                 raise Exception("only elements are allowed inside sets: " + e)
   58    58             elif e == '':
   59    59                 raise Exception("Breaks are not allowed inside sets!")
   60    60             else:
   61    -1                 self.parseEl(e)
   -1    61                 self.parse_el(e)
   62    62             self.dt = 0
   63    -1         self.dt = self.dtStep()
   -1    63         self.dt = self.dt_step()
   64    64 
   65    65     def stop(self):
   66    66         if len(self.stack) == 0: return
@@ -71,7 +71,7 @@ class IParser:
   71    71             elif e == '':
   72    72                 pass # already stopped
   73    73             elif e.isdigit():
   74    -1                 self.midi.noteOff(self.dt, self.ch, self.offset+int(e), 1)
   -1    74                 self.midi.note_off(self.dt, self.ch, self.offset+int(e), 1)
   75    75                 self.dt = 0
   76    76             else:
   77    77                 pass
@@ -82,7 +82,7 @@ class IParser:
   82    82                 # we already checked the validity of the set when parsing.
   83    83                 # we only need to check if this is a note, lyrics or '-'
   84    84                 if ee.isdigit():
   85    -1                     self.midi.noteOff(self.dt, self.ch, self.offset+int(ee), 1)
   -1    85                     self.midi.note_off(self.dt, self.ch, self.offset+int(ee), 1)
   86    86                     self.dt = 0
   87    87         else:
   88    88             raise Exception("Unexpected object on stack: " + e)
@@ -93,5 +93,5 @@ if __name__ == '__main__':
   93    93 
   94    94     f = MidiFile()
   95    95     ip = IParser(a, 0, 60)
   96    -1     f.addTrack(ip.midi)
   -1    96     f.add_track(ip.midi)
   97    97     f.write('test.mid')

diff --git a/midi.py b/midi.py

@@ -1,6 +1,6 @@
    1     1 # http://www.sonicspot.com/guide/midifiles.html
    2     2 
    3    -1 timeDevision = 0x00c0 # two bytes
   -1     3 TIME_DEVISION = 0x00c0 # two bytes
    4     4 
    5     5 
    6     6 class Midi:
@@ -22,65 +22,65 @@ class Midi:
   22    22         f.write(self._buf)
   23    23         f.close()
   24    24 
   25    -1     def writeFixed(self, n, k):
   -1    25     def write_fixed(self, n, k):
   26    26         if type(n) == type(''):
   27    27             self._buf += (' '*k + n)[-k:]
   28    28         else:
   29    29             if k != 0:
   30    -1                 self.writeFixed(n / 0x100, k-1)
   -1    30                 self.write_fixed(n / 0x100, k-1)
   31    31                 self._buf += chr(n % 0x100)
   32    32 
   33    -1     def writeVariable(self, n, _rec=False):
   -1    33     def write_variable(self, n, _rec=False):
   34    34         # b = bin(a)[2:]; eval('0b'+''.join([b[k*8:(k+1)*8][1:] for k in range(int(len(b)/8))]))
   35    35         if n == 0 and _rec: return 0
   36    -1         self.writeVariable(n / 0x80, True)
   -1    36         self.write_variable(n / 0x80, True)
   37    37         if _rec:
   38    38             self._buf += chr(n % 0x80 + 0x80)
   39    39         else:
   40    40             self._buf += chr(n % 0x80)
   41    41 
   42    -1     def chEvent(self, dt, event, ch, p1, p2=-1):
   43    -1         self.writeVariable(int(dt * timeDevision))
   44    -1         self.writeFixed((event << 4) + ch, 1)
   45    -1         self.writeFixed(p1, 1)
   -1    42     def ch_event(self, dt, event, ch, p1, p2=-1):
   -1    43         self.write_variable(int(dt * TIME_DEVISION))
   -1    44         self.write_fixed((event << 4) + ch, 1)
   -1    45         self.write_fixed(p1, 1)
   46    46         if not p2 == -1:
   47    -1             self.writeFixed(p2, 1)
   48    -1 
   49    -1     def metaEvent(self, dt, event, length, data):
   50    -1         self.writeVariable(int(dt * timeDevision))
   51    -1         self.writeFixed(0xff, 1)
   52    -1         self.writeFixed(event, 1)
   53    -1         self.writeVariable(length)
   54    -1         self.writeFixed(data, length)
   55    -1 
   56    -1     def sysEx(self, dt, length, data):
   57    -1         self.writeVariable(int(dt * timeDevision))
   58    -1         self.writeFixed(0xf0, 1)
   59    -1         self.writeVariable(length)
   60    -1         self.writeFixed(data, length)
   61    -1 
   62    -1     def setTempo(self, bpm):
   -1    47             self.write_fixed(p2, 1)
   -1    48 
   -1    49     def meta_event(self, dt, event, length, data):
   -1    50         self.write_variable(int(dt * TIME_DEVISION))
   -1    51         self.write_fixed(0xff, 1)
   -1    52         self.write_fixed(event, 1)
   -1    53         self.write_variable(length)
   -1    54         self.write_fixed(data, length)
   -1    55 
   -1    56     def sys_ex(self, dt, length, data):
   -1    57         self.write_variable(int(dt * TIME_DEVISION))
   -1    58         self.write_fixed(0xf0, 1)
   -1    59         self.write_variable(length)
   -1    60         self.write_fixed(data, length)
   -1    61 
   -1    62     def set_tempo(self, bpm):
   63    63         # midi uses microsec/quarter note
   64    64         msqn = 60000000/bpm
   65    -1         self.metaEvent(0, 0x51, 3, msqn)
   -1    65         self.meta_event(0, 0x51, 3, msqn)
   66    66 
   67    -1     def noteOn(self, dt, ch, key, vol=1):
   68    -1         self.chEvent(dt, 0x9, ch, key, int(vol * 0x7f))
   -1    67     def note_on(self, dt, ch, key, vol=1):
   -1    68         self.ch_event(dt, 0x9, ch, key, int(vol * 0x7f))
   69    69 
   70    -1     def noteOff(self, dt, ch, key, vol=1):
   71    -1         self.chEvent(dt, 0x8, ch, key, int(vol * 0x7f))
   -1    70     def note_off(self, dt, ch, key, vol=1):
   -1    71         self.ch_event(dt, 0x8, ch, key, int(vol * 0x7f))
   72    72 
   73    73     def lyrics(self, dt, s):
   74    -1         self.metaEvent(dt, 0x05, len(s), s)
   -1    74         self.meta_event(dt, 0x05, len(s), s)
   75    75 
   76    -1     def progCh(self, dt, ch, prog):
   77    -1         self.chEvent(dt, 0xC, ch, prog)
   -1    76     def prog_ch(self, dt, ch, prog):
   -1    77         self.ch_event(dt, 0xc, ch, prog)
   78    78 
   79    -1     def ctrlEvent(self, dt, ctrl, v):
   80    -1         self.chEvent(dt, 0xB, ch, ctrl, v)
   -1    79     def ctrl_event(self, dt, ctrl, v):
   -1    80         self.ch_event(dt, 0xb, ch, ctrl, v)
   81    81 
   82    -1     def setVol(self, dt, ch, vol=1):
   83    -1         self.ctrlEvent(dt, ch, 0x07, int(vol * 0x7f))
   -1    82     def set_vol(self, dt, ch, vol=1):
   -1    83         self.ctrl_event(dt, ch, 0x07, int(vol * 0x7f))
   84    84 
   85    85 
   86    86 class MidiFile(Midi):
@@ -90,18 +90,18 @@ class MidiFile(Midi):
   90    90 
   91    91     def update(self):
   92    92         self._buf = ''
   93    -1         self.writeFixed(0x4D546864, 4) # chunk ID "MThd"
   94    -1         self.writeFixed(6, 4) # chunk size
   95    -1         self.writeFixed(1, 2) # format type
   96    -1         self.writeFixed(len(self._tracks), 2) # numer of tracks
   97    -1         self.writeFixed(timeDevision, 2) # time devision
   -1    93         self.write_fixed(0x4D546864, 4) # chunk ID "MThd"
   -1    94         self.write_fixed(6, 4) # chunk size
   -1    95         self.write_fixed(1, 2) # format type
   -1    96         self.write_fixed(len(self._tracks), 2) # numer of tracks
   -1    97         self.write_fixed(TIME_DEVISION, 2) # time devision
   98    98         for track in self._tracks:
   99    -1             self.writeFixed(0x4D54726B, 4) # chunk ID "MTtr"
  100    -1             self.writeFixed(len(track._buf)+4, 4) # chunk size
   -1    99             self.write_fixed(0x4D54726B, 4) # chunk ID "MTtr"
   -1   100             self.write_fixed(len(track._buf)+4, 4) # chunk size
  101   101             self._buf += track._buf
  102    -1             self.metaEvent(0, 0x2f, 0, '') # endTrack event
   -1   102             self.meta_event(0, 0x2f, 0, '') # end_track event
  103   103 
  104    -1     def addTrack(self, data, update=True):
   -1   104     def add_track(self, data, update=True):
  105   105         self._tracks.append(data)
  106   106         if update:
  107   107             self.update()
@@ -110,16 +110,16 @@ class MidiFile(Midi):
  110   110 if __name__ == '__main__':
  111   111     # Example:
  112   112     f = MidiFile()
  113    -1     f.addTrack(Midi())
   -1   113     f.add_track(Midi())
  114   114     t = Midi()
  115    -1     t.setVol(0, 1, 1)
  116    -1     t.ctrlEvent(0, 1, 0x00, 0) # setting bank
  117    -1     t.noteOn(0.5, 1, 60)
  118    -1     t.noteOff(1, 1, 60)
  119    -1     t.noteOn(0, 1, 62)
  120    -1     t.noteOff(1, 1, 62)
  121    -1     t.noteOn(0, 1, 64)
  122    -1     t.noteOff(1, 1, 64)
  123    -1     f.addTrack(t)
   -1   115     t.set_vol(0, 1, 1)
   -1   116     t.ctrl_event(0, 1, 0x00, 0) # setting bank
   -1   117     t.note_on(0.5, 1, 60)
   -1   118     t.note_off(1, 1, 60)
   -1   119     t.note_on(0, 1, 62)
   -1   120     t.note_off(1, 1, 62)
   -1   121     t.note_on(0, 1, 64)
   -1   122     t.note_off(1, 1, 64)
   -1   123     f.add_track(t)
  124   124     print f
  125   125     f.write('test.mid')

diff --git a/xi2.py b/xi2.py

@@ -103,22 +103,22 @@ mf = MidiFile()
  103   103 
  104   104 # create first track with meta infos
  105   105 m = Midi()
  106    -1 m.setTempo(args.tempo)
  107    -1 mf.addTrack(m)
   -1   106 m.set_tempo(args.tempo)
   -1   107 mf.add_track(m)
  108   108 
  109   109 ch = 0
  110   110 for name, track in tracks.iteritems():
  111   111     m = Midi()
  112   112     # write meta info
  113    -1     m.metaEvent(0, 0x04, len(name), name)
   -1   113     m.meta_event(0, 0x04, len(name), name)
  114   114     try: prog = int(name)
  115   115     except ValueError: prog = 0
  116    -1     m.progCh(0, ch, prog)
   -1   116     m.prog_ch(0, ch, prog)
  117   117     # write data
  118   118     ip = IParser(track, ch=ch, offset=args.offset)
  119   119     m += ip.midi
  120   120     # write
  121    -1     mf.addTrack(m)
   -1   121     mf.add_track(m)
  122   122     ch += 1
  123   123 
  124   124 # write to file