jsb-synth/synthlib.py
2025-02-27 00:41:31 +01:00

246 lines
5.4 KiB
Python

#!/usr/bin/python3
import sys, os, mido
DEFAULT_ENV = {
"sampling": 16000,
"signed": True,
"verbose": False,
"bits": 16,
"msbfirst": True,
"tempo": 130
}
FP = sys.stdout
class SoundEncoder:
def __init__(self, env=DEFAULT_ENV, fp=sys.stdout, fp_log=sys.stderr):
self.env = env
self.fp = fp if fp.mode == "wb" else os.fdopen(fp.fileno(), 'wb')
self.fp_log = fp_log
def write_sample(self, amplitude):
smax = 2**(self.env["bits"]-self.env["signed"])-1
if self.env["signed"]:
v = int(amplitude*smax)
w = v
s = v < 0
v = abs(v)
if s:
v = smax-v
v |= (s<<(self.env["bits"]-1))
else:
v = int((amplitude*smax+smax)/2)
w = v
if self.env["msbfirst"]:
i = 0
while i < self.env["bits"]:
if self.fp.write(bytes([(v>>i)&255])) > 1:
self.fp_log.write(str(v))
b = bin((v>>i)&255)[2:]
if self.env["verbose"]:
self.fp_log.write("0"*(8-len(b))+b)
i += 8
else:
i = self.env["bits"]
while i > 0:
i -= 8
self.fp.write(bytes([(v>>i)&255]))
b = bin((v>>i)&255)[2:]
if self.env["verbose"]:
self.fp_log.write("0"*(8-len(b))+b)
if self.env["verbose"]:
self.fp_log.write(" "+"0"*(self.env["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():
return from_midi3(sys.argv[1])
def from_midi(filename, env=DEFAULT_ENV):
result = []
t = 0
tempo = env["tempo"]
mid = mido.MidiFile(filename)
for i, track in enumerate(mid.tracks):
for msg in track:
if msg.type == "set_tempo":
tempo = 6e7/msg.tempo
elif msg.type == "note_on":
if msg.time != 0:
result.append(Wait(msg.time/tempo/4))
result.append(Note(msg.note-50, msg.velocity/100, msg.channel))
elif msg.type == "end_of_track":
result.append(Wait(msg.time/tempo/4))
result.append(End())
return result
def from_midi2(filename, env=DEFAULT_ENV):
mid = mido.MidiFile(filename)
result = []
tracks = [0 for i in mid.tracks]
tempos = [env["tempo"] for i in mid.tracks]
wait = [None for i in mid.tracks]
end = [True for i in mid.tracks]
t = 0
loop = True
while loop:
loop = False
for i, track in enumerate(mid.tracks):
if end[i]:
loop = True
continue
if wait[i] == None:
wait[i] = track[tracks[i]].time/tempos[i]/4
#result.append(Wait(track[tracks[i]].time/tempo/4))
if wait[i] <= 0:
msg = track[tracks[i]]
if t > 0:
result.append(Wait(t))
t = 0
if msg.type == "set_tempo":
tempos[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] = False
tracks[i] += 1
if len(track) > tracks[i]:
wait[i] = track[tracks[i]].time/tempos[i]/4
else:
wait[i] -= 1
print(t)
t += 1
print(result)
exit()
return result
def from_midi3(filename, env=DEFAULT_ENV):
mid = mido.MidiFile(filename)
result = []
tracks = [0 for i in mid.tracks]
tempo = [env["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
###
"""loop = False
for i, track in enumerate(mid.tracks):
if end[i]:
loop = True
continue
if wait[i] == None:
wait[i] = track[tracks[i]].time/tempo[i]/4
#result.append(Wait(track[tracks[i]].time/tempo/4))
if wait[i] <= 0:
msg = track[tracks[i]]
if t > 0:
result.append(Wait(t))
t = 0
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] = False
tracks[i] += 1
if len(track) > tracks[i]:
wait[i] = track[tracks[i]].time/tempo[i]/4
else:
wait[i] -= 1
print(t)
t += 1"""
return result