2020.06.22 波形処理 第1回 音の波と三角関数
YouTubeでポイントを説明しています。画像をクリックすると再生できます。
新シリーズとして波形処理を追加しました。波形処理を通じて、音楽に関する理解を深めることが目的です。
第1回は、音の基本となる波を、三角関数を使って作り出します。
■開発環境

パソコンから、TeraTeamでラズパイにSSH接続、python によるコーディングを行い、実行します。
また、DACにI2S接続して音を鳴らします。
DACとの接続設定は、
I2S通信によるハイレゾ音源再生
を参照してください。
ラズパイのディストリビューションには、Raspbian Stretch を使用していますが、これから導入する際には、Raspbian Buster になります。
■予備知識
まずは、こちらの論文を一読されると、理解が深まると思います。
一般の方でも読みやすく、楽しめる内容になっています。
楽典の補足資料として、またシンセサイザーに興味のある方にも十分役立つ論文です。
音の波と三角関数
小林 亮 広島大学大学院理学研究科
■波を作り出す

半径1の円周上を座標(1,0)から、反時計回りに移動した場合の軌跡を、横軸に角度(ラジアン)、縦軸に振幅(高さ,Y座標)で示すと右側の波形で表現されます。
πはラジアン単位系の表記方法と考えてください。そしてPythonのライブラリで使われる三角関数でもこの単位系が使われています。
変に2×π=2×3.1415926・・・=6.28311852・・・とか考えてしまうと訳がわからなくなります。
反時計回りに90℃回転したら、1/2π、180℃でπ、1周で2π、2周で4πという尺度で考えましょう。
また、音ってなんでこんな形をしているんだろうと考えると、謎が深まるばかりで、最終的には拒絶反応を起こしてしまいます。
これは音の実態ではなく、音の特徴を視覚的に捉えるうえで、なにかと便利なので、こういう形で表現をしていると思ってください。
■波形を表示する
では、最初にPythonでこの波形を表現してみます。
数学関数ライブラリ numpy のインストール
numpy(ナムパイ)ライブラリは、多次元配列を操作するための数学関数ライブラリです。
$ sudo pip3 install numpy
Requirement already satisfied: numpy in /usr/lib/python3/dist-packages
※Python3をインストールした際に、一緒にインストールされていると思います。
グラフ描画ライブラリ matplotlib のインストール
matplotlib(マットプロットリブ)は、NumPyのためのグラフ描画ライブラリです。
$ sudo pip3 install matplotlib
Requirement already satisfied: matplotlib in /usr/lib/python3/dist-packages
※Python3をインストールした際に、一緒にインストールされていると思います。
$ vi sinecurve.py
import matplotlib.pyplot as plt
import numpy as np
if __name__ == '__main__':
# radian値を生成(0~2πラジアンまでを、2π/360刻みで配列に格納する)
radian = np.arange(0, 2*np.pi, 2*np.pi/360)
# 振幅(Y座標)を計算
amplitude = np.sin(radian)
# 結果表示
plt.plot(radian, amplitude)
plt.show()
plt.close()
$ python3 sinecurve.py
プログラムの実行は、VNCを利用するか、ラズパイで直接行って接続しているモニターに結果を表示させてください。

■スワップ領域拡張
次に、楽器を演奏する際の音合わせにも利用される440Hzのラの音を生成して、WAVファイルに保存してみましょう。
上記では、1周期分の波の振幅を360個の観測点で採取してグラフに表しましたが、実際の録音ではより細かく観測点を設けて、振幅の値を取得しています。
例えば、ラの音をサンプリング周波数44.1kHz で録音する際には、1秒間に発生する440回の波を、44100個の観測点で採取することになり、座標計算に大量のメモリを消費します。
Raspbian Stretch の初期状態でのスワップ領域として割り当てられているスワップファイルは、100Mバイトです。
$ free
total used free shared buff/cache available
Mem: 443076 96920 152964 7596 193192 275280
Swap: 102396 0 102396
これでは計算処理中に、MemoryError を起こします。
Raspbianでは、/etc/dphys-swapfileでスワップ領域の設定を行っています。
まずは、このファイルを編集して、スワップファイルのサイズを拡張します。
$ sudo vi /etc/dphys-swapfile
#CONF_SWAPSIZE=100
CONF_SWAPSIZE=2048
サービスを起動します。
$ sudo dphys-swapfile install
want /var/swap=2048MByte, checking existing: deleting wrong size file (104857600), generating swapfile ... of 2048MBytes
スワップ領域を有効にします。
$ sudo dphys-swapfile swapon
スワップファイルが2Gバイトに拡張されます。
$ free
total used free shared buff/cache available
Mem: 443076 97484 149428 7596 196164 274688
Swap: 2097148 0 2097148
■システム・バックアップ
スワップファイルは頻繁に更新されます。SDカードには書き込み回数上限があるので、突然SDカードが使えなくなる事態を想定しなければなりません。
開発環境を1から構築しなおすのは、とても面倒なので、SDカードをバックアップしておきます。

Raspbian Stretch (あるいはBuster)をインストールした際に使用したSDカードと同じものを用意すると問題が発生しません。

■ラ(440Hz)の音を録音する
ここでは、ラ(440Hz)の音を生成して、WAVファイルに保存します。
scipy(サイパイ)ライブラリのインストール
scipy.ioにあるwavfileの読み書きクラスを使って、音声ファイル(wavファイル)を保存します。
$ sudo apt-get install python3-scipy
※scipyを、pip でインストールしようとすると、うまくいかないことがあるようです。
$ vi savewave.py
import numpy as np
from scipy.io import wavfile
if __name__ == '__main__':
frequency = 440.0 # 生成周波数
playbackTime = 1.0 # 再生時間
samplingRate = 44100 # サンプリング周波数
slice = 2*np.pi*frequency / samplingRate
radian = np.arange(0, 2*np.pi*frequency*playbackTime, slice)
amplitude = np.sin(radian)
# 16bit の wav ファイルに書き出す
amplitude = (amplitude * float(2 ** 15 - 1)).astype(np.int16)
wavfile.write("La.wav", samplingRate, amplitude)
今度は、円周上1回転分ではなく、440回転分を、等間隔に44100の観測点で振幅を測定しています。
再生時間を1秒にしていますが、単純なラの音だけの録音なので、2秒にしても同じパターンが繰り返されるだけです。
振幅を16ビットの整数値に変換している部分では、振幅を最大限に引き伸ばしていますが、音を合成する際には、調整が必要になります。
writeにパラメータを引き渡すことで、自動的にwavファイルのヘッダー情報を付加した形て保存されます。
コードを実行します。
$ python3 savewave.py
■直接、音を鳴らす
必要なパッケージをインストールします。
$ sudo pip3 install pyaudio
$ sudo apt-get install portaudio19-dev
音を再生するのに利用できるデバイスについて調べます。
$ python3
>>> import pyaudio
>>> p = pyaudio.PyAudio()
ALSAのエラーメッセージは無視して続けます。
>>> for index in range(0, p.get_device_count()):
... print(p. get_device_info_by_index(index))
...
{'maxInputChannels': 0, 'index': 2, 'defaultLowInputLatency': -1.0, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultHighInputLatency': -1.0, 'defaultLowOutputLatency': 0.005804988662131519, 'name': 'snd_rpi_hifiberry_dac: - (hw:1,0)', 'defaultSampleRate': 44100.0, 'hostApi': 0, 'structVersion': 2, 'maxOutputChannels': 2}
I2S接続している PHAT DAC は、hifiberry_dac なので、インデックス番号2であることがわかります。
WAVファイルに保存していた部分を、サウンドデバイス出力用に書き換えます。
output_device_index に、上記インデックス番号の2 を指定しています。
$ vi playback.py
import pyaudio
import numpy as np
if __name__ == '__main__':
frequency = 440.0 # 生成周波数
playbackTime = 1.0 # 再生時間
samplingRate = 44100 # サンプリング周波数
slice = 2*np.pi*frequency / samplingRate
radian = np.arange(0, 2*np.pi*frequency*playbackTime, slice)
amplitude = np.sin(radian)
# 16bit の wav ファイルに書き出す
amplitude = (amplitude * float(2 ** 15 - 1)).astype(np.int16)
# ストリームを開く
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=samplingRate,
output=True,
output_device_index=2)
# 音声を再生
stream.write(amplitude)
stream.close()
p.terminate()
$ python3 playback.py
440Hzのラの音が鳴れば、成功しています。
ALSAのエラーメッセージを表示したくない場合は、エラーハンドラーを設置します。
$ vi playback.py
from ctypes import *
import pyaudio
import numpy as np
ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
def py_error_handler(filename, line, function, err, fmt):
pass # print('%s:%s %s %s' % (line, filename, function, err))
c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
asound = cdll.LoadLibrary('libasound.so')
if __name__ == '__main__':
# Set error handler
asound.snd_lib_error_set_handler(c_error_handler)
frequency = 440.0 # 生成周波数
playbackTime = 1.0 # 再生時間
samplingRate = 44100 # サンプリング周波数
slice = 2*np.pi*frequency / samplingRate
radian = np.arange(0, 2*np.pi*frequency*playbackTime, slice)
amplitude = np.sin(radian)
# 16bit の wav ファイルに書き出す
amplitude = (amplitude * float(2 ** 15 - 1)).astype(np.int16)
# ストリームを開く
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=samplingRate,
output=True,
output_device_index=2)
# 音声を再生
stream.write(amplitude)
stream.close()
p.terminate()
# Reset to default error handler
asound.snd_lib_error_set_handler(None)
■参考文献
小林 亮 広島大学大学院理学研究科 - 音の波と三角関数
SciPy.org - scipy.io.wavfile.write
PythonからPyAudioで録音/再生してみる
python - PyAudioは動作しますが、毎回エラーメッセージを吐き出します
|
Raspberry Pi(ラズベリー パイ)は、ARMプロセッサを搭載したシングルボードコンピュータ。イギリスのラズベリーパイ財団によって開発されている。
たいていのことは100日あれば、うまくいく。長田英知著
「時間がなくて、なかなか自分のやりたいことができない」
「一念発起して何かを始めても、いつも三日坊主で終わってしまう」
「色んなことを先延ばしにしたまま、時間だけが過ぎていく」
そこで本書では、そんな著者が独自に開発した、
まったく新しい目標達成メソッド「100日デザイン」について、
その知識と技術を、余すところなくご紹介します。
まんがで納得ナポレオン・ヒル 思考は現実化する
OLとして雑務をこなす日々に飽き足らず、科学者だった父が残した薬品を商品化すべく、起業を決意した内山麻由(27)。彼女はセミナーで知り合った謎の女性からサポートを得ながら、彼女と二人三脚でナポレオン・ヒルの成功哲学を実践し、さまざまな問題を乗り越えていく。
ヒル博士の<ゴールデンルール>に従い、仕事に、恋に全力疾走する彼女の、成功への物語。
今日は人生最悪で最高の日 1秒で世界を変えるたったひとつの方法 ひすいこたろう著
偉人の伝記を読むと、最悪な日は、不幸な日ではなく、新しい自分が始まる日であることがわかります。最悪な出来事は、自分の人生が、想像を超えて面白くなる兆しなのです。偉人伝を読むことで、このときの不幸があったおかげで、未来にこういう幸せがくるのかと、人生を俯瞰する視線が立ち上がるのです。
ご飯は私を裏切らない heisoku著
辛い現実から目を背けて食べるご飯は、いつも美味しく幸せを届けてくれる。
29歳、中卒、恋人いない歴イコール年齢。バイト以外の職歴もなく、短期バイトを転々とする日々。ぐるぐると思索に耽るけど、ご飯を食べると幸せになれる。奇才の新鋭・heisokuが贈るリアル労働グルメ物語!
【最新版Gemini 3に対応!】できるGemini (できるシリーズ)
Geminiを「最強の知的生産パートナー」として使いこなすための、実践的なノウハウを凝縮した一冊です。
基本的な操作方法から、具体的なビジネスシーンでの活用、日々の業務を自動化するGoogle Workspaceとの連携、さらには自分だけのオリジナルAIを作成する方法まで余すところなく解説します。
Rustプログラミング完全ガイド 他言語との比較で違いが分かる!
Rustの各手法や考え方を幅広く解説!
500以上のサンプルを掲載。実行結果も確認。
全24章の包括的なチュートリアル。
ポチらせる文章術
販売サイト・ネット広告・メルマガ・ブログ・ホームページ・SNS…
全WEB媒体で効果バツグン!
カリスマコピーライターが教える「見てもらう」「買ってもらう」「共感してもらう」すべてに効くネット文章術
小型で便利な Type-C アダプター USB C オス - USB3.1 オスアダプター
Type-C端子のマイコンボードをこのアダプタを介して直接Raspberry Piに挿すことができます。ケーブルなしで便利なツールです。
Divoom Ditoo Pro ワイヤレススピーカー
15W高音質重低音/青軸キーボード/Bluetooth5.3/ピクセルアート 専用アプリ/USB接続/microSDカード
電源供給USBケーブル スリム 【5本セット】
USB電源ケーブル 5V DC電源供給ケーブル スリム 【5本セット】 電源供給 バッテリー 修理 自作 DIY 電子工作 (100cm)
|