480 lines
No EOL
12 KiB
Zig
480 lines
No EOL
12 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 Link = @import ("link.zig").Link;
|
|
const Activity = @import("activity.zig").Activity;
|
|
const SoundSettings = @import("settings.zig").SoundSettings;
|
|
const utility = @import("utility.zig");
|
|
|
|
|
|
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 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 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();
|
|
|
|
// 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("{d}-- ", .{tick/44100});
|
|
}
|
|
|
|
|
|
for (a_soundnodes.items, 0..) |soundnode, i| {
|
|
//print("TICK {d} I {d} ", .{tick, i});
|
|
@memset(soundnode.pins[0..32], 0);
|
|
_ = i;
|
|
|
|
}
|
|
|
|
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 freq_q: u8 = @truncate(trg_node);
|
|
|
|
const sn = try SoundNode.create(allocator, uid, freq_q);
|
|
sn.pins[32]=1;
|
|
|
|
//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 src_pin: usize = @intFromFloat(op1);
|
|
const trg_pin: usize = @intFromFloat(op2);
|
|
|
|
const link = try Link.create(allocator, src, trg, src_pin, trg_pin);
|
|
try trg.links.append(link);
|
|
},
|
|
|
|
2 => {
|
|
|
|
end_tick = tick_end;
|
|
|
|
try stdout.writeInt(
|
|
u32,
|
|
end_tick * 2 * settings.sample_width,
|
|
Endian.little);
|
|
|
|
},
|
|
|
|
3 => {
|
|
|
|
const sn = soundnodes.items[src_node];
|
|
const sn_pin: usize = @intFromFloat(op1);
|
|
|
|
sn.pins[sn_pin] = op2;
|
|
|
|
},
|
|
|
|
9 => {
|
|
|
|
const sn = soundnodes.items[src_node];
|
|
sn.wantprint = true;
|
|
|
|
},
|
|
|
|
8 => {
|
|
const src = soundnodes.items[src_node];
|
|
const trg = soundnodes.items[trg_node];
|
|
//print("Wired nodes at tick {d}\n", .{tick});
|
|
|
|
const src_pin: usize = @intFromFloat(op1);
|
|
const trg_pin: usize = @intFromFloat(op2);
|
|
|
|
var found: bool = false;
|
|
var toremove: u64 = 0;
|
|
|
|
for (trg.links.items, 0..) |link, j| {
|
|
if (link.src_node == src and link.trg_node == trg and link.src_pin == src_pin and link.trg_pin == trg_pin) {
|
|
toremove = j;
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (found) {
|
|
_ = trg.links.orderedRemove(toremove);
|
|
}
|
|
},
|
|
|
|
else => {
|
|
|
|
const soundnode = soundnodes.items[src_node];
|
|
const a = try Activity.create(allocator, tick_start, tick_end, opcode, soundnode, [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 soundnode.activities.append(a);
|
|
|
|
if (soundnode.activities.items.len == 1 and soundnode.active == 0) {
|
|
|
|
soundnode.active = 1;
|
|
try reactivateSoundNode(&a_soundnodes, &p_soundnodes, soundnode);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//All but left and right
|
|
|
|
var iter: usize = 1;
|
|
//while (iter < 0) {
|
|
|
|
while (iter < a_soundnodes.items.len - 1) {
|
|
|
|
iter += 1;
|
|
const soundnode = a_soundnodes.items[iter];
|
|
|
|
for (soundnode.links.items, 0..) |link, j| {
|
|
|
|
if (link.src_node.active == 1 or link.trg_pin == 1) {
|
|
link.propagate();
|
|
|
|
}
|
|
_ = j;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (soundnode.wantprint == true) {
|
|
soundnode.wantprint = false;
|
|
soundnode.printState();
|
|
}
|
|
|
|
var j: usize = toremove.items.len;
|
|
|
|
if (j == 0) {
|
|
soundnode.fab.increment_tick();
|
|
}
|
|
|
|
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);
|
|
|
|
//print("DEACTIVATING {d} AT TICK {d} ", .{soundnode.uid, tick});
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Incrementing passive ones too
|
|
|
|
for (p_soundnodes.items, 0..) |soundnode, i| {
|
|
soundnode.fab.increment_tick();
|
|
_ = i;
|
|
|
|
}
|
|
|
|
//Left and right
|
|
|
|
iter = 0;
|
|
|
|
//while (iter < 0) {
|
|
while (iter < 2) {
|
|
|
|
const soundnode = soundnodes.items[iter];
|
|
iter += 1;
|
|
|
|
for (soundnode.links.items, 0..) |link, jz| {
|
|
if (link.src_node.active == 1 or link.trg_pin == 1) {
|
|
link.propagate();
|
|
}
|
|
_ = jz;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
if (soundnode.wantprint == true) {
|
|
soundnode.wantprint = false;
|
|
soundnode.printState();
|
|
}
|
|
|
|
var j: usize = toremove.items.len;
|
|
|
|
while (j > 0) {
|
|
j -= 1;
|
|
const activity_ind = toremove.items[j];
|
|
_ = soundnode.activities.swapRemove(activity_ind);
|
|
}
|
|
|
|
}
|
|
|
|
//Calculating and writing output amps
|
|
|
|
left = soundnodes.items[0];
|
|
right = soundnodes.items[1];
|
|
|
|
var left_r_amp = left.pins[0];
|
|
|
|
if (left_r_amp > 1) {
|
|
left_r_amp = 1;
|
|
} else if (left_r_amp < -1) {
|
|
left_r_amp = -1;
|
|
}
|
|
|
|
sample = @intFromFloat(left_r_amp * settings.max_amp_multiplier);
|
|
try stdout.writeInt(i24, sample, Endian.little);
|
|
|
|
var right_r_amp = right.pins[0];
|
|
|
|
if (right_r_amp > 1) {
|
|
right_r_amp = 1;
|
|
} else if (right_r_amp < -1) {
|
|
right_r_amp = -1;
|
|
}
|
|
|
|
sample = @intFromFloat(right_r_amp * settings.max_amp_multiplier);
|
|
try stdout.writeInt(i24, sample, Endian.little);
|
|
|
|
left.fab.current_tick += 1;
|
|
right.fab.current_tick += 1;
|
|
|
|
|
|
tick += 1;
|
|
|
|
}
|
|
|
|
try bw.flush();
|
|
print("\n", .{});
|
|
|
|
} |