今回はディレイを実装していきます。
いわゆる遅延系エフェクトでやまびこ効果とか言われるやつです。
ディレイの実装には参考文献に示すようにフィードバックの仕方によっていろいろな種類がありますが一般的なフィードバックありミックスありの通常のタイプをやって行きます。
まず時系列信号処理をするにあたってデータを保存するバッファを確保する必要があるのでリングバッファを使ってそれを実現します。
リングバッファの説明は参考文献の二つ目を参照してくれると理解しやすいと思う。
実装
というわけでさっそく実装していく
まずはエフェクトのパラメータであるfeedbackとmixとtimeを定義する。
delay_buffer_idx = 0 #リングバッファのインデックス feedback = 0.4 #フィードバック(0~1) delay_ms = 465 #ディレイタイム[ms] delay_buf_size = int(frame_fs * delay_ms / 1000) #バッファ確保[sample] level = 0.1 #level(0~1)
frame_fs は処理系によって異なりますが基本48000(48kHz)ぐらいだと思います。
48kHzサンプリングなので48000サンプルで1秒のディレイタイムが確保できる。
今回はPC上での実装なのでメモリはほぼ無限にあるがDSPだったり組み込みでの実装の場合限られた中で実装する必要もあるので頭に入れておくといいです。
次に入出力関係の整備を行う
ここはいつも通り入力バッファと出力バッファを確保しそれと今回使うディレイバッファを確保する。
input_buf = in_data#wavデータだったり正弦波だったりを格納しておく output_buf = np.zeros(frame_size,dtype=np.float)# delay_buffer = np.zeros(delay_buf_size, dtype=np.float)#パラメータのところで計算したサイズ分だけ確保する。
信号処理部分はコメントに示す通りの流れになる。
"""signal processing""" for i in range(frame_size): l_x = input_buf[i]*1.0+ delay_buffer[delay_buffer_idx] * level #入力(ゲインは1)+ディレイ delay_buffer[delay_buffer_idx] = input_buf[i] + delay_buffer[delay_buffer_idx]*feedback#入力とフェードバック分をバッファに書き込み delay_buffer_idx = int((delay_buffer_idx + 1) % delay_buf_size)#リングバッファをすすめる output_buf[i] = l_x#出力
これで処理自体は完了となる。
処理としてはメモリに入れて所定サンプル数経過後に原音と加算して出力してるだけなのでかなりコードが短い
最後に処理前後の波形を示しておきます。
音が切れてから残響効果により音が残る波形となっていることがわかる
サンプル音源
元音源
ディレイ
各種設定
"""delay param""" delay_buffer_idx = 0 #リングバッファのインデックス feedback = 0.4 #フィードバック delay_ms = 465 #ディレイタイム[ms] delay_buf_size = int(frame_fs * delay_ms / 1000) #バッファ確保[sample] level = 0.8 #ミックス(エフェクトレベル)
コード全体
かなり走り書きなので参考程度にお願いします。
import numpy as np import matplotlib.pyplot as plt import create_func as cf import wave import audio_func as af import struct import scipy wf = wave.open("GS04.wav", "r")#wav 扱うならお決まりのやつ num_data = scipy.fromstring(wf.readframes(wf.getnframes()),dtype = "int16") / 32768.0#正規化 frame_fs = wf.getframerate() frame_size = wf.getnframes() """delay param""" delay_buffer_idx = 0 #リングバッファのインデックス feedback = 0.4 #フィードバック delay_ms = 400 #ディレイタイム[ms] delay_buf_size = int(frame_fs * delay_ms / 1000) #バッファ確保[sample] level = 0.5 #ミックス(エフェクトレベル) if(wf.getnchannels() == 2): left = num_data[::2]#1 スライス right= num_data[1::2] in_data = left*0.5 + right*0.5#2ステレオモノラル化 #1:スライスの説明 #a[1,2,3,4,5]ていうリストがあったとして #a[::2] -> 1,3,5 #a[1::2] -> 2,4 #2:ステレオ->モノラル化 #もともとギターの録音で左右に同じ音ふってるのでLR分解して半分にして足せば同じようなもん input_buf = in_data output_buf = np.zeros(frame_size,dtype=np.float) delay_buffer = np.zeros(delay_buf_size, dtype=np.float) """signal processing""" for i in range(frame_size): l_x = input_buf[i] + delay_buffer[delay_buffer_idx] * level #入力+ディレイ delay_buffer[delay_buffer_idx] = input_buf[i] + delay_buffer[delay_buffer_idx]*feedback#入力とフェードバック分をバッファに書き込み delay_buffer_idx = int((delay_buffer_idx + 1) % delay_buf_size)#リングバッファをすすめる output_buf[i] = l_x#出力 plt.subplot(211) plt.plot(input_buf[0:frame_size]) plt.subplot(212) plt.plot(output_buf[0:frame_size]) plt.show() output = [int(x * 32768.0) for x in output_buf] output = struct.pack("h" * len(output), *output) af.play(output,wf.getsampwidth(),1,wf.getframerate()) #再生 #af.save(output, frame_fs,1,"delay2.wav")