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

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

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

 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()
#
#==================