#!/usr/bin/python3 import sys, os, subprocess, mido def getarg(pos, default): if len(sys.argv) > pos: return sys.argv[pos] return default class Encoder: def __init__(self, fp=None, fp_log=sys.stderr): self.fp = fp if fp == None or fp.mode == "wb" else os.fdopen(fp.fileno(), 'wb') self.fp_log = fp_log self.sampling = 16000 self.signed = True self.verbose = False self.bits = 16 self.msbfirst = True def write(self, amplitude): if self.fp == None: aplay = subprocess.Popen([ "aplay", "-r", str(self.sampling), "-f", ("S" if self.signed else "U") + str(self.bits) + "_" + ("LE" if self.msbfirst else "BE") ], stdin=subprocess.PIPE, stderr=self.fp_log) self.fp = aplay.stdin smax = 2**(self.bits-self.signed)-1 if self.signed: v = int(amplitude*smax) w = v s = v < 0 v = abs(v) if s: v = smax-v v |= s << (self.bits-1) else: v = int((amplitude*smax+smax)/2) w = v if self.msbfirst: i = 0 while i < self.bits: if self.fp.write(bytes([(v>>i)&255])) > 1: self.fp_log.write(str(v)) if self.verbose: self.fp_log.write(bin((v>>i)&255)[2:].zfill(8)) i += 8 else: i = self.bits while i > 0: i -= 8 self.fp.write(bytes([(v>>i)&255])) if self.verbose: self.fp_log.write(bin((v>>i)&255)[2:].zfill(8)) if self.verbose: self.fp_log.write(" "+"0"*(self.bits-len(bin(v))+2)+bin(v)[2:]+" "+str(v)+" "+str(w)+"\n") class Event: def __init__(self, type): self.type = type class Wait(Event): def __init__(self, value): Event.__init__(self, Wait) self.value = value def dict(self): return { "type": "wait", "number": self.value, } class End(Event): def __init__(self): Event.__init__(self, End) def dict(self): return { "type": "end" } class Note(Event): def __init__(self, number, velocity, channel=0, track=0): Event.__init__(self, Note) self.number = number self.velocity = velocity self.channel = channel self.track = track def dict(self): return { "type": "note", "number": self.number, "velocity": self.velocity, "channel": self.channel } def midi_events(): if len(sys.argv) < 2: raise ValueError("Il faut donner le nom du fichier: python3 "+sys.argv[0]+" fichier.mid") return read_midi(sys.argv[1], float(getarg(2, 80))) _midi = None _midi_active = {} _midi_wait = 0 _midi_tt = 0 _midi_loop = True def midi2(): global _midi, _midi_active, _midi_wait, _midi_tt, _midi_loop if _midi == None: _midi = midi_events() if _midi_loop: if _midi_wait == 0: while True: event = _midi[_midi_tt] _midi_tt += 1 if event.type == Note: if event.velocity == 0: if (event.number, event.track) in _midi_active: _midi_active.pop((event.number, event.track)) else: _midi_active[(event.number, event.track)] = event.velocity elif event.type == Wait: _midi_wait = int(event.value*8000) break elif event.type == End: _midi_loop = False break else: _midi_wait -= 1 return [[idx[0], idx[1], _midi_active[idx]] for idx in _midi_active] """ Lit le fichier MIDI donné en argument de la commande. Retourne, pour chaque échantillon, la liste des notes sous la forme [piste, note, amplitude]. """ class Midi: def __init__(self, sampling=16000): self.sampling = sampling def __iter__(self): self.active = {} self.wait = 0 self.tt = 0 self.loop = True self.events = midi_events() return self def __next__(self): if self.loop: if self.wait == 0: while True: event = self.events[self.tt] self.tt += 1 if event.type == Note: if event.velocity == 0: if (event.track, event.number) in self.active: self.active.pop((event.track, event.number)) else: sys.stderr.write("Piste="+str(event.track)+" Note="+str(event.number)+" Force="+str(event.velocity)+"\n") self.active[(event.track, event.number)] = event.velocity elif event.type == Wait: self.wait = int(event.value*self.sampling/2) break elif event.type == End: self.loop = False break else: self.wait -= 1 return [[idx[0], idx[1], self.active[idx]] for idx in self.active] raise StopIteration def read_midi(filename, tempo): mid = mido.MidiFile(filename) result = [] tracks = [0 for i in mid.tracks] tempo = [tempo for i in mid.tracks] wait = [None for i in mid.tracks] end = [False for i in mid.tracks] t = 0 loop = True while loop: loop = False # Find the first events first = [] firstt = None for i, track in enumerate(mid.tracks): if end[i]: continue if wait[i] == None: wait[i] = track[tracks[i]].time/tempo[i]/4 if firstt == None or wait[i] < firstt: firstt = wait[i] first = [i] elif wait[i] == firstt: first.append(i) if firstt > 0: result.append(Wait(firstt)) # Play the first events for i, track in enumerate(mid.tracks): if end[i]: continue loop = True msg = track[tracks[i]] if i in first: # Play event if msg.type == "set_tempo": tempo[i] = 6e7/msg.tempo elif msg.type == "note_on": result.append(Note(msg.note-50, msg.velocity/100, msg.channel, i)) elif msg.type == "end_of_track": result.append(End()) end[i] = True # Find next event tracks[i] += 1 wait[i] = None else: wait[i] -= firstt if not (False in end): loop = False return result