diff --git a/index.html b/index.html index c4a92a2..3ed3c84 100644 --- a/index.html +++ b/index.html @@ -33,6 +33,12 @@ + +
+ + + 0 +
@@ -40,12 +46,6 @@ 0
- -
@@ -77,6 +77,13 @@
+ +
+ + + 0 +
+
@@ -98,7 +105,7 @@
- Voice 3 + Voice 3 @@ -108,7 +115,7 @@
-
+
Octave Selection @@ -117,6 +124,13 @@
+
+ + + 0 +
+ +
@@ -142,31 +156,61 @@
- Chords - - - - - - - - - - - - - - - - - - - - - -
-
-
+ Chords + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
diff --git a/synth.js b/synth.js index 5ef9c93..e7c8ab1 100644 --- a/synth.js +++ b/synth.js @@ -6,16 +6,31 @@ function noteToHz(note) { switch (note) { case "C": return 261.63; + case "C#": + case "Db": + return 277.18; case "D": return 293.66; + case "D#": + case "Eb": + return 311.13; case "E": return 329.63; case "F": return 349.23; + case "F#": + case "Gb": + return 369.99; case "G": - return 392; + return 392.0; + case "G#": + case "Ab": + return 415.3; case "A": - return 440; + return 440.0; + case "A#": + case "Bb": + return 466.16; case "B": return 493.88; default: @@ -25,30 +40,64 @@ function noteToHz(note) { function composeChord(chord) { switch (chord) { - case "C": { - return ["C", "G", "E"]; - } - case "D": { - return ["D", "F", "A"]; - } - case "E": { - return ["E", "G", "B"]; - } - case "F": { - return ["F", "A", "C"]; - } - case "G": { - return ["G", "B", "D"]; - } - case "A": { - return ["A", "C", "E"]; - } - case "B": { - return ["B", "D", "F"]; - } - default: { - return ["C", "D", "E"]; - } + case "C": + return ["C", "E", "G"]; // C Major + case "Cm": + return ["C", "D#", "G"]; // C Minor + case "Cdim": + return ["C", "D#", "F#"]; // C Diminished + case "Caug": + return ["C", "E", "G#"]; // C Augmented + case "D": + return ["D", "F#", "A"]; // D Major + case "Dm": + return ["D", "F", "A"]; // D Minor + case "Ddim": + return ["D", "F", "G#"]; // D Diminished + case "Daug": + return ["D", "F#", "A#"]; // D Augmented + case "E": + return ["E", "G#", "B"]; // E Major + case "Em": + return ["E", "G", "B"]; // E Minor + case "Edim": + return ["E", "G", "A#"]; // E Diminished + case "Eaug": + return ["E", "G#", "C"]; // E Augmented + case "F": + return ["F", "A", "C"]; // F Major + case "Fm": + return ["F", "G#", "C"]; // F Minor + case "Fdim": + return ["F", "G#", "A#"]; // F Diminished + case "Faug": + return ["F", "A", "C#"]; // F Augmented + case "G": + return ["G", "B", "D"]; // G Major + case "Gm": + return ["G", "A#", "D"]; // G Minor + case "Gdim": + return ["G", "A#", "C#"]; // G Diminished + case "Gaug": + return ["G", "B", "D#"]; // G Augmented + case "A": + return ["A", "C#", "E"]; // A Major + case "Am": + return ["A", "C", "E"]; // A Minor + case "Adim": + return ["A", "C", "D#"]; // A Diminished + case "Aaug": + return ["A", "C#", "F"]; // A Augmented + case "B": + return ["B", "D#", "F#"]; // B Major + case "Bm": + return ["B", "D", "F#"]; // B Minor + case "Bdim": + return ["B", "D", "F"]; // B Diminished + case "Baug": + return ["B", "D#", "G"]; // B Augmented + default: + return ["C", "E", "G"]; // Default to C Major } } @@ -66,11 +115,14 @@ class Synth { createOscillator(type = "sine", freq = 440, startOctave) { const osc = this.audioContext.createOscillator(); + const gainNode = this.audioContext.createGain(); + gainNode.connect(this.gain); osc.type = type; osc.frequency.setValueAtTime(freq, this.audioContext.currentTime); // connect it to the gain node this.gain.gain.setTargetAtTime(0.1, this.audioContext.currentTime, 0); osc.connect(this.gain); + osc.connect(gainNode); // wrap around the container and add to array const oscContainer = { @@ -78,6 +130,7 @@ class Synth { isPlaying: false, baseFreq: freq, currentOctave: startOctave, + gainNode, }; return oscContainer; } @@ -207,82 +260,34 @@ function setupOctaveControls(voiceIndex, synth) { window.onload = function () { // start button - document - .getElementById("activateVoice1") - .addEventListener("click", (event) => { - console.log("voice 1 start clicked"); + const voiceIds = ["activateVoice1", "activateVoice2", "activateVoice3"]; + voiceIds.forEach((id, index) => { + document.getElementById(`${id}`).addEventListener("click", (event) => { + console.log(`voice: ${id} start clicked`); synth.audioContext.resume(); - let osc1 = synth.oscillators[0]; - if (osc1.isPlaying) { - synth.stopOsc(osc1); + let osc = synth.oscillators[index]; + if (osc.isPlaying) { + synth.stopOsc(osc); event.target.textContent = "On"; event.target.style.backgroundColor = "#2f855a"; } else { - synth.startOsc(osc1); - event.target.textContent = "Off"; - event.target.style.backgroundColor = "red"; - } - }); - - document - .getElementById("activateVoice2") - .addEventListener("click", (event) => { - console.log("voice 2 start clicked"); - synth.audioContext.resume(); - let osc2 = synth.oscillators[1]; - if (osc2.isPlaying) { - synth.stopOsc(osc2); - event.target.textContent = "On"; - event.target.style.backgroundColor = "#2f855a"; - } else { - synth.startOsc(osc2); - event.target.textContent = "Off"; - event.target.style.backgroundColor = "red"; - } - }); - - document - .getElementById("activateVoice3") - .addEventListener("click", (event) => { - console.log("voice 3 start clicked"); - synth.audioContext.resume(); - let osc3 = synth.oscillators[2]; - if (osc3.isPlaying) { - synth.stopOsc(osc3); - event.target.textContent = "On"; - event.target.style.backgroundColor = "#2f855a"; - } else { - synth.startOsc(osc3); + synth.startOsc(osc); event.target.textContent = "Off"; event.target.style.backgroundColor = "red"; } }); + }); // handle waveform selection - document.querySelectorAll("input[name='wavechoice1']").forEach((rb) => { - rb.addEventListener("change", (event) => { - let selectedWaveform = document.querySelector( - "input[name='wavechoice1']:checked" - ).value; - synth.oscillators[0].osc.type = selectedWaveform; - }); - }); - - document.querySelectorAll("input[name='wavechoice2']").forEach((rb) => { - rb.addEventListener("change", (event) => { - let selectedWaveform = document.querySelector( - "input[name='wavechoice2']:checked" - ).value; - synth.oscillators[1].osc.type = selectedWaveform; - }); - }); - - document.querySelectorAll("input[name='wavechoice3']").forEach((rb) => { - rb.addEventListener("change", (event) => { - let selectedWaveform = document.querySelector( - "input[name='wavechoice3']:checked" - ).value; - synth.oscillators[2].osc.type = selectedWaveform; + const waveChoices = ["wavechoice1", "wavechoice2", "wavechoice3"]; + waveChoices.forEach((id, index) => { + document.querySelectorAll(`input[name='${id}']`).forEach((rb) => { + rb.addEventListener("change", (event) => { + let selectedWaveform = document.querySelector( + `input[name='${id}']:checked` + ).value; + synth.oscillators[index].osc.type = selectedWaveform; + }); }); }); @@ -300,19 +305,28 @@ window.onload = function () { let osc = synth.oscillators[index]; let detune = parseFloat(detuneSlider.value); console.log(detune); + detuneDisplay.textContent = detune; updateFrequency(event, synth, osc, index, null, null, detune); }); }); - // const filterSliderVoice1 = document.getElementById("filtervoice1"); - // const filtervoice1Display = document.getElementById("filtervoice1display"); - // filterSliderVoice1.addEventListener("input", (event) => { - // const osc = synth.oscillators[0]; - // let selectedFreq = parseFloat(filterSliderVoice1.value); - // const lpf = synth.createFilter("lowpass", 500, 1); - // osc.osc.connect(lpf); - // lpf.connect(synth.gain); - // }); + const volumeIds = ["volumevoice1", "volumevoice2", "volumevoice3"]; + + volumeIds.forEach((id, index) => { + let oscillator = synth.oscillators[index]; + + const volSlider = document.getElementById(`${id}`); + const volDisplay = document.getElementById(`${id}` + "display"); + volSlider.addEventListener("input", (event) => { + console.log(oscillator); + let vol = parseFloat(volSlider.value); + console.log(vol); + oscillator.gainNode.gain.setValueAtTime( + vol, + synth.audioContext.currentTime + ); + }); + }); // handle chord changes document.querySelectorAll("input[name='chordchoice']").forEach((rb) => {