diff --git a/pysonnum/activity.py b/pysonnum/activity.py index 04f40fd..d348d93 100644 --- a/pysonnum/activity.py +++ b/pysonnum/activity.py @@ -8,7 +8,20 @@ OPCODES = { 'endtick': 3, 'relay': 4, 'setpos': 5, - 'sine': 6, + 'setbasefreq': 6, + 'setgain': 7, + 'setphase': 8, + 'sine': 9, + 'triangle': 10, + 'square': 11, + 'sawtooth': 12, + 'setskew': 13, + 'skewsine': 14, + 'slidebasefreq': 15, + 'slidegain': 16, + 'slidephase': 17, + 'slideskew': 18, + 'slidepos': 19, } class Activity: diff --git a/pysonnum/notes.py b/pysonnum/notes.py new file mode 100644 index 0000000..c5e773a --- /dev/null +++ b/pysonnum/notes.py @@ -0,0 +1,37 @@ +NOTES_OF_OCTAVE = { + 0: ['C', 'B#'], + 1: ['C#', 'Db'], + 2: ['D'], + 3: ['D#', 'Eb'], + 4: ['E', 'Fb'], + 5: ['F', 'E#'], + 6: ['F#', 'Gb'], + 7: ['G'], + 8: ['G#', 'Ab'], + 9: ['A'], + 10: ['A#', 'Bb'], + 11: ['B', 'Cb'] +} + +def note_to_freq(note): + + note_no = note_to_note_no(note) + return note_no_to_freq(note_no) + +def note_to_note_no(note): + + octave = int(note[-1]) + note = note[:-1] + + note_no_oct = 1 + for note_no_oct, notelst in NOTES_OF_OCTAVE.items(): + + if note in notelst: + break + + corr_note_no_oct = note_no_oct - 8 + return corr_note_no_oct + (octave*12) + +def note_no_to_freq(note_no): + + return 2**((note_no-49) / 12.0) * 440 \ No newline at end of file diff --git a/pysonnum/sonnum.py b/pysonnum/sonnum.py index 8742b5a..d788695 100644 --- a/pysonnum/sonnum.py +++ b/pysonnum/sonnum.py @@ -1,4 +1,5 @@ from .soundnode import SoundNode +from .notes import * class Sonnum: @@ -41,6 +42,9 @@ class Sonnum: def sec(self, seconds): return seconds * 44100 + def note(self, note): + return note_to_freq(note) + def relay(self): node = self.add_node("") diff --git a/pysonnum/soundnode.py b/pysonnum/soundnode.py index 46f7b0f..94b8cdc 100644 --- a/pysonnum/soundnode.py +++ b/pysonnum/soundnode.py @@ -8,6 +8,7 @@ class SoundNode: self.name = name self.properties = dict() # {k: v} + def __repr__(self): return f'{self.order}:{self.name}' @@ -23,6 +24,44 @@ class SoundNode: def setpos(self, start_tick, x, y, z): self.act('setpos', start_tick, start_tick, self, None, [x, y, z]) - def sine(self, start_tick, end_tick, freq, r_amp): - self.act('sine', start_tick, end_tick, self, None, [freq, r_amp]) - \ No newline at end of file + def setbasefreq(self, start_tick, basefreq): + self.act('setbasefreq', start_tick, start_tick, self, None, [basefreq]) + + def setgain(self, start_tick, gain): + self.act('setgain', start_tick, start_tick, self, None, [gain]) + + def setphase(self, start_tick, phase): + self.act('setphase', start_tick, start_tick, self, None, [phase]) + + def sine(self, start_tick, end_tick): + self.act('sine', start_tick, end_tick, self, None, []) + + def triangle(self, start_tick, end_tick): + self.act('triangle', start_tick, end_tick, self, None, []) + + def square(self, start_tick, end_tick): + self.act('square', start_tick, end_tick, self, None, []) + + def sawtooth(self, start_tick, end_tick): + self.act('sawtooth', start_tick, end_tick, self, None, []) + + def setskew(self, start_tick, skew): + self.act('setskew', start_tick, start_tick, self, None, [skew]) + + def skewsine(self, start_tick, end_tick): + self.act('skewsine', start_tick, end_tick, self, None, []) + + 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]) \ No newline at end of file diff --git a/sineex.py b/sineex.py new file mode 100644 index 0000000..9bc51de --- /dev/null +++ b/sineex.py @@ -0,0 +1,18 @@ +ln = s.sec(4) + +s.endtick(ln) + +left = s.relay() +right = s.relay() + +left.setpos(0, -0.3, 0, 0) +right.setpos(0, 0.3, 0, 0) + +tr1 = s.node() +tr1.setbasefreq(ln, s.note("C4")) +tr1.sine(0, ln) +tr1.setgain(0, 0.3) + +tr1.setpos(0, 100, 0, 0) +s.air(tr1, [left]) +s.air(tr1, [right]) \ No newline at end of file diff --git a/zigsonnum/activity.zig b/zigsonnum/activity.zig index e3724a3..4d2bdf0 100644 --- a/zigsonnum/activity.zig +++ b/zigsonnum/activity.zig @@ -35,25 +35,26 @@ pub const Activity = struct { switch (self.opcode) { 4 => { self.relay(); }, 5 => { self.setpos(); }, - 6 => { self.sine(); }, + 6 => { try self.setbasefreq(); }, + 7 => { try self.setgain(); }, + 8 => { try self.setphase(); }, + 9 => { self.sine(); }, + 10 => { self.triangle(); }, + 11 => { self.square(); }, + 12 => { self.sawtooth(); }, + 13 => { try self.setskew(); }, + 14 => { self.skewsine(); }, + 15 => { try self.slidebasefreq(); }, + 16 => { try self.slidegain(); }, + 17 => { try self.slidephase(); }, + 18 => { try self.slideskew(); }, + 19 => { self.slidepos(); }, else => {}, } } - pub fn sine(self: *Activity) void { - - const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); - - const freq = self.operands[0]; - const r_amp = self.operands[1]; - const phase = self.operands[2]; - - const amp = r_amp * math.sin(utility.corrected_tau * freq * current_tick - phase * utility.tau); - self.soundnode.fab.add_r_amp(amp); - - } - pub fn relay(self: *Activity) void { + pub fn relay_imprecise(self: *Activity) void { const current_tick: u32 = self.soundnode.fab.current_tick; @@ -88,12 +89,240 @@ pub const Activity = struct { } - pub fn setpos(self: *Activity) void { - self.soundnode.location.x = @as(f32, @floatCast(self.operands[0])); - self.soundnode.location.y = @as(f32, @floatCast(self.operands[1])); - self.soundnode.location.z = @as(f32, @floatCast(self.operands[2])); + pub fn relay(self: *Activity) void { + + const current_tick: u32 = self.soundnode.fab.current_tick; + + for (self.soundnode.wire_in.items, 0..) |wired_sn, i| { + + const relayed_r_amp = wired_sn.fab.get_r_amp(current_tick); + self.soundnode.fab.add_r_amp(relayed_r_amp); + _ = i; + + } + + for (self.soundnode.air_in.items, 0..) |aired_sn, i| { + + const dist: f64 = self.soundnode.distance(aired_sn); + + var b_sample_tick: u32 = 0; + var n_sample_tick: u32 = 0; + + const tck = @as(f64, @floatFromInt(current_tick)) - (128.571428 * dist); + + if (tck > 0) { + + b_sample_tick = @intFromFloat( @floor(tck) ); + n_sample_tick = b_sample_tick + 1; + + const attenuation: f64 = @as(f64, std.math.exp(-(dist / 100.0))); + + const b_r_amp = aired_sn.fab.get_r_amp(b_sample_tick); + const n_r_amp = aired_sn.fab.get_r_amp(n_sample_tick); + + const tick_diff = tck - @as(f64, @floatFromInt(b_sample_tick)); + const relayed_r_amp = (b_r_amp + (tick_diff * (n_r_amp - b_r_amp))) * attenuation; + + self.soundnode.fab.add_r_amp(relayed_r_amp); + + } + + _ = i; + + } } + pub fn setbasefreq(self: *Activity) !void { + try self.soundnode.s("basefreq", self.operands[0]); + + } + + pub fn setgain(self: *Activity) !void { + try self.soundnode.s("gain", self.operands[0]); + } + + pub fn setphase(self: *Activity) !void { + try self.soundnode.s("phase", self.operands[0]); + } + + pub fn setpos(self: *Activity) void { + self.soundnode.x = @as(f32, @floatCast(self.operands[0])); + self.soundnode.y = @as(f32, @floatCast(self.operands[1])); + self.soundnode.z = @as(f32, @floatCast(self.operands[2])); + + } + + pub fn sine(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + + const freq = self.soundnode.g("basefreq"); + const r_amp = self.soundnode.g("gain"); + const phase = self.soundnode.g("phase"); + + const amp = r_amp * math.sin(utility.corrected_tau * freq * current_tick - phase * utility.tau); + self.soundnode.fab.add_r_amp(amp); + + } + + pub fn triangle(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + + const freq = self.soundnode.g("basefreq"); + const r_amp = self.soundnode.g("gain"); + const phase = self.soundnode.g("phase"); + + const period = 44100 / freq; + const tp = current_tick / period; + + const amp = r_amp * (@abs(2 * (2 * ( tp - @floor(tp + 0.5) ) )) - 1 - (phase * utility.tau)); + self.soundnode.fab.add_r_amp(amp); + + } + + pub fn square(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + + const freq = self.soundnode.g("basefreq"); + const r_amp = self.soundnode.g("gain"); + const phase = self.soundnode.g("phase"); + + const sin = math.sin(utility.corrected_tau * freq * current_tick - phase * utility.tau); + + if (sin > 0) { + self.soundnode.fab.add_r_amp(r_amp); + } else { + self.soundnode.fab.add_r_amp(-r_amp); + } + + } + + pub fn sawtooth(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + + const freq = self.soundnode.g("basefreq"); + const r_amp = self.soundnode.g("gain"); + const phase = self.soundnode.g("phase"); + + const period = 44100 / freq; + const tp = current_tick / period; + + const amp = r_amp * (2 * (tp - @floor(0.5+tp) - (phase * utility.tau))); + self.soundnode.fab.add_r_amp(amp); + + } + + pub fn setskew(self: *Activity) !void { + try self.soundnode.s("skew", self.operands[0]); + } + + pub fn skewsine(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + + const freq = self.soundnode.g("basefreq"); + const r_amp = self.soundnode.g("gain"); + const phase = self.soundnode.g("phase"); + const skew = self.soundnode.g("skew"); + + if (skew == 0) { + const amp = r_amp * math.sin(utility.corrected_tau * freq * current_tick - phase * utility.tau); + self.soundnode.fab.add_r_amp(amp); + } else if (skew > 0) { + const m = (utility.corrected_tau * freq * current_tick); + const sincos = skew * math.sin(m) / (1 - skew*math.cos(m)); + const amp = r_amp * ((1/skew) * math.atan(sincos) - (phase * utility.tau)); + self.soundnode.fab.add_r_amp(amp); + } else if (skew < 0) { + const m = (utility.corrected_tau * freq * current_tick); + const sincos = skew * math.cos(m) / (1 + skew*math.sin(m)); + const amp = r_amp * ((1/skew) * math.atan(sincos) - (phase * utility.tau)); + self.soundnode.fab.add_r_amp(amp); + } + + } + + pub fn slidebasefreq(self: *Activity) !void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + const start_tick: f64 = @floatFromInt(self.start_tick); + const end_tick: f64 = @floatFromInt(self.end_tick); + + const startfreq = self.operands[0]; + const endfreq = self.operands[1]; + + const freq = utility.interpolate(current_tick, start_tick, end_tick, startfreq, endfreq); + try self.soundnode.s("basefreq", freq); + + } + + pub fn slidegain(self: *Activity) !void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + const start_tick: f64 = @floatFromInt(self.start_tick); + const end_tick: f64 = @floatFromInt(self.end_tick); + + const startgain = self.operands[0]; + const endgain = self.operands[1]; + + const gain = utility.interpolate(current_tick, start_tick, end_tick, startgain, endgain); + try self.soundnode.s("gain", gain); + + } + + pub fn slidephase(self: *Activity) !void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + const start_tick: f64 = @floatFromInt(self.start_tick); + const end_tick: f64 = @floatFromInt(self.end_tick); + + const startphase = self.operands[0]; + const endphase = self.operands[1]; + + const phase = utility.interpolate(current_tick, start_tick, end_tick, startphase, endphase); + try self.soundnode.s("phase", phase); + + } + + pub fn slideskew(self: *Activity) !void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + const start_tick: f64 = @floatFromInt(self.start_tick); + const end_tick: f64 = @floatFromInt(self.end_tick); + + const startskew = self.operands[0]; + const endskew = self.operands[1]; + + const skew = utility.interpolate(current_tick, start_tick, end_tick, startskew, endskew); + try self.soundnode.s("skew", skew); + + } + + pub fn slidepos(self: *Activity) void { + + const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick); + const start_tick: f64 = @floatFromInt(self.start_tick); + const end_tick: f64 = @floatFromInt(self.end_tick); + + const start_x = self.operands[0]; + const start_y = self.operands[1]; + const start_z = self.operands[2]; + const end_x = self.operands[3]; + const end_y = self.operands[4]; + const end_z = self.operands[5]; + + const x = utility.interpolate(current_tick, start_tick, end_tick, start_x, end_x); + const y = utility.interpolate(current_tick, start_tick, end_tick, start_y, end_y); + const z = utility.interpolate(current_tick, start_tick, end_tick, start_z, end_z); + + self.soundnode.x = @as(f32, @floatCast(x)); + self.soundnode.y = @as(f32, @floatCast(y)); + self.soundnode.z = @as(f32, @floatCast(z)); + + } }; \ No newline at end of file diff --git a/zigsonnum/point.zig b/zigsonnum/point.zig index 077a3d9..8e2ba74 100644 --- a/zigsonnum/point.zig +++ b/zigsonnum/point.zig @@ -13,6 +13,13 @@ pub const Pnt = struct { print("{d} {d} {d}\n", .{self.x, self.y, self.z}); } + pub fn set(self: *Pnt, x: f32, y: f32, z: f32) void { + self.x = x; + self.y = y; + self.z = z; + } + + }; pub fn distanceBetweenPoints(pt1: Pnt, pt2: Pnt) f64 { diff --git a/zigsonnum/sonnum.zig b/zigsonnum/sonnum.zig index ce91a33..3388134 100644 --- a/zigsonnum/sonnum.zig +++ b/zigsonnum/sonnum.zig @@ -102,11 +102,11 @@ pub fn main() !void { while (cursor < buf.len) { opcode = std.mem.readVarInt(u16, buf[cursor..cursor+2], .big); - print("----\nCURRENT TICK {d}\nOPCODE {d} :: {any}\n", .{tick, opcode, buf[cursor..cursor+2]}); + //print("----\nCURRENT TICK {d}\nOPCODE {d} :: {any}\n", .{tick, opcode, buf[cursor..cursor+2]}); cursor += 2; tick_start = std.mem.readVarInt(u32, buf[cursor..cursor+4], .big); - print("TICKSTART {d} :: {any}\n", .{tick_start, buf[cursor..cursor+4]}); + //print("TICKSTART {d} :: {any}\n", .{tick_start, buf[cursor..cursor+4]}); cursor += 4; if (tick_start > tick) { @@ -117,11 +117,11 @@ pub fn main() !void { } else { tick_end = std.mem.readVarInt(u32, buf[cursor..cursor+4], .big); - print("TICKEND {d} :: {any}\n", .{tick_end, buf[cursor..cursor+4]}); + //print("TICKEND {d} :: {any}\n", .{tick_end, buf[cursor..cursor+4]}); cursor += 4; src_node = std.mem.readVarInt(u16, buf[cursor..cursor+2], .big); - print("SRCNODE {d} :: {any}\n", .{src_node, buf[cursor..cursor+2]}); + //print("SRCNODE {d} :: {any}\n", .{src_node, buf[cursor..cursor+2]}); cursor += 2; trg_node = std.mem.readVarInt(u16, buf[cursor..cursor+2], .big); @@ -162,6 +162,11 @@ pub fn main() !void { const sn = try SoundNode.create(allocator, nodename); //print("Added node {s} at tick {d}\n", .{nodename, tick}); try soundnodes.append(sn); + + try sn.s("basefreq", @as(f64, 440)); + try sn.s("gain", @as(f64, 1)); + try sn.s("phase", @as(f64, 0)); + }, 1 => { @@ -196,7 +201,7 @@ pub fn main() !void { const src = soundnodes.items[src_node]; const a = try Activity.create(allocator, tick_start, tick_end, opcode, src, [6]f64{op1, op2, op3, op4, op5, op6}); - print("Set activity {d} for node {d} s at tick {d}\n", .{opcode, src_node, tick}); + //print("Set activity {d} for node {d} s at tick {d}\n", .{opcode, src_node, tick}); try src.activities.append(a); }, diff --git a/zigsonnum/soundnode.zig b/zigsonnum/soundnode.zig index a21326d..da2ddd2 100644 --- a/zigsonnum/soundnode.zig +++ b/zigsonnum/soundnode.zig @@ -23,13 +23,16 @@ pub const SoundNode = struct { allocator: Allocator, name: []const u8 = "soundnode", - location: Pnt = Pnt{.x = 0, .y = 0, .z = 0}, air_in: ArrayList(*SoundNode), wire_in: ArrayList(*SoundNode), activities: ArrayList(*Activity), props: StringHashMap(f64), + x: f32 = 0, + y: f32 = 0, + z: f32 = 0, + fab: FreqAmpBuffer, pub fn create(allocator: Allocator, name: []const u8) !*SoundNode { @@ -50,6 +53,9 @@ pub const SoundNode = struct { .activities = activities, .fab = fab, .props = props, + .x = 0, + .y = 0, + .z = 0, }; return sn; @@ -72,6 +78,9 @@ pub const SoundNode = struct { .activities = activities, .fab = fab, .props = props, + .x = 0, + .y = 0, + .z = 0, }; } @@ -92,9 +101,34 @@ pub const SoundNode = struct { } - pub fn distance(self: *SoundNode, other: *SoundNode) f64 { + pub fn g(self: *SoundNode, key: []const u8) f64 { + + const value = self.props.get(key); + + if (value) |v| { + return v; + } else { + return @as(f64, 0.0); + } + } + + pub fn s(self: *SoundNode, key: []const u8, value: f64) !void { - return pnt.distanceBetweenPoints(self.location, other.location); + try self.props.put(key, value); + + } + + pub fn distance(self: *SoundNode, other: *SoundNode) f64 { + + if ((self.x == other.x) and (self.y == other.y) and (self.z == other.z)) { + return 0; + } + + const dx: f32 = self.x - other.x; + const dy: f32 = self.y - other.y; + const dz: f32 = self.z - other.z; + + return math.sqrt(dx*dx + dy*dy + dz*dz); } diff --git a/zigsonnum/utility.zig b/zigsonnum/utility.zig index 68a3c96..86c936f 100644 --- a/zigsonnum/utility.zig +++ b/zigsonnum/utility.zig @@ -6,4 +6,8 @@ pub const corrected_tau: f64 = tau / 44100; pub fn prnt(s: []const u8) void { print("{s}\n", .{s}); +} + +pub fn interpolate(x: f64, x0: f64, x1: f64, y0: f64, y1: f64) f64 { + return y0 + ((y1-y0)*(x-x0) / (x1-x0)); } \ No newline at end of file