ピアノの88の鍵盤の音の周波数とWeb Audio API

ピアノの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>

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA