From 1623cffd675d842bfc49d5336bd625bf368f464f Mon Sep 17 00:00:00 2001 From: ergz Date: Fri, 6 Oct 2023 00:01:58 -0700 Subject: [PATCH] start to organize the code better --- basic.js | 73 +++++++++++++++++++++++++++--------- src/basic.ts | 6 ++- synth.html | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ synth.js | 60 ++++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 synth.html create mode 100644 synth.js diff --git a/basic.js b/basic.js index eecbda4..a2f5bec 100644 --- a/basic.js +++ b/basic.js @@ -3,6 +3,8 @@ let gain = null; let selectedWave = "square"; let baseFreq = 440; let currentFreq = 261.63; +let notes = ["C", "D", "E", "F", "G", "A", "B"]; +let arpInterval = null; function octavehz(hz, octave) { const val = parseFloat(octave); @@ -14,7 +16,7 @@ function noteToHz(note) { case "C": return 261.63; case "D": - return 269.66; + return 293.66; case "E": return 329.63; case "F": @@ -25,6 +27,8 @@ function noteToHz(note) { return 440; case "B": return 493.88; + default: + return 0; } } @@ -35,7 +39,6 @@ window.onload = function () { let selectedNoteButton = document.querySelector( "input[name='notechoice']:checked" ); - console.log("note choice changed to: " + selectedNoteButton.value); let selectedOctaveButton = document.querySelector( "input[name='octavechoice']:checked" ); @@ -83,8 +86,20 @@ window.onload = function () { rb.addEventListener("change", updateOscFrequency); }); + // handle keyboard window.addEventListener("keydown", (event) => { + if (event.repeat) return; switch (event.key) { + // play/stop + case "s": + { + if (osc) { + osc.stop(); + osc = null; + } + } + break; + // "piano roll" case "1": selectNote("C"); break; @@ -124,6 +139,7 @@ window.onload = function () { } } + // gain slider const gainSlider = document.getElementById("gain"); const gainDisplay = document.getElementById("gainDisplay"); gainSlider.addEventListener("input", () => { @@ -133,6 +149,7 @@ window.onload = function () { gainDisplay.textContent = level; }); + // start button document.getElementById("start").addEventListener("click", () => { if (!osc) { osc = audioContext.createOscillator(); @@ -143,7 +160,6 @@ window.onload = function () { gain = audioContext.createGain(); } - console.log(selectedWave); osc.type = selectedWave; osc.frequency.setValueAtTime(currentFreq, audioContext.currentTime); gain.gain.setTargetAtTime( @@ -157,22 +173,45 @@ window.onload = function () { audioContext.resume(); }); + // stop button document.getElementById("stop").addEventListener("click", () => { - osc.stop(); - osc = null; - }); - - document.getElementById("arp").addEventListener("click", () => { - const notes = ["C", "D", "E", "F", "G", "A", "B"]; - for (let i = 0; i < notes.length; i++) { - for (let j = 0; j < 1000; j++) { - let x = 100; + if (osc) { + osc.stop(); + osc = null; + if (arpInterval) { + clearInterval(arpInterval); + arpInterval = null; } - console.log(notes[i]); - osc.frequency.setValueAtTime( - noteToHz(notes[i]), - audioContext.currentTime - ); + } else { + console.log("no osc to stop"); } }); + + // arp - work in progress + function startArp() { + if (!arpInterval) { + baseNote = "C"; + let currentOctave = -2; + if (osc) { + arpInterval = setInterval(() => { + if (currentOctave > 2) { + currentOctave = -2; + } + + let noteFreq = noteToHz(baseNote); + let freqOctaveChoice = octavehz(noteFreq, currentOctave); + osc.frequency.setValueAtTime( + freqOctaveChoice, + audioContext.currentTime + ); + + currentOctave++; + }, 100); + } else { + console.log("no osc to run arp on"); + } + } + } + + document.getElementById("arp").addEventListener("click", startArp); }; diff --git a/src/basic.ts b/src/basic.ts index 85341ad..a10594a 100644 --- a/src/basic.ts +++ b/src/basic.ts @@ -9,7 +9,7 @@ function octavehz(hz: number, octave: string): number { return hz * 2 ** val; } -function noteToHz(note: string): number | undefined { +function noteToHz(note: string): number { switch (note) { case "C": return 261.63; @@ -25,11 +25,13 @@ function noteToHz(note: string): number | undefined { return 440; case "B": return 493.88; + default: + return 0; } } window.onload = function () { - const audioContext = new AudioContext(); + const audioContext: AudioContext = new AudioContext(); function updateOscFrequency() { let selectedNoteButton = document.querySelector( diff --git a/synth.html b/synth.html new file mode 100644 index 0000000..c6224bf --- /dev/null +++ b/synth.html @@ -0,0 +1,102 @@ + + + + + + + + simple-synth + + + + +
+
+ Voice 1 + + + + + + + + +
+ +
+ +
+ Voice 2 + + + + + + + + +
+ + + +
+
+ Keyboard + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+ Octave Selection + + + + + + + + + + + + + + + +
+
+ + +
+ + + +
+ + + .5 +
+ + \ No newline at end of file diff --git a/synth.js b/synth.js new file mode 100644 index 0000000..6c4dcbf --- /dev/null +++ b/synth.js @@ -0,0 +1,60 @@ +class Synth { + constructor() { + this.audioContext = new AudioContext(); + this.oscillators = []; + this.gain = this.audioContext.createGain(); + this.gain.connect(this.audioContext.destination); + } + + createOscillator(type = "sine", freq = 440) { + const osc = this.audioContext.createOscillator(); + osc.type = type; + osc.frequency.setValueAtTime(freq, this.audioContext.currentTime); + this.gain.gain.setTargetAtTime(0.5, this.audioContext.currentTime, 0); + osc.connect(this.gain); + this.gain.connect(this.audioContext.destination); + this.oscillators.push(osc); + return osc; + } + + startOsc(osc) { + osc.start(); + } + + stopOsc(osc) { + osc.stop(); + const index = this.oscillators.indexOf(osc); + if (index !== -1) { + this.oscillators.splice(index, 1); + } + } + + stopAll() { + for (let osc of this.oscillators) { + this.stopOsc(osc); + } + } +} + +function hzoctave(freq, octave) { + return freq * 2 ** octave; +} + +window.onload = function () { + let synth = new Synth(); + let osc1 = synth.createOscillator("square", hzoctave(440, -1)); + let osc2 = synth.createOscillator("square", hzoctave(440, -1) - 2); + // start button + + document.getElementById("activateVoice1").addEventListener("click", () => { + console.log("voice 1 start clicked"); + synth.audioContext.resume(); + synth.startOsc(osc1); + }); + + document.getElementById("activateVoice2").addEventListener("click", () => { + console.log("voice 2 start clicked"); + synth.audioContext.resume(); + synth.startOsc(osc2); + }); +};