diff --git a/index.html b/index.html index c57a983..a5c6e51 100644 --- a/index.html +++ b/index.html @@ -15,60 +15,61 @@
-
-
-

-

+
+

-

+ +
+
+ + +
+
+
+ Tuning -
-
-
-
-
- Tuning - - - - - - - -
-
- + + + + + + +
+
+ + +
diff --git a/index.js b/index.js index 6b908b7..c31034a 100644 --- a/index.js +++ b/index.js @@ -98,7 +98,19 @@ class Voice { } } +function radioValue(name) { + try { + return document.getElementsByName(name).values().filter(e => e.checked).next().value.value; + } catch (e) { + // TypeError + } + return undefined; +} + function playNote(frequency, duration=86400) { + if (frequency < kA0Frequency) { + return undefined; + } const voice = new Voice(frequency); voice.play(duration); return voice; @@ -116,10 +128,16 @@ function noteFrequency(key) { } function setFrequency(key, frequency) { - frequencies[keyIndex(key)] = frequency; + const index = keyIndex(key); + for (let f = frequency, i = index; i >= 0; f *= 0.5, i -= 12) { + frequencies[i] = f; + } + for (let f = frequency * 2, i = index + 12; i < 88; f *= 2, i += 12) { + frequencies[i] = f; + } } -function tuneEqual() { +function tuneAllEqual() { const startFrequency = kA0Frequency; let frequency = startFrequency; for (let i = 0; i < kMaxKeys; i++) { @@ -132,7 +150,8 @@ function updateOptions(key) { const noteSpan = document.getElementById("current-note"); const frequencySpan = document.getElementById("current-frequency"); noteSpan.innerHTML = key; - frequencySpan.value = noteFrequency(key).toFixed(2); + const freq = noteFrequency(key); + frequencySpan.value = freq.toFixed(2); const index = keyIndex(key); const hold = document.getElementById("hold"); if (holds[index] !== undefined) { @@ -140,6 +159,8 @@ function updateOptions(key) { } else { hold.checked = false; } + document.getElementById("adjust").value = 1.0; + document.getElementById("adjust-value").innerHTML = freq.toFixed(2) + ' Hz'; } function onKeyClick(event) { @@ -148,6 +169,10 @@ function onKeyClick(event) { if (holds[keyIndex(key)] === undefined) { playNote(noteFrequency(key), 0.5); } + const currentKey = document.getElementById("current-note").innerHTML; + if (currentKey !== '-') { + document.getElementById('tuning-from').value = currentKey; + } updateOptions(key); keySpan.classList.add('playing'); @@ -174,6 +199,7 @@ function makeKey(note) { function setupKeyboard(firstKey=kFirstKey, numKeys=kNumKeys) { const keyboard = document.getElementById('keyboard'); + const tuningFrom = document.getElementById('tuning-from'); keyboard.innerHTML = ''; let curIndex = kNotes.indexOf(firstKey.charAt(0)); @@ -183,6 +209,9 @@ function setupKeyboard(firstKey=kFirstKey, numKeys=kNumKeys) { const note = kNotes[curIndex] + octave; const key = makeKey(note) keyboard.appendChild(key); + const opt = document.createElement('option'); + opt.innerHTML = note; + tuningFrom.appendChild(opt); curIndex += 1; if (curIndex >= kNotes.length) { @@ -214,14 +243,33 @@ function refreshHolds() { } } +function tuneFraction(key, fromKey, fraction) { + const fromIndex = keyIndex(fromKey); + const freq = frequencies[fromIndex] * fraction; + setFrequency(key, freq); +} + +function tuneEqual(key, fromKey) { + const index = keyIndex(key); + const fromIndex = keyIndex(fromKey); + const diff = index - fromIndex; + const freq = frequencies[fromIndex] * Math.pow(kSemiToneMultiplier, diff); + setFrequency(key, freq); +} + function setupListeners() { const freq = document.getElementById("current-frequency"); - freq.onchange = e => { + const adjust = document.getElementById("adjust"); + freq.oninput = e => { const key = document.getElementById("current-note").innerHTML; - setFrequency(key, parseFloat(freq.value)); + const adj = parseFloat(adjust.value); + const fre = parseFloat(freq.value); + const newFreq = adj * fre; + setFrequency(key, newFreq); refreshHolds(); + document.getElementById("adjust-value").innerHTML = newFreq.toFixed(2) + ' Hz'; }; - freq.oninput = freq.onchange; + adjust.oninput = freq.oninput; const hold = document.getElementById("hold"); hold.onchange = e => { @@ -232,16 +280,50 @@ function setupListeners() { removeHold(key); } } - const range = document.getElementById("range"); - range.oninput = e => { - const val = parseFloat(range.value); + const volume = document.getElementById("volume"); + volume.oninput = e => { + const val = parseFloat(volume.value); globalVolume.gain.value = val; refreshHolds(); } + document.getElementById("reset").onclick = () => { + tuneAllEqual(); + refreshHolds(); + const currentKey = document.getElementById("current-note").innerHTML; + if (currentKey !== '-') { + updateOptions(currentKey); + } + } + document.getElementById("zero").onclick = () => { + frequencies.fill(0.0); + refreshHolds(); + const currentKey = document.getElementById("current-note").innerHTML; + if (currentKey !== '-') { + updateOptions(currentKey); + } + } + document.getElementById('tune').onclick = () => { + const key = document.getElementById("current-note").innerHTML; + const fromkey = document.getElementById('tuning-from').value; + const method = radioValue('tuning'); + if (method === 'equal') { + tuneEqual(key, fromkey); + } else if (method === '5th') { + tuneFraction(key, fromkey, 3/2); + } else if (method === '4th') { + tuneFraction(key, fromkey, 4/3); + } else if (method === 'maj3rd') { + tuneFraction(key, fromkey, 5/4); + } else if (method === 'min3rd') { + tuneFraction(key, fromkey, 6/5); + } + refreshHolds(); + updateOptions(key); + } } window.addEventListener('load', e => { setupKeyboard('C2', 49); - tuneEqual(); + tuneAllEqual(); setupListeners(); });