先週、Common Lisp版のOATSP信号を作りました。
Julia版もあると便利かと思ってサクッと実装してみました(OATSPの説明などは上記のページを見てください)。一つの関数で測定信号と逆信号を作れると便利なので、まとめてしまっています。関数本体のコードは後に掲載するとして、まずは使い方です。
音ファイルの保存に必要になるパッケージなどを読み込みます。また、保存するときの標本化周波数は48000 Hzとします。
using SampledSignals, FFTW using FileIO import LibSndFile fs = 48000;
測定信号と逆信号を作り、それぞれWAVファイルとして保存します。ただ、そのままだと振幅が小さいので-3 dBに正規化しています。また、PCM16Sampleのところで16 bitで保存するように指定しています。ここではNに4096、mに1200と、論文に書かれている信号長を指定しましたが、室内の残響時間を測定するときなどにはもっと長い信号を使うことになると思います。
N = 4096; m = 1200; # make measurement signal x = make_oatsp(N, m); xx = x ./ maximum(abs.(x)) * sqrt(1/2); save("oatsp.wav", SampleBuf(PCM16Sample.(xx), fs)); # make inverse signal y = make_oatsp(N, m, inverse=true); yy = y ./ maximum(abs.(y)) * sqrt(1/2); save("oatsp_inverse.wav", SampleBuf(PCM16Sample.(yy), fs));
正規化する前の測定信号と逆信号を環状畳み込みして、インパルスに戻るか確かめてみましょう。
using Plots # check whether inverse works z = real.(ifft(fft(x) .* fft(y))); bar(z[1:50], legend=false)
1サンプル目だけに1が入っています。きれいに戻っていますね。
以下、関数本体です。
""" x = make_oatsp(N, m; inverse=false) Makes an Optimum Aoshima's Time Stretched Pulse with specified duration and signal width. # References * Nobuharu Aoshima. ``Computer-generated pulse signal applied for sound measurement.'' The Journal of the Acoustical Society of America, vol.69, no.5, pp.1484--1488. May 1981. https://doi.org/10.1121/1.385782 * Yoiti Suzuki, Futoshi Asano, Hack-Yoon Kim, and Toshio Sone. ``An optimum computer-generated pulse signal suitable for the measurement of very long impulse responses.'' The Journal of the Acoustical Society of America, vol.97, no.2, pp.1119--1123. February 1995. https://doi.org/10.1121/1.412224 """ function make_oatsp(N, m; inverse=false) p = π*m / (N/2)^2 k = 0 : N÷2 H = zeros(ComplexF64, N) if inverse == false H[1 : N÷2+1] = exp.(1.0im * p * k .* k) H[N÷2+2 : N] = conj(reverse(H[2 : N÷2])) else H[1 : N÷2+1] = exp.(-1.0im * p * k .* k) H[N÷2+2 : N] = conj(reverse(H[2 : N÷2])) end Y = real.(ifft(H)) # circular shift n_rot = N÷2 - m if inverse == false Y = [Y[n_rot+1:end] ; Y[1:n_rot]] else Y = [Y[end-n_rot+1:end] ; Y[1:end-n_rot]] end return(Y) end