Skip to content

Commit

Permalink
Merge pull request #11 from eshaz/threads
Browse files Browse the repository at this point in the history
Max Threads
  • Loading branch information
eshaz authored Jan 23, 2023
2 parents 9845a7e + dffa5af commit 037c593
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 209 deletions.
142 changes: 127 additions & 15 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ <h4>Sync Multiple Clips</h4>
* syncWorker: Executes in a Web Worker.
* syncWorkerConcurrent: Executes in multiple Web Workers (multi-threaded).
"
id="correlation-method-row"
>
<td>
<label for="correlation-method">Correlation Method</label>
Expand All @@ -420,6 +421,23 @@ <h4>Sync Multiple Clips</h4>
</select>
</td>
</tr>
<tr
title="Selects number of threads to spawn when running concurrent operations"
id="correlation-threads-row"
>
<td>
<label for="correlation-threads">Correlation Threads</label>
</td>
<td>
<input
id="correlation-threads"
name="correlationThreads"
type="number"
min="1"
value="1"
/>
</td>
</tr>
<tr
title="Selects the sample size in audio samples to compare when determining the best correlation.
* This value can be increased for better accuracy, at the expense of execution time."
Expand Down Expand Up @@ -454,6 +472,26 @@ <h4>Sync Multiple Clips</h4>
/>
</td>
</tr>
<tr
title="Threshold that will filter out any low correlation matches.
* Only applicable to Multiple Clips (`syncMultiple`)"
id="correlation-threshold-row"
style="display: none"
>
<td>
<label for="correlation-threshold">Correlation Threshold</label>
</td>
<td>
<input
id="correlation-threshold"
name="correlationThreshold"
type="number"
value="0.5"
min="0"
max="1"
/>
</td>
</tr>
</table>
</fieldset>
<fieldset id="fft-options" class="column" style="flex-grow: 1">
Expand Down Expand Up @@ -503,11 +541,47 @@ <h4>Sync Multiple Clips</h4>
const comparisonAudioClipKey = "comparison";
const correlationAudioClipKey = "correlation";

const audioCtx = new AudioContext();
audioCtx.onstatechange = () => {
if (audioCtx !== "running") audioCtx.resume();
};
audioCtx.destination.channelCount = audioCtx.destination.maxChannelCount;
const AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;

// statically initialize audio context and start using a DOM event
if (AudioContext) {
const audioCtxErrorHandler = (e) => {
console.error(
"Failed to start the AudioContext. WebAudio playback will not be possible.",
e
);
};

// hack for iOS Audio element controls support
// iOS will only enable AudioContext.resume() when called directly from a UI event
// https://stackoverflow.com/questions/57510426
const events = ["touchstart", "touchend", "mousedown", "keydown"];

const unlock = () => {
events.forEach((e) => document.removeEventListener(e, unlock));

audioCtx = new AudioContext({
latencyHint: "interactive",
});

audioCtx
.resume()
.then(() => {
// hack for iOS to continue playing while locked
audioCtx.onstatechange = () => {
if (audioCtx.state !== "running")
audioCtx.resume().catch(audioCtxErrorHandler);
};
})
.catch(audioCtxErrorHandler);

audioCtx.destination.channelCount =
audioCtx.destination.maxChannelCount;
};

events.forEach((e) => document.addEventListener(e, unlock));
}

const PROGRESS_FACTOR = 100;

Expand Down Expand Up @@ -1243,6 +1317,18 @@ <h4>Sync Multiple Clips</h4>
// correlation options
const correlationMethodEl = document.getElementById("correlation-method");

const correlationMethodRowEl = document.getElementById(
"correlation-method-row"
);

const correlationThreadsRowEl = document.getElementById(
"correlation-threads-row"
);

const correlationThresholdRowEl = document.getElementById(
"correlation-threshold-row"
);

// correlation results
const correlationCoefficientRowEl = document.getElementById(
"correlation-coefficient-row"
Expand Down Expand Up @@ -1304,14 +1390,34 @@ <h4>Sync Multiple Clips</h4>
const twoClipsMode = "two-clips-mode";
const multipleClipsMode = "multiple-clips-mode";

let mode = twoClipsMode;
let mode;

// thread option control
document.getElementById("correlation-threads").value =
navigator.hardwareConcurrency - 1;

const correlationThreadControl = () => {
if (
correlationMethodEl.value === "syncWorkerConcurrent" ||
mode === multipleClipsMode
) {
correlationThreadsRowEl.style = "";
} else {
correlationThreadsRowEl.style = "display: none;";
}
};

correlationMethodEl.addEventListener("change", correlationThreadControl);

const setTwoClipsMode = () => {
mode = twoClipsMode;

// destroy the multiple clip ffts
multipleClipsFieldsetEl.style = "display: none;";
correlationResultsJsonRowEl.style = "display: none;";
correlationMethodEl.disabled = false;
correlationThresholdRowEl.style = "display: none;";
correlationMethodRowEl.style = "";
correlationThreadControl();
reset();
twoClipsModeEl.checked = true;
multipleClipsModeEl.checked = false;
Expand Down Expand Up @@ -1352,10 +1458,12 @@ <h4>Sync Multiple Clips</h4>

// destroy the two clip ffts
twoClipsFieldsetEl.style = "display: none;";
correlationMethodEl.disabled = true;
// hide single match results
correlationCoefficientRowEl.style = "display: none;";
correlationSampleOffsetRowEl.style = "display: none;";
correlationThresholdRowEl.style = "";
correlationMethodRowEl.style = "display: none;";
correlationThreadControl();
reset();
multipleClipsModeEl.checked = true;
twoClipsModeEl.checked = false;
Expand Down Expand Up @@ -1392,10 +1500,12 @@ <h4>Sync Multiple Clips</h4>
};

const destroyCorrelationFFT = () => {
if (audioData[correlationAudioClipKey])
audioData[correlationAudioClipKey].fft.destroy();

delete audioData[correlationAudioClipKey];
for (const key in audioData) {
if (key.match(/correlation/)) {
audioData[key].fft.destroy();
delete audioData[key];
}
}

if (mode === twoClipsMode) {
resultContainer.innerHTML = getFFTContainer(
Expand Down Expand Up @@ -1569,13 +1679,15 @@ <h4>Sync Multiple Clips</h4>
initialGranularity: parseInt(
document.getElementById("initial-granularity").value
),
correlationThreshold: 0.5,
correlationThreshold: parseFloat(
document.getElementById("correlation-threshold")
),
});

const start = performance.now();
const results = await synAudio.syncMultiple(
params,
window.hardwareConcurrency
parseInt(document.getElementById("correlation-threads").value)
);
const duration = (performance.now() - start) / 1000;

Expand Down Expand Up @@ -1663,7 +1775,7 @@ <h4>Sync Multiple Clips</h4>
const result = await synAudio[method](
audioBufferToSynAudioParameter(baseAudioBuffer),
audioBufferToSynAudioParameter(comparisonAudioBuffer),
navigator.hardwareConcurrency - 1
parseInt(document.getElementById("correlation-threads").value)
);
const duration = (performance.now() - start) / 1000;

Expand Down
39 changes: 30 additions & 9 deletions demo/synaudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,18 +438,33 @@
// overlap at the start of the buffer by correlation sample size
// overlap at the end of the buffer by correlation sample size

// correlation sample size overlap imposes a maximum thread count for small datasets
const minProcessingRatio = 4 / 1; // unique date / overlap
// initial granularity low -> high, more -> less threads
// correlation sample low -> high, less -> more threads
// file size low -> high, less -> more threads

const correlationSampleSize = this._getCorrelationSampleSize(a, b);
const maxThreads = Math.ceil(
a.samplesDecoded / correlationSampleSize / minProcessingRatio

// rough estimate for a good max thread count for performance
const maxThreads =
(Math.log(a.samplesDecoded * correlationSampleSize) /
Math.log(this._initialGranularity + 1)) *
Math.log(correlationSampleSize / 10000 + 1);

threads = Math.max(
Math.round(
Math.min(
threads,
maxThreads,
a.samplesDecoded / correlationSampleSize / 4
)
),
1
);
threads = Math.min(threads, maxThreads);

const aLength = Math.ceil(a.samplesDecoded / threads);

let offset = 0;
for (let i = 1; i <= threads; i++) {
for (let t = 0; t < threads; t++) {
const aSplit = {
channelData: [],
};
Expand Down Expand Up @@ -520,8 +535,12 @@
);
}

async syncWorkerConcurrent(a, b, threads = 1) {
return this._instance._syncWorkerConcurrentMain(a, b, threads);
async syncWorkerConcurrent(a, b, threads) {
return this._instance._syncWorkerConcurrentMain(
a,
b,
threads >= 1 ? threads : 1
);
}

async syncWorker(a, b) {
Expand All @@ -532,7 +551,9 @@
return this._instance._sync(a, b);
}

async syncMultiple(clips, threads = 8) {
async syncMultiple(clips, threads) {
threads = threads >= 1 ? threads : 8;

const workers = [];
const graph = [];

Expand Down
Loading

0 comments on commit 037c593

Please sign in to comment.