Implement tunings
This commit is contained in:
parent
ebba9f43a2
commit
865823b00d
95
index.html
95
index.html
@ -15,60 +15,61 @@
|
||||
<div id="keyboard" class="overflow-auto"></div>
|
||||
<label>
|
||||
Volume
|
||||
<input type="range" min="0.0" max="2.0" value="1.0" step="0.01" id="range">
|
||||
<input type="range" min="0.0" max="2.0" value="1.0" step="0.01" id="volume">
|
||||
</label>
|
||||
</article>
|
||||
<section id="options">
|
||||
<form>
|
||||
<article>
|
||||
<h3 id="current-note">-</h3>
|
||||
<article>
|
||||
<h3 id="current-note">-</h3>
|
||||
<label>
|
||||
<input type="checkbox" role="switch" id="hold">
|
||||
Hold
|
||||
</label>
|
||||
</article>
|
||||
<article>
|
||||
<label>
|
||||
<small>Frequency</small>
|
||||
<input type="number" id="current-frequency" step="0.01">
|
||||
</label>
|
||||
<label>
|
||||
<small id="adjust-value">- Hz</small>
|
||||
<input type="range" min="0.944" max="1.059" value="1.0" step="0.001" id="adjust">
|
||||
</label>
|
||||
</article>
|
||||
<article>
|
||||
<fieldset>
|
||||
<legend><small>Tuning</small></legend>
|
||||
<label>
|
||||
<input type="checkbox" role="switch" id="hold">
|
||||
Hold
|
||||
<input type="radio" name="tuning" value="equal">
|
||||
Equal temperament
|
||||
</label>
|
||||
</article>
|
||||
<article>
|
||||
<label>
|
||||
<small>Frequency</small>
|
||||
<input type="number" id="current-frequency" step="0.01">
|
||||
<input type="radio" name="tuning" value="5th">
|
||||
Perfect 5th
|
||||
</label>
|
||||
</article>
|
||||
<article>
|
||||
<fieldset>
|
||||
<legend><small>Tuning</small></legend>
|
||||
<label>
|
||||
<input type="radio" name="tuning">
|
||||
Equal temperament
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="tuning">
|
||||
Perfect 5th
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="tuning">
|
||||
Perfect 4th
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="tuning">
|
||||
Perfect major 3rd
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="tuning">
|
||||
Perfect minor 3rd
|
||||
</label>
|
||||
<div id="tune-from" style="display: none">
|
||||
<label>
|
||||
From:
|
||||
<select name="tuning-from">
|
||||
<option selected disabled value></option>
|
||||
<option>C2</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<button>Tune</button>
|
||||
</fieldset>
|
||||
</article>
|
||||
</form>
|
||||
<label>
|
||||
<input type="radio" name="tuning" value="4th">
|
||||
Perfect 4th
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="tuning" value="maj3rd">
|
||||
Perfect major 3rd
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="tuning" value="min3rd">
|
||||
Perfect minor 3rd
|
||||
</label>
|
||||
<label>
|
||||
From:
|
||||
<select id="tuning-from"></select>
|
||||
</label>
|
||||
<button id="tune">Tune</button>
|
||||
</fieldset>
|
||||
</article>
|
||||
<article>
|
||||
<button class="secondary" id="reset">Reset all</button>
|
||||
<button class="secondary" id="zero">Zero all</button>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
|
102
index.js
102
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();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user