midi stuff?
Will Ware
wware at world.std.com
Sun Dec 26 02:24:35 EST 1999
I (wware at world.std.com) wrote:
> Is there any Python code floating around for generating MIDI
> files, starting from any sort of human-readable score?
The following works on a RedHat 6.1 system.
##################################################################
#!/usr/bin/env python
import os, sys, string
badNoteError = 'bad note error'
class Midi:
def __init__(self, outf=sys.stdout):
self.quarterNoteTime = 256
self.outf = outf
def dumpByte(self,b):
self.outf.write(chr(b & 0xFF))
def dumpInt16(self,i):
self.dumpByte(i >> 8)
self.dumpByte(i)
def dumpInt(self,i):
self.dumpInt16(i >> 16)
self.dumpInt16(i)
def dumpVariable(self,v):
for b in self.variable(v):
self.dumpByte(b)
def variable(self,i,toobig=0):
if i < 0x80:
if toobig:
return [0x80 + i,]
else:
return [i,]
else:
lowSeven = i & 0x7f
return (self.variable(i >> 7, 1) +
self.variable(i & 0x7f, toobig))
class MidiFile(Midi):
tracks = [ ]
def register(self,track):
self.tracks.append(track)
return self.quarterNoteTime, self.outf
def dump(self):
# dump the header
self.outf.write('MThd')
self.dumpInt(6) # six bytes follow
self.dumpInt16(1)
self.dumpInt16(len(self.tracks))
self.dumpInt16(self.quarterNoteTime)
for t in self.tracks:
t.dump()
self.outf.close()
class Track(Midi):
def __init__(self,channel,owner):
self.data = [ ]
self.channel = channel - 1
self.quarterNoteTime, self.outf = owner.register(self)
self.currentNoteLength = 1.
self.currentOctave = 60
self.pendingRest = 0.
def dump(self):
self.outf.write('MTrk')
self.dumpInt(len(self.data))
for b in self.data:
self.dumpByte(b)
def event(self, time, bytes):
self.data = self.data + self.variable(time) + bytes
def note(self, pitch, duration):
self.event(int(self.pendingRest * self.quarterNoteTime),
[0x90 + self.channel, pitch, 127])
self.pendingRest = 0
self.event(int(duration * self.quarterNoteTime),
[0x80 + self.channel, pitch, 127])
def parseNote(self, str):
pitchOffset = 0
rest = 0
while str:
c = str[0]
str = str[1:]
if c == '+': # octave up
self.currentOctave = self.currentOctave + 12
elif c == '-': # octave down
self.currentOctave = self.currentOctave - 12
elif c == '#': # sharp
pitchOffset = pitchOffset + 1
elif c == '@': # flat
pitchOffset = pitchOffset - 1
# numbers are translated to note lengths, measured
# in multiples of a quarter note
elif (c >= '0' and c <= '9') or c == '.':
while (len(str) > 0 and
(str[0] == '.' or
(str[0] >= '0' and str[0] <= '9'))):
c = c + str[0]
str = str[1:]
# this number is a note length in quarter notes
self.currentNoteLength = eval(c)
# here are note names (A-G), it would be nice if
# the key got picked up automatically here
elif c >= 'a' and c <= 'g':
pitchOffset = pitchOffset + \
{'c': 0, 'd': 2, 'e': 4, 'f': 5,
'g': 7, 'a': 9, 'b': 11}[c]
elif c >= 'A' and c <= 'G':
pitchOffset = pitchOffset + \
{'C': 0, 'D': 2, 'E': 4, 'F': 5,
'G': 7, 'A': 9, 'B': 11}[c]
# rests
elif c == 'r' or c == 'R':
rest = 1
else:
raise badNoteError
if rest:
self.pendingRest = self.pendingRest + self.currentNoteLength
else:
self.note(self.currentOctave + pitchOffset,
self.currentNoteLength)
def parseVoice(self, str):
for s in string.split(str):
self.parseNote(s)
mf = MidiFile(open('foo.mid', 'w'))
t1 = Track(1, mf)
t2 = Track(2, mf)
# the first few notes of the Crab Canon
t1.parseVoice('c2 e@ g a@ b-3 g+1 f#2 f e e at 3 d1 d@ c b- a.5 g c1+ f e at 2 d c')
t2.parseVoice('''
c+ e@ g c+ b-.5 c+ d e@ f e@ d c d g- d+ f e@ d c b- a b c+ e@ d c
b-
''')
mf.dump()
# playmidi loses last notes sometimes, xplaymidi doesn't, I dunno why
os.system('xplaymidi foo.mid')
############################################################
--
- - - - - - - - - - - - - - - - - - - - - - - -
Resistance is futile. Capacitance is efficacious.
Will Ware email: wware @ world.std.com
More information about the Python-list
mailing list