Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c6aa133
[client] start work on visualizations
yellowsink Jun 7, 2024
58fa51a
[vis] improve waveform
yellowsink Jun 7, 2024
738dd08
[vis] allow storing buffer in the worker
yellowsink Jun 7, 2024
6c8dee1
[vis] switch TheWaveform to use a canvas
yellowsink Jun 8, 2024
149dde6
[vis] not yet working great attempts at showing one waveform
yellowsink Jun 8, 2024
1470ab5
[vis] add spectrum analyzer
yellowsink Jun 8, 2024
467322c
[vis] very unfinished work on switching to wasm
yellowsink Jun 9, 2024
19ed89d
[vis] [wasm] implement more fft with simd
yellowsink Jun 9, 2024
ba5637e
[vis] [wasm] impl fft
yellowsink Jun 9, 2024
c052d7b
[vis] [wasm] get wasm fully working for downscale and fft
yellowsink Jun 9, 2024
0a9c416
[vis] [wasm] get the waveform working
yellowsink Jun 9, 2024
789db3f
[vis] [wasm] housekeeping
yellowsink Jun 9, 2024
a72fc7b
[vis] add fft persistence, tweak seek update scheduling, refactoring
yellowsink Jun 9, 2024
105d681
[vis] start on specific view for it
yellowsink Jun 10, 2024
a9046ea
[vis] add centered slice, bug fixing and tweaks
yellowsink Jun 10, 2024
ef7cce8
[vis] wasm fastutils to better isolate unsafe code and add better ass…
yellowsink Jun 11, 2024
f20633d
[vis] basic metering
yellowsink Jun 12, 2024
b8b34cd
[vis] improve metering to better match daws
yellowsink Jun 12, 2024
f1223ca
[vis] improve waveform, fix some bad simd memory bugs!
yellowsink Jun 12, 2024
aebe32b
[vis] a start on an anti-shimmer for the scrolling waveform
yellowsink Jun 12, 2024
cab85f9
[vis] add multi-channel metering
yellowsink Jun 12, 2024
9715dc8
[vis] add goniometer
yellowsink Jun 12, 2024
5d2ebc7
[vis] adjust layout a bit
yellowsink Jun 12, 2024
af28348
[vis] add M/S switch, bugfix
yellowsink Jun 12, 2024
4132634
[vis] prevent giant stutter if not rendering for a while
yellowsink Jun 12, 2024
903130f
[vis] entirely fix stutter on open
yellowsink Jun 12, 2024
09b8819
add a note
yellowsink Jun 12, 2024
0ebd9c2
[vis] start implementing spectogram - issue, very slow
yellowsink Jun 13, 2024
64204de
Merge branch 'master' into gen-z-mode
xirreal Jun 21, 2026
90e4cc7
redundant info mode is now more redundant
xirreal Jun 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface SessionRepository {
suspend fun connectSyncClient()

suspend fun requestData()

fun observeSyncClientState(): Flow<ClientState>

fun observeProgressbar(): Flow<Long>
Expand Down
24 changes: 24 additions & 0 deletions clients/web/dsp-asm/asconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"targets": {
"debug": {
"outFile": "build/debug.wasm",
"textFile": "build/debug.wat",
"sourceMap": true,
"debug": true,
"bindings": "esm"
},
"release": {
"outFile": "build/release.wasm",
"textFile": "build/release.wat",
"sourceMap": true,
"optimizeLevel": 3,
"shrinkLevel": 0,
"converge": false,
"noAssert": false
}
},
"options": {
"bindings": "raw",
"enable": ["simd"]
}
}
165 changes: 165 additions & 0 deletions clients/web/dsp-asm/assembly/fastutils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// uwr fastutils for assemblyscript
// a small and application specific wrapper library for otherwise unsafe ops by hazel

/** UNSAFE CONTRACT: len MUST be a multiple of 4, and within buffer bounds */
export function listAbsMax_f32_UNCHECKED(buf: Float32Array, len: i32): f32 {
let running = v128.splat<f32>(0);

for (let i = 0; i < len; i += sizeof<v128>() / sizeof<f32>()) {
// pmax is faster than max due to not treating nulls etc
// https://source.chromium.org/chromium/chromium/src/+/main:v8/src/compiler/backend/x64/code-generator-x64.cc;drc=8ab75a56a24f34d4f582261c99939ffa1446a3b7;l=2712
running = f32x4.pmax(running, f32x4.abs(f32x4_load(buf, i)));
}

return Mathf.max(
Mathf.max(f32x4.extract_lane(running, 0), f32x4.extract_lane(running, 1)),
Mathf.max(f32x4.extract_lane(running, 2), f32x4.extract_lane(running, 3)),
);
}

/** UNSAFE CONTRACT: len MUST be a multiple of 4, and within buffer bounds */
export function listMax_f32_UNCHECKED(buf: Float32Array, len: i32): f32 {
let running = v128.splat<f32>(f32.MIN_VALUE);

for (let i = 0; i < len; i += sizeof<v128>() / sizeof<f32>()) {
running = f32x4.pmax(running, f32x4_load(buf, i));
}

return Mathf.max(
Mathf.max(f32x4.extract_lane(running, 0), f32x4.extract_lane(running, 1)),
Mathf.max(f32x4.extract_lane(running, 2), f32x4.extract_lane(running, 3)),
);
}

/** finds the maximum element of the buffer with SIMD */
export function listMax_f32(buf: Float32Array): f32 {
assert(buf.length, "cannot get max of an empty buffer");

const simdSafeLen = 4 * (buf.length / 4);

// if buffer is smaller than 4, this will return f32.MIN_VALUE
let running = listMax_f32_UNCHECKED(buf, simdSafeLen);

for (let i = simdSafeLen; i < buf.length; i++) {
running = Mathf.max(running, buf[i]);
}

return running;
}

/** finds the maximum element of the absolute values of the buffer with SIMD */
export function listAbsMax_f32(buf: Float32Array): f32 {
assert(buf.length, "cannot get abs max of an empty buffer");

const simdSafeLen = 4 * (buf.length / 4);

// if buffer is smaller than 4, this will return 0
let running = listAbsMax_f32_UNCHECKED(buf, simdSafeLen);

for (let i = simdSafeLen; i < buf.length; i++) {
running = Mathf.max(running, Mathf.abs(buf[i]));
}

return running;
}

/** UNSAFE CONTRACT: len MUST be a multiple of 4, and within buffer bounds */
export function listSqSum_f32_UNCHECKED(buf: Float32Array, len: i32): f32 {
let running = v128.splat<f32>(0);

for (let i = 0; i < len; i += sizeof<v128>() / sizeof<f32>()) {
const vec = f32x4_load(buf, i)
running = f32x4.add(running, f32x4.mul(vec, vec));
}

return f32x4.extract_lane(running, 0)
+ f32x4.extract_lane(running, 1)
+ f32x4.extract_lane(running, 2)
+ f32x4.extract_lane(running, 3);
}

/** finds the sum of the squares of the buffer with SIMD */
export function listSqSum_f32(buf: Float32Array): f32 {
assert(buf.length, "cannot get abs max of an empty buffer");

const simdSafeLen = 4 * (buf.length / 4);

// if buffer is smaller than 4, this will return 0
let running = listSqSum_f32_UNCHECKED(buf, simdSafeLen);

for (let i = simdSafeLen; i < buf.length; i++) {
running += buf[i] * buf[i];
}

return running;
}

/** negates a buffer with SIMD */
export function listNeg_f32(buf: Float32Array): void {
if (!buf.length) return;

const simdSafeLen = 4 * (buf.length / 4);
for (let i = 0; i < simdSafeLen; i += sizeof<v128>() / sizeof<f32>()) {
f32x4_store(f32x4.neg(f32x4_load(buf, i)), buf, i);
}

for (let i = simdSafeLen; i < buf.length; i++) {
buf[i] = -buf[i];
}
}

/** checked fast memory copy between two buffers.
* pass two buffers, the bytes per element, the offsets for where to write to, and the number of elemments.
* a raw byte copy can be achieved with sz = 1.
* takes isize not usize for checking reasons. */
export function memcpy(
src: ArrayBuffer,
dst: ArrayBuffer,
srcOset: isize,
dstOset: isize,
elSz: isize,
n: isize,
): void {
assert((srcOset + n) * elSz <= src.byteLength, "source will overflow");
assert((dstOset + n) * elSz <= dst.byteLength, "destination will overflow");
assert(elSz > 0, "element size must be >0");
assert(srcOset >= 0, "source offset must be >=0");
assert(dstOset >= 0, "destination offset must be >=0");
assert(n >= 0, "elems copied must be >=0");

// https://github.com/AssemblyScript/assemblyscript/issues/743#issuecomment-518022808
const srcPtr = changetype<usize>(src);
const dstPtr = changetype<usize>(dst);

if (n !== 0) {
memory.copy(dstPtr + elSz * dstOset, srcPtr + elSz * srcOset, elSz * n);
}
}

/** checked fast memory copy between two float32arrays */
@inline
export function memcpy_f32(
src: Float32Array,
dst: Float32Array,
srcOset: isize,
dstOset: isize,
n: isize,
): void {
return memcpy(src.buffer, dst.buffer, srcOset, dstOset, Float32Array.BYTES_PER_ELEMENT, n);
}

/** checked load f32x4 from Float32Array */
export function f32x4_load(buf: Float32Array, i: isize): v128 {
assert(i >= 0, "index must be >=0");
assert(i + 3 < buf.length, "index will overflow");

return v128.load(buf.dataStart + i * Float32Array.BYTES_PER_ELEMENT);
}

/** checked store f32x4 to Float32Array */
export function f32x4_store(v: v128, buf: Float32Array, i: isize): void {
assert(i >= 0, "index must be >=0");
assert(i + 3 < buf.length, "index will overflow");

v128.store(buf.dataStart + i * Float32Array.BYTES_PER_ELEMENT, v);
}
Loading
Loading