いぬおさんのおもしろ数学実験室

おいしい紅茶でも飲みながら数学、物理、工学、プログラミング、そして読書を楽しみましょう

waveファイルの作り方を分かりやすく解説

 この記事に直接関連した内容の書籍をブログ管理人が出版しました。記事をリファインして、極力詳しく解説しています。見ていただけるとうれしいです(2022年5月15日(日))。↓

 waveファイルを作ってみました。必要なパラメータをそろえ、サウンドデータをこしらえて保存すればOKです。作曲などはできませんので、10秒間、単純に左右で異なる高さの音が鳴るだけのものにしておきます。単純な方が原理は分かりやすいので、ちょうどよいと思います。例によってPythonを使います。waveファイルのパラメータ(ステレオかモノラルか、サンプリング周波数はいくらかなど)のセットはwaveモジュールで簡単にできますから、問題なのはサウンドデータをどうやって組み立てるか、です。分かりやすく解説します。

 waveファイルでは、音は8ビットか16ビットのデータで記録します。16ビットなら整数値-32768~32767を用います(0が無音を表す)。今回は16ビットで記録します。サンプリング周波数 fs も決めなければなりません。これはwaveファイルに1秒あたり記録するフレーム数です。モノラル録音なら1フレームでさっきの整数値1個、ステレオ録音なら左、右の組が1フレームで、整数値2個です。今回はステレオ録音します。あちこちのwaveファイルを見てみると、サンプリング周波数は44100Hz、22050Hzあたりを見かけます。このコードでは fs = 22050Hz にしました。1秒間にこれだけの回数、音楽などをサンプリングして、それを整数値で表して記録するということです。今回は s = 10秒のwaveファイルを作るので、必要なフレーム数 N は N = fs * s = 22050*10=220500個です。さっきの整数値はこの2倍(左右で1個ずつなので)、441000個必要です。
 コーディングでは t = np.arange(0, s, 1/fs) で「s秒間、1/fs秒毎」の時刻の配列(要素数はs*fs)を作り、それぞれの時刻 t でどんな大きさなのか、左のデータは ldata = 30000 * np.sin(2*pi*440*t)、右のデータは rdata = 30000 * np.sin(2*pi*700*t) で定義しています。3角関数では sin(2π*□*t) の周波数は□の部分なのでした。だから左のデータは440Hz(ドレミでいうと「ラ」の音)、右は700Hzの音です。3000倍してあるのは、sin の値は-1~1で、記録する整数値である-32768~32767に比べて小さすぎです。そこである程度大きくしたということです。これで左右のサウンドデータができあがりました。あとはwaveファイルの構造の規則に従って、順に左、右、左、右、……とかみ合わせるようにすればOKです。具体的には sounddata[0::2] = ldata で、配列 sounddata の先頭から1個おきに ldata の内容をコピーします。同様に rdata もコピーします。最後にwaveファイルのパラメータですが、nchannels=2ならステレオ、sampwidth=2なら16ビットで録音(-32768~32767の整数値で録音)、framerateはサンプリング周波数、nframesはここまでで説明した「フレーム数」です。
 waveファイルの構造については前に紹介した本などをご参考に。またよいサイトもたくさんあります。
www.omoshiro-suugaku.com

#==================
#waveファイルを作る。左は440Hz、右は700Hzで10秒間。
#
import sys
import numpy as np
import wave
pi = np.pi
fs = 22050      #サンプリング周波数
s  = 10         #10秒の長さのwaveファイルを作る
#s秒分のサウンドデータを作るには、fs*sフレームが必要。
N  = fs * s     #フレーム数(サウンドデータ数。左右で1フレーム)
t = np.arange(0, s, 1/fs)   #横軸
#sin(2π*□*t) の周波数は□の部分。16ビットで録音するので、
#-32768~32767がデータの範囲。だから-30000~30000の範囲の
#大きさの音を作ることにした。
ldata = 30000 * np.sin(2*pi*440*t)  #440Hz
rdata = 30000 * np.sin(2*pi*700*t)  #700Hz
sounddata = np.zeros(N * 2, dtype= "int16") #左右のデータ分が必要
sounddata[0::2] = ldata
sounddata[1::2] = rdata
writewave = wave.Wave_write("keshitene.wav") #保存先ファイル
#setparamの引数は
#(nchannels, sampwidth, framerate, nframes, comptype, compname)
#先頭から順にステレオ、サンプルのサイズ2バイト、
#サンプリング周波数fs、フレーム数N。
writewave.setparams((2, 2, fs, N, 'NONE', 'NONE'))
writewave.writeframes(sounddata) #編集したサウンドデータを保存
writewave.close()
sys.exit()
#
#==================