157 lines
6.5 KiB
JavaScript
157 lines
6.5 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
Tone.start();
|
|
|
|
const synth = new Tone.Synth().toDestination();
|
|
|
|
const a440Input = document.getElementById('a440');
|
|
const transposeInput = document.getElementById('transpose');
|
|
const instrumentSelect = document.getElementById('instrument');
|
|
const instlabel = document.getElementById('instrument-label');
|
|
const tuningSelect = document.getElementById('tuning');
|
|
const tuningModeSelect = document.getElementById('tuning-mode');
|
|
const stringsDiv = document.getElementById('strings');
|
|
const playAllButton = document.getElementById('play-all');
|
|
const outputDiv = document.getElementById('output');
|
|
|
|
const instrumentTunings = {
|
|
"ukulele": {
|
|
"standard": [67 + 12, 60 + 12, 64 + 12, 69 + 12],
|
|
"low-g": [55 + 12, 60 + 12, 64 + 12, 69 + 12],
|
|
"harmonic-minor": [67 + 12, 58 + 12, 62 + 12, 67 + 12],
|
|
"suspended-fourth": [67 + 12, 60 + 12, 53 + 12, 60 + 12],
|
|
"lydian": [67 + 12, 60 + 12, 64 + 12, 66 + 12],
|
|
"diminished": [67 + 12, 59 + 12, 62 + 12, 65 + 12],
|
|
"augmented": [67 + 12, 61 + 12, 64 + 12, 68 + 12],
|
|
"open-fifths": [67 + 12, 62 + 12, 69 + 12, 62 + 12],
|
|
"double-unison": [67 + 12, 67 + 12, 60 + 12, 60 + 12],
|
|
"ionian": [67 + 12, 60 + 12, 64 + 12, 69 + 12],
|
|
"dorian": [67 + 12, 58 + 12, 62 + 12, 69 + 12],
|
|
"mixo-dorian": [65 + 12, 58 + 12, 67 + 12, 69 + 12],
|
|
"phrygian": [67 + 12, 56 + 12, 62 + 12, 69 + 12],
|
|
"mixolydian": [67 + 12, 60 + 12, 62 + 12, 69 + 12],
|
|
"aeolian": [67 + 12, 58 + 12, 62 + 12, 67 + 12],
|
|
"locrian": [67 + 12, 56 + 12, 60 + 12, 67 + 12]
|
|
},
|
|
"guitar": {
|
|
"standard": [40, 45, 50, 55, 59, 64],
|
|
"drop-d": [38, 45, 50, 55, 59, 64],
|
|
"dadgad": [38, 45, 50, 55, 57, 64],
|
|
"open-g": [38, 43, 47, 50, 55, 59],
|
|
"open-d": [38, 43, 50, 54, 57, 64],
|
|
"open-c": [36, 40, 43, 48, 52, 57],
|
|
"half-step-down": [39, 43, 48, 52, 55, 60],
|
|
"full-step-down": [38, 43, 48, 53, 57, 62],
|
|
"double-drop-d": [38, 43, 48, 50, 55, 59],
|
|
"new-standard": [36, 40, 45, 50, 54, 59],
|
|
"nashville-high-strung": [40, 45, 50, 55, 59, 64],
|
|
"orkney": [36, 40, 43, 36, 40, 43],
|
|
"modal-tuning-1": [40, 45, 39, 50, 45, 64],
|
|
"modal-tuning-2": [40, 45, 37, 50, 45, 64],
|
|
"db-custom": [49, 54, 59, 56, 71, 63]
|
|
}
|
|
};
|
|
let currentTuning = [];
|
|
let currentA440 = 440;
|
|
let currentTranspose = 0;
|
|
let tuningMode = "equal";
|
|
|
|
const harmonicFrequencyRatios = {
|
|
"C": 1.0, "Db": 17/16, "D": 9/8, "Eb": 19/16, "E": 5/4,
|
|
"F": 21/16, "Gb": 11/8, "G": 3/2, "Ab": 13/8, "A": 5/3,
|
|
"Bb": 7/4, "B": 15/8, "C_octave": 2.0
|
|
};
|
|
|
|
function updateInstrument() {
|
|
const selectedInstrument = instrumentSelect.value;
|
|
tuningSelect.innerHTML = "";
|
|
let inst = instrumentSelect.value;
|
|
instlabel.innerText = inst.charAt(0).toUpperCase() + inst.slice(1) + " Tuner";
|
|
Object.keys(instrumentTunings[selectedInstrument]).forEach(tuning => {
|
|
let option = document.createElement("option");
|
|
option.value = tuning;
|
|
option.textContent = tuning.replace(/-/g, " ").toUpperCase();
|
|
tuningSelect.appendChild(option);
|
|
});
|
|
updateTuning();
|
|
}
|
|
|
|
function updateTuning() {
|
|
const selectedInstrument = instrumentSelect.value;
|
|
const selectedTuning = tuningSelect.value;
|
|
currentTuning = instrumentTunings[selectedInstrument][selectedTuning];
|
|
updateStringButtons();
|
|
}
|
|
|
|
function updateStringButtons() {
|
|
stringsDiv.innerHTML = "";
|
|
currentTuning.forEach((midiNote, index) => {
|
|
let button = document.createElement("button");
|
|
button.classList.add("string-button");
|
|
button.textContent = `String ${index + 1}`;
|
|
button.addEventListener("click", () => playNote(midiNote));
|
|
stringsDiv.appendChild(button);
|
|
});
|
|
}
|
|
|
|
function calculateFrequency(midiNote, tuningMode) {
|
|
const referenceNote = 60;
|
|
const referenceFreq = currentA440 * Math.pow(2, (referenceNote - 69) / 12);
|
|
|
|
if (tuningMode === "harmonic") {
|
|
const noteNames = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
|
|
const octave = Math.floor(midiNote / 12) - 5;
|
|
const note = noteNames[midiNote % 12];
|
|
return referenceFreq * harmonicFrequencyRatios[note] * Math.pow(2, octave);
|
|
} else {
|
|
return 440 * Math.pow(2, (midiNote - 69) / 12);
|
|
}
|
|
}
|
|
|
|
function playNote(midiNote) {
|
|
const a440Value = parseFloat(a440Input.value);
|
|
const transposeValue = parseInt(transposeInput.value);
|
|
|
|
if (!isNaN(a440Value)) currentA440 = a440Value;
|
|
if (!isNaN(transposeValue)) currentTranspose = transposeValue;
|
|
|
|
const adjustedMidiNote = midiNote + currentTranspose;
|
|
const frequency = calculateFrequency(adjustedMidiNote, tuningMode);
|
|
|
|
const referenceFrequencyRatio = currentA440 / 440;
|
|
const adjustedFrequency = frequency * referenceFrequencyRatio;
|
|
|
|
synth.set({ oscillator: { type: 'sine' } });
|
|
synth.triggerAttackRelease(adjustedFrequency, "1.75s");
|
|
|
|
outputDiv.textContent = `Playing: ${Tone.Frequency(adjustedFrequency).toNote()} (Freq: ${adjustedFrequency.toFixed(2)} Hz, A4 Ref: ${currentA440} Hz, Transpose: ${currentTranspose} semitones)`;
|
|
}
|
|
|
|
playAllButton.addEventListener('click', () => {
|
|
let delay = 0;
|
|
Tone.Transport.stop();
|
|
Tone.Transport.cancel();
|
|
currentTuning.forEach((midiNote) => {
|
|
Tone.Transport.scheduleOnce(() => {
|
|
playNote(midiNote);
|
|
}, `+${delay}`);
|
|
delay += 0.2;
|
|
});
|
|
Tone.Transport.start();
|
|
});
|
|
|
|
instrumentSelect.addEventListener('change', updateInstrument);
|
|
tuningSelect.addEventListener('change', updateTuning);
|
|
tuningModeSelect.addEventListener('change', () => {
|
|
tuningMode = tuningModeSelect.value;
|
|
});
|
|
updateInstrument();
|
|
|
|
a440Input.addEventListener('change', () => {
|
|
outputDiv.textContent = `A4 Reference set to ${a440Input.value} Hz`;
|
|
});
|
|
|
|
transposeInput.addEventListener('change', () => {
|
|
outputDiv.textContent = `Transpose set to ${transposeInput.value} semitones`;
|
|
});
|
|
});
|