From 9e4c4932c0b096e8e927ff104751d5af7c966acd Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Wed, 14 Dec 2016 17:27:27 +0100 Subject: [PATCH 1/4] Add interpolator to apply_delays. --- sfs/time/drivingfunction.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/sfs/time/drivingfunction.py b/sfs/time/drivingfunction.py index a4e5c28..6ccb6c9 100644 --- a/sfs/time/drivingfunction.py +++ b/sfs/time/drivingfunction.py @@ -223,7 +223,7 @@ def driving_signals(delays, weights, signal): return util.DelayedSignal(data * weights, samplerate, signal_offset) -def apply_delays(signal, delays): +def apply_delays(signal, delays, interpolator=None): """Apply delays for every channel. Parameters @@ -233,6 +233,8 @@ def apply_delays(signal, delays): rate (in Hertz). A `DelayedSignal` object can also be used. delays : (C,) array_like Delay in seconds for each channel (C), negative values allowed. + interpolator : function, optional + Interpolator for fractional delays. See: TODO Returns ------- @@ -247,10 +249,14 @@ def apply_delays(signal, delays): delays = util.asarray_1d(delays) delays += initial_offset - delays_samples = np.rint(samplerate * delays).astype(int) - offset_samples = delays_samples.min() - delays_samples -= offset_samples - out = np.zeros((delays_samples.max() + len(data), len(delays_samples))) - for column, row in enumerate(delays_samples): - out[row:row + len(data), column] = data - return util.DelayedSignal(out, samplerate, offset_samples / samplerate) + integer_delays = np.rint(samplerate * delays).astype(int) + fractional_delays = samplerate * delays - integer_delays + offset = integer_delays.min() + integer_delays -= offset + out = np.zeros((integer_delays.max() + len(data), len(integer_delays))) + for channel, cdelay in enumerate(integer_delays): + out[cdelay:cdelay + len(data), channel] = data + if interpolator is not None: + out, filter_offset = interpolator(out, fractional_delays) + offset += filter_offset + return util.DelayedSignal(out, samplerate, offset / samplerate) From e2a9d33d5e11e8b83c76492e44a8da5b676074cc Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Sat, 17 Dec 2016 14:09:11 +0100 Subject: [PATCH 2/4] Add interpolator to driving_signals --- sfs/time/drivingfunction.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sfs/time/drivingfunction.py b/sfs/time/drivingfunction.py index 6ccb6c9..180aea0 100644 --- a/sfs/time/drivingfunction.py +++ b/sfs/time/drivingfunction.py @@ -193,7 +193,7 @@ def wfs_25d_focused(x0, n0, xs, xref=[0, 0, 0], c=None): return delays, weights -def driving_signals(delays, weights, signal): +def driving_signals(delays, weights, signal, interpolator=None): """Get driving signals per secondary source. Returned signals are the delayed and weighted mono input signal @@ -208,6 +208,8 @@ def driving_signals(delays, weights, signal): signal : (N,) array_like + float Excitation signal consisting of (mono) audio data and a sampling rate (in Hertz). A `DelayedSignal` object can also be used. + interpolator : function, optional + Interpolator for fractional delays. See: TODO Returns ------- @@ -219,7 +221,8 @@ def driving_signals(delays, weights, signal): """ delays = util.asarray_1d(delays) weights = util.asarray_1d(weights) - data, samplerate, signal_offset = apply_delays(signal, delays) + data, samplerate, signal_offset = apply_delays( + signal, delays, interpolator) return util.DelayedSignal(data * weights, samplerate, signal_offset) From b74777aa71623ecfbf78dfdcaa7eb47f6b6604a6 Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Wed, 15 Feb 2017 18:11:17 +0100 Subject: [PATCH 3/4] Add Lagrange Interpolator for fractional delays --- sfs/time/drivingfunction.py | 10 +++++----- sfs/util.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/sfs/time/drivingfunction.py b/sfs/time/drivingfunction.py index 180aea0..3c775bc 100644 --- a/sfs/time/drivingfunction.py +++ b/sfs/time/drivingfunction.py @@ -193,7 +193,7 @@ def wfs_25d_focused(x0, n0, xs, xref=[0, 0, 0], c=None): return delays, weights -def driving_signals(delays, weights, signal, interpolator=None): +def driving_signals(delays, weights, signal, interpolator=None, **kwargs): """Get driving signals per secondary source. Returned signals are the delayed and weighted mono input signal @@ -221,12 +221,12 @@ def driving_signals(delays, weights, signal, interpolator=None): """ delays = util.asarray_1d(delays) weights = util.asarray_1d(weights) - data, samplerate, signal_offset = apply_delays( - signal, delays, interpolator) + data, samplerate, signal_offset = apply_delays(signal, delays, + interpolator, **kwargs) return util.DelayedSignal(data * weights, samplerate, signal_offset) -def apply_delays(signal, delays, interpolator=None): +def apply_delays(signal, delays, interpolator=None, **kwargs): """Apply delays for every channel. Parameters @@ -260,6 +260,6 @@ def apply_delays(signal, delays, interpolator=None): for channel, cdelay in enumerate(integer_delays): out[cdelay:cdelay + len(data), channel] = data if interpolator is not None: - out, filter_offset = interpolator(out, fractional_delays) + out, filter_offset = interpolator(out, fractional_delays, **kwargs) offset += filter_offset return util.DelayedSignal(out, samplerate, offset / samplerate) diff --git a/sfs/util.py b/sfs/util.py index 006bf47..25234ea 100644 --- a/sfs/util.py +++ b/sfs/util.py @@ -562,3 +562,38 @@ def spherical_hn2(n, z): """ return spherical_jn(n, z) - 1j * spherical_yn(n, z) + + +def _fftconvolve_1d(a, v, axis): + """1-d fast convolution along axis. """ + output_length = a.shape[axis] + v.shape[axis] - 1 + fft_length = int(2 ** np.ceil(np.log2(output_length))) + A = np.fft.rfft(a, fft_length, axis) + V = np.fft.rfft(v, fft_length, axis) + out = np.fft.irfft(A*V, fft_length, axis) + return np.delete(out, np.s_[output_length:], axis) + + +def _lagrange(N, tau): + """Lagrange interpolator for fractional shift. + + Parameters + ---------- + N : int + Order. + tau : (M,) array_like + Fractional shifts. + + Returns + ----- + h : (N+1, M) ndarray + FIR filters. + + """ + tau = asarray_1d(tau) + h = np.ones([N+1, len(tau)]) + for n in range(0, N+1): + for k in range(0, N+1): + if n != k: + h[n, :] *= (tau + N//2 - k)/(n-k) + return h From d8dd46e8487c5f999f257ba3688afd52346cea21 Mon Sep 17 00:00:00 2001 From: Till Rettberg Date: Wed, 15 Feb 2017 21:31:38 +0100 Subject: [PATCH 4/4] Fix a sign flip --- sfs/time/drivingfunction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfs/time/drivingfunction.py b/sfs/time/drivingfunction.py index 3c775bc..44fdcd6 100644 --- a/sfs/time/drivingfunction.py +++ b/sfs/time/drivingfunction.py @@ -261,5 +261,5 @@ def apply_delays(signal, delays, interpolator=None, **kwargs): out[cdelay:cdelay + len(data), channel] = data if interpolator is not None: out, filter_offset = interpolator(out, fractional_delays, **kwargs) - offset += filter_offset + offset -= filter_offset return util.DelayedSignal(out, samplerate, offset / samplerate)