From f2868d8dcd7b65e52bce5a6d4ff5cfcf47224afa Mon Sep 17 00:00:00 2001 From: aprilnightk Date: Thu, 18 Sep 2025 00:38:04 +0300 Subject: [PATCH] pin and bridge renamed to feed and copy; added whitenoise, mute and adsr --- mysynths/sintest.py | 23 ++++++----- pysonnum/activity.py | 24 +++-------- pysonnum/sonnum.py | 8 ++-- pysonnum/soundnode.py | 60 +++++++++++++--------------- zigsonnum/activities/basicsynths.zig | 21 +++++++++- zigsonnum/activities/generators.zig | 53 ++++++++++++++++++++++-- zigsonnum/activity.zig | 17 ++++++-- zigsonnum/freqamp.zig | 10 +++++ 8 files changed, 144 insertions(+), 72 deletions(-) diff --git a/mysynths/sintest.py b/mysynths/sintest.py index 1c0f262..0567c25 100644 --- a/mysynths/sintest.py +++ b/mysynths/sintest.py @@ -1,19 +1,24 @@ -s.setup(s.sec(10)) +ln = s.sec(10) +s.setup(ln) m = s.node() n = s.node() +adsr = s.node() -s.pin(m, BASEFREQ, n, IN_BASEFREQ) +adsr.setpin(0, OUT8, 0.9) +adsr.setpin(0, OUT9, 0.4) +adsr.adsr(0, s.sec(6), s.sec(0.1), s.sec(0.2), s.sec(0.5), s.sec(0.5), s.sec(1.7)) -m.singenN(0, s.sec(10), 50.4, 0, 0.003, 440) +adsr.feed(n, GAIN, IN_GAIN) + +m.feed(n, BASEFREQ, IN_BASEFREQ) +m.singenN(0, ln, 50.4, 0, 0.003, 440) + +n.copy(0, ln, IN_BASEFREQ, BASEFREQ) +n.copy(0, ln, IN_GAIN, GAIN) -n.bridge(0, s.sec(10), IN_BASEFREQ, BASEFREQ) -#n.setpin(0, BASEFREQ, 330) n.setpin(0, OUT8, 0.3) -n.pulse(0, s.sec(10)) - -n.printstate(30000, 30000) -n.printstate(30001, 30001) +n.skewsine(0, ln) s.wire(n, s.left) s.wire(n, s.right) \ No newline at end of file diff --git a/pysonnum/activity.py b/pysonnum/activity.py index b6fe4dc..ca60ffd 100644 --- a/pysonnum/activity.py +++ b/pysonnum/activity.py @@ -3,11 +3,12 @@ import struct OPCODES = { 'create': 0, - 'pin': 1, + 'feed': 1, 'endtick': 2, 'setpin': 3, 'relay': 4, - 'bridge': 5, + 'copy': 5, + 'mute': 6, 'printstate': 9, 'setpos': 10, @@ -18,24 +19,11 @@ OPCODES = { 'sawtooth': 53, 'skewsine': 54, 'pulse': 55, + 'whitenoise': 55, 'singenN': 100, - - 'setskew': 12, - 'slidebasefreq': 15, - 'slidegain': 16, - 'slidephase': 17, - 'slideskew': 18, - 'slidepos': 19, - 'fmsetup': 21, - 'fm': 22, - 'am': 23, - 'mute': 24, - 'whitenoise': 25, - 'setadsrgain': 26, - 'setadsrsustain': 27, - 'adsr': 28, - + 'adsr': 150, + } class Activity: diff --git a/pysonnum/sonnum.py b/pysonnum/sonnum.py index e795166..08f91bc 100644 --- a/pysonnum/sonnum.py +++ b/pysonnum/sonnum.py @@ -79,7 +79,7 @@ class Sonnum: for src_node in src_nodes: for trg_node in trg_nodes: - self.act('pin', 0, 0, src_node, trg_node, [0, 0]) + self.act('feed', 0, 0, src_node, trg_node, [0, 0]) def air(self, src_nodes, trg_nodes): @@ -91,9 +91,9 @@ class Sonnum: for src_node in src_nodes: for trg_node in trg_nodes: - self.act('pin', 0, 0, src_node, trg_node, [0, 1]) + self.act('feed', 0, 0, src_node, trg_node, [0, 1]) - def pin(self, src_nodes, src_pin, trg_nodes, trg_pin): + def feed(self, src_nodes, src_pin, trg_nodes, trg_pin): if not isinstance(src_nodes, list): src_nodes = [src_nodes] @@ -103,4 +103,4 @@ class Sonnum: for src_node in src_nodes: for trg_node in trg_nodes: - self.act('pin', 0, 0, src_node, trg_node, [src_pin, trg_pin]) \ No newline at end of file + self.act('feed', 0, 0, src_node, trg_node, [src_pin, trg_pin]) \ No newline at end of file diff --git a/pysonnum/soundnode.py b/pysonnum/soundnode.py index 2cf2bd0..a351a08 100644 --- a/pysonnum/soundnode.py +++ b/pysonnum/soundnode.py @@ -21,6 +21,30 @@ class SoundNode: def act(self, *args): self.c.add_activity(*args) + def feed(self, trg_nodes, src_pin, trg_pin): + + if not isinstance(trg_nodes, list): + trg_nodes = [trg_nodes] + + for trg_node in trg_nodes: + self.act('feed', 0, 0, self, trg_node, [src_pin, trg_pin]) + + def wire(self, trg_nodes): + + if not isinstance(trg_nodes, list): + trg_nodes = [trg_nodes] + + for trg_node in trg_nodes: + self.act('feed', 0, 0, self, trg_node, [0, 0]) + + def air(self, trg_nodes): + + if not isinstance(trg_nodes, list): + trg_nodes = [trg_nodes] + + for trg_node in trg_nodes: + self.act('feed', 0, 0, self, trg_node, [0, 1]) + def setpos(self, start_tick, x, y, z): self.act('setpos', start_tick, start_tick, self, None, [x, y, z]) @@ -30,8 +54,8 @@ class SoundNode: def printstate(self, start_tick, end_tick): self.act('printstate', start_tick, end_tick, self, None, []) - def bridge(self, start_tick, end_tick, in_pin, out_pin): - self.act('bridge', start_tick, end_tick, self, None, [in_pin, out_pin]) + def copy(self, start_tick, end_tick, in_pin, out_pin): + self.act('copy', start_tick, end_tick, self, None, [in_pin, out_pin]) def sine(self, start_tick, end_tick, gainmult = 0.0): self.act('sine', start_tick, end_tick, self, None, [gainmult]) @@ -51,44 +75,14 @@ class SoundNode: def skewsine(self, start_tick, end_tick, gainmult = 0.0): self.act('skewsine', start_tick, end_tick, self, None, [gainmult]) - def slidebasefreq(self, start_tick, end_tick, startfreq, endfreq): - self.act('slidebasefreq', start_tick, end_tick, self, None, [startfreq, endfreq]) - - def slidegain(self, start_tick, end_tick, startgain, endgain): - self.act('slidegain', start_tick, end_tick, self, None, [startgain, endgain]) - - def slidephase(self, start_tick, end_tick, startphase, endphase): - self.act('slidephase', start_tick, end_tick, self, None, [startphase, endphase]) - - def slideskew(self, start_tick, end_tick, startskew, endskew): - self.act('slideskew', start_tick, end_tick, self, None, [startskew, endskew]) - - def slidepos(self, start_tick, end_tick, sx, sy, sz, ex, ey, ez): - self.act('slidepos', start_tick, end_tick, self, None, [sx, sy, sz, ex, ey, ez]) - def pulse(self, start_tick, end_tick, gainmult = 0.0): self.act('pulse', start_tick, end_tick, self, None, [gainmult]) - - def fmsetup(self, start_tick, fmbasefreq, fmq): - self.act('fmsetup', start_tick, start_tick, self, None, [fmbasefreq, fmq]) - - def fm(self, start_tick, end_tick): - self.act('fm', start_tick, end_tick, self, None, []) - - def am(self, start_tick, end_tick): - self.act('am', start_tick, end_tick, self, None, []) - + def mute(self, start_tick, end_tick): self.act('mute', start_tick, end_tick, self, None, []) def whitenoise(self, start_tick, end_tick, gainmult = 0.0, seed = 0.259624856928): self.act('whitenoise', start_tick, end_tick, self, None, [seed, gainmult]) - def setadsrgain(self, start_tick, adsrgain): - self.act('setadsrgain', start_tick, start_tick, self, None, [adsrgain]) - - def setadsrsustain(self, start_tick, adsrsustain): - self.act('setadsrsustain', start_tick, start_tick, self, None, [adsrsustain]) - def adsr(self, start_tick, end_tick, attack_tick, hold_tick, decay_tick, sustain_tick, release_tick, gainmult = 0.0): self.act('adsr', start_tick, end_tick, self, None, [attack_tick, hold_tick, decay_tick, sustain_tick, release_tick, gainmult]) \ No newline at end of file diff --git a/zigsonnum/activities/basicsynths.zig b/zigsonnum/activities/basicsynths.zig index 23166d7..d7532ef 100644 --- a/zigsonnum/activities/basicsynths.zig +++ b/zigsonnum/activities/basicsynths.zig @@ -73,7 +73,7 @@ pub fn sawtooth(self: *Activity) void { } // Produces a skewed sine wave to R_AMP pin -// Skeweness factor is taken from OUT8 pin +// Skewness factor is taken from OUT8 pin pub fn skewsine(self: *Activity) void { const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); @@ -126,3 +126,22 @@ pub fn pulse(self: *Activity) void { self.soundnode.add_r_amp(final_amp); } + +// Whitenoise +// Seed is taken from OUT8 +// +pub fn whitenoise(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + + const seed = self.soundnode.out8; + const gain = self.soundnode.corrGain(self.operands[0]); + + const w = (seed*current_tick) / math.sin(current_tick); + const amp = (2 * (w - @floor(w))) - 1; + + const final_amp = gain * amp; + + self.soundnode.fab.add_r_amp(final_amp); + +} \ No newline at end of file diff --git a/zigsonnum/activities/generators.zig b/zigsonnum/activities/generators.zig index e5f7088..43749f4 100644 --- a/zigsonnum/activities/generators.zig +++ b/zigsonnum/activities/generators.zig @@ -8,7 +8,8 @@ const dbg = utility.dbg; const idbg = utility.idbg; -// Produces a sine wave to r_amp pin +// Produces a sine wave (range N) +// y = M * sin(TF + P) + L pub fn singenN(self: *Activity) void { const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); @@ -17,10 +18,56 @@ pub fn singenN(self: *Activity) void { const freq = self.operands[2]; const ylevel = self.operands[3]; - const y = magnitude * math.sin(current_tick * freq + phase) + ylevel; + var y = magnitude * math.sin(current_tick * freq + phase) + ylevel; + + if (y < 0) { + y = 0; + } self.soundnode.basefreq = y; self.soundnode.out10 = y; self.soundnode.out11 = y; -} \ No newline at end of file +} + +// Generates a linear ADSR envelope (range 0) to GAIN +// Takes attack gain from OUT8 +// Takes sustain gain from OUT9 +// Tick count starts from 0, as if start_tick was 0 +pub fn adsr(self: *Activity) void { + + const real_current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + const start_tick: f64 = @floatFromInt(self.start_tick); + const current_tick = real_current_tick - start_tick; + + var adsrgain = self.soundnode.out8; + const adsrsustain = self.soundnode.out9; + + const attack_tick = self.operands[0]; + const hold_tick = self.operands[1]; + const decay_tick = self.operands[2]; + const sustain_tick = self.operands[3]; + const release_tick = self.operands[4]; + const gainmult = self.operands[5]; + + if (gainmult > 0) { + adsrgain *= gainmult; + } + + var gain: f64 = 0; + + if (current_tick <= attack_tick) { + gain = adsrgain * current_tick / attack_tick; + } else if (current_tick <= hold_tick) { + gain = adsrgain; + } else if (current_tick <= decay_tick) { + gain = ((current_tick-hold_tick)*(adsrsustain-adsrgain)/(decay_tick - hold_tick)) + adsrgain; + } else if (current_tick <= sustain_tick) { + gain = adsrsustain; + } else if (current_tick <= release_tick) { + gain = (adsrsustain * (current_tick - sustain_tick) / (sustain_tick - release_tick)) + adsrsustain; + } + + self.soundnode.gain = gain; + +} diff --git a/zigsonnum/activity.zig b/zigsonnum/activity.zig index b7e8e59..dab12e3 100644 --- a/zigsonnum/activity.zig +++ b/zigsonnum/activity.zig @@ -38,7 +38,8 @@ pub const Activity = struct { switch (self.opcode) { 4 => { self.relay(); }, - 5 => { self.bridge(); }, + 5 => { self.copy(); }, + 6 => { self.mute(); }, 10 => { spatial.setpos(self); }, @@ -48,8 +49,10 @@ pub const Activity = struct { 53 => { basicsynths.sawtooth(self); }, 54 => { basicsynths.skewsine(self); }, 55 => { basicsynths.pulse(self); }, + 56 => { basicsynths.whitenoise(self); }, 100 => { generators.singenN(self); }, + 150 => { generators.adsr(self); }, else => {}, @@ -59,12 +62,12 @@ pub const Activity = struct { pub fn relay(self: *Activity) void { - self.soundnode.add_r_amp(self.soundnode.in_wire); - self.soundnode.add_r_amp(self.soundnode.in_air); + self.soundnode.add_r_amp(self.soundnode.in_wire * self.soundnode.gain); + self.soundnode.add_r_amp(self.soundnode.in_air * self.soundnode.gain); } - pub fn bridge(self: *Activity) void { + pub fn copy(self: *Activity) void { const in_pin: usize = @intFromFloat(self.operands[0]); const out_pin: usize = @intFromFloat(self.operands[1]); @@ -119,6 +122,12 @@ pub const Activity = struct { } + } + + pub fn mute(self: *Activity) void { + + self.soundnode.fab.set_r_amp(0); + self.soundnode.r_amp = 0; } diff --git a/zigsonnum/freqamp.zig b/zigsonnum/freqamp.zig index 9eb7334..1a01546 100644 --- a/zigsonnum/freqamp.zig +++ b/zigsonnum/freqamp.zig @@ -43,6 +43,16 @@ pub const FreqAmpBuffer = struct { } + pub fn set_r_amp(self: *FreqAmpBuffer, r_amp: f64) void { + + self.fal_array[self.current_index] = r_amp; + + if (self.fal_array[self.current_index] > 1) { + self.fal_array[self.current_index] = 1; + } else if (self.fal_array[self.current_index] < -1) { + self.fal_array[self.current_index] = -1; + } + } pub fn add_r_amp(self: *FreqAmpBuffer, r_amp: f64) void {