Implement tunings
This commit is contained in:
parent
ebba9f43a2
commit
865823b00d
31
index.html
31
index.html
@ -15,11 +15,10 @@
|
|||||||
<div id="keyboard" class="overflow-auto"></div>
|
<div id="keyboard" class="overflow-auto"></div>
|
||||||
<label>
|
<label>
|
||||||
Volume
|
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>
|
</label>
|
||||||
</article>
|
</article>
|
||||||
<section id="options">
|
<section id="options">
|
||||||
<form>
|
|
||||||
<article>
|
<article>
|
||||||
<h3 id="current-note">-</h3>
|
<h3 id="current-note">-</h3>
|
||||||
<label>
|
<label>
|
||||||
@ -32,43 +31,45 @@
|
|||||||
<small>Frequency</small>
|
<small>Frequency</small>
|
||||||
<input type="number" id="current-frequency" step="0.01">
|
<input type="number" id="current-frequency" step="0.01">
|
||||||
</label>
|
</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>
|
||||||
<article>
|
<article>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend><small>Tuning</small></legend>
|
<legend><small>Tuning</small></legend>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="tuning">
|
<input type="radio" name="tuning" value="equal">
|
||||||
Equal temperament
|
Equal temperament
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="tuning">
|
<input type="radio" name="tuning" value="5th">
|
||||||
Perfect 5th
|
Perfect 5th
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="tuning">
|
<input type="radio" name="tuning" value="4th">
|
||||||
Perfect 4th
|
Perfect 4th
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="tuning">
|
<input type="radio" name="tuning" value="maj3rd">
|
||||||
Perfect major 3rd
|
Perfect major 3rd
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="tuning">
|
<input type="radio" name="tuning" value="min3rd">
|
||||||
Perfect minor 3rd
|
Perfect minor 3rd
|
||||||
</label>
|
</label>
|
||||||
<div id="tune-from" style="display: none">
|
|
||||||
<label>
|
<label>
|
||||||
From:
|
From:
|
||||||
<select name="tuning-from">
|
<select id="tuning-from"></select>
|
||||||
<option selected disabled value></option>
|
|
||||||
<option>C2</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
<button id="tune">Tune</button>
|
||||||
<button>Tune</button>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</article>
|
</article>
|
||||||
</form>
|
<article>
|
||||||
|
<button class="secondary" id="reset">Reset all</button>
|
||||||
|
<button class="secondary" id="zero">Zero all</button>
|
||||||
|
</article>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</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) {
|
function playNote(frequency, duration=86400) {
|
||||||
|
if (frequency < kA0Frequency) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
const voice = new Voice(frequency);
|
const voice = new Voice(frequency);
|
||||||
voice.play(duration);
|
voice.play(duration);
|
||||||
return voice;
|
return voice;
|
||||||
@ -116,10 +128,16 @@ function noteFrequency(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setFrequency(key, frequency) {
|
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;
|
const startFrequency = kA0Frequency;
|
||||||
let frequency = startFrequency;
|
let frequency = startFrequency;
|
||||||
for (let i = 0; i < kMaxKeys; i++) {
|
for (let i = 0; i < kMaxKeys; i++) {
|
||||||
@ -132,7 +150,8 @@ function updateOptions(key) {
|
|||||||
const noteSpan = document.getElementById("current-note");
|
const noteSpan = document.getElementById("current-note");
|
||||||
const frequencySpan = document.getElementById("current-frequency");
|
const frequencySpan = document.getElementById("current-frequency");
|
||||||
noteSpan.innerHTML = key;
|
noteSpan.innerHTML = key;
|
||||||
frequencySpan.value = noteFrequency(key).toFixed(2);
|
const freq = noteFrequency(key);
|
||||||
|
frequencySpan.value = freq.toFixed(2);
|
||||||
const index = keyIndex(key);
|
const index = keyIndex(key);
|
||||||
const hold = document.getElementById("hold");
|
const hold = document.getElementById("hold");
|
||||||
if (holds[index] !== undefined) {
|
if (holds[index] !== undefined) {
|
||||||
@ -140,6 +159,8 @@ function updateOptions(key) {
|
|||||||
} else {
|
} else {
|
||||||
hold.checked = false;
|
hold.checked = false;
|
||||||
}
|
}
|
||||||
|
document.getElementById("adjust").value = 1.0;
|
||||||
|
document.getElementById("adjust-value").innerHTML = freq.toFixed(2) + ' Hz';
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeyClick(event) {
|
function onKeyClick(event) {
|
||||||
@ -148,6 +169,10 @@ function onKeyClick(event) {
|
|||||||
if (holds[keyIndex(key)] === undefined) {
|
if (holds[keyIndex(key)] === undefined) {
|
||||||
playNote(noteFrequency(key), 0.5);
|
playNote(noteFrequency(key), 0.5);
|
||||||
}
|
}
|
||||||
|
const currentKey = document.getElementById("current-note").innerHTML;
|
||||||
|
if (currentKey !== '-') {
|
||||||
|
document.getElementById('tuning-from').value = currentKey;
|
||||||
|
}
|
||||||
updateOptions(key);
|
updateOptions(key);
|
||||||
|
|
||||||
keySpan.classList.add('playing');
|
keySpan.classList.add('playing');
|
||||||
@ -174,6 +199,7 @@ function makeKey(note) {
|
|||||||
|
|
||||||
function setupKeyboard(firstKey=kFirstKey, numKeys=kNumKeys) {
|
function setupKeyboard(firstKey=kFirstKey, numKeys=kNumKeys) {
|
||||||
const keyboard = document.getElementById('keyboard');
|
const keyboard = document.getElementById('keyboard');
|
||||||
|
const tuningFrom = document.getElementById('tuning-from');
|
||||||
keyboard.innerHTML = '';
|
keyboard.innerHTML = '';
|
||||||
|
|
||||||
let curIndex = kNotes.indexOf(firstKey.charAt(0));
|
let curIndex = kNotes.indexOf(firstKey.charAt(0));
|
||||||
@ -183,6 +209,9 @@ function setupKeyboard(firstKey=kFirstKey, numKeys=kNumKeys) {
|
|||||||
const note = kNotes[curIndex] + octave;
|
const note = kNotes[curIndex] + octave;
|
||||||
const key = makeKey(note)
|
const key = makeKey(note)
|
||||||
keyboard.appendChild(key);
|
keyboard.appendChild(key);
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.innerHTML = note;
|
||||||
|
tuningFrom.appendChild(opt);
|
||||||
|
|
||||||
curIndex += 1;
|
curIndex += 1;
|
||||||
if (curIndex >= kNotes.length) {
|
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() {
|
function setupListeners() {
|
||||||
const freq = document.getElementById("current-frequency");
|
const freq = document.getElementById("current-frequency");
|
||||||
freq.onchange = e => {
|
const adjust = document.getElementById("adjust");
|
||||||
|
freq.oninput = e => {
|
||||||
const key = document.getElementById("current-note").innerHTML;
|
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();
|
refreshHolds();
|
||||||
|
document.getElementById("adjust-value").innerHTML = newFreq.toFixed(2) + ' Hz';
|
||||||
};
|
};
|
||||||
freq.oninput = freq.onchange;
|
adjust.oninput = freq.oninput;
|
||||||
|
|
||||||
const hold = document.getElementById("hold");
|
const hold = document.getElementById("hold");
|
||||||
hold.onchange = e => {
|
hold.onchange = e => {
|
||||||
@ -232,16 +280,50 @@ function setupListeners() {
|
|||||||
removeHold(key);
|
removeHold(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const range = document.getElementById("range");
|
const volume = document.getElementById("volume");
|
||||||
range.oninput = e => {
|
volume.oninput = e => {
|
||||||
const val = parseFloat(range.value);
|
const val = parseFloat(volume.value);
|
||||||
globalVolume.gain.value = val;
|
globalVolume.gain.value = val;
|
||||||
refreshHolds();
|
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 => {
|
window.addEventListener('load', e => {
|
||||||
setupKeyboard('C2', 49);
|
setupKeyboard('C2', 49);
|
||||||
tuneEqual();
|
tuneAllEqual();
|
||||||
setupListeners();
|
setupListeners();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user