ピアノの88の鍵盤の音の周波数を表にまとめ、Web Audio APIのOscillatorNodeで出力するようにしました。
音の周波数は1オクターブで2倍になり、1オクターブ内の12の音の周波数は $\sqrt[12]{2}=2^{\frac1{12}}$ 倍ずつ高くなります。そのため、A4(4オクターブ目のラ)の音を440Hzとすると、88の鍵盤のうちn番目の鍵盤の音の周波数は下記の式で表されます。
\[f(n) = 27.5 \cdot 2^{\frac{n-1}{12}}\]
上記の式の27.5HzはA0(0オクターブ目のラ)の音の周波数です。1オクターブ下がると音の周波数は半分になるため、A4(4オクターブ目のラ)の音を440Hzとすると、A3は220Hz、A2は110Hz、A1は55Hz、A0は27.5Hzになります。
下記の表はJavaScriptで生成しました。音はピアノの音ではなく、Web Audio APIのOscillatorNodeで波形パラメータに正弦波を指定して出力しています。
ボタンをクリックすると音が流れます。音量(特に周波数の高い音)に注意してください。
ボタンをもう一度クリックすると音が消えます。
上の表は下記のJavaScriptで生成しました。Bootstrap (v5.0.2)を使用しています。また、数式を表示するWordPressのプラグインSimple MathJaxを使用して$\flat$を表示しています。
<script type="text/javascript">
// -------------------------------------------------------
// Output Table
// -------------------------------------------------------
const key_names =
[
["ラ", "", "A", ""],
["ラ#", "シ$\\flat$", "A#", "B$\\flat$"],
["シ", "","B", ""],
["ド", "","C", ""],
["ド#", "レ$\\flat$","C#", "D$\\flat$"],
["レ", "","D", ""],
["レ#", "ミ$\\flat$","D#", "E$\\flat$"],
["ミ", "","E", ""],
["ファ", "","F", ""],
["ファ#", "ソ$\\flat$","F#", "G$\\flat$"],
["ソ", "", "G", ""],
["ソ#", "ラ$\\flat$", "G#", "A$\\flat$"]
];
let table_html = "<table class='table table-striped'>";
table_html += "<tr>";
table_html += "<td>鍵盤番号</td>";
table_html += "<td>周波数(Hz)</td>";
table_html += "<td colspan='4'>音階名</td>";
table_html += "<td>音を聴く</td>";
table_html += "</tr>";
for (n = 0; n < 88; n++) {
const frequency = getAudioFrequency(n);
let octave = parseInt(n/12);
if (n % 12 >= 3) {
octave++;
}
const key_japanese = key_names[n % 12][0] + octave;
let key_japanese_flat = "";
if (key_names[n % 12][1] != "") {
key_japanese_flat = key_names[n % 12][1] + octave;
}
const key_english = key_names[n % 12][2] + octave;
let key_english_flat = "";
if (key_names[n % 12][3] != "") {
key_english_flat = key_names[n % 12][3] + octave;
}
const button_id = "start-stop-oscillator-" + n;
const button = "<button class='btn btn-warning' id='" + button_id + "'/>";
const keyboard_number = n + 1;
table_html += "<tr>";
table_html += "<td>" + keyboard_number + "</td>";
table_html += "<td>" + frequency.toFixed(3) + "</td>";
table_html += "<td>" + key_japanese + "</td>";
table_html += "<td>" + key_japanese_flat + "</td>";
table_html += "<td>" + key_english + "</td>";
table_html += "<td>" + key_english_flat + "</td>";
table_html += "<td>" + button + "</td>"
table_html += "</tr>";
}
table_html += "</table>";
document.getElementById('piano-keys-table').innerHTML = table_html;
// -------------------------------------------------------
// Codes for Web Audio API
// -------------------------------------------------------
// create web audio api context
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const oscillatorMap = new Map();
const isPlayingMap = new Map();
for (n = 0; n < 88; n++) {
const frequency = getAudioFrequency(n);
const buttonText = 'audio (sine, ' + frequency.toFixed(3) + 'Hz)';
const button_id = "start-stop-oscillator-" + n;
CreateAudioOscillator(buttonText, button_id, 'sine', frequency);
}
// -------------------------------------------------------
// functions
// -------------------------------------------------------
function getAudioFrequency(n) {
return 27.5 * ( Math.pow( Math.pow(2, 1/12), n) );
}
function CreateAudioOscillator(buttonText, buttonID, oscillatorType, frequency) {
const button = document.getElementById(buttonID);
button.textContent = buttonText;
button.addEventListener("click", () => {
let oscillator = oscillatorMap.get(buttonID);
let isPlaying = isPlayingMap.get(buttonID);
if (isPlaying === undefined) {
isPlaying = false;
}
if (isPlaying === false) {
oscillator = audioCtx.createOscillator();
oscillator.type = oscillatorType;
oscillator.frequency.setValueAtTime(frequency, audioCtx.currentTime);
oscillator.connect(audioCtx.destination);
oscillator.start();
isPlaying = true;
button.textContent = buttonText + " - playing";
} else {
oscillator.stop();
isPlaying = false;
button.textContent = buttonText;
}
oscillatorMap.set(buttonID, oscillator);
isPlayingMap.set(buttonID, isPlaying);
});
}
</script>