593 lines
No EOL
16 KiB
Zig
593 lines
No EOL
16 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 => { self.setbasefreq(); },
|
|
7 => { self.setgain(); },
|
|
8 => { self.setphase(); },
|
|
9 => { self.sine(); },
|
|
10 => { self.triangle(); },
|
|
11 => { self.square(); },
|
|
12 => { self.sawtooth(); },
|
|
13 => { 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 {
|
|
|
|
self.soundnode.fab.add_r_amp(self.soundnode.in_wire);
|
|
self.soundnode.fab.add_r_amp(self.soundnode.in_air);
|
|
|
|
self.soundnode.r_amp += self.soundnode.in_wire;
|
|
self.soundnode.r_amp += self.soundnode.in_air;
|
|
|
|
}
|
|
|
|
pub fn setbasefreq(self: *Activity) void {
|
|
self.soundnode.basefreq = self.operands[0];
|
|
}
|
|
|
|
pub fn setgain(self: *Activity) !void {
|
|
self.soundnode.gain = self.operands[0];
|
|
}
|
|
|
|
pub fn setbasephase(self: *Activity) !void {
|
|
self.soundnode.basephase = 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| {
|
|
|
|
if (wired_sn.active == 1) {
|
|
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| {
|
|
|
|
if (aired_sn.active == 1) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}; |