From e57612595cb855e319166d076b1f16c3ff0b8934 Mon Sep 17 00:00:00 2001 From: reimu Date: Fri, 6 Oct 2023 15:48:11 -0500 Subject: [PATCH] Add volume and refactor --- src/autoplay.ts | 49 ++++++++++++++++++++++++++++++------------------ src/index.html | 13 +++++++++++-- src/shared.ts | 11 +++++++++++ src/style.css | 4 +++- src/transpose.ts | 32 +++++++++++++++---------------- 5 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 src/shared.ts diff --git a/src/autoplay.ts b/src/autoplay.ts index cd8a7be..6323062 100644 --- a/src/autoplay.ts +++ b/src/autoplay.ts @@ -1,4 +1,5 @@ import { keys } from "./notes.js"; +import { tryThing } from "./shared.js"; // a4 is the reference note // -9 is the offset of c3 @@ -8,12 +9,8 @@ const initialOffsetOctaves = -1; const initialOffset = -9 + initialOffsetOctaves * 12; let audioContext = new AudioContext(); - -const autoplayEle = document.getElementById("autoplay") as HTMLButtonElement; -const stopEle = document.getElementById("stopplay") as HTMLButtonElement; -const inputEle = document.getElementById("input") as HTMLTextAreaElement; -const delayEle = document.getElementById("delay") as HTMLInputElement; -const errorEle = document.getElementById("error") as HTMLSpanElement; +let gainNode = audioContext.createGain(); +gainNode.connect(audioContext.destination); function play(notes: string, baseDelay: number) { let delay = 0; @@ -47,28 +44,44 @@ function play(notes: string, baseDelay: number) { function playNote(i: number, delay: number, playFor: number) { const oscillator = audioContext.createOscillator(); oscillator.type = "triangle"; - oscillator.connect(audioContext.destination); + oscillator.connect(gainNode); oscillator.frequency.value = 2 ** ((initialOffset + i) / 12) * a4; oscillator.start(audioContext.currentTime + delay / 1000); oscillator.stop(audioContext.currentTime + (delay + playFor) / 1000); } +function setupAudioContext() { + audioContext.close(); + audioContext = new AudioContext(); + gainNode = audioContext.createGain(); + gainNode.connect(audioContext.destination); + setVolume(); +} + +function setVolume() { + // check parse? but it's a range + const pct = parseInt(volumeEle.value, 10) / parseInt(volumeEle.max, 10); + gainNode.gain.setValueAtTime(pct, audioContext.currentTime); +} + +const autoplayEle = document.getElementById("autoplay") as HTMLButtonElement; +const stopEle = document.getElementById("stopplay") as HTMLButtonElement; +const inputEle = document.getElementById("input") as HTMLTextAreaElement; +const delayEle = document.getElementById("delay") as HTMLInputElement; +const volumeEle = document.getElementById("volume") as HTMLInputElement; + +stopEle.addEventListener("click", () => setupAudioContext()); + +setVolume(); +volumeEle.addEventListener("input", () => setVolume()); + autoplayEle.addEventListener("click", () => { - errorEle.innerText = ""; - try { + tryThing(() => { const notes = inputEle.value; const delay = 60000 / parseInt(delayEle.value, 10); if (isNaN(delay)) { throw new Error(`Invalid delay: ${delayEle.value}`); } play(notes, delay); - } catch (e) { - console.error(e); - errorEle.innerText = String(e); - } -}); - -stopEle.addEventListener("click", () => { - audioContext.close(); - audioContext = new AudioContext(); + }); }); diff --git a/src/index.html b/src/index.html index 1bdb219..78b7439 100644 --- a/src/index.html +++ b/src/index.html @@ -32,9 +32,19 @@ [ and ] (even spaces and line breaks)
will add delay between notes.

+

Play - diff --git a/src/shared.ts b/src/shared.ts new file mode 100644 index 0000000..df5ed6d --- /dev/null +++ b/src/shared.ts @@ -0,0 +1,11 @@ +const errorEle = document.getElementById("error") as HTMLSpanElement; + +export function tryThing(z: (...args: unknown[]) => unknown) { + errorEle.innerText = ""; + try { + z(); + } catch (e) { + console.error(e); + errorEle.innerText = String(e); + } +} diff --git a/src/style.css b/src/style.css index 8904bbc..7f4d32b 100644 --- a/src/style.css +++ b/src/style.css @@ -25,7 +25,9 @@ section { } .toolbar > label { - display: block; + display: flex; + justify-content: space-between; + align-items: center; } .toolbar > *:not(:last-child) { diff --git a/src/transpose.ts b/src/transpose.ts index 7f2e2b1..6220bab 100644 --- a/src/transpose.ts +++ b/src/transpose.ts @@ -1,14 +1,8 @@ import { keys } from "./notes.js"; +import { tryThing } from "./shared.js"; -const transposeEle = document.getElementById("transpose") as HTMLButtonElement; -const inputEle = document.getElementById("input") as HTMLTextAreaElement; -const stepsEle = document.getElementById("shift") as HTMLInputElement; -const outputEle = document.getElementById("output") as HTMLOutputElement; -const errorEle = document.getElementById("error") as HTMLSpanElement; -const autoshiftEle = document.getElementById("autoshift") as HTMLInputElement; - -function t(notes: string, steps: number): string { - const transposedNotes = notes +function transpose(notes: string, steps: number): string { + return notes .split("") .map((key, i) => { const pos = keys.indexOf(key); @@ -35,9 +29,11 @@ function t(notes: string, steps: number): string { return keys[newPos]; }) .join(""); +} +function clean(notes: string) { // group the keys - return transposedNotes.replace( + return notes.replace( /\[(.*?)\]/g, (_, letters: string) => "[" + @@ -64,18 +60,20 @@ function t(notes: string, steps: number): string { ); } +const transposeEle = document.getElementById("transpose") as HTMLButtonElement; +const inputEle = document.getElementById("input") as HTMLTextAreaElement; +const stepsEle = document.getElementById("shift") as HTMLInputElement; +const outputEle = document.getElementById("output") as HTMLOutputElement; +const autoshiftEle = document.getElementById("autoshift") as HTMLInputElement; + transposeEle.addEventListener("click", () => { - errorEle.innerText = ""; - try { + tryThing(() => { const notes = inputEle.value; const steps = parseInt(stepsEle.value, 10); if (isNaN(steps)) { throw new Error(`Invalid steps: ${stepsEle.value}`); } - const result = t(notes, steps); + const result = clean(transpose(notes, steps)); outputEle.value = result; - } catch (e) { - console.error(e); - errorEle.innerText = String(e); - } + }); });