サンプリング周波数変換(リサンプリング,アップ/ダウンサンプリング)は,非常によく利用されるオーディオファイル処理です。
ただ、リサンプリングは時間がかかる処理なので、なるべく高速なアルゴリズム・ライブラリを使いたいところです。
特に大量のデータを扱っている時や、時間的に長いオーディオファイルを扱う時に重要になります。
また、アンチエイリアシングフィルタ(アップ/ダウンサンプリングでエイリアシングのために用いるローパスフィルタ)等によって音質に違いが出ます。
今回紹介する Python 向けリサンプリング ライブラリ resampy
は、オーディオ信号向けの効率的なリサンプリング処理ができます。
https://resampy.readthedocs.io/en/master/
GitHub - bmcfee/resampy: Efficient sample rate conversion in python
なお、resampy
のリポジトリの所有者は音楽信号分析ライブラリ LibROSA のプライマリメンテナである bmcfee (Brian McFee)氏であり、resampy
はLibROSAのオーディオファイルの読み込み関数librosa.load()
でも利用されています。
以下,この記事ではリサンプリング ライブラリresampy
の使い方を説明します。
使い方
import resampy
y = resampy.resample(x, sr_orig, sr_mod)
sr_orig
に入力するNumpy配列x
のサンプリング周波数、sr_mod
にサンプリング周波数変換後のサンプリング周波数を与えます。
入力信号がステレオ信号のような複数チャネルの信号の場合、入力となるNumpy配列の形状は(チャンネル, サンプル数)となります。
また、キーワード引数filter
やnum_zero
を与えることで、アンチエイリアシングフィルタの形状などを変更することができます。
以下、ドキュメントのAdvanced filtering
からの引用です。
# Or a shorter sinc-filter than the default (num_zeros=64) y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window', num_zeros=32) # Or use the pre-built high-quality filter y = resampy.resample(x, sr_orig, sr_new, filter='kaiser_best') # Or use the pre-built fast filter 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 # 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()
まとめ
Python向けのリサンプリングのライブラリ resampy
の使い方の紹介と、窓関数(ローパスフィルタ)の種類による音質と計算時間の比較を行いました。
デフォルトの窓関数であるkaiser_best
は音質に優れていますが、計算に少し時間がかかってしまいます。
一方で、kaiser_fast
は音質は劣化が目立ちますが、高速に動作するというメリットがあります。
従って、kaiser_best
は人が聞く音信号に、kaiser_fast
は分析用信号に対して適用するのがよいと思います。