Skip to content

Commit

Permalink
implement library functions for dsp graph building and realtime and o…
Browse files Browse the repository at this point in the history
…ffline audio buffer comparison

Co-authored-by: kizjkre <kizjkre@users.noreply.github.com>
  • Loading branch information
terryzfeng and Kizjkre committed Jun 26, 2024
1 parent 0c9e322 commit 055cd61
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 171 deletions.
16 changes: 16 additions & 0 deletions src/tests/playwright/pages/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="play">Play</button>
<script type="module">
document.getElementById("play").addEventListener("click", async () => {
import ('./util/audioComparer.js')
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import concat from '../../util/concat.js';

export default async (ctx, length) => {
console.assert(ctx instanceof AudioContext);
console.assert(length instanceof Number);
console.assert(typeof length === 'number' && length > 0);

const mutex = new Promise((resolve) =>
setTimeout(resolve, 1000 * length));
Expand All @@ -24,14 +24,13 @@ export default async (ctx, length) => {
arrays.forEach((array, i) => res[i] = concat(...array));

const buf = new AudioBuffer({
length: res[0].byteLength,
length: res[0].length,
sampleRate: ctx.sampleRate,
numberOfChannels: res.length,
});

res.forEach((array, i) => buf.copyToChannel(array, i));

resolve(res[0]);
resolve(buf);
});

return {recorder, buffer};
Expand Down
162 changes: 0 additions & 162 deletions src/tests/playwright/pages/util/audioBufferToWav.js

This file was deleted.

82 changes: 82 additions & 0 deletions src/tests/playwright/pages/util/audioComparer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { bufferinator } from './bufferinator.js';
import { createGraphCache } from './graph.js';

const SAMPLE_RATE = 48000;
const NUM_CHANNELS = 1;

// CREATE DSP GRAPH HERE!!!
const createGraph = ctx => {
// My Graph
const osc = new OscillatorNode(ctx);
osc.type = 'sawtooth';
const gain = new GainNode(ctx);
gain.gain.value = 2.2;
const biq = new BiquadFilterNode(ctx);
biq.type = 'bandpass';
biq.frequency.value = 1000;
biq.Q.value = 10;
osc.connect(gain).connect(biq).connect(ctx.destination);

// My render time
const length = 1;

osc.start();
osc.stop(ctx.currentTime + length);
}

/**
* Compares two audio buffers for equality.
* @param {AudioBuffer} myBuf
* @param {AudioBuffer} refBuf
* @returns {number} returns the percentage of samples that are equal
*/
function bufferComparer(myBuf, refBuf) {
let numCorrect = 0;
const numChannels = myBuf.numberOfChannels;
for (let c = 0; c < numChannels; c++) {
const myChannel = myBuf.getChannelData(c);
const refChannel = refBuf.getChannelData(c);
console.log(myChannel.length, refChannel.length)
for (let i = 0; i < myChannel.length; i++) {
if (myChannel[i] === refChannel[i]) {
numCorrect++;
}
}
}
return numCorrect / (numChannels * myBuf.length);
}

/**
* Takes in an dsp graph to build for realtime and offline context + length of buffer to render.
* Records output of both contexts as audio buffers.
* Compares the real time buffer and the offline buffer for equality.
* @param {(ctx: AudioContext) => void} ctxGraph dsp graph to build in context
* @param {number} length length of buffer to render in seconds
* @returns {boolean} true if buffers are equal
*/
async function evaluateGraph(ctxGraph, length = 1) {
const audioContext = new AudioContext({ sampleRate: SAMPLE_RATE });
audioContext.suspend();
const offlineAudioContext = new OfflineAudioContext({
numberOfChannels: NUM_CHANNELS,
length: length * SAMPLE_RATE,
sampleRate: SAMPLE_RATE
});

// create ctxGraph cache
const cache = createGraphCache();

// create realtime and offline graphs
ctxGraph(audioContext);
ctxGraph(offlineAudioContext);

// render graphs to audio buffer
const realtimeBuffer = await bufferinator(audioContext, length, cache)
const offlineBuffer = await bufferinator(offlineAudioContext)

const score = bufferComparer(realtimeBuffer, offlineBuffer);

return score;
}

console.log(await evaluateGraph(createGraph, 1));
14 changes: 9 additions & 5 deletions src/tests/playwright/pages/util/bufferinator.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import record from "../processors/recorder/recorder-main";
import record from "../processors/recorder/recorder-main.js";

export const bufferinator = async (ctx, length, graph) => {
if (ctx instanceof AudioContext) {
const {recorder, buffer} = await record(ctx, length);
recorder.connect(ctx.destination);
graph.get(ctx)?.forEach(([from, to]) => {
if (to instanceof AudioDestinationNode) {
from.disconnect(to);
from.connect(recorder);
from._WAS_disconnect(to);
from._WAS_connect(recorder);
}
});

recorder.connect(ctx.destination);
ctx.resume();

// for realtime
return await buffer;
}

return ctx.startRendering();
// for offline
return ctx.startRendering();
};
8 changes: 8 additions & 0 deletions src/tests/playwright/pages/util/graph.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
/**
* @type {Map<BaseAudioContext, Set<[AudioNode, AudioNode]>}
* @description A map of AudioContexts to a set of connected nodes.
* Build graph to track connections between nodes.
*/
const graph = new Map();

export const createGraphCache = () => {
const connect = AudioNode.prototype.connect;
const disconnect = AudioNode.prototype.disconnect;

AudioNode.prototype._WAS_connect = connect;
AudioNode.prototype._WAS_disconnect = disconnect;

AudioNode.prototype.connect = function () {
if (!graph.has(this.context)) {
graph.set(this.context, new Set());
Expand Down

0 comments on commit 055cd61

Please sign in to comment.