この記事に直接関連した内容の書籍をブログ管理人が出版しました。記事をリファインして、極力詳しく解説しています。見ていただけるとうれしいです(2022年5月15日(日))。↓
タイトル通りです。Pythonでやってみました。FFT、IFFTを使います。まず、waveファイル(コードでは'BGM.wav')をオープンしてサウンドデータを読み取り、waveファイルのパラメータを表示します。表示を見てステレオ録音であることを確認してください(コードはステレオであることを仮定して書きました)。ステレオ録音されたファイルの場合、サウンドデータは左、右、左、右、……と並んでいますから、左データと右データを別々にまとめ、それぞれを指定フレーム数毎にFFTを施して周波数成分を求めて高周波成分をカット、IFFTでサウンドデータに戻します。最後にもとのwaveファイルのパラメータを調べてもとと同じ形式で別のwaveファイルに保存し直します。サウンドデータにFFTを施すと、出てくる離散フーリエ係数はを満たします(離散フーリエ係数の性質)。だから、サウンドデータを編集する際にはこれが成立するようにしなければなりません。コードのyf[int(N / 2 - j)]=0、 yf[int(N / 2 + j)] = 0の部分がこれに当たります。例によって分かりやすさを考え、ほぼ最小限のコードです。
import sys import numpy as np import scipy as sp from scipy.fftpack import fft import wave N = 1024 #FFTするサイズ。2の累乗。 #-------------------------------- #サウンドデータの高域成分をカットする # #databuf: サウンドデータの入ったバッファ #num: 上から何個分の高周波成分を書き換えるか。maxはN/2-1。 def editsound(databuf, num): databuf1 = np.zeros(databuf.size) for i in range(int(databuf.size / N)): #Nフレームずつ処理 yf = sp.fftpack.fft(databuf[N * i: N * (i+1)]) for j in range(num): yf[int(N / 2 - j)] = 0 #高周波成分を0に yf[int(N / 2 + j)] = 0 #yf[j] = 0 #低周波成分を0に #yf[(N - 1) - j] = 0 #ifftの結果は複素数。もとが実データなので結果の虚数部分は0。 #だがコピー先は複素数の配列ではないので実数部分だけをコピーする。 databuf1[N * i: N * (i+1)] = sp.fftpack.ifft(yf).real return databuf1 #-------------------------------- wavefile = wave.open('BGM.wav',"rb") buf = wavefile.readframes(wavefile.getnframes())#サウンドデータ部分の読み取り buf = np.frombuffer(buf, dtype= "int16")#16bit符号付き整数に変換 wavefile.close() print(wavefile.getparams()) sounddata = buf.copy() #サウンドデータ用バッファ sounddata[0::2] = editsound(sounddata[0::2], 400) #左の音を編集 sounddata[1::2] = editsound(sounddata[1::2], 400) #右の音を編集 writewave = wave.Wave_write("keshitene.wav") #保存先ファイル名 writewave.setparams(wavefile.getparams()) #保存先ファイルにもとと同じパラメータをセット writewave.writeframes(sounddata) #編集したサウンドデータを保存 writewave.close() sys.exit()
editsound()を書き換えれば「高音の成分を弱くする」、「低音をカット」(コードの中でコメントアウトしています)、……など色々できます。ウィンドウにつまみをいくつも配置していろいろな周波数成分を変更できるようにしてやればイコライザのできあがありです!!
面白いです。数学、PCを使ってとにかく色々なことができます。数学は苦手な人が敬遠する科目の多分筆頭ですが、簡単に諦めてしまうのはもったいなさ過ぎだと思います。