🎃

あらゆる理論で音楽を作成・分析できるPythonライブラリ Neuma を作成する (3) - 音の調律 -

2023/08/06に公開

今回は、Neuma の音の調律について書いていきます。
Neuma では、音の表示方法を Scale として表現していますが、同様に 音の調律もクラスとして表現 しています。

前回 ->

2023/9/28 追記
TunningSystem 周りを大幅に変更したため、記事の内容を更新しました。

音の調律

皆さんが普段使っている・聞き慣れている音は、 12平均律 という調律で調律されています。
12平均律は、 1オクターブを12等分 したものです。
1オクターブは周波数で2倍になるので、12平均律では、1オクターブを2の12乗根で割った周波数の音を1音階としています。

12平均律は非常に便利です。
なぜなら、 全ての音が等間隔 になっているからです。
全ての音が等間隔なら、カラオケでいうところのキー変更が自由にできます。

キー変更について

当然ながら、クラシックの人たちは、キー変更なんてしません。
音の絶対的な高さも重要視される要素だからです。

じゃあ、なぜ全ての音が等間隔というのが重要視されたのかと言うと、響きの問題です。
詳しくは調べてみて欲しいのですが、この調律に至るまでいくつもの調律がありました。
この流れは全て、全てのキーでコードの響きが美しい ということを目指していたのです。

平均律によって、全てのキーでコードの響きが美しいということが実現されました。
実は、平均律のコードの響きは美しくないんですけどね。

他にも、 純正律ピタゴラス律 、一部界隈で流行っている? 31平均律 などがあります。
困ったことにここまで説明した調律は、音階では「ドレミファソラシ」を使う西洋の音階で表示します。
だからこそ、 音の名前と音の高さを一致させることができない のです。

実装

調律抽象クラス

ことによって、抽象クラスを作成しています。
これを継承することで、音の調律を表現するクラスを作成することができます。

tunning_system.py
from abc import ABC, abstractmethod
from dataclasses import dataclass

from ..note.note import Note


@dataclass(frozen=True)
class TunningSystem(ABC):
    base: float

    @abstractmethod
    def get_freq(self, note: Note) -> float:
        pass

    @abstractmethod
    def fine_freq(self, freq: float, fine: float) -> float:
        pass

base は、基準となる周波数です。
get_freq は、音を表す Note クラスを受け取り、その音の周波数を返します。
fine_freq は、周波数を受け取り、その周波数を微調整した周波数を返します。
これらを実装することで、音の調律を表現するクラスを作成することができます。

n平均律クラス

Neuma では、音の調律を表現するクラスとして n平均律クラス を用意しています。
当然、12平均律も n平均律クラス の一つです。

tunning_system.py
class EqualTemperament(TunningSystem):
    """平均律クラス

    Description:
        平均律の調律クラス。
        基本倍音を等分する。

    Attributes:
        base_tone (int): 基準となる Note.tone。
        base_freq (float): 基準となる周波数。
        divisions (int): 基本倍音の分割数。
        overtone (float): オクターブを表す倍音。
        key (list[int]): 音階のリスト。
    """

    divisions: int
    overtone: float
    key: list[int]

    def __init__(
        self,
        base_tone: int = 6,
        base_freq: float = 440.0,
        divisions: int = 12,
        overtone: float = 2,
        key: Optional[list[int]] = None,
    ) -> None:
        if key is None:
            key = [i for i in range(divisions)]
        super().__init__(base_tone, base_freq, key)
        kwargs: dict[str, Any] = {
            "base_tone": base_tone,
            "divisions": divisions,
            "overtone": overtone,
        }
        for key, value in kwargs.items():
            object.__setattr__(self, key, value)

    def get_freq(self, note: Note) -> float:
        tone: int = self.get_liner_tone(note)
        freq: float = self.base_freq * self.overtone ** (tone / self.divisions)
        return freq

    def __get_diff(self, diff: Fraction) -> int:
        rtn: float = int(diff)
        return rtn

    def fine_freq(self, freq: float, fine: float) -> float:
        fine /= 100
        freq *= self.overtone ** (fine / self.divisions)
        return freq

    def get_liner_tone(self, note: Note) -> int:
        tone: int = self.key[note.tone - 1]
        tone -= self.key[self.base_tone - 1]
        tone += note.octave * self.divisions
        tone += self.__get_diff(note.diff)
        return tone


class EDO12(EqualTemperament):
    """12平均律クラス

    Description:
        12平均律の調律クラス。
    """

    def __init__(
        self,
        base_freq: float = 440,
    ) -> None:
        base_tone: int = 6
        divisions: int = 12
        overtone: float = 2
        key: list[int] = [0, 2, 4, 5, 7, 9, 11]
        super().__init__(base_tone, base_freq, divisions, overtone, key)

引数のデフォルトは、440HzのAを基準とした12平均律です。

base_tone は、基準となる音の音階です。
ここでは、Aである 6 を基準としています。

base_freq は、基準となる音の周波数です。

divisions は、1オクターブの音階の数です。
n平均律での1オクターブの音階の数は、 n なので、12平均律では12です。

key は、音階の配列です。
先頭から順に、ドレミファソラシの音階を表しています。
正確には、 Note クラスの tone に対応しています。
base_tone もこの配列に対応しているので、 base_tone6 なら、基準が A になります。

get_freqfine はこのクラスではセントを単位としています。
セントは、 1オクターブを1200分割した単位 ですがここでは「半音100セント」を基準にしています。
そのため、 division が 20 の場合、「1オクターブを2000分割した単位」になります。
セント単位での音高調整はシンセサイザーなどで、よく見ます。

どうやって使う?

調律クラスは基本的に音そのものに関わるときに用いられます。
例えば、楽器クラスといった音を出すものや、周波数を分析に用いるときなどです。
ぶっちゃけ使い道は少ないですが、音を出すことを考えると必須級なので、作りました。

平均律と純正律で音階の違いを見せる、とかやりたかったのですが優先度が低く12平均律でしか動作を確認していません。
他の平均律ならできると思いますが、面倒なので…… (実装済みです。(2023/9/28 追記))
比較を作ったら、追記します。

まとめ

今回は、内容が薄いですね。
というのも話すことがあまりないんです……
調律の歴史を辿るのも面白いのですが、 tech カテゴリで実装さえしていない調律の話を3回分やるのも……
と言うことで、実装中心ですが説明しました。

次回は、音を鳴らす関連で楽器クラスについて、の前に音クラスについて説明します。
音そのものを処理し、保存するクラスです。

Discussion