12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

この記事誰得? 私しか得しないニッチな技術で記事投稿!

正弦波で「ビッグブリッジの死闘」を奏でてみた

Last updated at Posted at 2023-06-22

はじめに

pythonで正弦波を上手く使って音楽を奏でてみました。打ち込みの仕組みってこういうことなんだ、と感動したので、共有します。

プログラム

make_music.py
from scipy.io.wavfile import write
import numpy as np
import os


class GenFreq:

    def __init__(self, Fsample=44100.0, duration=1.0, sound = "normal"):
        # Fs はサンプリング周波数(Hz),dur は持続時間(秒)
        self.Fcs = {
            "C": 262.0,#ド
            "C#":277.0,
            "D": 294.0,#レ
            "D#":311.0,
            "E": 330.0,#ミ
            "F": 349.0,#ファ
            "F#":370.0,
            "G": 392.0,#ソ
            "G#":415.0,
            "A": 440.0,#ラ
            "A#":466.0,
            "B": 494.0,#シ
            "C5":523.0,#ド
            "C5#":554.0,
            "D5":587.0,#レ
            "D5#":622.0,
        }
        self.Fs = Fsample                                   # サンプリング周波数
        delta = 1.0 / Fsample                               # 間隔
        self.dur = duration                                 # 時間
        self.N = int(duration * Fsample)                    # 音の長さ
        self.t = np.arange(self.N) * delta                  # 時間刻み
        self.x = np.zeros(self.N)                           # 音信号
        self.sound = sound                                  # 音の種類

    def getSound(self):
        # 波形データ配列を返す
        return self.x

    def getLen(self):
        # データ配列長を返す
        return self.N

    def setTone(self, code="C", Amp=1.0):
        # 音を設定する
        if(code != ""):
            if(self.sound == "normal"):
              # 正弦波
                f = self.Fcs[code]
                self.x = Amp * np.sin(2.0 * np.pi * f * self.t)
            elif(self.sound == "triangle"):
              # 三角波
                n = 100
                f = self.Fcs[code]
                for i in range(1, n+1):
                    harmonic = (2*i - 1)**2
                    self.x += Amp  /harmonic * np.sin(2.0 * np.pi * f * harmonic * self.t)
            elif(self.sound == "square"):
              # 矩形波
                n = 100
                f = self.Fcs[code]
                for i in range(1, n+1):
                    harmonic = 2*i - 1
                    self.x += Amp * (1 / harmonic) * np.sin(2.0 * np.pi * f * harmonic * self.t)
            elif(self.sound =="sawtooth"):
              # 鋸波
                n = 100
                f = self.Fcs[code]
                for i in range(1, n+1):
                    harmonic = i
                    self.x += Amp * (1 / harmonic) * np.sin(2.0 * np.pi * f * harmonic * self.t)
                    
    def normalize(self):
        # 音の振幅範囲を [-1, 1] に設定
        xmax = np.abs(self.x).max()
        self.x /= xmax

    def __add__(self, other):
        # 音をつなげる
        if isinstance(other, self.__class__) and self.Fs == other.Fs:
            tmp = GenFreq(self.Fs, self.dur + other.dur)
            tmp.x = np.hstack((self.x, other.x))
            return tmp
        else:  # サンプリング周波数違うときは足せないのでエラーを発出
            raise NotImplementedError()
        
    def __mul__(self, code="C", Amp=1.0):
        # 音を重ねて和音にする
        f = self.Fcs[code]
        self.x += Amp * np.sin(2.0 * np.pi * f * self.t)



if __name__ == "__main__":
    ########################################################
    # 音のパターンを定義するリスト(「ビッグブリッヂの死闘」)
    pattern = [
        ("F", 0.2),
        ("F", 0.2),
        ("", 0.2),
        ("F", 0.2),
        ("G#", 0.2),
        ("F", 0.2),
        ("",0.2),
        ("F", 0.2),
        ("G#",0.2),
        ("F",0.2),
        ("A#",0.2),
        ("B", 0.2),
        ("A#",0.2),
        ("G#", 0.2),
        ("F", 0.2),
        ("D#", 0.2),
        ("F", 0.2),
        ("F", 0.2),
        ("", 0.2),
        ("F", 0.2),
        ("G#", 0.2),
        ("G",0.2),
        ("F", 0.5),
        ("", 1.5),
        
        ("F",0.2),
        ("G",0.2),
        ("G#",0.2),
        ("F",0.2),
        ("A#",0.2),
        ("G#",0.2),
        ("G",0.2),
        ("F",0.2),
        ("C5",0.2),
        ("F",0.2),
        ("C5#",0.2),
        ("F",0.2),
        ("C5",0.2),
        ("G#",0.4),
        ("",0.01),
        ("G#",0.2),
        ("A#",0.2),
        ("D#",0.2),
        ("C5",0.2),
        ("D#",0.2),
        ("A#",0.2),
        ("G",0.4),
        ("",0.01),
        ("G",0.2),
        ("G#",0.2),
        ("G",0.2),
        ("F",0.2),
        ("G",0.2),
        ("G#",0.2),
        ("A#",0.2),
        ("G#",0.2),
        ("G",0.2),
        
        ("F",0.2),
        ("G",0.2),
        ("G#",0.2),
        ("F",0.2),
        ("A#",0.2),
        ("G#",0.2),
        ("G",0.2),
        ("F",0.2),
        ("C5",0.2),
        ("F",0.2),
        ("C5#",0.2),
        ("F",0.2),
        ("C5",0.2),
        ("G#",0.4),
        ("",0.01),
        ("G#",0.2),
        ("A#",0.2),
        ("D#",0.2),
        ("C5",0.2),
        ("D#",0.2),
        ("A#",0.2),
        ("G",0.4),
        ("",0.01),
        ("G",0.2),
        ("G#",0.2),
        ("A#",0.2),
        ("C5",0.2),
        ("C5#",0.2),
        ("D5#",0.2),
        ("C5#",0.2),
        ("C5",0.2),
        ("C5#",0.2),
        ("D5#",1.5)
    ]
    ########################################################
    Fs = 44100.0  # サンプリング周波数
    v = GenFreq(Fs, duration=0)  # 空の音インスタンスを作る

    for tone, duration in pattern:
        t = GenFreq(Fs, duration=duration, sound= "square")  # 矩形波で音を生成
        t.setTone(code=tone)
        v += t  # 音を加算していく
    current_path = os.path.dirname(__file__)
    fname2 = os.path.join(current_path, "bigbridge.wav")
    v.normalize()
    write(fname2, int(Fs), v.getSound().astype("float32"))

これを実行すると、音声ファイルが生成されるので聞いてみてください。今回は矩形波で作りましたが、引数を"normal"や"triangle"、"sawtooth"にした場合、音の感じが変わります。

おわりに

応用するとMIDIアプリを作れそうなので、暇なときにやってみようかと思います。

12
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?