enhancements
This commit is contained in:
parent
35e8a2c448
commit
0a3bc7ab20
10 changed files with 281 additions and 93 deletions
|
|
@ -26,7 +26,7 @@ class TimedAction:
|
|||
self.end(node)
|
||||
|
||||
else:
|
||||
self.progress(t - self.start_t, node)
|
||||
self.progress((t - self.start_t) / float(self.duration_t), node)
|
||||
|
||||
# REIMPLEMENT
|
||||
|
||||
|
|
@ -38,6 +38,6 @@ class TimedAction:
|
|||
|
||||
pass
|
||||
|
||||
def progress(self, rel_t, node):
|
||||
def progress(self, prc, node):
|
||||
|
||||
pass
|
||||
82
core/actions/basics.py
Normal file
82
core/actions/basics.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
from ..action import TimedAction
|
||||
|
||||
class PlayAction(TimedAction):
|
||||
|
||||
def __init__(self, start_t, duration_t, nodes, program):
|
||||
|
||||
super().__init__(start_t, duration_t, nodes, program)
|
||||
|
||||
def start(self, node):
|
||||
|
||||
node.active = True
|
||||
|
||||
def end(self, node):
|
||||
|
||||
node.active = False
|
||||
|
||||
|
||||
class NoteAction(TimedAction):
|
||||
|
||||
def __init__(self, note, start_t, duration_t, nodes, program):
|
||||
|
||||
super().__init__(start_t, duration_t, nodes, program)
|
||||
self.note = note
|
||||
|
||||
def start(self, node):
|
||||
|
||||
node.freq = self.program.note_to_freq(self.note)
|
||||
node.active = True
|
||||
|
||||
def end(self, node):
|
||||
|
||||
node.active = False
|
||||
|
||||
class LinearPitchTransition(TimedAction):
|
||||
|
||||
def __init__(self, start_note, end_node, start_t, duration_t, nodes, program):
|
||||
|
||||
super().__init__(start_t, duration_t, nodes, program)
|
||||
self.start_note = start_note
|
||||
self.end_node = end_node
|
||||
|
||||
self.start_freq = self.program.note_to_freq(start_note)
|
||||
self.end_freq = self.program.note_to_freq(end_node)
|
||||
|
||||
self.freq_range = self.end_freq - self.start_freq
|
||||
|
||||
def start(self, node):
|
||||
|
||||
node.freq = self.start_freq
|
||||
node.active = True
|
||||
|
||||
def progress(self, prc, node):
|
||||
|
||||
node.freq = self.start_freq + (self.freq_range * prc)
|
||||
|
||||
def end(self, node):
|
||||
|
||||
node.active = False
|
||||
|
||||
|
||||
class LinearSpatialTransition(TimedAction):
|
||||
|
||||
def __init__(self, start_coords, end_coords, start_t, duration_t, nodes, program):
|
||||
|
||||
super().__init__(start_t, duration_t, nodes, program)
|
||||
self.start_coords = start_coords
|
||||
self.end_coords = end_coords
|
||||
|
||||
self.delta_coords = [end_coords[0] - start_coords[0], end_coords[1] - start_coords[1], end_coords[2] - start_coords[2]]
|
||||
|
||||
def start(self, node):
|
||||
|
||||
node.start_location = self.start_coords
|
||||
|
||||
def progress(self, prc, node):
|
||||
|
||||
l = []
|
||||
l.append(self.start_coords[0] + (self.delta_coords[0] * prc))
|
||||
l.append(self.start_coords[1] + (self.delta_coords[1] * prc))
|
||||
l.append(self.start_coords[2] + (self.delta_coords[2] * prc))
|
||||
node.start_location = tuple(l)
|
||||
|
||||
27
core/nodes/sawtooth.py
Normal file
27
core/nodes/sawtooth.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import math
|
||||
|
||||
from ..soundnode import SoundNode
|
||||
|
||||
class SawtoothNode(SoundNode):
|
||||
|
||||
def __init__(self, freq, room):
|
||||
|
||||
super().__init__("saw", room)
|
||||
self.freq = freq
|
||||
self.harmonics_q = 50
|
||||
self.active = False
|
||||
self.volume = 0.5
|
||||
|
||||
def calc_r_amps(self, t):
|
||||
|
||||
if not self.active:
|
||||
self.r_amps[t] = dict()
|
||||
return
|
||||
|
||||
if not t in self.r_amps:
|
||||
self.r_amps[t] = dict()
|
||||
|
||||
for i in range(0, self.harmonics_q):
|
||||
|
||||
freq = self.freq*(i + 1)
|
||||
self.r_amps[t][freq] = self.volume / float(i+1)
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
import math
|
||||
|
||||
from ..soundnode import SoundNode
|
||||
from ..actions import TimedAction
|
||||
|
||||
class SineNode(SoundNode):
|
||||
|
||||
def __init__(self, freqs, room):
|
||||
def __init__(self, freq, room):
|
||||
|
||||
super().__init__("sine", room)
|
||||
self.freqs = freqs
|
||||
self.freq = freq
|
||||
self.active = False
|
||||
self.volume = 1
|
||||
|
||||
|
|
@ -17,44 +16,5 @@ class SineNode(SoundNode):
|
|||
if not self.active:
|
||||
self.r_amps[t] = dict()
|
||||
return
|
||||
|
||||
tdct = dict()
|
||||
|
||||
for freq in self.freqs:
|
||||
tdct[freq] = self.volume * math.sin(self.room.sine_multiplier * freq * t)
|
||||
|
||||
self.r_amps[t] = tdct
|
||||
|
||||
###
|
||||
|
||||
class PlayAction(TimedAction):
|
||||
|
||||
def __init__(self, start_t, duration_t, nodes, program):
|
||||
|
||||
super().__init__(start_t, duration_t, nodes, program)
|
||||
|
||||
def start(self, node):
|
||||
|
||||
node.active = True
|
||||
|
||||
def end(self, node):
|
||||
|
||||
node.active = False
|
||||
|
||||
|
||||
class NoteAction(TimedAction):
|
||||
|
||||
def __init__(self, note, start_t, duration_t, nodes, program):
|
||||
|
||||
super().__init__(start_t, duration_t, nodes, program)
|
||||
self.note = note
|
||||
|
||||
def start(self, node):
|
||||
|
||||
note_freq = self.program.note_to_freq(self.note)
|
||||
node.freqs = [note_freq]
|
||||
node.active = True
|
||||
|
||||
def end(self, node):
|
||||
|
||||
node.active = False
|
||||
self.r_amps[t] = {self.freq: self.volume}
|
||||
|
|
|
|||
|
|
@ -26,4 +26,7 @@ class SinkNode(SoundNode):
|
|||
res[freq] = vol
|
||||
else:
|
||||
res[freq] += vol
|
||||
self.r_amps[t] = res
|
||||
|
||||
self.r_amps[t] = res
|
||||
#if res:
|
||||
# print(self.r_amps[t])
|
||||
33
core/nodes/triangle.py
Normal file
33
core/nodes/triangle.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import math
|
||||
|
||||
from ..soundnode import SoundNode
|
||||
|
||||
class TriangleNode(SoundNode):
|
||||
|
||||
def __init__(self, freq, room):
|
||||
|
||||
super().__init__("trg", room)
|
||||
self.freq = freq
|
||||
self.harmonics_q = 30
|
||||
self.active = False
|
||||
self.volume = 0.8
|
||||
|
||||
def calc_r_amps(self, t):
|
||||
|
||||
if not self.active:
|
||||
self.r_amps[t] = dict()
|
||||
return
|
||||
|
||||
if not t in self.r_amps:
|
||||
self.r_amps[t] = dict()
|
||||
|
||||
for i in range(0, self.harmonics_q):
|
||||
|
||||
freq = self.freq*(i*2 + 1)
|
||||
|
||||
if i%2 == 0:
|
||||
m = 1
|
||||
else:
|
||||
m = -1
|
||||
|
||||
self.r_amps[t][freq] = m * self.volume / float((i*2 + 1)**2)
|
||||
|
|
@ -1,8 +1,14 @@
|
|||
import os
|
||||
import wave
|
||||
import math
|
||||
|
||||
from .room import Room
|
||||
|
||||
TAU = 2 * math.pi
|
||||
|
||||
def stb(i: int, bytelen: int) -> bytes:
|
||||
return i.to_bytes(bytelen, byteorder='little', signed=True)
|
||||
|
||||
|
||||
class Program:
|
||||
|
||||
|
|
@ -66,13 +72,75 @@ class Program:
|
|||
start_t = int(start_t_sec * self.room.sample_rate)
|
||||
end_t = int(end_t_sec * self.room.sample_rate)
|
||||
|
||||
order_of_passage = []
|
||||
|
||||
for t in range(start_t, end_t):
|
||||
|
||||
if t%1000 == 0:
|
||||
print(f'{int(t / float(end_t - start_t) * 100)}%', end='\r')
|
||||
|
||||
self.tick(t)
|
||||
left_frame, right_frame = self.room.generate_frame(t)
|
||||
|
||||
for node in self.room.nodes:
|
||||
node.tick_done = False
|
||||
|
||||
updates = True
|
||||
|
||||
if not order_of_passage:
|
||||
|
||||
while updates:
|
||||
|
||||
updates = False
|
||||
|
||||
for node in self.room.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.room.max_amp * sum([vol * math.sin(self.room.sine_multiplier * freq * t) for freq, vol in self.room.left_sink.r_amps[t].items()])
|
||||
right_total_amp = self.room.max_amp * sum([vol * math.sin(self.room.sine_multiplier * freq * t) for freq, vol in self.room.right_sink.r_amps[t].items()])
|
||||
|
||||
if left_total_amp > self.room.max_amp:
|
||||
left_total_amp = self.room.max_amp
|
||||
|
||||
if left_total_amp < self.room.min_amp:
|
||||
left_total_amp = self.room.min_amp
|
||||
|
||||
if right_total_amp > self.room.max_amp:
|
||||
right_total_amp = self.room.max_amp
|
||||
|
||||
if right_total_amp < self.room.min_amp:
|
||||
right_total_amp = self.room.min_amp
|
||||
|
||||
|
||||
left_frame, right_frame = stb(int(left_total_amp), self.room.sample_width), stb(int(right_total_amp), self.room.sample_width)
|
||||
|
||||
frames.append(left_frame)
|
||||
frames.append(right_frame)
|
||||
print('100%', end='\r')
|
||||
|
||||
return frames
|
||||
|
||||
|
|
@ -105,11 +173,13 @@ class Program:
|
|||
|
||||
cmd = ''
|
||||
while cmd != 'q':
|
||||
cmd = input('\n[vol {self.volume*100}%]>> ')
|
||||
cmd = input(f'\n[vol {self.volume*100}%]>> ')
|
||||
|
||||
if cmd.startswith('g '):
|
||||
import cProfile
|
||||
end_sec = float(cmd[2:])
|
||||
self.export(0, end_sec)
|
||||
cProfile.runctx("self.export(0, end_sec)", globals(), locals())
|
||||
#self.export(0, end_sec)
|
||||
|
||||
if cmd.startswith('vol '):
|
||||
vol = float(cmd[4:])
|
||||
|
|
|
|||
54
core/room.py
54
core/room.py
|
|
@ -42,40 +42,46 @@ class Room:
|
|||
for node in self.nodes:
|
||||
node.tick_done = False
|
||||
|
||||
if t%1000 == 0:
|
||||
print(t, end='\r', flush=True)
|
||||
|
||||
updates = True
|
||||
order_of_passage = []
|
||||
|
||||
while updates:
|
||||
if not order_of_passage:
|
||||
|
||||
updates = False
|
||||
|
||||
for node in self.nodes:
|
||||
while updates:
|
||||
|
||||
if not (t in node.r_amps):
|
||||
updates = False
|
||||
|
||||
for node in self.nodes:
|
||||
|
||||
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 (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:
|
||||
|
||||
node.calc_r_amps(t)
|
||||
node.tick_done = True
|
||||
updates = True
|
||||
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 for freq, vol in self.left_sink.r_amps[t].items()])
|
||||
right_total_amp = self.max_amp * sum([vol for freq, vol in self.right_sink.r_amps[t].items()])
|
||||
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
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ class SoundNode:
|
|||
|
||||
loc = self.location(t)
|
||||
other_loc = other_node.location(t)
|
||||
|
||||
diff_x = loc[0]-other_loc[0]
|
||||
diff_y = loc[1]-other_loc[1]
|
||||
diff_z = loc[2]-other_loc[2]
|
||||
return diff_x*diff_x + diff_y*diff_y + diff_z * diff_z
|
||||
return (loc[0]-other_loc[0])**2 + (loc[1]-other_loc[1])**2 + (loc[2]-other_loc[2])**2
|
||||
|
||||
def sample_r_amps_by_wire(self, source_node, current_t):
|
||||
|
|
@ -40,7 +43,9 @@ class SoundNode:
|
|||
sample_t = current_t - int(dist / self.room.speed_of_sound)
|
||||
|
||||
if sample_t in source_node.r_amps:
|
||||
return {f: a / float(dist) for f, a in source_node.r_amps[sample_t].items()}
|
||||
|
||||
attenuation = math.exp(-(dist/100.0))
|
||||
return {f: a * attenuation for f, a in source_node.r_amps[sample_t].items()}
|
||||
|
||||
return dict()
|
||||
|
||||
|
|
|
|||
38
test.py
38
test.py
|
|
@ -4,6 +4,9 @@ from core.room import Room
|
|||
from core.program import Program
|
||||
from core.soundnode import SoundNode
|
||||
from core.nodes.sinenode import *
|
||||
from core.nodes.triangle import *
|
||||
from core.nodes.sawtooth import *
|
||||
from core.actions.basics import *
|
||||
|
||||
class TestProgram(Program):
|
||||
|
||||
|
|
@ -12,31 +15,30 @@ class TestProgram(Program):
|
|||
super().__init__("testprogram")
|
||||
|
||||
def setup(self):
|
||||
|
||||
self.reset()
|
||||
|
||||
sn = SineNode([], self.room)
|
||||
sn = SineNode(440, self.room)
|
||||
sn2 = SawtoothNode(440, self.room)
|
||||
|
||||
sn.air_to(self.room.left_sink)
|
||||
#sn.air_to(self.room.right_sink)
|
||||
|
||||
NoteAction('A4', self.st(0), self.st(0.5), [sn], self)
|
||||
NoteAction('G4', self.st(1), self.st(0.5), [sn], self)
|
||||
NoteAction('F4', self.st(2), self.st(0.5), [sn], self)
|
||||
NoteAction('E4', self.st(3), self.st(0.5), [sn], self)
|
||||
|
||||
sn2 = SineNode([], self.room)
|
||||
|
||||
#sn2.air_to(self.room.left_sink)
|
||||
sn.air_to(self.room.right_sink)
|
||||
sn2.air_to(self.room.left_sink)
|
||||
sn2.air_to(self.room.right_sink)
|
||||
|
||||
NoteAction('A3', self.st(0), self.st(0.5), [sn2], self)
|
||||
NoteAction('G3', self.st(1), self.st(0.5), [sn2], self)
|
||||
NoteAction('F3', self.st(2), self.st(0.5), [sn2], self)
|
||||
NoteAction('E3', self.st(3), self.st(0.5), [sn2], self)
|
||||
|
||||
|
||||
sn.start_location = (4,0,0)
|
||||
sn2.start_location = (-4,0,0)
|
||||
|
||||
"""
|
||||
NoteAction('A4', self.st(0), self.st(0.5), [sn, tn], self)
|
||||
NoteAction('G4', self.st(1), self.st(0.5), [sn, tn], self)
|
||||
NoteAction('F4', self.st(2), self.st(0.5), [sn, tn], self)
|
||||
NoteAction('E4', self.st(3), self.st(0.5), [sn, tn], self)
|
||||
"""
|
||||
LinearPitchTransition('A4', 'E4', self.st(0), self.st(5), [sn], self)
|
||||
LinearPitchTransition('E4', 'A4', self.st(0), self.st(5), [sn2], self)
|
||||
|
||||
LinearSpatialTransition((-8,0,0),(8,0,0), self.st(0), self.st(5), [sn], self)
|
||||
|
||||
TP = TestProgram()
|
||||
TP.setup()
|
||||
TP.interface()
|
||||
Loading…
Add table
Reference in a new issue