import time import sys import functools import operator import array from midi import MidiFile import gameduino import GDprep import random def perftime(): return int((time.time() - t0) * 192) def until(t): while perftime() < t: pass def midifreq(m): return 440 * (2 ** ((m - 69) / 12.)) gd = gameduino.Gameduino(sys.argv[1], 115200) gd.silence() # commands # wait # silence # target class LivePlayer(object): def __init__(self, bps): self.t0 = time.time() self.bps = bps def perftime(self): return int((time.time() - self.t0) * self.bps) def wait(self, nt): while self.perftime() < nt: pass def target(self, v, freq, a): gd.voice(v, 0, f, a, a) def amp(self, v, a): gd.wr16(gameduino.VOICES + (4 * v) + 2, (a << 8) | a) def silence(self, v): self.amp(v, 0) class Schedule(object): def __init__(self): self.bytes = [] self.t = 0 def wait(self, nt): if nt != self.t: d = nt - self.t assert d < 64 self.bytes.append(0x00 | d) self.t = nt def silence(self, v): self.bytes.append(0x40 | v) def target(self, v, freq, a): self.bytes.append(0x80 | v) self.bytes.append(freq & 0xff) self.bytes.append((freq >> 8) & 0xff) self.bytes.append(a) def amp(self, v, a): self.bytes.append(0xc0 | v) self.bytes.append(a) def dump(self, hh, name): GDprep.dump(hh, name, array.array('B', self.bytes)) flute = GDprep.spectrum("americanflute.txt", cutoff = 6, volume = 90) string = GDprep.spectrum("string.txt", cutoff = 3, volume = 10) piano = GDprep.spectrum("piano.txt", cutoff = 3, volume = 30) choir = GDprep.spectrum("choir2.txt", cutoff = 3, volume = 60) sine = [(1, 10)] bass = [(1, 8), (2,16)] if 1: # 0 empty # 1 baseline (2) acoustic guitar # 2 melody (5) / pan flute # 3 harmony (6) / flute # 4 no idea / choir (hardly used) # 5 bass (4) / picked bass # 6 harmony (6) / string ensemble # 7 (6) / tango accordion # 8 empty # 9 empty # 10 triangle (1) m = MidiFile() m.open("MONT.MID") m.read() m.close() instruments = {1: bass, 2: flute, 3: piano, 4: bass, 5: bass, 6: sine, 7: sine, 10: sine} selected = [m.tracks[t] for t in (2,3)] selected = [m.tracks[t] for t in (1,2,3,4,5,6,7)] else: # 1 m = MidiFile() m.open("Maestoso.mid") m.read() m.close() organ = list(enumerate([9,3,3,9,3,6], 1)) instruments = {2:organ, 3:organ, 4:sine, 5:sine, 6:sine, 7:sine, 8:sine, 9:sine, 10:sine, 11:sine, 12:sine, } selected = [m.tracks[t] for t in (2,)] selected = m.tracks allevents = sum([t.events for t in selected], []) allevents = sorted(allevents, key=lambda e: e.time) p2v = {} maxvoices = set([]) if 0: for e in allevents: e.time -= 10560 allevents = [e for e in allevents if 0 <= e.time < 23040] sched = LivePlayer(1 * 192) sched = Schedule() prevf = [-1] * 64 for e in allevents: if e.type == "NOTE_ON": sched.wait(e.time) # print e.type, e.channel, e.time, e.pitch, e.velocity used = functools.reduce(operator.__or__, p2v.values(), set([])) maxvoices |= used k = (e.channel,e.pitch) if e.velocity: instrument = instruments.get(e.channel, sine) if k in p2v: del p2v[k] available = set(range(64)) - used # voices = set(random.sample(available, len(instrument))) # voices = set(sorted(available)[:len(instrument)]) # assert len(voices) == len(instrument) voices = set([]) fundamental = instrument[0][0] for i in range(len(instrument)): f = int((instrument[i][0] / fundamental) * midifreq(e.pitch) * 4) a = (instrument[i][1] * e.velocity) / 100 matches = [v for v in range(64) if v in available and prevf[v] == f] if matches: v = matches[0] sched.amp(v, a) else: v = min(available) v = random.choice(list(available)) sched.target(v, f, a) available.remove(v) voices.add(v) # prevf[v] = f p2v[k] = voices # print "voice", voice, "freq", midifreq(e.pitch), "amp", a, p2v else: if k in p2v: for v in p2v[k]: sched.silence(v) del p2v[k] # sched.append(1) gd.silence() print "max voices", len(maxvoices) print len(sched.bytes) sched.dump(open("../mont.h", "w"), "mont")