sonnum/zigsonnum/activity.zig
2025-09-15 19:23:03 +03:00

623 lines
No EOL
17 KiB
Zig

const std = @import("std");
const math = std.math;
const print = std.debug.print;
const Allocator = std.mem.Allocator;
const SoundNode = @import("soundnode.zig").SoundNode;
const FreqAmpBuffer = @import("freqamp.zig").FreqAmpBuffer;
const FreqAmpList = @import("freqamp.zig").FreqAmpList;
const utility = @import("utility.zig");
pub const Activity = struct {
start_tick: u32,
end_tick: u32,
opcode: u16,
soundnode: *SoundNode,
operands: [6]f64 = std.mem.zeroes([6]f64),
pub fn create(allocator: Allocator, start_tick: u32, end_tick: u32, opcode: u16, soundnode: *SoundNode, operands: [6]f64) !*Activity {
const a = try allocator.create(Activity);
a.* = .{
.start_tick = start_tick,
.end_tick = end_tick,
.opcode = opcode,
.soundnode = soundnode,
.operands = operands,
};
return a;
}
pub fn do(self: *Activity) !void {
switch (self.opcode) {
4 => { self.relay(); },
5 => { self.setpos(); },
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(); },
20 => { self.pulse(); },
21 => { try self.fmsetup(); },
22 => { try self.fm(); },
23 => { try self.am(); },
24 => { try self.mute(); },
25 => { self.whitenoise(); },
26 => { try self.setadsrgain(); },
27 => { try self.setadsrsustain(); },
28 => { try self.adsr(); },
else => {},
}
}
pub fn relay_imprecise(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 sample_tick: u32 = 0;
const tck = @as(f64, @floatFromInt(current_tick)) - (128.571428 * dist);
if (tck > 0) {
sample_tick = @intFromFloat( @floor(tck) );
const attenuation: f64 = @as(f64, std.math.exp(-(dist / 100.0)));
const relayed_r_amp = aired_sn.fab.get_r_amp(sample_tick) * attenuation;
self.soundnode.fab.add_r_amp(relayed_r_amp);
}
_ = i;
}
}
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 phase = self.soundnode.g("phase");
const gainmult = self.operands[0];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
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 phase = self.soundnode.g("phase");
const gainmult = self.operands[0];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
const period = 44100 / freq;
const tp = (current_tick - (period * phase)) / period;
const amp = r_amp * (@abs(2 * (2 * ( tp - @floor(tp + 0.5) ) )) - 1);
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 phase = self.soundnode.g("phase");
const gainmult = self.operands[0];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
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 phase = self.soundnode.g("phase");
const gainmult = self.operands[0];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
const period = 44100 / freq;
const tp = (current_tick - (period * phase)) / period;
const amp = r_amp * (2 * (tp - @floor(0.5+tp)));
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 phase = self.soundnode.g("phase");
const gainmult = self.operands[0];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
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) - (phase * utility.tau);
const sincos = skew * math.sin(m) / (1 - skew*math.cos(m));
const amp = r_amp * ((1/skew) * math.atan(sincos));
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));
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 x = (current_tick * (current_tick / 2)) - (start_tick*current_tick);
const xx = ((endfreq - startfreq) / (end_tick - start_tick)) * x;
const freq = ((startfreq * current_tick) + xx) / current_tick;
try self.soundnode.s("basefreq", freq);
}
pub fn slidebasefreq_iterative(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 instfreq = utility.interpolate(current_tick, start_tick, end_tick, startfreq, endfreq);
const c = current_tick / 44100;
const prevphase = self.soundnode.g("phase");
const prevfreq = self.soundnode.g("basefreq");
const pp = prevphase + c*(instfreq - prevfreq) + @floor(c * prevfreq - prevphase);
const newphase = pp - @floor(pp);
try self.soundnode.s("phase", newphase);
try self.soundnode.s("basefreq", instfreq);
}
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));
}
pub fn pulse(self: *Activity) void {
const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick);
const freq = self.soundnode.g("basefreq");
const phase = self.soundnode.g("phase");
const gainmult = self.operands[0];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
const period = 44100 / freq;
const tp = current_tick / period;
const tp_detract = (current_tick - (period * phase)) / period;
const amp =2 * (tp - @floor(0.5+tp));
const amp_detract = 2 * (tp_detract - @floor(0.5+tp_detract));
const final_amp = r_amp * (amp - amp_detract);
self.soundnode.fab.add_r_amp(final_amp);
}
pub fn fmsetup(self: *Activity) !void {
try self.soundnode.s("fmbasefreq", self.operands[0]);
try self.soundnode.s("instfreq", self.operands[0]);
try self.soundnode.s("fmq", self.operands[1]);
}
pub fn fm(self: *Activity) !void {
const current_tick: u32 = self.soundnode.fab.current_tick;
const fcurrent_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick);
var fprev_tick: f64 = 0;
if (fcurrent_tick > 0) {
fprev_tick = @floatFromInt(current_tick - 1);
}
const fmq = self.soundnode.g("fmq");
const fmbasefreq = self.soundnode.g("fmbasefreq");
const prevphase = self.soundnode.g("phase");
const prevfreq = self.soundnode.g("basefreq");
var srcamp: f64 = 0;
for (self.soundnode.wire_in.items, 0..) |wired_sn, i| {
const relayed_r_amp = wired_sn.fab.get_r_amp(current_tick);
srcamp += 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;
srcamp += relayed_r_amp;
}
_ = i;
}
if (srcamp > 1) {
srcamp = 1;
} else if (srcamp < -1) {
srcamp = -1;
}
const instfreq = fmbasefreq + (fmbasefreq * fmq * srcamp);
const c = fcurrent_tick / 44100;
const pp = prevphase + c*(instfreq - prevfreq) + @floor(c * prevfreq - prevphase);
const newphase = pp - @floor(pp);
try self.soundnode.s("phase", newphase);
try self.soundnode.s("basefreq", instfreq);
}
pub fn am(self: *Activity) !void {
const current_tick: u32 = self.soundnode.fab.current_tick;
var srcamp: f64 = 0;
for (self.soundnode.wire_in.items, 0..) |wired_sn, i| {
const relayed_r_amp = wired_sn.fab.get_r_amp(current_tick);
srcamp += 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;
srcamp += relayed_r_amp;
}
_ = i;
}
if (srcamp > 1) {
srcamp = 1;
} else if (srcamp < -1) {
srcamp = -1;
}
try self.soundnode.s("gain", srcamp);
}
pub fn mute(self: *Activity) !void {
try self.soundnode.s("gain", 0);
}
pub fn whitenoise(self: *Activity) void {
const current_tick: f64 = @floatFromInt(self.soundnode.fab.current_tick);
const seed = self.operands[0];
const gainmult = self.operands[1];
var r_amp = self.soundnode.g("gain");
if (gainmult > 0) {
r_amp *= gainmult;
}
const w = (seed*current_tick) / math.sin(current_tick);
const rnd = (2 * (w - @floor(w))) - 1;
const amp = r_amp * rnd;
self.soundnode.fab.add_r_amp(amp);
}
pub fn setadsrgain(self: *Activity) !void {
try self.soundnode.s("adsrgain", self.operands[0]);
}
pub fn setadsrsustain(self: *Activity) !void {
try self.soundnode.s("adsrsustain", self.operands[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.g("adsrgain");
const adsrsustain = self.soundnode.g("adsrsustain");
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;
}
try self.soundnode.s("gain", gain);
}
};