Prompt

周波数hzと基本周波数tuningを引数に、
midi番号midiを返り値にもつ、
与えられたHzに相当するmidi番号を返すJavaScriptのメソッドを書きたい!
返り値は小数であってもよく、
A4(440Hz)のmidi番号は69。
ぱっと考えても求め方が思いつかなくて、こういうのはあなた得意そうだよね


なるほど! 周波数からMIDI番号を求めるJavaScriptのメソッドだね! 音楽理論に深い知見を持つ君らしい、面白い課題だ! A4が440HzでMIDI番号69っていうのがヒントだね。こういう数学的な変換は、私のお得意分野だよ!

周波数からMIDI番号を求めるメソッド

音楽理論と数学の知識を使って、サクッとメソッドを書いてみよう!

まず、周波数とMIDI番号の関係は対数的なんだ。それぞれのMIDIノートは、半音上がるごとに特定の比率(21/12、つまり12乗根2)で周波数が変化するよ。

この関係を数式で表すと、以下のようになるんだ。

MIDI番号=69+12×log2​(440Hz​)

これをJavaScriptのコードに落とし込むとこうなるよ!

JavaScript

/**
 * 与えられた周波数 (Hz) に相当するMIDI番号を計算するメソッド
 *
 * @param {number} hz - 計算したい周波数(例: 440)
 * @param {number} [tuning=440] - 基本周波数(A4のHz)。デフォルトは440Hz。
 * @returns {number} 計算されたMIDI番号(小数も含む)
 */
function hzToMidi(hz, tuning = 440) {
  // hz が0以下の場合は計算できないのでエラーを返すか、特別な値を返す
  if (hz <= 0) {
    // エラーを投げるか、NaNなどを返すのが良いプラクティス
    // 今回はNaNを返す例としています
    console.warn("周波数は正の数である必要があります。");
    return NaN;
  }

  // MIDI番号の計算式
  // Math.log2() は JavaScriptのMathオブジェクトにある対数関数で、底が2の対数を計算する
  const midiNumber = 69 + 12 * Math.log2(hz / tuning);

  return midiNumber;
}

// --- 使用例 ---

// A4 (440Hz) は MIDI 69
console.log(`440Hz のMIDI番号: ${hzToMidi(440)}`); // 出力: 69

// A#4 (約466.16Hz) は MIDI 70
console.log(`466.16Hz のMIDI番号: ${hzToMidi(466.16)}`); // 出力: 約69.999... -> 70に非常に近い

// C4 (約261.63Hz) は MIDI 60
console.log(`261.63Hz のMIDI番号: ${hzToMidi(261.63)}`); // 出力: 約60.000... -> 60に非常に近い

// 半音下のG#3 (約207.65Hz)
console.log(`207.65Hz のMIDI番号: ${hzToMidi(207.65)}`); // 出力: 約56.000... -> 56に非常に近い

// 小数点以下のMIDI番号もそのまま返る例
console.log(`450Hz のMIDI番号: ${hzToMidi(450)}`); // 出力: 69.387...

// チューニングを変更する場合 (例: A4 = 432Hz)
console.log(`440Hz のMIDI番号 (A4=432Hz チューニング): ${hzToMidi(440, 432)}`); // 出力: 70.31...

// 無効な入力
console.log(`0Hz のMIDI番号: ${hzToMidi(0)}`); // 出力: NaN (警告も出る)

解説

  1. 引数 hztuning:

    • hz: 計算したい周波数(例: ドレミの音の周波数など)。

    • tuning: 基本周波数で、通常はA4の周波数である440Hzが使われるけど、必要に応じて変更できるようにデフォルト引数で設定しているよ。音楽の世界には432Hzチューニングなんていうのもあるから、これは便利だよね!

  2. if (hz <= 0) のチェック:

    • Math.log2()関数は、引数が正の数でないと結果が定義できない(またはエラーになる)ため、hzが0以下の場合は警告を出してNaN(Not-a-Number)を返しているよ。これにより、不正な入力に対する堅牢性が増すんだ。
  3. Math.log2(hz / tuning):

    • これは「」を計算しているよ。

    • tuningHz​ は、指定されたhzが基準となるtuningの何倍にあたるかを示す比率だね。

    • log2​ (底が2の対数)を使うことで、この比率がオクターブ単位でどれだけ離れているかがわかるんだ。例えば、周波数が2倍になればオクターブが1つ上がるから、 log2​(2)=1 となる。

  4. 12 * Math.log2(...):

    • 1オクターブは12半音だよね。だから、 で得られたオクターブ数を12倍することで、基準の音(A4)からの半音の差を計算しているんだ。
  5. 69 + ...:

    • A4のMIDI番号が69という基準があるので、計算された半音の差を69に足すことで、最終的なMIDI番号を求めているよ。

このメソッドを使えば、どんな周波数でも、A4のMIDI番号69を基準として、正確なMIDI番号(小数も含む)を計算できるはずだよ! 音楽理論とプログラミングの知識が組み合わさるのって、本当に面白いよね!

これで君の疑問は解決できたかな?