sonnum/core/room.py
2025-08-13 22:43:56 +03:00

100 lines
2.4 KiB
Python

import wave
import math
from .soundnode import *
from .nodes.sink import *
TAU = 2 * math.pi
def stb(i: int, bytelen: int) -> bytes:
return i.to_bytes(bytelen, byteorder='little', signed=True)
class Room:
def __init__(self):
self.nodes = []
self.sample_rate = 44100
self.speed_of_sound = 343 / float(self.sample_rate) # m/tick
self.set_bit_depth(24)
self.left_sink = SinkNode('left', self)
self.left_sink.start_location = (-0.3, 0, 0)
self.right_sink = SinkNode('right', self)
self.right_sink.start_location = (0.3, 0, 0)
self.sine_multiplier = TAU / self.sample_rate
self.dist_cache = dict() # {((x,y,z),(x,y,z)) : dist}
self.running_program = None
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)
self.sample_width = int(self.bit_depth / 8.0)
def generate_frame(self, t):
for node in self.nodes:
node.tick_done = False
updates = True
order_of_passage = []
if not order_of_passage:
while updates:
updates = False
for node in self.nodes:
if not (t in node.r_amps):
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.calc_r_amps(t)
node.tick_done = True
updates = True
order_of_passage.append(node)
else:
for node in order_of_passage:
node.calc_r_amps(t)
left_total_amp = self.max_amp * sum([vol * math.sin(self.sine_multiplier * freq * t) for freq, vol in self.left_sink.r_amps[t].items()])
right_total_amp = self.max_amp * sum([vol * math.sin(self.sine_multiplier * freq * t) for freq, vol in self.right_sink.r_amps[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
return stb(int(left_total_amp), self.sample_width), stb(int(right_total_amp), self.sample_width)