ガレスタさんのDIY日記

電子回路、Web、組み込み、自作エフェクターを語るblog

Python:ディレイを実装してみる:1

今回はディレイを実装していきます。
いわゆる遅延系エフェクトでやまびこ効果とか言われるやつです。


ディレイの実装には参考文献に示すようにフィードバックの仕方によっていろいろな種類がありますが一般的なフィードバックありミックスありの通常のタイプをやって行きます。

f:id:gsmcustomeffects:20180820231509p:plain

まず時系列信号処理をするにあたってデータを保存するバッファを確保する必要があるのでリングバッファを使ってそれを実現します。
リングバッファの説明は参考文献の二つ目を参照してくれると理解しやすいと思う。

実装

というわけでさっそく実装していく

まずはエフェクトのパラメータである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#出力

これで処理自体は完了となる。
処理としてはメモリに入れて所定サンプル数経過後に原音と加算して出力してるだけなのでかなりコードが短い

最後に処理前後の波形を示しておきます。
f:id:gsmcustomeffects:20180821001507p:plain

音が切れてから残響効果により音が残る波形となっていることがわかる

サンプル音源

元音源

ディレイ

各種設定

"""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")