課題の講評#

%%ai vioras
ピタゴラス音律について

ピタゴラス音律は、古代ギリシャの数学者ピタゴラスにちなんで名付けられた音階の調整法です。ピタゴラス音律では、全ての音程が純粋な比率、つまり整数の比率で表されます。これは、弦の長さや振動数が一定の比率になることを意味します。

具体的には、一度(完全一度)は「1:1」、五度(完全五度)は「3:2」、八度(完全八度)は「2:1」という比率で表されます。また、半音は「$ \sqrt[12]{2} : 1 $」または「256:243」という比率で表されます。このように、ピタゴラス音律では、音の間隔は純粋な数学的比率に基づいています。

しかし、ピタゴラス音律には問題もあります。全てのキーで純粋な比率を保つことはできず、特定のキーでは音程が「ずれる」ことがあります。これは「ピタゴラスのコンマ」と呼ばれる問題です。この問題を解決するために、さまざまな音律法(例えば平均律)が開発されました。

それにもかかわらず、ピタゴラス音律は音楽理論と音響学の基礎を築きました。また、この音律法は特に古代ギリシャの音楽や中世の音楽、一部の民族音楽で使用されています。

%%ai vioras
ピタゴラス音律と、音階や和音との関係について

ピタゴラス音律は、音階や和音の形成に直接的な影響を与えます。音階は一連の音の配列であり、特定のキーに基づいています。ピタゴラス音律は、これらの音の間隔(音程)を数学的に明確に定義します。したがって、ピタゴラス音律は、音階がどのように構成されるかを決定します。

たとえば、ピタゴラス音律を使用してCメジャー音階を作成すると、次のようになります(ここでの数字は、基準となる音(ここではC)の周波数に対する比率を示しています):

  • C: 1:1

  • D: 9:8

  • E: 81:64

  • F: 4:3

  • G: 3:2

  • A: 27:16

  • B: 243:128

  • C: 2:1

和音もまた、特定の音程の組み合わせで構成されます。ピタゴラス音律はこれらの音程を定義します。たとえば、Cメジャー和音(C-E-G)は、CからEへの比率(81:64)と、CからGへの比率(3:2)を使用して定義できます。

しかし、ピタゴラス音律には「ピタゴラスのコンマ」と呼ばれる問題があり、全てのキーで完全に一貫した和音を生成することはできません。これは、12回の完全五度を重ねて完全八度を12回重ねたものと比較したときに微妙な差異が生じるためです。この問題を解決するために、西洋音楽では平均律が一般的に用いられています。

Pythagorean method#

ピタゴラスは次の式を満たす \(m, n \geq 0 \) を探して \(7\) 音階を定めた:

\[ 1 \leq \left( \frac{3}{2} \right)^m \times \left( \frac{1}{2} \right)^{n} \leq 2\]

または $\( 1 \leq \left( \frac{2}{3} \right)^m \times 2^n \leq 2\)$

例えば

\[\left( \frac{2}{3} \right)^1 \times 2^1 = \frac{4}{3}\]
\[\left(\frac{3}{2} \right)^4 \times \left( \frac{1}{2} \right)^2 = \frac{3^4}{2^6} = \frac{81}{64} = 1.265625 \approx 1.25 = \frac{5}{4}\]

C, E, Gの比を求める:

\[ 1 : \frac{81}{64} : \frac{3}{2} \approx 1 : \frac{5}{4} : \frac{3}{2} = \frac{4}{4} : \frac{5}{4} : \frac{6}{4} = 4 : 5 : 6\]

3/2系列でピタゴラス音階を生成する#

from fractions import Fraction
import itertools
import numpy as np
pyt = [(m*7 % 12, m, n, Fraction(x))
       for (m, n, x) in [(m, n, (3/2)**m * (1/2)**n) 
                         for m, n in itertools.product(range(13), repeat=2)]
       if 1.0 <= x <= 2.0]
pyt
[(0, 0, 0, Fraction(1, 1)),
 (7, 1, 0, Fraction(3, 2)),
 (2, 2, 1, Fraction(9, 8)),
 (9, 3, 1, Fraction(27, 16)),
 (4, 4, 2, Fraction(81, 64)),
 (11, 5, 2, Fraction(243, 128)),
 (6, 6, 3, Fraction(729, 512)),
 (1, 7, 4, Fraction(2187, 2048)),
 (8, 8, 4, Fraction(6561, 4096)),
 (3, 9, 5, Fraction(19683, 16384)),
 (10, 10, 5, Fraction(59049, 32768)),
 (5, 11, 6, Fraction(177147, 131072)),
 (0, 12, 7, Fraction(531441, 524288))]

Note

この系列で\(m \times 7\)\(12\)で割った余りは (半) 音階に対応する事が知られています

pyt = np.array(pyt)
pyt[pyt[:,3].argsort()]
array([[0, 0, 0, Fraction(1, 1)],
       [0, 12, 7, Fraction(531441, 524288)],
       [1, 7, 4, Fraction(2187, 2048)],
       [2, 2, 1, Fraction(9, 8)],
       [3, 9, 5, Fraction(19683, 16384)],
       [4, 4, 2, Fraction(81, 64)],
       [5, 11, 6, Fraction(177147, 131072)],
       [6, 6, 3, Fraction(729, 512)],
       [7, 1, 0, Fraction(3, 2)],
       [8, 8, 4, Fraction(6561, 4096)],
       [9, 3, 1, Fraction(27, 16)],
       [10, 10, 5, Fraction(59049, 32768)],
       [11, 5, 2, Fraction(243, 128)]], dtype=object)
import numpy as np
import IPython.display as ipd

import sys
sys.path.append('..')
import libfmp.c1

def compute_ratio(num3, num2):
    return (3 ** num3) / (2 ** num2)

num3 = 0
num2 = 0
note = 0
diff = 0
s = np.zeros((13, 6))
s[0] = [0, note, num3, num2, compute_ratio(num3, num2), diff] 

for m in range(1, 13):
    note = (note + 7) % 12
    if note == 0: note = 12
    num3 = num3 + 1
    num2 = num2 + 1 
    ratio = compute_ratio(num3, num2)
    if ratio > 2:
        num2 = num2 + 1
        ratio = compute_ratio(num3, num2)
    diff = (np.log2(ratio) - 1 / 12) * 1200
    diff = np.remainder(diff, 100)        
    s[m] = [m, note, num3, num2, compute_ratio(num3, num2), diff]   
s
array([[ 0.        ,  0.        ,  0.        ,  0.        ,  1.        ,
         0.        ],
       [ 1.        ,  7.        ,  1.        ,  1.        ,  1.5       ,
         1.95500087],
       [ 2.        ,  2.        ,  2.        ,  3.        ,  1.125     ,
         3.91000173],
       [ 3.        ,  9.        ,  3.        ,  4.        ,  1.6875    ,
         5.8650026 ],
       [ 4.        ,  4.        ,  4.        ,  6.        ,  1.265625  ,
         7.82000346],
       [ 5.        , 11.        ,  5.        ,  7.        ,  1.8984375 ,
         9.77500433],
       [ 6.        ,  6.        ,  6.        ,  9.        ,  1.42382812,
        11.73000519],
       [ 7.        ,  1.        ,  7.        , 11.        ,  1.06787109,
        13.68500606],
       [ 8.        ,  8.        ,  8.        , 12.        ,  1.60180664,
        15.64000692],
       [ 9.        ,  3.        ,  9.        , 14.        ,  1.20135498,
        17.59500779],
       [10.        , 10.        , 10.        , 15.        ,  1.80203247,
        19.55000865],
       [11.        ,  5.        , 11.        , 17.        ,  1.35152435,
        21.50500952],
       [12.        , 12.        , 12.        , 19.        ,  1.01364326,
        23.46001038]])
%%ai vioras
次のプログラムを解説してください
---
import numpy as np
import IPython.display as ipd

import sys
sys.path.append('..')
import libfmp.c1

def compute_ratio(num3, num2):
    return (3 ** num3) / (2 ** num2)

num3 = 0
num2 = 0
note = 0
diff = 0
s = np.zeros((13, 6))
s[0] = [0, note, num3, num2, compute_ratio(num3, num2), diff] 

for m in range(1, 13):
    note = (note + 7) % 12
    if note == 0: note = 12
    num3 = num3 + 1
    num2 = num2 + 1 
    ratio = compute_ratio(num3, num2)
    if ratio > 2:
        num2 = num2 + 1
        ratio = compute_ratio(num3, num2)
    diff = (np.log2(ratio) - 1 / 12) * 1200
    diff = np.remainder(diff, 100)        
    s[m] = [m, note, num3, num2, compute_ratio(num3, num2), diff]

このプログラムは、ピタゴラス音律 (Pythagorean tuning) に基づく音の周波数比を計算します。ピタゴラス音律は、音楽の音階を生成するための古代のシステムで、全てのピッチが純粋な完全五度(周波数比3:2)の積であるという原理に基づいています。このプログラムでは、そのプロセスを模擬しています。

以下、コードの各部分の詳細な説明です:

まず、必要なパッケージをインポートします:

import numpy as np
import IPython.display as ipd
import sys
sys.path.append('..')
import libfmp.c1

次に、指定された数値に対して周波数比(3のnum3乗 ÷ 2のnum2乗)を計算する関数compute_ratioを定義します:

def compute_ratio(num3, num2):
    return (3 ** num3) / (2 ** num2)

その後、初期変数を設定し、結果を保存するための空の2次元配列(13行6列)を作成します:

num3 = 0
num2 = 0
note = 0
diff = 0
s = np.zeros((13, 6))
s[0] = [0, note, num3, num2, compute_ratio(num3, num2), diff]

次に、12回のループを行い、各ステップで新しい音符を生成します:

for m in range(1, 13):
    note = (note + 7) % 12
    if note == 0: note = 12
    num3 = num3 + 1
    num2 = num2 + 1 
    ratio = compute_ratio(num3, num2)
    if ratio > 2:
        num2 = num2 + 1
        ratio = compute_ratio(num3, num2)
    diff = (np.log2(ratio) - 1 / 12) * 1200
    diff = np.remainder(diff, 100)        
    s[m] = [m, note, num3, num2, compute_ratio(num3, num2), diff]

これらのステップを詳しく見てみましょう:

  • note = (note + 7) % 12:これは新しい音符を生成します。音符は7つ進み(完全五度)、そして12で割ることで、音符が12を超えるときにそれをラッピングします(12音階システムに対応)。if note == 0: note = 12は、音符が0になるときにそれを12にリセットします。

  • num3 = num3 + 1num2 = num2 + 1:これらは、次の周波数比を計算するために、3の指数と2の指数をそれぞれインクリメントします。

  • その後、新しい周波数比を計算します:ratio = compute_ratio(num3, num2)

  • if ratio > 2: num2 = num2 + 1; ratio = compute_ratio(num3, num2):これは、新しい比率が2を超えた場合に、比率が1から2の範囲になるように2の指数を増やします。

  • diff = (np.log2(ratio) - 1 / 12) * 1200; diff = np.remainder(diff, 100):これは、新しい音高の周波数比と等 temperament scale の間の差分をセントで計算します。そしてその差分を100で割った余りを求めます。

  • 最後に、生成した情報を配列sに保存します:s[m] = [m, note, num3, num2, compute_ratio(num3, num2), diff]

そして、このプログラムの結果として、各音符(ノート)の周波数比と、それが等 temperament scale とどの程度異なるかが計算されます。

Note

ピタゴラス音階は、\(\left(\frac{3}{2}\right)^m \times \left(\frac{1}{2}\right)^n = \frac{3^m}{2^{m+n}}\)系列と \(\left(\frac{2}{3}\right)^m \times 2^n = \frac{2^{m+n}}{3^m}\)系列の合成で構成されます