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()