オーディオファイルのサンプリング周波数変換処理(リサンプリング)は非常によく利用する処理です。
信号処理のプログラムを書いていると、リサンプリングはなるべく高速に処理して欲しくなります。特に大量のデータを扱っている時や、時間的に長いオーディオファイルを扱う時に重要になります。
また、アンチエイリアシングフィルタ(アップ/ダウンサンプリングでエイリアシングのために用いるローパスフィルタ)等によっては、音質に違いが出ます。
今回紹介する Python のライブラリ resampy
は、オーディオ信号向けの効率的なリサンプリング処理ができます。
Introduction — resampy 0.2.2 documentation
GitHub - bmcfee/resampy: Efficient sample rate conversion in python
なお、リポジトリの所有者はLibROSAのプライマリメンテナである bmcfee (Brian McFee)氏であり、resampy
はLibROSAのオーディオファイルの読み込み関数librosa.load()
でも利用されています。
Librosa
使い方
y = resampy.resample(x, sr_orig, sr_mod)
sr_orig
に入力するNumpy配列x
のサンプリング周波数、sr_mod
にサンプリング周波数変換後のサンプリング周波数を与えます。
入力信号がステレオ信号のような複数チャネルの信号の場合、入力となるNumpy配列の形状は(チャンネル, サンプル数)となります。
また、キーワード引数filter
やnum_zero
を与えることで、アンチエイリアシングフィルタの形状などを変更することができます。
以下、ドキュメントのAdvanced filtering
からの引用です。
y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', num_zeros=32)
y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_best')
y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')
以下の5パターンについて、処理時間と誤差(アップサンプリング後にダウンサンプリング)を計測してみました。
resampy.resample(x, sr_orig, sr_new)
resampy.resample(x, sr_orig, sr_new, filter='sinc_window', window=scipy.signal.hann)
resampy.resample(x, sr_orig, sr_new, filter='sinc_window', num_zeros=32)
resampy.resample(x, sr_orig, sr_new, filter='kaiser_best')
resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')


seaborn
を使って少し綺麗なプロットにしてみました。
import numpy as np
import scipy.signal
import resampy
import timeit
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
number = 5
sr_orig = 44100
sr_new = 48000
n_samples = sr_orig * 100
x = np.random.normal(0, 0.2, (n_samples))
t = np.zeros(5)
err = np.zeros(5)
index = np.arange(1,5+1)
t[0] = timeit.timeit(lambda:
resampy.resample(x, sr_orig, sr_new)
, number=number)
t[1] = timeit.timeit(lambda:
resampy.resample(x, sr_orig, sr_new, filter='sinc_window', window=scipy.signal.hann)
, number=number)
t[2] = timeit.timeit(lambda:
resampy.resample(x, sr_orig, sr_new, filter='sinc_window', num_zeros=32)
, number=number)
t[3] = timeit.timeit(lambda:
resampy.resample(x, sr_orig, sr_new, filter='kaiser_best')
, number=number)
t[4] = timeit.timeit(lambda:
resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')
, number=number)
y = resampy.resample(x, sr_orig, sr_new)
z = resampy.resample(y, sr_new, sr_orig)
err[0] = np.sqrt(np.mean((z-x)**2))
y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', window=scipy.signal.hann)
z = resampy.resample(y, sr_new, sr_orig, filter='sinc_window', window=scipy.signal.hann)
err[1] = np.sqrt(np.mean((z-x)**2))
y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', num_zeros=32)
z = resampy.resample(y, sr_new, sr_orig, filter='sinc_window', num_zeros=32)
err[2] = np.sqrt(np.mean((z-x)**2))
y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_best')
z = resampy.resample(y, sr_new, sr_orig, filter='kaiser_best')
err[3] = np.sqrt(np.mean((z-x)**2))
y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast')
z = resampy.resample(y, sr_new, sr_orig, filter='kaiser_fast')
err[4] = np.sqrt(np.mean((z-x)**2))
print(t)
print(err)
plt.clf()
plt.bar(index, t, color="rgbym", alpha=0.5)
plt.ylabel("Time [sec]")
plt.show()
plt.clf()
plt.bar(index, err, color="rgbym", alpha=0.5)
plt.ylabel("Error")
plt.yscale("log")
plt.show()