154 lines
3.8 KiB
Python
154 lines
3.8 KiB
Python
def main() :
|
|
True
|
|
|
|
class Chord() :
|
|
pitches = None
|
|
|
|
## [0-9]?[a-gA-G][bMmd]?
|
|
# octave
|
|
# note
|
|
# flat/Major chord/minor chord/deminutive chord
|
|
def __init__(self, s) :
|
|
note = Note(s)
|
|
self.pitches = note.pitch
|
|
if note.mod == "M" :
|
|
raise Exception("not impl")
|
|
elif note.mod == "m" :
|
|
raise Exception("not impl")
|
|
elif note.mod == "d" :
|
|
raise Exception("not impl")
|
|
elif note.mod == "" :
|
|
pass
|
|
else :
|
|
raise Exception(f"invalid note mod '{note.mod}'")
|
|
|
|
class Note():
|
|
octave = None
|
|
offset = None
|
|
s = None
|
|
pitch = None
|
|
mod = None
|
|
|
|
# [0-9]?[a-gA-G][b]?
|
|
# octave (default 4)
|
|
# note (case insensitive)
|
|
def __init__(self, s):
|
|
assert(s)
|
|
self.s = s
|
|
self.octave = 4
|
|
self.offset = 0
|
|
if s[0].isnumeric() :
|
|
self.octave = int(s[0])
|
|
s = s[1:]
|
|
note = s[0].lower()
|
|
self.mod = s[1:]
|
|
assert(note >= 'a' and note <= 'g')
|
|
self.offset = ord(note) - ord('a') - 2
|
|
if self.offset < 0 :
|
|
self.offset += 7
|
|
# de gab
|
|
# c f
|
|
self.offset += len([j for j in [1,2,4,5,6] if self.offset >= j])
|
|
if self.mod == "b" and note in "degab" :
|
|
self.mod = ""
|
|
self.offset -= 1
|
|
self.pitch = 12 * self.octave + self.offset
|
|
|
|
class Writer():
|
|
writer = None
|
|
|
|
def __init__(self, writer):
|
|
self.writer = writer
|
|
|
|
def chord(self, chord, duration=1, track=0, channel=0, volume=100):
|
|
return self.chords([chord], duration, track, channel, volume)
|
|
|
|
def chords(self, chords, duration=1, track=0, channel=0, volume=100):
|
|
for chord in chords:
|
|
chord = Chord(chord)
|
|
for pitch in chord.pitches :
|
|
self.writer.pitch(pitch, duration, track, channel, volume)
|
|
|
|
class MIDIWriter():
|
|
time = 0
|
|
driver = None
|
|
|
|
def __init__(self, driver):
|
|
self.driver = driver
|
|
|
|
def pitch(self, pitch, duration, track, channel, volume):
|
|
self.pitches([pitch], duration, track, channel, volume)
|
|
|
|
def pitches(self, pitches, duration, track, channel, volume):
|
|
for pitch in pitches :
|
|
self.driver.pitch(self.time, pitch, duration, track, channel, volume)
|
|
self.time += 1
|
|
|
|
def flush(self, path):
|
|
self.driver.write(path)
|
|
|
|
class MIDIDriver():
|
|
def pitch(self, time, pitches, duration, track, channel, volume):
|
|
raise Exception("not impl")
|
|
|
|
def write(self, path) :
|
|
raise Exception("not impl")
|
|
|
|
class MIDIDriverStream(MIDIDriver):
|
|
stream = None
|
|
buff = None
|
|
def __init__(self, stream=None) :
|
|
super().__init__()
|
|
self.stream = stream
|
|
self.buff = ""
|
|
|
|
def pitch(self, time, pitch, duration, track, channel, volume):
|
|
if self.buff :
|
|
self.buff += "\n"
|
|
self.buff += f't={time}, p={pitch}, d={duration}'
|
|
|
|
def write(self, path) :
|
|
print(self.buff, file=self.stream)
|
|
self.buff = ""
|
|
|
|
class MIDIDriverStdout(MIDIDriverStream):
|
|
def __init__(self) :
|
|
super().__init__()
|
|
|
|
class MIDIDriverImpl(MIDIDriver):
|
|
midi = None
|
|
bpm = None
|
|
def __init__(self, bpm=60):
|
|
self.bpm = bpm
|
|
self.__set_midi__()
|
|
super().__init__()
|
|
|
|
def __set_midi__(self) :
|
|
import midiutil
|
|
|
|
ttime = 0 # ?
|
|
tempo = self.bpm
|
|
|
|
self.midi = midiutil.MIDIFile(1)
|
|
for track in range(11) :
|
|
self.midi.addTempo(track, ttime, tempo)
|
|
|
|
def pitch(self, time, pitch, duration, track, channel, volume):
|
|
assert(track >= 0 and track <= 10)
|
|
self.midi.addNote(
|
|
track=track,
|
|
channel=channel,
|
|
pitch=int(pitch),
|
|
time=time,
|
|
duration=duration,
|
|
volume=volume,
|
|
)
|
|
|
|
def write(self, path) :
|
|
with open(path, "wb") as f :
|
|
self.midi.writeFile(f)
|
|
self.__set_midi__()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|