Pythonは科学計算のライブラリが充実している。それらのライブラリを使うことで、サイン波を手軽に生成できる。そうであれば、簡単なシンセサイザーを作ることもできるだろう。今回は、PythonのライブラリPyAudioとNumPyで音楽の生成に挑戦してみよう。
PyAudioのインストール
PyAudioは、Pythonのオーディオ関連ライブラリだ。音声の録音、再生、書き出しに対応している。今回は、このPyAudioとNumPyを利用して音楽を奏でてみようと思う。
今回は、Anaconda3に、PyAudioをインストールしてみる。Anacondaのインストールは、本連載の45回目を参考にしよう。
Windowsなら、スタートメニューからAnaconda Promptを起動しよう。macOSであれば、ターミナル.appを起動しよう。そして、以下のコマンドを実行する。
conda install pyaudio
なお、NumPyは最初からAnacondaにインストールされている。
簡単なビープ音を鳴らしてみよう
それでは、最も簡単に、ビープ音を再生するプログラムを紹介しよう。以下のプログラムを「beep.py」という名前で保存しよう。
import pyaudio
import numpy as np
# 音声を出力するためのストリームを開く --- (*1)
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32,
channels=1,
rate=44100,
frames_per_buffer=1024,
output=True)
# 適当なサイン波を生成する --- (*2)
samples = np.sin(np.arange(50000) / 20)
# サイン波を再生する --- (*3)
print("play")
stream.write(samples.astype(np.float32).tostring())
stream.close()
コマンドラインより、以下のコマンドを実行すると、ポーンと言うシンプルなサイン波が再生される。
python beep.py
プログラムを見てみよう。(*1)の部分では、PyAudioの出力ストリームを開き、音声を再生できる状態にする。そして、(*2)の部分でサイン波を生成する。そして、プログラムの(*3)の部分でサイン波を再生する。
なお、ここで再生した波形はNumPyのsinメソッドで生成した波形で、最初の500個だけ取り出して見てみると、以下のような波形になっている。以下はJupyter Notebbokで波形を表示したところだ。
サイン波の生成に関してだが、NumPyを使うことで簡単に生成できる。上記のプログラム(*2)の部分を詳しくみてみよう。まず、「np.arange(10)」のように記述すると、[0,1,2,3,...9]のような連番の配列を作成する。「np.arange(10) / 10」と書くと、[0, 0.1, 0.2, 0.3 ... 0.9]のような値が生成される。そこで、「np.sin( np.arange(50000) / 20 )」のように書くと、各値にsin関数を適用したサイン波が生成される。
音程を再生してみよう
次にドレミの音程を再生してみよう。以下のプログラムを「doremi.py」という名前で保存する。
import pyaudio
import numpy as np
# サンプリングレートを定義 --- (*1)
RATE = 44100
# BPMや音長を定義 --- (*2)
BPM = 100
L1 = (60 / BPM * 4)
L2,L4,L8 = (L1/2,L1/4,L1/8)
# ドレミ...の周波数を定義 --- (*3)
C,D,E,F,G,A,B,C2 = (
261.626, 293.665, 329.628,
349.228, 391.995, 440.000,
493.883, 523.251)
# サイン波を生成 --- (*4)
def tone(freq, length, gain):
slen = int(length * RATE)
t = float(freq) * np.pi * 2 / RATE
return np.sin(np.arange(slen) * t) * gain
# 再生 --- (*5)
def play_wave(stream, samples):
stream.write(samples.astype(np.float32).tostring())
# 出力用のストリームを開く --- (*6)
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32,
channels=1,
rate=RATE,
frames_per_buffer=1024,
output=True)
# ドレミを再生 --- (*7)
print("play")
play_wave(stream, tone(C, L8, 1.0))
play_wave(stream, tone(D, L8, 1.0))
play_wave(stream, tone(E, L4, 1.0))
stream.close()
このプログラムを実行するには、以下のコマンドを打ち込もう。
python doremi.py
プログラムを確認してみよう。(*1)の部分では、サンプリングレートを指定する。サンプリングレートというのは、1秒をいくつのデータで表すかというものだ。ここでは、44100(44.1kHz)を指定しているので、1秒間に44100個のデータを再生デバイスに送信するという意味になる。ちなみに、CDのサンプリングレートは44.1kHz、FMラジオは33kHz、電話は8kHzだ。
そして、(*2)の部分では、音楽のテンポを表すBPM(Beat Per Minute)や音の長さを定義する。ここでは、BPMを100に指定した。これは、1分間あたりに刻む拍数を100回にするという意味だ。そして、ここでは、音の長さを秒単位で、全分音符(L1)、二分音符(L2)、四分音符(L4)、八分音符(L8)を計算する。
次に、(*3)の部分では、ドレミの音の周波数を定義している。ドの音(C)は261.626Hz、レの音(D)は293.665Hz、ミの音(E)は329.628Hz ... のように周波数を指定した。
(*4)の部分ではサイン波を生成する関数toneを定義した。NumPyを利用することでサイン波を手軽に作成している。(*5)の部分では、生成したサイン波をPyAudioのストリームに書き込む関数play_waveを定義した。(*6)の部分は、PyAudioでストリームを開く処理で、(*7)の部分で実際にドレミーと再生を行う。
なお、音程によるサイン波の違いを確認してみよう。以下のように、ド・レ・ラ・シの四つの音程の波形を生成した。
tone_c = tone(C, L4, 1.0) # ド
tone_d = tone(D, L4, 1.0) # レ
tone_a = tone(A, L4, 1.0) # ラ
tone_b = tone(B, L4, 1.0) # シ
上記の波形データをJupyterで先ほどと同様の方法で描画してみた。音程ごとにサイン波の周期が異なることが分かるだろう。
周波数と十二平均律の関係
ところで、プログラムの(*3)の部分で、ドレミの周波数を実数で指定した。しかし、この値は計算によって求めることができる。数学と音楽の関係は面白い。せっかくなので計算してみよう。
まず、前提条件として、身近な音楽は、十二平均律でチューニングされている。これは、1オクターブを12等分して表したものである。つまり、12半音上が1オクターブ上となる。
そして、よくギターのチューニングで使われる440Hzはラの音だ。ここから、1オクターブ上の音を求めるには周波数に2を掛ければ良い。そのため、1オクターブ上のラの周波数は880Hzとなる。ここから、考えてみると、半音上の音の周波数を計算したい場合、元の周波数に2の12乗根を掛け合わせば良い。
分かりやすく、Pythonのプログラムで確認してみよう。ラ(440Hz)の半音上、ラ♯(またはシ♭)を求めるには、以下のようなにする。実行すると、466.1637615180899が表示される。
a = 440
a_sharp = a * (2 ** (1/12))
print(a_sharp)
これを利用して、128段階の周波数を計算するプログラムを作ってみよう。128段階とはMIDI楽器の鍵盤に対応する周波数だ。
base_a = 440
names = ("C","C#","D","D#","E","F","F#","G","G#","A","A#","B")
res = []
for n in range(0, 128):
hz = 440 * 2 ** ((n-69) / 12)
name = names[n % 12]
o = int(n / 12)
res.append([name, o, hz])
import pandas as pd
df = pd.DataFrame(res, columns=["Note", "Octave", "Freq"])
df
まとめ
以上、今回は、Pythonで音楽を再生する方法を紹介した。科学計算ライブラリのNumPyと音声ライブラリのPyAudioを使うことで、気軽に音を生成して鳴らすことができた。こうした波形合成を行うには、数学の知識も必要になるが、ドレミを鳴らすだけであれば、それほど難しい計算は必要ではない。今回のプログラムを叩き台にすることで、簡単なシンセサイザーをPythonで自作することもできるだろう。Pythonでオリジナル楽器を作成するのも楽しいだろう。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。