simplify playback speed code
cherrypicked from957d46e
(rework playback speed, 2024-06-29) andd1a8e5e
(simplify audioworklet, 2024-06-21)
This commit is contained in:
parent
ba4742cfd8
commit
5ec0219691
4 changed files with 77 additions and 94 deletions
|
@ -64,13 +64,11 @@ class BytebeatProcessor extends AudioWorkletProcessor {
|
|||
lastValue = [0, 0];
|
||||
lastFuncValue = [null, null];
|
||||
|
||||
isPlaying = false;
|
||||
|
||||
func = null;
|
||||
calcByteValue = null;
|
||||
songData = { sampleRate: null, mode: null };
|
||||
sampleRateDivisor = 1;
|
||||
playSpeed = 1;
|
||||
playSpeed = 0;
|
||||
|
||||
postedErrorPriority = null;
|
||||
constructor() {
|
||||
|
@ -91,7 +89,7 @@ class BytebeatProcessor extends AudioWorkletProcessor {
|
|||
const data = e.data;
|
||||
|
||||
// set vars
|
||||
for (let v of ["isPlaying", "songData", "sampleRateDivisor", "playSpeed"]) {
|
||||
for (let v of ["songData", "sampleRateDivisor", "playSpeed"]) {
|
||||
if (data[v] !== undefined) {
|
||||
this[v] = data[v];
|
||||
}
|
||||
|
@ -219,7 +217,7 @@ class BytebeatProcessor extends AudioWorkletProcessor {
|
|||
): boolean {
|
||||
const chData = outputs[0];
|
||||
const chDataLen = chData[0].length; // for performance
|
||||
if (!chDataLen || !this.isPlaying || !this.func) {
|
||||
if (!chDataLen || !this.playSpeed || !this.func) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
111
src/bytebeat.ts
111
src/bytebeat.ts
|
@ -7,6 +7,19 @@ import { Oscillioscope } from "./oscillioscope.ts";
|
|||
const timeUnits = ["t", "s"] as const;
|
||||
type TimeUnit = (typeof timeUnits)[number];
|
||||
|
||||
// XXX: global variables
|
||||
let timeUnit: TimeUnit;
|
||||
let volume: number;
|
||||
let byteSample = 0;
|
||||
let playSpeed = 0;
|
||||
let songData: {
|
||||
sampleRate: number;
|
||||
mode: SongMode;
|
||||
} = {
|
||||
sampleRate: 8000, // float32
|
||||
mode: "Bytebeat",
|
||||
};
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
|
||||
const audioPromise = initAudioContext();
|
||||
|
@ -18,35 +31,23 @@ if (globalThis.loadLibrary !== false) {
|
|||
}
|
||||
|
||||
handleWindowResize(true);
|
||||
document.defaultView.addEventListener("resize", () => handleWindowResize(false));
|
||||
|
||||
let volume: number;
|
||||
let timeUnit: TimeUnit;
|
||||
addEventListener("resize", () => handleWindowResize(false));
|
||||
|
||||
const { audioCtx, audioGain, audioWorklet } = await audioPromise;
|
||||
|
||||
loadSettings();
|
||||
|
||||
let urlData: Song | undefined;
|
||||
if (window.location.hash && globalThis.useUrlData !== false) {
|
||||
urlData = await fromUrlData(window.location.hash);
|
||||
{
|
||||
let urlData: Song | undefined;
|
||||
if (location.hash && globalThis.useUrlData !== false) {
|
||||
urlData = await fromUrlData(location.hash);
|
||||
}
|
||||
|
||||
setSong(urlData, false);
|
||||
updateCounterValue();
|
||||
}
|
||||
|
||||
let isPlaying = false;
|
||||
let playSpeed = 1;
|
||||
let byteSample = 0;
|
||||
|
||||
let songData: {
|
||||
sampleRate: number;
|
||||
mode: SongMode;
|
||||
} = {
|
||||
sampleRate: 8000, // float32
|
||||
mode: "Bytebeat",
|
||||
};
|
||||
|
||||
setSong(urlData, false);
|
||||
updateCounterValue();
|
||||
|
||||
// XXX: global variables
|
||||
let nextErrType = null;
|
||||
let nextErr = null;
|
||||
let nextErrPriority = undefined;
|
||||
|
@ -54,8 +55,8 @@ let errorPriority = -Infinity;
|
|||
|
||||
async function initAudioContext() {
|
||||
let audioContextSampleRate = Number(searchParams.get("baseSampleRate"));
|
||||
// also true for NaN
|
||||
if (!(audioContextSampleRate > 0)) {
|
||||
// also true for NaN
|
||||
audioContextSampleRate = 48000; // TODO this should be set to an lcm > 44100
|
||||
}
|
||||
|
||||
|
@ -99,7 +100,7 @@ export function setSong(songData?: StrongPartial<Song>, play = true) {
|
|||
refresh();
|
||||
if (play) {
|
||||
resetTime();
|
||||
togglePlay(true);
|
||||
setPlaySpeed(1);
|
||||
}
|
||||
}
|
||||
/// call refresh after calling this
|
||||
|
@ -154,7 +155,7 @@ function handleMessage(e: MessageEvent<any>) {
|
|||
typeof data.errorMessage.err === "string" &&
|
||||
typeof (data.errorMessage.priority ?? 0) === "number"
|
||||
) {
|
||||
if (isPlaying) {
|
||||
if (playSpeed) {
|
||||
nextErrType = data.errorMessage.type;
|
||||
nextErr = data.errorMessage.err;
|
||||
nextErrPriority = data.errorMessage.priority;
|
||||
|
@ -178,7 +179,6 @@ function handleWindowResize(force?: boolean) {
|
|||
}
|
||||
export function autoSizeCanvas(force: boolean = false) {
|
||||
if (!elements.canvas.dataset["forcedWidth"]) {
|
||||
const innerWidth = window.innerWidth;
|
||||
// 768 is halfway between 512 and 1024, 3 added for outline
|
||||
if (innerWidth >= 768 + 3) {
|
||||
let width = 1024;
|
||||
|
@ -215,21 +215,10 @@ function changeScale(amount: number) {
|
|||
}
|
||||
|
||||
osc.showTimeCursor(timeCursorShouldBeVisible());
|
||||
osc.moveTimeCursor(byteSample, playSpeed > 0);
|
||||
osc.moveTimeCursor(byteSample, playSpeed >= 0);
|
||||
saveSettings();
|
||||
}
|
||||
}
|
||||
function animationFrame() {
|
||||
osc.draw(byteSample, playSpeed > 0);
|
||||
osc.moveTimeCursor(byteSample, playSpeed > 0);
|
||||
if (nextErr) {
|
||||
showErrorMessage(nextErrType, nextErr, nextErrPriority);
|
||||
}
|
||||
|
||||
if (isPlaying) {
|
||||
window.requestAnimationFrame(() => animationFrame());
|
||||
}
|
||||
}
|
||||
|
||||
function showErrorMessage(errType, err, priority = 0) {
|
||||
if (elements.error && (errorPriority < 2 || priority > 0)) {
|
||||
|
@ -259,10 +248,11 @@ function hideErrorMessage() {
|
|||
|
||||
function resetTime() {
|
||||
setByteSample(0, true, true);
|
||||
if (!isPlaying) {
|
||||
if (!playSpeed) {
|
||||
elements.canvasTogglePlay.classList.add("canvas-toggleplay-show");
|
||||
}
|
||||
}
|
||||
/// TODO document
|
||||
function setByteSample(value: number, send = true, clear = false) {
|
||||
if (audioWorklet && isFinite(value)) {
|
||||
byteSample = value;
|
||||
|
@ -270,28 +260,47 @@ function setByteSample(value: number, send = true, clear = false) {
|
|||
if (send) {
|
||||
audioWorklet.port.postMessage({ setByteSample: [value, clear] });
|
||||
}
|
||||
osc.moveTimeCursor(byteSample, playSpeed > 0);
|
||||
osc.moveTimeCursor(byteSample, playSpeed >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
function setPlaySpeed(value: number) {
|
||||
if (audioWorklet && value !== playSpeed) {
|
||||
elements.canvasTogglePlay.classList.toggle("canvas-toggleplay-pause", !!value);
|
||||
if (value) {
|
||||
elements.canvasTogglePlay.classList.remove("canvas-toggleplay-show");
|
||||
audioCtx.resume();
|
||||
startAnimation();
|
||||
} else {
|
||||
//audioCtx.suspend();
|
||||
stopAnimation();
|
||||
}
|
||||
playSpeed = value;
|
||||
audioWorklet.port.postMessage({ playSpeed, updateSampleRatio: true });
|
||||
}
|
||||
}
|
||||
function togglePlay(play: boolean = !isPlaying) {
|
||||
if (audioWorklet && play !== isPlaying) {
|
||||
elements.canvasTogglePlay.classList.toggle("canvas-toggleplay-pause", play);
|
||||
if (play) {
|
||||
elements.canvasTogglePlay.classList.remove("canvas-toggleplay-show");
|
||||
if (audioCtx?.resume) {
|
||||
audioCtx.resume();
|
||||
}
|
||||
window.requestAnimationFrame(() => animationFrame());
|
||||
function togglePlay() {
|
||||
setPlaySpeed(+!playSpeed);
|
||||
}
|
||||
|
||||
{
|
||||
let request = undefined as unknown as number;
|
||||
|
||||
function animationFrame() {
|
||||
osc.draw(byteSample, playSpeed >= 0);
|
||||
osc.moveTimeCursor(byteSample, playSpeed >= 0);
|
||||
if (nextErr) {
|
||||
showErrorMessage(nextErrType, nextErr, nextErrPriority);
|
||||
}
|
||||
isPlaying = play;
|
||||
audioWorklet.port.postMessage({ isPlaying });
|
||||
|
||||
startAnimation();
|
||||
}
|
||||
function startAnimation() {
|
||||
request = requestAnimationFrame(() => animationFrame());
|
||||
}
|
||||
function stopAnimation() {
|
||||
animationFrame();
|
||||
cancelAnimationFrame(request);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,23 +33,11 @@ html(data-embed lang="en")
|
|||
)
|
||||
span.control-group.control-time.buttons
|
||||
button#control-restart(onclick="bytebeat.resetTime()" title="Restart") ⏮︎
|
||||
button#control-fr(
|
||||
onclick="bytebeat.setPlaySpeed(-4); bytebeat.togglePlay(true)"
|
||||
title="Fast reverse"
|
||||
) ⏪︎
|
||||
button#control-reverse(
|
||||
onclick="bytebeat.setPlaySpeed(-1); bytebeat.togglePlay(true)"
|
||||
title="Reverse"
|
||||
) ⏴︎
|
||||
button#control-pause(onclick="bytebeat.togglePlay(false)" title="Pause") ⏸︎
|
||||
button#control-play(
|
||||
onclick="bytebeat.setPlaySpeed(1); bytebeat.togglePlay(true)"
|
||||
title="Play"
|
||||
) ⏵︎
|
||||
button#control-ff(
|
||||
onclick="bytebeat.setPlaySpeed(4); bytebeat.togglePlay(true)"
|
||||
title="Fast Forward"
|
||||
) ⏩︎
|
||||
button#control-fr(onclick="bytebeat.setPlaySpeed(-4)" title="Fast reverse") ⏪︎
|
||||
button#control-reverse(onclick="bytebeat.setPlaySpeed(-1)" title="Reverse") ⏴︎
|
||||
button#control-pause(onclick="bytebeat.setPlaySpeed(0)" title="Pause") ⏸︎
|
||||
button#control-play(onclick="bytebeat.setPlaySpeed(1)" title="Play") ⏵︎
|
||||
button#control-ff(onclick="bytebeat.setPlaySpeed(4)" title="Fast Forward") ⏩︎
|
||||
span.control-group.control-playback
|
||||
input#control-volume(
|
||||
oninput="bytebeat.setVolume()"
|
||||
|
@ -97,7 +85,7 @@ html(data-embed lang="en")
|
|||
label.control-playback(for="control-sample-rate-divisor") /
|
||||
input#control-sample-rate-divisor.control-playback.control-round-left.control-round-right(
|
||||
onchange="bytebeat.setSampleRateDivisor(+this.value); bytebeat.refresh()"
|
||||
style="width: 3em"
|
||||
style="width:3em"
|
||||
value="1"
|
||||
step="1"
|
||||
min="1"
|
||||
|
@ -114,7 +102,7 @@ html(data-embed lang="en")
|
|||
option(value="48000")
|
||||
#error
|
||||
#canvas-container(
|
||||
onclick="if (this.dataset.disabled !== 'true') {bytebeat.setPlaySpeed(1); bytebeat.togglePlay()}"
|
||||
onclick="if (this.dataset.disabled !== 'true') {bytebeat.togglePlay()}"
|
||||
)
|
||||
canvas#canvas-main(height="256" width="1024" aria-label="Oscillioscope")
|
||||
#canvas-timecursor
|
||||
|
|
|
@ -29,23 +29,11 @@ html(lang="en")
|
|||
)
|
||||
span.control-group.control-time.buttons
|
||||
button#control-restart(onclick="bytebeat.resetTime()" title="Restart") ⏮︎
|
||||
button#control-fr(
|
||||
onclick="bytebeat.setPlaySpeed(-4); bytebeat.togglePlay(true)"
|
||||
title="Fast reverse"
|
||||
) ⏪︎
|
||||
button#control-reverse(
|
||||
onclick="bytebeat.setPlaySpeed(-1); bytebeat.togglePlay(true)"
|
||||
title="Reverse"
|
||||
) ⏴︎
|
||||
button#control-pause(onclick="bytebeat.togglePlay(false)" title="Pause") ⏸︎
|
||||
button#control-play(
|
||||
onclick="bytebeat.setPlaySpeed(1); bytebeat.togglePlay(true)"
|
||||
title="Play"
|
||||
) ⏵︎
|
||||
button#control-ff(
|
||||
onclick="bytebeat.setPlaySpeed(4); bytebeat.togglePlay(true)"
|
||||
title="Fast Forward"
|
||||
) ⏩︎
|
||||
button#control-fr(onclick="bytebeat.setPlaySpeed(-4)" title="Fast reverse") ⏪︎
|
||||
button#control-reverse(onclick="bytebeat.setPlaySpeed(-1)" title="Reverse") ⏴︎
|
||||
button#control-pause(onclick="bytebeat.setPlaySpeed(0)" title="Pause") ⏸︎
|
||||
button#control-play(onclick="bytebeat.setPlaySpeed(1)" title="Play") ⏵︎
|
||||
button#control-ff(onclick="bytebeat.setPlaySpeed(4)" title="Fast Forward") ⏩︎
|
||||
span.control-group.control-playback
|
||||
input#control-volume(
|
||||
oninput="bytebeat.setVolume()"
|
||||
|
@ -93,7 +81,7 @@ html(lang="en")
|
|||
label.control-playback(for="control-sample-rate-divisor") /
|
||||
input#control-sample-rate-divisor.control-playback.control-round-left.control-round-right(
|
||||
onchange="bytebeat.setSampleRateDivisor(+this.value); bytebeat.refresh()"
|
||||
style="width: 3em"
|
||||
style="width:3em"
|
||||
value="1"
|
||||
step="1"
|
||||
min="1"
|
||||
|
@ -109,7 +97,7 @@ html(lang="en")
|
|||
option(value="44100")
|
||||
option(value="48000")
|
||||
#error
|
||||
#canvas-container(onclick="bytebeat.setPlaySpeed(1); bytebeat.togglePlay()")
|
||||
#canvas-container(onclick="bytebeat.togglePlay()")
|
||||
canvas#canvas-main(height="256" width="1024" aria-label="Oscillioscope")
|
||||
#canvas-timecursor
|
||||
#canvas-toggleplay.canvas-toggleplay-show
|
||||
|
|
Loading…
Reference in a new issue