sonnum/zigsonnum/sonnum.zig
2025-09-16 23:16:48 +03:00

455 lines
No EOL
11 KiB
Zig

const std = @import("std");
const math = std.math;
const print = std.debug.print;
const ArrayList = std.ArrayList;
const Endian = std.builtin.Endian;
const SoundNode = @import("soundnode.zig").SoundNode;
const Pin = @import ("pin.zig").Pin;
const Activity = @import("activity.zig").Activity;
const SoundSettings = @import("settings.zig").SoundSettings;
const utility = @import("utility.zig");
pub fn singleSineTick(st: *SoundSettings, freq: f64, t: u32, phase: f64) f64 {
const ft: f64 = @floatFromInt(t);
return math.sin(st.sine_multiplier * freq * ft - phase * utility.tau);
}
pub fn returnSoundNodeToList(snlist: *ArrayList(*SoundNode), sn: *SoundNode) !void {
for (snlist.items, 0..) |soundnode, ind| {
if (sn.uid < soundnode.uid) {
try snlist.insert(ind, sn);
return;
}
}
try snlist.append(sn);
return;
}
pub fn removeSoundNodeFromList(snlist: *ArrayList(*SoundNode), sn: *SoundNode) u8 {
var found: u8 = 0;
var toremove: usize = 0;
for (snlist.items, 0..) |soundnode, ind| {
if (sn.uid == soundnode.uid) {
toremove = ind;
found = 1;
}
}
if (found == 1) {
_ = snlist.orderedRemove(toremove);
}
return found;
}
pub fn returnPinToList(pinlist: *ArrayList(*Pin), pn: *Pin) !void {
for (pinlist.items, 0..) |pin, ind| {
if (pn.uid < pin.uid) {
try pinlist.insert(ind, pn);
return;
}
}
try pinlist.append(pn);
return;
}
pub fn removePinFromList(pinlist: *ArrayList(*Pin), pn: *Pin) !void {
var found: u8 = 0;
var toremove: usize = 0;
for (pinlist.items, 0..) |pin, ind| {
if (pn.uid == pin.uid) {
toremove = ind;
found = 1;
}
}
if (found == 1) {
_ = pinlist.orderedRemove(toremove);
}
return found;
}
pub fn reactivateSoundNode(alist: *ArrayList(*SoundNode), plist: *ArrayList(*SoundNode), sn: *SoundNode) !void {
const remove_success = removeSoundNodeFromList(plist, sn);
_ = remove_success;
try returnSoundNodeToList(alist, sn);
}
pub fn deactivateSoundNode(alist: *ArrayList(*SoundNode), plist: *ArrayList(*SoundNode), sn: *SoundNode) !void {
const remove_success = removeSoundNodeFromList(alist, sn);
_ = remove_success;
try plist.append(sn);
}
pub fn reactivatePin(alist: *ArrayList(*Pin), plist: *ArrayList(*Pin), pn: *Pin) !void {
const remove_success = removePinFromList(plist, pn);
_ = remove_success;
try returnSoundNodeToList(alist, pn);
}
pub fn deactivatePin(alist: *ArrayList(*Pin), plist: *ArrayList(*Pin), pn: *Pin) !void {
const remove_success = removePinFromList(alist, pn);
_ = remove_success;
try plist.append(pn);
}
pub fn main() !void {
const allocator = std.heap.c_allocator;
// Initializing sound settings
const settings: SoundSettings = SoundSettings{};
// Creating a list of soundnodes
var soundnodes = ArrayList(*SoundNode).init(allocator);
defer soundnodes.deinit();
// Active and passive soundnodes
var a_soundnodes = ArrayList(*SoundNode).init(allocator);
defer a_soundnodes.deinit();
var p_soundnodes = ArrayList(*SoundNode).init(allocator);
defer p_soundnodes.deinit();
// Creating a list of soundnodes
var pins = ArrayList(*Pin).init(allocator);
defer pins.deinit();
// Determining length of resulting audio in ticks
const start_tick: u32 = 0;
var end_tick: u32 = 44100 * 10;
// Loading the input binary code, preparing registers
const file = try std.fs.cwd().openFile("source.snmb", .{});
defer file.close();
const stat = try file.stat();
const buf: []u8 = try file.readToEndAlloc(allocator, stat.size);
var cursor: u32 = 0;
var opcode: u16 = 0;
var tick_start: u32 = 0;
var tick_end: u32 = 0;
var src_node: u16 = 0;
var trg_node: u16 = 0;
var op1: f64 = 0;
var op2: f64 = 0;
var op3: f64 = 0;
var op4: f64 = 0;
var op5: f64 = 0;
var op6: f64 = 0;
// Preparing to write wav data to stdout
const stdout_file = std.io.getStdOut().writer();
var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer();
// Writing WAV header
try stdout.writeAll("RIFF");
try stdout.writeInt(
u32,
36 + (end_tick * 2 * settings.sample_width),
Endian.little,
);
try stdout.writeAll("WAVE");
try stdout.writeAll("fmt ");
try stdout.writeInt(u32, 16, Endian.little);
try stdout.writeInt(u16, 1, Endian.little);
try stdout.writeInt(u16, 2, Endian.little);
try stdout.writeInt(u32, settings.sample_rate, Endian.little);
const block_align: u16 = @intCast(2 * settings.sample_width);
try stdout.writeInt(u32, @as(u32, block_align) * settings.sample_rate, Endian.little);
try stdout.writeInt(u16, block_align, Endian.little);
try stdout.writeInt(u16, settings.bit_depth, Endian.little);
try stdout.writeAll("data");
// Setting up tick iteration
var tick: u32 = start_tick;
var sample: i24 = 0.0;
var left: *SoundNode = undefined;
var right: *SoundNode = undefined;
var nextopcodetick: u32 = 0;
while (tick < end_tick) {
if (tick%44100 == 0) {
print("T {d} ", .{tick});
}
if (tick == nextopcodetick) {
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]});
cursor += 2;
tick_start = std.mem.readVarInt(u32, buf[cursor..cursor+4], .big);
//print("TICKSTART {d} :: {any}\n", .{tick_start, buf[cursor..cursor+4]});
cursor += 4;
if (tick_start > tick) {
cursor -= 6;
nextopcodetick = tick_start;
break;
} else {
tick_end = std.mem.readVarInt(u32, buf[cursor..cursor+4], .big);
//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]});
cursor += 2;
trg_node = std.mem.readVarInt(u16, buf[cursor..cursor+2], .big);
//print("TRGNODE {d} :: {any}\n", .{trg_node, buf[cursor..cursor+2]});
cursor += 2;
op1 = @bitCast(std.mem.readVarInt(u64, buf[cursor..cursor+8], .big));
//print("OP1 {d} :: {any}\n", .{op1, buf[cursor..cursor+8]});
cursor += 8;
op2 = @bitCast(std.mem.readVarInt(u64, buf[cursor..cursor+8], .big));
//print("OP2 {d} :: {any}\n", .{op2, buf[cursor..cursor+8]});
cursor += 8;
op3 = @bitCast(std.mem.readVarInt(u64, buf[cursor..cursor+8], .big));
//print("OP3 {d} :: {any}\n", .{op3, buf[cursor..cursor+8]});
cursor += 8;
op4 = @bitCast(std.mem.readVarInt(u64, buf[cursor..cursor+8], .big));
//print("OP4 {d} :: {any}\n", .{op4, buf[cursor..cursor+8]});
cursor += 8;
op5 = @bitCast(std.mem.readVarInt(u64, buf[cursor..cursor+8], .big));
//print("OP5 {d} :: {any}\n", .{op5, buf[cursor..cursor+8]});
cursor += 8;
op6 = @bitCast(std.mem.readVarInt(u64, buf[cursor..cursor+8], .big));
//print("OP6 {d} :: {any}\n", .{op6, buf[cursor..cursor+8]});
cursor += 8;
// Executing opcodes
switch (opcode) {
0 => {
const uid: usize = @as(usize, src_node);
const sn = try SoundNode.create(allocator, uid);
//print("Added node {s} at tick {d}\n", .{nodename, tick});
try soundnodes.append(sn);
try p_soundnodes.append(sn);
},
1 => {
const src = soundnodes.items[src_node];
const trg = soundnodes.items[trg_node];
//print("Wired nodes at tick {d}\n", .{tick});
const uid: usize = pins.items.len;
const src_pin: usize = @intFromFloat(op1);
const trg_pin: usize = @intFromFloat(op2);
const pin = try Pin.create(allocator, uid, src, trg, src_pin, trg_pin);
try pins.append(pin);
try src.pins.append(pin);
},
3 => {
end_tick = tick_end;
try stdout.writeInt(
u32,
end_tick * 2 * settings.sample_width,
Endian.little);
},
else => {
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});
try src.activities.append(a);
if (src.activities.items.len == 1 and src.active == 0) {
src.active = 1;
try reactivateSoundNode(&a_soundnodes, &p_soundnodes, src);
for (src.pins.items, 0..) |pin, zj| {
pin.active = 1;
_ = zj;
}
}
},
}
}
}
}
for (soundnodes.items, 0..) |soundnode, i| {
soundnode.nullizeStartTick();
_ = i;
}
//All but left and right
if (a_soundnodes.items.len > 2) {
for (a_soundnodes.items[2..], 0..) |soundnode, i| {
var toremove = ArrayList(usize).init(allocator);
defer toremove.deinit();
for (soundnode.activities.items, 0..) |activity, j| {
if (tick <= activity.end_tick and tick >= activity.start_tick) {
try activity.do();
} else if (tick >= activity.end_tick) {
try toremove.append(j);
}
}
for (soundnode.pins.items, 0..) |pin, zj| {
pin.propagate();
_ = zj;
}
var j: usize = toremove.items.len;
while (j > 0) {
j -= 1;
const activity_ind = toremove.items[j];
_ = soundnode.activities.swapRemove(activity_ind);
if (soundnode.activities.items.len == 0 and soundnode.active == 1) {
soundnode.active = 0;
try deactivateSoundNode(&a_soundnodes, &p_soundnodes, soundnode);
for (soundnode.pins.items, 0..) |pin, zj| {
pin.active = 0;
_ = zj;
}
}
}
soundnode.fab.increment_tick();
_ = i;
}
}
// Incrementing passive ones too
for (p_soundnodes.items, 0..) |soundnode, i| {
soundnode.fab.increment_tick();
_ = i;
}
//Left and right
for (soundnodes.items[0..2], 0..) |soundnode, i| {
var toremove = ArrayList(usize).init(allocator);
defer toremove.deinit();
for (soundnode.activities.items, 0..) |activity, j| {
if (tick <= activity.end_tick and tick >= activity.start_tick) {
try activity.do();
} else if (tick >= activity.end_tick) {
try toremove.append(j);
}
}
for (soundnode.pins.items, 0..) |pin, jz| {
pin.propagate();
_ = jz;
}
var j: usize = toremove.items.len;
while (j > 0) {
j -= 1;
const activity_ind = toremove.items[j];
_ = soundnode.activities.swapRemove(activity_ind);
}
_ = i;
}
//Calculating and writing output amps
left = soundnodes.items[0];
right = soundnodes.items[1];
sample = @intFromFloat(left.r_amp * settings.max_amp_multiplier);
try stdout.writeInt(i24, sample, Endian.little);
sample = @intFromFloat(right.r_amp * settings.max_amp_multiplier);
try stdout.writeInt(i24, sample, Endian.little);
left.fab.increment_tick();
right.fab.increment_tick();
tick += 1;
}
try bw.flush();
}