From b20299ff78a33b3f99335723de7c9ee5b274a81c Mon Sep 17 00:00:00 2001 From: alex-de Date: Fri, 8 Aug 2025 15:23:16 +0300 Subject: [PATCH] test version; does not work --- .gitignore | 1 + core/room.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++ core/soundnode.py | 45 +++++++++++++++++++ test.py | 28 ++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 core/room.py create mode 100644 core/soundnode.py create mode 100644 test.py diff --git a/.gitignore b/.gitignore index c253841..a22ba61 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ testfiles/ env/ build/ osm/env +*.wav osm/cache *.jpg *.bin diff --git a/core/room.py b/core/room.py new file mode 100644 index 0000000..5251e2d --- /dev/null +++ b/core/room.py @@ -0,0 +1,110 @@ +import wave +import math + +from .soundnode import * + +TAU = 2 * math.pi + +def stb(i: int) -> bytes: + return i.to_bytes(3, byteorder='big', signed=True) + +class Room: + + def __init__(self): + + self.speed_of_sound = 330 #m/s + self.dissipation_quotient = 0.9993 + self.sample_rate = 44100 + 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) + + def set_bit_depth(self, bit_depth): + + self.bit_depth = bit_depth + self.max_amp = int((2**bit_depth) / 2.0 - 1) + self.min_amp = -int((2**bit_depth) / 2.0) + + def link_air(self, node1, node2): + + if not node2 in node1.air_out: + node1.air_out.append(node2) + + if not node1 in node2.air_in: + node2.air_in.append(node1) + + def link_wire(self, node1, node2): + + if not node2 in node1.wire_out: + node1.wire_out.append(node2) + + if not node1 in node2.wire_in: + node2.wire_in.append(node1) + + 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)) + wav.setsampwidth(3) + wav.setframerate(self.sample_rate) + + frames = [] + + start_t = start_t_sec * self.sample_rate + end_t = end_t_sec * self.sample_rate + + for t in range(start_t, end_t): + + if t%1000 == 0: + print(t) + f = self.lowest_freq + + left_total_amp = 0 + right_total_amp = 0 + + left_amps = [] + right_amps = [] + + while f < self.highest_freq: + + for in_node in self.left_sink.air_in: + left_amps.append(in_node.amp_at_tick_by_air(f, t, self.left_sink)) + + for in_node in self.left_sink.wire_in: + left_amps.append(in_node.amp_at_tick(f, t)) + + for in_node in self.right_sink.air_in: + right_amps.append(in_node.amp_at_tick_by_air(f, t, self.right_sink)) + + for in_node in self.right_sink.wire_in: + right_amps.append(in_node.amp_at_tick(f, t)) + + f += self.freq_sample_step + + left_total_amp = sum(left_amps) + right_total_amp = sum(right_amps) + + 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))) + frames.append(stb(int(right_total_amp))) + + wav.writeframes(b''.join(frames)) \ No newline at end of file diff --git a/core/soundnode.py b/core/soundnode.py new file mode 100644 index 0000000..6461887 --- /dev/null +++ b/core/soundnode.py @@ -0,0 +1,45 @@ +import math +TAU = 2 * math.pi + +class SoundNode: + + def __init__(self, name, room): + + self.name = name + self.room = room + + self.air_in = [] + self.air_out = [] + self.wire_in = [] + self.wire_out = [] + + self.start_location = (0, 0, 0) + + def location(self, t): + + # Location of the soundnote (x,y,z) in meters + # at time t. + + return self.start_location + + def distance_to_node(self, other_node, t): + + loc = self.location(t) + other_loc = other_node.location(t) + + 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 amp_at_tick(self, f, t): + + return self.room.max_amp * self.frequency_max_rel_amp(f, t) * math.sin(TAU * f * t/float(self.room.sample_rate)) + + def amp_at_tick_by_air(self, f, t, node): + + dist = self.distance_to_node(node, t) + t = t - ((self.room.speed_of_sound*dist)/float(self.room.sample_rate)) + + return self.room.max_amp * self.frequency_max_rel_amp(f, t) * math.sin(TAU * f * t/float(self.room.sample_rate)) + \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..e218749 --- /dev/null +++ b/test.py @@ -0,0 +1,28 @@ +from core.room import Room +from core.soundnode import SoundNode + +R = Room() +R.left_sink.start_location = (-1, 0, 0) +R.right_sink.start_location = (2, 0, 0) + +class SineNode(SoundNode): + + def __init__(self, freq, room): + + super().__init__("sine", room) + self.freq = freq + self.volume = 0.8 + + def frequency_max_rel_amp(self, f, t): + + if f == self.freq: + return self.volume + + return 0 + +sn = SineNode(440, R) + +R.link_wire(sn, R.left_sink) +R.link_wire(sn, R.right_sink) + +R.record('test5.wav', 0, 2) \ No newline at end of file