
2026/09/03 5:19
プログラマーのための音楽理論
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
デジタルサウンドは、高いサンプリングレート(例:1 秒間に 44,000 回)で物理的な空気圧を数値データに変換し、スピーカーの位置を表します。単純な正弦波が存在する一方で、実在の楽器は調和系列によって複雑な音色を生み出します。ピッチの関係は対数的であり、周波数を 2 倍にするとオクターブとなり、不協和音は調和基本波の間にわずかにずれた周波数によるビートが生じることで生じます。ピタゴラスのコーマのような数学的矛盾を解決するために、システムはオクターブを 12 個の一致するステップに分割して全てのキーで演奏可能にする等律調律を採用します。標準化されたフレームワークがこれらの複雑さを管理します:MIDI は注記 69 を基準周波数として固定し、任意のピッチを計算するための汎用周波数公式を定義し、楽譜は音程を全音階軸上でシリアライズし、臨時記号と期間を表す 2 の累乗を用います。さらに、音楽構造はメジャー・スケールや第 3 を積み重ねて作られた和音の特定の間隔パターンによって定義され、ローマ数字分析法を用いることで、固定されたピッチではなく調内での級に対する和音を記述することで転調が容易になります。
本文
物理法則と算術による音楽の原理:ゼロからコードで理解する
楽器演奏は困難な挑戦ですが、「正しい音」とはなぜ「正しく」なのかという根本的な理由を理解することはできません。従来の説明では慣例や暗記が強調されがちですが、音楽には物理法則と数学的根拠に基づいた明確なシステムが存在します。
この文章では、ゼロから基本原理をコード(JavaScript)で実装し、十二平均律、音階、コード進行、記譜法までを数理的に導出する方法を示します。楽器も「信仰」も不要です。すべては数値の時間変化と物理現象に過ぎません。
1. 音とは何であるか
音の本質
- 音は空気の圧力の揺らぎであり、スピーカーコーンの往復運動によって発生します。
- コンピュータ上のオーディオ処理とは、「コーンがどの位置にあるか」を表す数値のリストを生成することです。
- サウンドカードは毎秒 44,000 回(サンプリングレート) この数値を更新します。
- 音声ファイルは記録されたデータ、シンセサイザーはリアルタイムで生成したデータです。
コードによる音の生成
最も単純な「サイン波」を用いて、毎秒 440 回の振動(周波数)を発生させます。
const ctx = new AudioContext(); const out = ctx.destination; const osc = ctx.createOscillator(); osc.frequency.value = 440; // 周波数:Hz (ヘルツ) osc.connect(out); osc.start(); osc.stop(ctx.currentTime + 1);
重要なポイント
- 440Hz は恣意的な基準(ラ)であり、システム全体のピッチフォークです。これを
に変えれば異なる高さの音になりますが、オシレーター自体は「音符」という概念を持っていません。単に「一秒間に何回振動するか」だけが事実です。300 - 周波数は音高(ピッチ)を表します。数値が高いほど音が高音になります。
2. 音楽的な音を創出する(エンベロープ)
クリック音の問題
オシレーターを単純に停止すると、波形が極端な位置で切り取られ、物理的な圧力変化として「パチッ」というクリック音が鳴ります。
解決策:エンベロープ(音量包絡線)
音を滑らかにするために、時間とともに変化する音量を追加します。これを音楽では「エンベロープ」と呼びます。
- アタック (Attack): 0 秒で無音 → 0.01 秒で最大音量へ急上昇。
- デケイ (Decay): その後ゆっくりとほぼゼロまで減衰。
// ヘルパー関数:周波数、開始時間、長さ、波形種別を指定して音を作成 function note(freq, start = 0, length = 0.5, type = "sine") { const t = ctx.currentTime + start; const osc = ctx.createOscillator(); const env = ctx.createGain(); // オシレーター設定 osc.type = type; osc.frequency.value = freq; // エンベロープ設定 (ADSR) env.gain.setValueAtTime(0, t); // アタック前:無音 env.gain.linearRampToValueAtTime(0.3, t + 0.01); // アタック:0.01 秒で最大へ env.gain.exponentialRampToValueAtTime(0.001, t + length); // デケイ:滑らかにフェードアウト osc.connect(env).connect(out); osc.start(t); osc.stop(t + length); }
- 音色の決定要因: ピッチ(周波数)だけでなく、主にこのエンベロープが音符の性格を決定づけます。アタック時間を長くすればドラムのような打撃音から、弓引きのような擦れ音へ変化します。
調和系列(ハーモニック)
サイン波は単一周波数ですが、実際の楽器は基本音と倍音が同時に振動しています。これらを「調和系列」と呼びます。
- バイオリン:基本音 (220Hz) とその整数倍 (440, 660, 880...) が複雑に重なっています。
- 音色の秘密: ハーモニックの**相対的な強さ(レシピ)**の違いが、楽器同士の区別になります。
:単一周波数(検査音源)。sine
/triangle
/square
:ブラウザ標準の波形種類。sawtooth
// 異なる波形を鳴らしてみる ["sine", "triangle", "square", "sawtooth"].forEach((type, i) => { note(220, i * 0.7, 0.6, type); });
3. 周波数とピッチの法則
オクターブ(倍増)の不思議
周波数を 2 倍にすると、音楽的には「同じ音符(高いオクターブ)」として認識されます。
- 音高は乗法的である: 「足す」のではなく「掛ける」ことで八度を上がります。
- $110 \to 220$ と $880 \to 1760$ の感覚的距離は同じです。
- 対数的空間: 音楽の音高空間は線形ではなく、周波数の比(レシオ)で表されます。
二つの黄金則
- 音高は比:八度アップ = ×2。
- 一オクターブ:倍増して元の音に戻れるため、すべてのピッチ問題は「ある周波数とその 2 倍の間の空間」をどう分割するかという問題に還元されます。
4. 良い響きを生む比率(純正律)
人間は周波数のすべてを均等には区別せず、単純な比を持つ間隔が安定して聞こえます(協和音)。
- 良い響きの例:
- 2/1: オクターブ
- 3/2: 純正五度(最も重要な比率)
- 4/3: 純正四度
- 5/4: 長三度
- 悪い響きの例:
- 16/15: セミトーン(不安定)
- √2 (無理数): 車のアラームのような音
物理的な理由:ビート現象
二つの音が一致しない場合、位相がずれたことで音量の脈動(ビート)として耳に聞こえます。
- 協和音: ハーモニック(倍音)が完全に重なり合い、周期的なパターンをすぐに作ります。
- 不協和音: ハーモニックが一致せず、複雑な干渉を生じます(例:√2 の比率)。
// 五度 (3/2) を重ねてみる const pureFifth = (3 / 2); // 純正五度 console.log("純正五度の比率:", pureFifth);
5. ピタゴラスマと十二平均律
バグ:ピタゴラスマ
「純粋な五度(3/2)」を 12 回積み重ねると、オクターブの正確な倍率($2^7$)にはなりませんでした。
- ピタゴラスマ: $1.0136$ 倍の隙間が発生します(約 23 セント)。これを補正しないと音楽は回りません。
- 理由: $3^{12} \neq 2^7$。素数である 3 と 2 は同じべき乗にはなりません。
解決策:平均律
現代の音楽が採用しているのは、妥協です。
- オクターブ(構造的に完璧)を 12 等分します。
- その残りの間隔はすべて近似値とします。
- この「半音」は
の2
乗根 (1/12
) です。Math.pow(2, 1/12)
// 十二平均律の半音率 const semitone = Math.pow(2, 1 / 12); console.log("1 セミトーン:", semitone.toFixed(4)); // オクターブを 12 ステップで完璧に回る確認 let freq = 220; for(let i=0; i<12; i++) { freq *= semitone; } console.log("12 ステップ後の周波数:", (freq/220).toFixed(4)); // 約 2.0000
MIDI ノンバーリング:整数化された音符
平均律を導入した結果、すべてのピッチは整数化されました。
- 基準: MIDI 番号
が69
(ラ) です。440Hz - 変換式: $f(n) = 440 \times 2^{(n-69)/12}$
const midiToFreq = (n) => 440 * Math.pow(2, (n - 69) / 12); // 例:MIDI 番号から周波数を計算 console.log("MIDI 60 (中央 C):", midiToFreq(60).toFixed(2)); console.log("MIDI 69 (La):", midiToFreq(69).toFixed(2));
6. 音階は「隙間のリスト」である
なぜ 12 つの音符か?
- 12 を順番に弾くと音楽的ではありません。
- 西洋音楽では、12 のうち 7 つを選んで不均衡な間隔(ステップ)を作ります。
- **音階は「隙間のリスト」**です。
const buildScale = (root, steps) => { let notes = [root]; steps.forEach(step => notes.push(notes[notes.length - 1] + step)); return notes; }; // メジャー音階のパターン:[2, 2, 1, 2, 2, 2, 1] (半音数) const cMajorSteps = [2, 2, 1, 2, 2, 2, 1]; const cMajor = buildScale(60, cMajor);
メジャーとマイナーの違い
- 同じ 7 つの音符を使っても、ステップの並び替えだけで全く異なる感情になります。
- メジャー (Ionian):
→ 明るい。[2, 2, 1, 2, 2, 2, 1] - 自然マイナー (Aeolian):
→ 悲しい。[2, 1, 2, 2, 1, 2, 2]
- メジャー (Ionian):
- これらはモード(モード)と呼ばれ、同じ配列を回転させたものです。
const majorSteps = [2, 2, 1, 2, 2, 2, 1]; // ローマン数字 I-VII を生成 const scale = buildScale(60, majorSteps); // コード(トリアド)の構成:[ルート、三度 (ステップ+2)、五度 (ステップ+4)] const triad = [scale[0], scale[2], scale[4]];
7. コードとコード進行
トリアド(和音)とは
- 定義: スケール上で隔たった音符(3 つ離れている = 三度)を重ねたもの。
- 安定性: 調和系列が重なるため、非常に響きます。
- メジャー:
(半音間隔)[0, 4, 7] - マイナー:
[0, 3, 7]
- メジャー:
- 感情の違い: 中間の音を半音変えるだけで、メジャー ↔ マイナーが切り替わります。
セブンスコード
三度の上にさらに三度を積み上げるとセブンスコードになり、より複雑な響きと機能を持ちます。
- メジャーセブンス
: ジャズカフェ風。[0, 4, 7, 11] - ドミナントセブンス
: 解決を強く促す。[0, 4, 7, 10]
ローマン数字と相対アドレス
コード進行は絶対的な周波数ではなく、**スケール上の位置(ローマン数字)**で記述されます。
やI V vi IV
はどのキーでも同じ形状です。ii V I- 移調するには単に基準音をずらすだけで、進行の論理は不変です。
const chordsInKey = (root) => { const scale = buildScale(root, [2, 2, 1, 2, 2, 2, 1]).slice(0, 7); return (degree) => { // ローマン数字から実際の MIDI ノートへの変換 const notes = [0, 2, 4].map((step) => { const i = degree - 1 + step; return scale[i % 7] + Math.floor(i / 7) * 12; }); return notes; }; }; // 進行:I -> V -> vi -> IV const progression = [0, 4, 6, 3]; // C メジャーの場合 (C, G, Am, F)
8. 緊張と解放(コード進行の原理)
音楽とは、「到達した」と感じる場所と「疑問符」を置いた場所を配置する技術です。
なぜ V → I が強力か?
- 導音 (Leading Tone): V コードには I のルートより半音低い音が含まれ、「目的地への扉叩き」のように働きます。
- トリトーン: ドミナントセブンスに含まれる不協和な間隔(6 セミトーン)。I への移動時、両方の音が逆方向に解決します(緊張の緩和)。
コード進行のデザイン
- 1 (I) で終わる: 完成感。
- 5 (V) で終わる: 続きがある。
- 4 (IV) や 6 (vi) で終わる: 不安定、解決していない。
// I V vi IV は最も一般的でポップな進行の一つ const popProgression = [1, 5, 6, 4];
9. 記譜法とは何か?
前述の原理(整数の配列と周波数の計算)だけで十分ですが、伝統的な五線譜も存在します。
- シリアライズ形式: 古い技術であり、人間が即座に視覚化できるように設計されています。
- ダイアトニック軸: 白鍵(メジャーな音符)を基準線とし、黒鍵はオフセットとして表現されます。
- 臨時記号 (シャープ/フラット): エスケープ文字のような役割で、キーの定義やピッチの変更を視覚化します。
- 節拍: 2 の累乗(1, 1/2, 1/4, 1/8...)で表現され、時間を効率よく保存しています。
(ドット) は長さの増加を表し、二進法に基づく計算です。.
結論:理論はすべてコード化できる
ここまでで導出したすべての概念(キー、スケール、コード進行、テンポ)を約 40 行の JavaScript で表現可能です。「暗記すべき trivia」ではなく、エンジニアリングとしての音楽が明らかになりました。
- 十二平均律: 2 と 3 のべき乗の妥協。
- 音階: 耳に識別可能な隙間のサブセット。
- コード: 調和列が重なる安定した集合体。
- 記譜法: 情報を保存・伝達するための古くからの形式。
これらはすべて長年月を経て最適化されたエンジニアリングの決定です。なぜなら、音楽には明確な物理的・算術的な理由があるからです。
お勧め資料
さらに深く学びたい方へ:
- 本: 『Music Theory for Nerds』(著者:Eevee)。理論を論理的に理解したい必読書。
- ツール: LightNote。Web 上で動作する美しいオーディオエディタ。
- ドキュメント: MDN Web Audio API。依存関係なくブラウザで動く純粋な JavaScript 例が豊富。