@@ -228,6 +228,11 @@ export fn file_title(index: Db.File.Index) String {
return String.init(db.stringToSlice(db.file(index).title));
}
export fn playbackFileIndex() Db.File.OptionalIndex {
const head = currentPlayHead() orelse return .none;
return head.item.file.toOptional();
}
export fn playbackTitle() String {
const head = currentPlayHead() orelse return String.empty;
return String.init(db.stringToSlice(head.file.title));
@@ -243,6 +248,64 @@ export fn playbackDuration() f64 {
return @as(f64, @floatFromInt(head.duration)) / std.time.ns_per_s;
}
var waveform_result: [waveform_row_size * waveform_h]u8 = undefined;
const waveform_row_size = Db.waveform_width * 4;
const waveform_h = 256;
const peak_color = [4]u8{ 0x00, 0x73, 0xff, 0xff };
const transparent_color = [4]u8{ 0x00, 0x73, 0xff, 0x00 };
const rms_color = [4]u8{ 0xcc, 0xcc, 0xee, 0xff };
export fn fileWaveform(opt_file_index: Db.File.OptionalIndex) Slice(u8) {
const file_index = opt_file_index.unwrap() orelse {
@memset(&waveform_result, 0);
return Slice(u8).init(&waveform_result);
};
const channel_len = Db.waveform_width / Twi.slice_len; // number of u64 in the channel
const channel_count = @typeInfo(Twi.ColumnSlice).Struct.fields.len;
const waveform_size = channel_count * channel_len; // total number of u64 in the waveform
const start = waveform_size * @intFromEnum(file_index);
const max_array = db.waveforms.items[start + channel_len * 0 ..][0..channel_len];
const min_array = db.waveforms.items[start + channel_len * 1 ..][0..channel_len];
const rms_array = db.waveforms.items[start + channel_len * 2 ..][0..channel_len];
var decoder = Twi.Decoder.init;
var out_max: [Twi.slice_len]i8 = undefined;
var out_min: [Twi.slice_len]i8 = undefined;
var out_rms: [Twi.slice_len]i8 = undefined;
for (max_array, min_array, rms_array, 0..) |max_slice, min_slice, rms_slice, slice_index| {
decoder.decode(.{
.max = max_slice,
.min = min_slice,
.rms = rms_slice,
}, &out_max, &out_min, &out_rms);
for (out_max, out_min, out_rms, 0..) |max, decoded_min, rms, i| {
const min = @min(decoded_min, max);
const inv_max: usize = @intCast(waveform_h - (128 + @as(i32, max)));
const inv_min: usize = @intCast(waveform_h - (128 + @as(i32, min)));
const x = Twi.slice_len * slice_index + i;
for (0..inv_max) |y| {
waveform_result[y * waveform_row_size + x * 4 ..][0..4].* = transparent_color;
}
for (inv_max..inv_min) |y| {
waveform_result[y * waveform_row_size + x * 4 ..][0..4].* = peak_color;
}
for (inv_min..256) |y| {
waveform_result[y * waveform_row_size + x * 4 ..][0..4].* = transparent_color;
}
const rms_mag: usize = @intCast(128 + @as(i32, rms));
const half = rms_mag / 2;
log.debug("rms={d} rms_mag={d} half={d}", .{ rms, rms_mag, half });
for (127 - half..127 + half) |y| {
waveform_result[y * waveform_row_size + x * 4 ..][0..4].* = rms_color;
}
}
}
return Slice(u8).init(&waveform_result);
}
fn send(bytes: []const u8) void {
js.send(bytes.ptr, bytes.len);
}