Skip to content

Commit

Permalink
Add volume and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
reimu committed Oct 6, 2023
1 parent bd576e9 commit e576125
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 38 deletions.
49 changes: 31 additions & 18 deletions src/autoplay.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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();
});
});
13 changes: 11 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@
[ and ] (even spaces and line breaks) <br />
will add delay between notes.
</p>
<label title="Volume">
Volume
<input
id="volume"
type="range"
min="0"
max="100"
value="10"
/>
</label>
<p>
<button id="autoplay" title="Autoplay output"
>Autoplay</button
>Play</button
>
<button id="stopplay" title="Stop autoplaying">
Stop</button
Expand All @@ -58,7 +68,6 @@
</section>
</section>

<script type="module" src="notes.js"></script>
<script type="module" src="transpose.js"></script>
<script type="module" src="autoplay.js"></script>
</body>
Expand Down
11 changes: 11 additions & 0 deletions src/shared.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
4 changes: 3 additions & 1 deletion src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ section {
}

.toolbar > label {
display: block;
display: flex;
justify-content: space-between;
align-items: center;
}

.toolbar > *:not(:last-child) {
Expand Down
32 changes: 15 additions & 17 deletions src/transpose.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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) =>
"[" +
Expand All @@ -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);
}
});
});

0 comments on commit e576125

Please sign in to comment.