Initial commit
This commit is contained in:
commit
778fe31c38
35 changed files with 1849 additions and 0 deletions
BIN
fugue1.mid
Normal file
BIN
fugue1.mid
Normal file
Binary file not shown.
11
level1.py
Normal file
11
level1.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
"""
|
||||
Niveau 1
|
||||
Jouer des sons
|
||||
"""
|
||||
|
||||
import synthlib, math
|
||||
|
||||
sound_encoder = synthlib.SoundEncoder()
|
||||
for i in range(13):
|
||||
for t in range(4000):
|
||||
sound_encoder.write_sample(math.sin(t/16000*440*2**(i/12)*2*math.pi))
|
||||
18
level2.py
Normal file
18
level2.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
"""
|
||||
Niveau 2
|
||||
Jouer une mélodie
|
||||
"""
|
||||
|
||||
import synthlib, math
|
||||
|
||||
music = [
|
||||
(1, 0),
|
||||
(1, 2),
|
||||
(2, 4)
|
||||
]
|
||||
|
||||
sound_encoder = synthlib.SoundEncoder()
|
||||
for note in music:
|
||||
for t in range(note[0]*4000):
|
||||
sound_encoder.write_sample(math.sin(t/16000*440*2**(note[1]/12)*2*math.pi))
|
||||
|
||||
22
level3.py
Normal file
22
level3.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
Niveau 3
|
||||
Jouer des accords
|
||||
|
||||
Ne pas introduire le facteur d'amplitude avant d'avoir entendu le résultat sans lui
|
||||
"""
|
||||
|
||||
import synthlib, math
|
||||
|
||||
music = [
|
||||
(1, [-9]),
|
||||
(1, [-5]),
|
||||
(1, [-2]),
|
||||
(3, [-9, -5, -2])
|
||||
]
|
||||
|
||||
sound_encoder = synthlib.SoundEncoder()
|
||||
for note in music:
|
||||
for t in range(note[0]*6000):
|
||||
sound_encoder.write_sample(sum([0.3*math.sin(t/16000*440*2**(i/12)*2*math.pi) for i in note[1]]))
|
||||
|
||||
|
||||
42
level4.py
Normal file
42
level4.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
"""
|
||||
Niveau 4
|
||||
Jouer des accords
|
||||
|
||||
Ne pas introduire le facteur d'amplitude avant d'avoir entendu le résultat sans lui
|
||||
"""
|
||||
|
||||
import synthlib, math, sys
|
||||
|
||||
music = synthlib.midi()
|
||||
|
||||
active = {}
|
||||
wait = 0
|
||||
tt = 0
|
||||
t = 0
|
||||
loop = True
|
||||
|
||||
sound_encoder = synthlib.SoundEncoder()
|
||||
while loop:
|
||||
if wait == 0:
|
||||
while True:
|
||||
event = music[tt]
|
||||
tt += 1
|
||||
if event.type == synthlib.Note:
|
||||
if event.velocity == 0:
|
||||
if event.number in active:
|
||||
active.pop(event.number)
|
||||
sys.stderr.write("pop "+str(event.number)+"\n")
|
||||
else:
|
||||
active[event.number] = event.velocity
|
||||
sys.stderr.write(str(event.velocity)+" "+str(event.number)+" ("+str(len(active))+")\n")
|
||||
elif event.type == synthlib.Wait:
|
||||
wait = int(event.value*8000)
|
||||
break
|
||||
elif event.type == synthlib.End:
|
||||
loop = False
|
||||
break
|
||||
else:
|
||||
wait -= 1
|
||||
|
||||
sound_encoder.write_sample(sum([active[number]*math.sin(t/16000*440*2**(number/12)*2*math.pi) for number in active])/max(1,len(active)))
|
||||
t += 1
|
||||
70
level5.py
Normal file
70
level5.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""
|
||||
Niveau 5
|
||||
Nouveaux instruments
|
||||
"""
|
||||
|
||||
import synthlib, math, sys
|
||||
|
||||
rate = 44100
|
||||
|
||||
def sine(t, f):
|
||||
return math.sin(t/rate*f*2*math.pi)
|
||||
|
||||
def cosine(t, f):
|
||||
return math.cosin(t/rate*f*2*math.pi)
|
||||
|
||||
def sine2(t, f):
|
||||
return math.atan(math.sin(t/rate*f*2*math.pi))
|
||||
|
||||
def sine3(t, f):
|
||||
return (sine(t,f) - sine2(t,f))*math.pi
|
||||
|
||||
def sine4(t, f):
|
||||
return (cosine(t,f) - sine3(t,f))*2
|
||||
|
||||
polynome = [32, 30, 8, 4, 2, 1]
|
||||
lpolynome = len(polynome)
|
||||
spolynome = sum(polynome)
|
||||
def polynomial(t, f):
|
||||
return sum(j*math.sin(t/rate*f*2*math.pi*i) for i,j in enumerate(polynome)) / spolynome
|
||||
|
||||
music = synthlib.midi()
|
||||
|
||||
active = {}
|
||||
wait = 0
|
||||
tt = 0
|
||||
t = 0
|
||||
loop = True
|
||||
|
||||
sound_encoder = synthlib.SoundEncoder(env={
|
||||
"sampling": rate,
|
||||
"signed": True,
|
||||
"verbose": False,
|
||||
"bits": 32,
|
||||
"msbfirst": True,
|
||||
"tempo": 130
|
||||
})
|
||||
while loop:
|
||||
if wait == 0:
|
||||
while True:
|
||||
event = music[tt]
|
||||
tt += 1
|
||||
if event.type == synthlib.Note:
|
||||
if event.velocity == 0:
|
||||
if event.number in active:
|
||||
active.pop(event.number)
|
||||
else:
|
||||
active[event.number] = event.velocity
|
||||
#sys.stderr.write(str(event.velocity)+"\n")
|
||||
elif event.type == synthlib.Wait:
|
||||
wait = int(event.value*rate/2)
|
||||
break
|
||||
elif event.type == synthlib.End:
|
||||
loop = False
|
||||
break
|
||||
else:
|
||||
wait -= 1
|
||||
|
||||
sound_encoder.write_sample(sum([active[number]*polynomial(t, 440*2**(number/12)) for number in active])/max(1,len(active)))
|
||||
t += 1
|
||||
|
||||
29
loop.py
Normal file
29
loop.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import synthlib, math, sys
|
||||
|
||||
rate = 44100
|
||||
|
||||
# freq is multiplied by factor every period
|
||||
def sine(t, period, decay, factor, full_period):
|
||||
tt = (t + decay) % full_period
|
||||
if tt == 0:
|
||||
volume = 0
|
||||
elif tt <= full_period/2:
|
||||
volume = tt/(full_period/2)
|
||||
else:
|
||||
volume = (full_period-tt)/(full_period/2)
|
||||
freq_factor = 27.5*factor**(tt/period)
|
||||
return volume**2*math.sin(tt*freq_factor/rate*2*math.pi)
|
||||
|
||||
nb = 3
|
||||
|
||||
sound_encoder = synthlib.SoundEncoder(env={
|
||||
"sampling": rate,
|
||||
"signed": True,
|
||||
"verbose": False,
|
||||
"bits": 32,
|
||||
"msbfirst": True,
|
||||
"tempo": 130
|
||||
})
|
||||
for t in range(rate*16):
|
||||
sound_encoder.write_sample(sum([sine(t, rate, i*rate/nb*4, 2, rate*4) for i in range(nb)])/nb)
|
||||
|
||||
BIN
mario.mid
Normal file
BIN
mario.mid
Normal file
Binary file not shown.
13
pub/level1.py
Normal file
13
pub/level1.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"""
|
||||
Niveau 1
|
||||
Jouer un son
|
||||
"""
|
||||
|
||||
# Permet d'utiliser les fonctions audio
|
||||
import synth
|
||||
|
||||
# crée un encodeur audio
|
||||
encoder = synth.Encoder()
|
||||
|
||||
# écrit un échantillon
|
||||
encoder.write(0)
|
||||
8
pub/level2.py
Normal file
8
pub/level2.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
"""
|
||||
Niveau 2
|
||||
Jouer des notes
|
||||
"""
|
||||
|
||||
import synth
|
||||
|
||||
encoder = synth.Encoder()
|
||||
21
pub/level3.py
Normal file
21
pub/level3.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
"""
|
||||
Niveau 3
|
||||
Jouer une mélodie
|
||||
"""
|
||||
|
||||
import synth
|
||||
|
||||
encoder = synth.Encoder()
|
||||
|
||||
# Liste de notes, sous la forme [durée, note]
|
||||
music = [
|
||||
[1, 4],
|
||||
[1, 3],
|
||||
[1, 4],
|
||||
[1, 3],
|
||||
[1, 4],
|
||||
[1, -1],
|
||||
[1, 2],
|
||||
[1, 0],
|
||||
[2, -3]
|
||||
]
|
||||
18
pub/level4.py
Normal file
18
pub/level4.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
"""
|
||||
Niveau 4
|
||||
Jouer des accords
|
||||
"""
|
||||
|
||||
import synth
|
||||
|
||||
encoder = synth.Encoder()
|
||||
|
||||
# Liste d'accords, sous la forme [durée, [note, note, note, ...]]
|
||||
music = [
|
||||
[2, [5, 9]],
|
||||
[2, [4, 8]],
|
||||
[1, [-12]],
|
||||
[1, [0, 2, 4]],
|
||||
[1, [-10]],
|
||||
[1, [2, 4, 6]],
|
||||
]
|
||||
13
pub/level5.py
Normal file
13
pub/level5.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"""
|
||||
Niveau 5
|
||||
Jouer une partition
|
||||
"""
|
||||
|
||||
import synth
|
||||
|
||||
encoder = synth.Encoder()
|
||||
|
||||
music = synth.Midi()
|
||||
|
||||
#for notes in music:
|
||||
# notes est une liste dont les éléments sont [piste, note, force]
|
||||
13
pub/level6.py
Normal file
13
pub/level6.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"""
|
||||
Niveau 6
|
||||
Appliquer des filtres
|
||||
* Écho : garder une copie du son et la jouer en décalé et moins fort
|
||||
* Wah-wah : atténuer le son périodiquement
|
||||
* Phaser : comme l'écho, mais en rejouant périodiquement des échantillons plus ou moins récents
|
||||
"""
|
||||
|
||||
import synth
|
||||
|
||||
encoder = synth.Encoder()
|
||||
|
||||
music = synth.Midi()
|
||||
237
pub/synth.py
Normal file
237
pub/synth.py
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
#!/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], getarg(2, 130))
|
||||
|
||||
_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
|
||||
BIN
pub/tutorial.pdf
Normal file
BIN
pub/tutorial.pdf
Normal file
Binary file not shown.
BIN
reunion.mid
Normal file
BIN
reunion.mid
Normal file
Binary file not shown.
4
sheet
Normal file
4
sheet
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Activer le son:
|
||||
amixer set Master 1+ toggle
|
||||
|
||||
python level5.py tetris2.mid | aplay -r 44100 -f S32_LE
|
||||
BIN
stage/__pycache__/synth.cpython-311.pyc
Normal file
BIN
stage/__pycache__/synth.cpython-311.pyc
Normal file
Binary file not shown.
11
stage/level1.py
Normal file
11
stage/level1.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
"""
|
||||
Niveau 1
|
||||
Jouer un son
|
||||
"""
|
||||
|
||||
import synth
|
||||
|
||||
encoder = synth.Encoder()
|
||||
for t in range(64000):
|
||||
#encoder.write(math.sin(t/16000*400))
|
||||
encoder.write(t/16000*400%1)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue