Extract the shared realtime audio graph owner
This commit is contained in:
75
frontend/audio-graph.test.js
Normal file
75
frontend/audio-graph.test.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { readFileSync } = require('node:fs');
|
||||
const { join } = require('node:path');
|
||||
const AudioGraph = require('./audio-graph');
|
||||
|
||||
test('AudioGraph creates its context only on demand and retains the first context options', () => {
|
||||
let contexts = 0, optionsSeen;
|
||||
class Context {
|
||||
constructor(options) { contexts++; optionsSeen = options; this.sampleRate = options?.sampleRate || 44100; }
|
||||
}
|
||||
const graph = AudioGraph.create({ getConstructor: () => Context });
|
||||
assert.equal(graph.snapshot().contextCreated, false);
|
||||
const first = graph.context({ sampleRate: 48000 });
|
||||
assert.equal(graph.context({ sampleRate: 44100 }), first);
|
||||
assert.equal(contexts, 1);
|
||||
assert.deepEqual(optionsSeen, { sampleRate: 48000 });
|
||||
assert.equal(first.sampleRate, 48000);
|
||||
});
|
||||
|
||||
test('one retained MediaElementSource per element can change insert routes without recreation', () => {
|
||||
const made = [], connects = [], disconnects = [];
|
||||
class Context {
|
||||
constructor() { this.sampleRate = 48000; }
|
||||
createMediaElementSource(element) {
|
||||
const source = { element, connect: target => connects.push([element.id, target.id]), disconnect: () => disconnects.push(element.id) };
|
||||
made.push(source); return source;
|
||||
}
|
||||
}
|
||||
const graph = AudioGraph.create({ getConstructor: () => Context });
|
||||
const a = { id: 'video' }, b = { id: 'audio' }, standard = { id: 'standard' }, dsp = { id: 'dsp' };
|
||||
const original = graph.connect(a, standard);
|
||||
graph.connect(a, standard);
|
||||
graph.routeAll([a, b], dsp);
|
||||
graph.routeAll([a, b], standard);
|
||||
assert.equal(made.length, 2);
|
||||
assert.equal(graph.source(a), original);
|
||||
assert.deepEqual(connects, [['video', 'standard'], ['video', 'dsp'], ['audio', 'dsp'], ['video', 'standard'], ['audio', 'standard']]);
|
||||
assert.deepEqual(disconnects, ['video', 'video', 'audio']);
|
||||
assert.deepEqual(graph.snapshot(), { contextCreated: true, sampleRate: 48000, sourceCount: 2, failedCount: 0, routedCount: 2 });
|
||||
});
|
||||
|
||||
test('a failed source creation is remembered and is never attempted twice', () => {
|
||||
let attempts = 0;
|
||||
class Context {
|
||||
constructor() { this.sampleRate = 48000; }
|
||||
createMediaElementSource() { attempts++; throw new Error('already sourced'); }
|
||||
}
|
||||
const graph = AudioGraph.create({ getConstructor: () => Context }), element = {};
|
||||
assert.throws(() => graph.source(element), /already sourced/);
|
||||
assert.throws(() => graph.source(element), /already sourced/);
|
||||
assert.equal(attempts, 1);
|
||||
assert.deepEqual(graph.snapshot(), { contextCreated: true, sampleRate: 48000, sourceCount: 0, failedCount: 1, routedCount: 0 });
|
||||
});
|
||||
|
||||
test('the extracted Standard EQ build block is identical to the original except for context ownership', () => {
|
||||
const source = readFileSync(join(__dirname, 'app.js'), 'utf8');
|
||||
const original = readFileSync(join(__dirname, '..', 'tests', 'fixtures', 'standard-eq-build.txt'), 'utf8').trimEnd();
|
||||
const start = source.indexOf(' function build() {', source.indexOf('const EQ = (() => {'));
|
||||
const end = source.indexOf('\n function attach(el)', start);
|
||||
assert.ok(start >= 0 && end > start, 'EQ build block exists');
|
||||
const current = source.slice(start, end).trimEnd();
|
||||
assert.equal(current, original.replace('st.ctx = new AC();', 'st.ctx = AudioGraph.context();'));
|
||||
assert.match(source, /AudioGraph\.connect\(el, st\.filters\[0\]\)/);
|
||||
assert.doesNotMatch(source, /\.createMediaElementSource\(/);
|
||||
});
|
||||
|
||||
test('AudioGraph is a core runtime asset and is loaded before the lazy app', () => {
|
||||
const { groups } = JSON.parse(readFileSync(join(__dirname, 'assets.json'), 'utf8'));
|
||||
assert.ok(groups.core.files.includes('/audio-graph.js'));
|
||||
const html = readFileSync(join(__dirname, 'index.html'), 'utf8');
|
||||
assert.ok(html.indexOf('src="audio-graph.js"') < html.indexOf('src="section-rail.js"'));
|
||||
const embedded = JSON.parse(html.match(/id="ytp-assets">([^<]+)<\/script>/)[1]);
|
||||
assert.deepEqual(embedded.groups, groups);
|
||||
});
|
||||
Reference in New Issue
Block a user