tkinterを使って波形ジェエネレーターを作ろう
tkinterはpythonでGUIを作れるモジュールです。ボタンやプルダウンボックスを指定してインタラクティブなアプリケーションを作成することができます。今回は、これを使って指定した三和音の波形を表示する簡易ファンクションジェネレータを作ってみました。
音階と波形
先に入力値となる音階についてまとめます。まず、波形ジェエネレーターでは三つの音を選択します。音はドイツ語名で表記してプルダウンボックスで選択する形にします。選択できる音は次に示すA(ラ)からG#/Ab(ラ♭)までの12音です。
["A","A#/Bb", "B", "C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#/Ab"]
西洋音階では一オクターブ低い音の波長は元の音の波長の半分で、残り12音で等比数列的に波長が変化します。このような音の間隔になっている音階を平均律と呼びます。したがって、$i$番目の音の周波数$f_i$と$i+1$番目の音の周波数$f_{i+1}$の関係は次のようになります。
$$f_i = \sqrt[12]{2} f_{i+1}$$
ここで、基準はA(ラ)の音で$f_A=$440 Hzになります。ここから、$i$番目の音の波形$y_i$は次のようになります。
$$y_i = \sin(2 \pi f_i t) = \sin(2 \pi \sqrt[12]{2}^n f_At)$$
$t$は時間で、$i$番目の音は基準のAから$n+1$度高い音とします。B♭の時は2度高い音なので$n=1$です。三和音の波形はこの三角関数を足し合わせれば求められます。
プログラム
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import tkinter.ttk as ttk
def quit():
root.quit()
root.destroy()
#三和音の合成波の計算とプロット
def graph(n1, n2, n3):
A = 440
r = 2 ** (1/12)
x = np.linspace(0, 0.15, 5000)
wave1 = np.sin(2*np.pi*A*(r**n1)*x)
wave2 = np.sin(2*np.pi*A*(r**n2)*x)
wave3 = np.sin(2*np.pi*A*(r**n3)*x)
wave_sum = wave1 + wave2 + wave3
fig = plt.figure(figsize=(8,4))
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
plt.xlabel('Time',fontsize=18)
plt.ylabel('Intensity',fontsize=18)
fig.tight_layout()
plt.plot(x, wave_sum)
return fig
#ボタンの実行内容
def btn_click():
option = ["A","A#/Bb", "B", "C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#/Ab"]
n1 = option.index(combo1.get())
n2 = option.index(combo2.get())
n3 = option.index(combo3.get())
global canvas
canvas.get_tk_widget().pack_forget()
canvas = FigureCanvasTkAgg(graph(n1, n2, n3), master=root)
canvas.get_tk_widget().pack()
plt.close()
#GUIの作成
root = tk.Tk()
root.geometry("800x400")
root.protocol('WM_DELETE_WINDOW', quit)
btn1=tk.Button(root,text="RUN",command=btn_click)
btn1.pack()
option = ["A","A#/Bb", "B", "C", "C#/Db", "D", "D#/Eb", "E", "F", "F#/Gb", "G", "G#/Ab"]
variable = tk.StringVar()
combo1 = ttk.Combobox(root,values=option,textvariable=variable)
combo1.bind("<<ComboboxSelected>>")
combo1.pack()
combo1.set(option[0])
variable = tk.StringVar()
combo2 = ttk.Combobox(root,values=option,textvariable=variable)
combo2.bind("<<ComboboxSelected>>")
combo2.pack()
combo2.set(option[0])
variable = tk.StringVar()
combo3 = ttk.Combobox(root,values=option,textvariable=variable)
combo3.bind("<<ComboboxSelected>>")
combo3.pack()
combo3.set(option[0])
fig0 = graph(0, 0, 0)
canvas = FigureCanvasTkAgg(fig0, root)
canvas.draw()
canvas.get_tk_widget().pack()
plt.close()
# root実行
root.mainloop()
実行結果は次のような感じになります。
三和音の波形
それでは波形を見ていきましょう。
メジャーコード
主音に3度上、5度上の音を重ねた和音。一番基本的なコードです。
C: C, E, G
それぞれの波が混ざり合って複雑な波形になっています。AとCでは主音が違いますが構成音の間隔は同じです。すると波形も近しいものになります。
マイナーコード
主音に短3度上、5度上の音を重ねた和音。メジャーコードに比べて暗い響きになります。
Am: A, C, E
メジャーコードと明らかに波形が違いますね。メジャーコードよりランダムな感じがします。
オーギュメント
主音に対して3度上の音と増5度上の音を重ねた和音です。
Aaug: A, C#/Db, F
ぱっと見はマイナーコードに近い感じがします。オーギュメントは和音を聞いた感じもすごい不安定な感じがしますよね。
サスフォー
主音に4度上、5度上の音を重ねた和音。
Asus: A, D, E
上の他のコードに比べて規則正しく繰り返しているような感じがします。
不協和音
A, A#/Bb, B
連続する三つの音を重ねると今までの和音とは違うのがわかります。音がぶつかり合っているのは波形ではこんな感じなんですね。
まとめ
まず、初めてtkinter使ったので勉強になりました。簡易的なアプリケーションであれば簡単に作れます。Qiitaやブログ等々でたくさんの方が解説してくださっているので、わからなくても調べれば解決できることが多いのが便利です。
波形の重ね合わせ自体も和音によって見てわかる程度に差があって面白かったです。和音の波形についてもブログ等での解説があります。より詳しい解説はそちらを参照してください1$,$2。楽器をお持ちの方は、和音を鳴らしながら波形を眺めてみるのはいかがでしょうか。
-
和音の波形のについての参考ブログ1
https://nose-akira.hatenablog.com/entry/2018/11/11/175859 ↩ -
和音の波形のについての参考ブログ2
https://life-research.net/mathphys/make_chords/ ↩ -
2023/9/23修正: 波形の三角関数の修正とそれに伴うプログラムと図の修正、平均律に関しての文言追加、参考ブログの注釈追加 ↩
-
2023/9/24修正:Asusの構成音の訂正 ↩