revamp
This commit is contained in:
parent
9560346cdd
commit
72e828aaae
3 changed files with 183 additions and 41 deletions
145
core/room.py
145
core/room.py
|
|
@ -5,25 +5,50 @@ from .soundnode import *
|
|||
|
||||
TAU = 2 * math.pi
|
||||
|
||||
def stb(i: int) -> bytes:
|
||||
return i.to_bytes(3, byteorder='little', signed=True)
|
||||
def stb(i: int, bytelen: int) -> bytes:
|
||||
return i.to_bytes(bytelen, byteorder='little', signed=True)
|
||||
|
||||
class SinkNode(SoundNode):
|
||||
|
||||
def __init__(self, name, room):
|
||||
|
||||
super().__init__(name, room)
|
||||
|
||||
def fill_amp_cache(self, t):
|
||||
# This function returns volumes of each relevant freq
|
||||
# at tick t
|
||||
|
||||
res = dict()
|
||||
|
||||
for source_node in self.wire_in:
|
||||
for freq, vol in self.sample_freqs_by_wire(source_node, t).items():
|
||||
if not freq in res:
|
||||
res[freq] = vol
|
||||
else:
|
||||
res[freq] += vol
|
||||
|
||||
for source_node in self.air_in:
|
||||
for freq, vol in self.sample_freqs_by_air(source_node, t).items():
|
||||
if not freq in res:
|
||||
res[freq] = vol
|
||||
else:
|
||||
res[freq] += vol
|
||||
|
||||
self.amp_cache[t] = res
|
||||
|
||||
class Room:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.nodes = []
|
||||
|
||||
self.dissipation_quotient = 0.9993
|
||||
self.sample_rate = 44100
|
||||
self.speed_of_sound = 343 / float(self.sample_rate) #m/tick
|
||||
self.speed_of_sound = 343 / float(self.sample_rate) # m/tick
|
||||
self.set_bit_depth(24)
|
||||
|
||||
self.lowest_freq = 430
|
||||
self.highest_freq = 450
|
||||
self.freq_sample_step = 1
|
||||
|
||||
self.producers = []
|
||||
self.left_sink = SoundNode('LEFT', self)
|
||||
self.right_sink = SoundNode('RIGHT', self)
|
||||
self.left_sink = SinkNode('LEFT', self)
|
||||
self.right_sink = SinkNode('RIGHT', self)
|
||||
|
||||
self.sine_multiplier = TAU / self.sample_rate
|
||||
|
||||
|
|
@ -32,29 +57,98 @@ class Room:
|
|||
self.bit_depth = bit_depth
|
||||
self.max_amp = int((2**bit_depth) / 2.0 - 1)
|
||||
self.min_amp = -int((2**bit_depth) / 2.0)
|
||||
self.sample_width = int(self.bit_depth / 8.0)
|
||||
|
||||
def link_air(self, node1, node2):
|
||||
|
||||
if not node2 in node1.air_out:
|
||||
node1.air_out.append(node2)
|
||||
def generate_frames(self, start_t_sec, end_t_sec):
|
||||
|
||||
if not node1 in node2.air_in:
|
||||
node2.air_in.append(node1)
|
||||
frames = []
|
||||
|
||||
def link_wire(self, node1, node2):
|
||||
start_t = start_t_sec * self.sample_rate
|
||||
end_t = end_t_sec * self.sample_rate
|
||||
|
||||
if not node2 in node1.wire_out:
|
||||
node1.wire_out.append(node2)
|
||||
for t in range(start_t, end_t):
|
||||
|
||||
if not node1 in node2.wire_in:
|
||||
node2.wire_in.append(node1)
|
||||
for node in self.nodes:
|
||||
node.tick_done = False
|
||||
|
||||
if t%1000 == 0:
|
||||
print(t)
|
||||
|
||||
updates = True
|
||||
|
||||
while updates:
|
||||
|
||||
updates = False
|
||||
|
||||
for node in self.nodes:
|
||||
|
||||
if not (t in node.amp_cache):
|
||||
|
||||
has_unprocessed_inputs = False
|
||||
|
||||
for in_node in node.air_in:
|
||||
if not in_node.tick_done:
|
||||
has_unprocessed_inputs = True
|
||||
break
|
||||
|
||||
if not has_unprocessed_inputs:
|
||||
for in_node in node.wire_in:
|
||||
if not in_node.tick_done:
|
||||
has_unprocessed_inputs = True
|
||||
break
|
||||
|
||||
if not has_unprocessed_inputs:
|
||||
|
||||
node.fill_amp_cache(t)
|
||||
node.tick_done = True
|
||||
updates = True
|
||||
|
||||
left_total_amp = self.max_amp * sum([vol for freq, vol in self.left_sink.amp_cache[t].items()])
|
||||
right_total_amp = self.max_amp * sum([vol for freq, vol in self.right_sink.amp_cache[t].items()])
|
||||
|
||||
if left_total_amp > self.max_amp:
|
||||
left_total_amp = self.max_amp
|
||||
|
||||
if left_total_amp < self.min_amp:
|
||||
left_total_amp = self.min_amp
|
||||
|
||||
if right_total_amp > self.max_amp:
|
||||
right_total_amp = self.max_amp
|
||||
|
||||
if right_total_amp < self.min_amp:
|
||||
right_total_amp = self.min_amp
|
||||
|
||||
frames.append(stb(int(left_total_amp), self.sample_width))
|
||||
frames.append(stb(int(right_total_amp), self.sample_width))
|
||||
|
||||
return frames
|
||||
|
||||
def write_frames_to_wavefile(self, fn, frames):
|
||||
|
||||
with wave.open(fn, 'wb') as wav:
|
||||
|
||||
wav.setnchannels(2)
|
||||
|
||||
wav.setsampwidth(self.sample_width)
|
||||
wav.setframerate(self.sample_rate)
|
||||
wav.writeframes(b''.join(frames))
|
||||
|
||||
def record(self, fn, start_t_sec, end_t_sec):
|
||||
|
||||
frames = self.generate_frames(start_t_sec, end_t_sec)
|
||||
self.write_frames_to_wavefile(fn, frames)
|
||||
|
||||
|
||||
"""
|
||||
def record(self, fn, start_t_sec, end_t_sec):
|
||||
|
||||
with wave.open(fn, 'wb') as wav:
|
||||
|
||||
wav.setnchannels(2)
|
||||
wav.setsampwidth(int(self.bit_depth / 8.0))
|
||||
|
||||
self.sample_width = int(self.bit_depth / 8.0)
|
||||
wav.setsampwidth(self.sample_width)
|
||||
wav.setframerate(self.sample_rate)
|
||||
|
||||
frames = []
|
||||
|
|
@ -90,8 +184,8 @@ class Room:
|
|||
|
||||
f += self.freq_sample_step
|
||||
|
||||
left_total_amp = sum(left_amps)
|
||||
right_total_amp = sum(right_amps)
|
||||
left_total_amp = self.max_amp * sum(left_amps)
|
||||
right_total_amp = self.max_amp * sum(right_amps)
|
||||
|
||||
if left_total_amp > self.max_amp:
|
||||
left_total_amp = self.max_amp
|
||||
|
|
@ -105,7 +199,8 @@ class Room:
|
|||
if right_total_amp < self.min_amp:
|
||||
right_total_amp = self.min_amp
|
||||
|
||||
frames.append(stb(int(left_total_amp)))
|
||||
frames.append(stb(int(right_total_amp)))
|
||||
frames.append(stb(int(left_total_amp), self.sample_width))
|
||||
frames.append(stb(int(right_total_amp), self.sample_width))
|
||||
|
||||
wav.writeframes(b''.join(frames))
|
||||
"""
|
||||
|
|
@ -7,14 +7,17 @@ class SoundNode:
|
|||
|
||||
self.name = name
|
||||
self.room = room
|
||||
self.room.nodes.append(self)
|
||||
|
||||
self.amp_cache = dict() # {tick: {freq: amp}}
|
||||
|
||||
self.air_in = []
|
||||
self.air_out = []
|
||||
self.wire_in = []
|
||||
self.wire_out = []
|
||||
|
||||
self.start_location = (0, 0, 0)
|
||||
|
||||
self.tick_done = False
|
||||
|
||||
def location(self, t):
|
||||
|
||||
# Location of the soundnote (x,y,z) in meters
|
||||
|
|
@ -29,17 +32,49 @@ class SoundNode:
|
|||
|
||||
return (loc[0]-other_loc[0])**2 + (loc[1]-other_loc[1])**2 + (loc[2]-other_loc[2])**2
|
||||
|
||||
def frequency_max_rel_amp(self, f, t):
|
||||
return 0
|
||||
|
||||
def fill_amp_cache(self, t):
|
||||
|
||||
self.amp_cache[t] = dict()
|
||||
|
||||
def sample_freqs_by_wire(self, source_node, current_t):
|
||||
|
||||
if current_t in source_node.amp_cache:
|
||||
return source_node.amp_cache[current_t]
|
||||
|
||||
return dict()
|
||||
|
||||
def sample_freqs_by_air(self, source_node, current_t):
|
||||
|
||||
dist = self.distance_to_node(source_node, current_t)
|
||||
sample_t = current_t - int(dist / self.room.speed_of_sound)
|
||||
|
||||
if sample_t in source_node.amp_cache:
|
||||
return source_node.amp_cache[sample_t]
|
||||
|
||||
return dict()
|
||||
|
||||
def add_air_output(self, out_node):
|
||||
|
||||
if not self in out_node.air_in:
|
||||
out_node.air_in.append(self)
|
||||
|
||||
def add_wire_output(self, out_node):
|
||||
|
||||
if not self in out_node.wire_in:
|
||||
out_node.wire_in.append(self)
|
||||
|
||||
"""
|
||||
|
||||
def amp_at_tick(self, f, t):
|
||||
|
||||
return self.room.max_amp * self.frequency_max_rel_amp(f, t) * math.sin(self.room.sine_multiplier * f * t)
|
||||
return self.frequency_max_rel_amp(f, t) * math.sin(self.room.sine_multiplier * f * t)
|
||||
|
||||
def amp_at_tick_by_air(self, f, t, node):
|
||||
|
||||
dist = self.distance_to_node(node, t)
|
||||
t = t - int(dist / self.room.speed_of_sound)
|
||||
|
||||
return self.room.max_amp * self.frequency_max_rel_amp(f, t) * math.sin(self.room.sine_multiplier * f * t)
|
||||
return self.frequency_max_rel_amp(f, t) * math.sin(self.room.sine_multiplier * f * t)
|
||||
"""
|
||||
|
||||
26
test.py
26
test.py
|
|
@ -14,16 +14,28 @@ class SineNode(SoundNode):
|
|||
self.freq = freq
|
||||
self.volume = 0.8
|
||||
|
||||
def frequency_max_rel_amp(self, f, t):
|
||||
|
||||
if (f > self.freq-20) and (f < self.freq+20):
|
||||
return self.volume*0.05*math.sin(f)
|
||||
def calc_freqs_volumes(self, t):
|
||||
# This function returns volumes of each relevant freq
|
||||
# at tick t
|
||||
|
||||
return 0
|
||||
res = dict()
|
||||
for freq in range(self.freq-20, self.freq+20):
|
||||
res[freq] = self.volume*0.05*math.sin(freq)
|
||||
return res
|
||||
|
||||
def fill_amp_cache(self, t):
|
||||
|
||||
tdct = dict()
|
||||
for freq, vol in self.calc_freqs_volumes(t).items():
|
||||
tdct[freq] = vol * math.sin(self.room.sine_multiplier * freq * t)
|
||||
|
||||
self.amp_cache[t] = tdct
|
||||
|
||||
sn = SineNode(440, R)
|
||||
|
||||
R.link_air(sn, R.left_sink)
|
||||
R.link_air(sn, R.right_sink)
|
||||
sn.add_air_output(R.left_sink)
|
||||
sn.add_air_output(R.right_sink)
|
||||
|
||||
R.record('test5.wav', 0, 6)
|
||||
|
||||
R.record('test6.wav', 0, 2)
|
||||
Loading…
Add table
Reference in a new issue