forked from jdonley/SoundZone_Tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
synthSweep.m
178 lines (127 loc) · 5.4 KB
/
synthSweep.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
function [sweep, invsweepfft, sweepRate] = synthSweep(T,FS,f1,f2,tail,AsdB)
% Synthesize a logarithmic sine sweep
% [sweep invsweepfft sweepRate] = SYNTHSWEEP(T,FS,f1,f2,tail,AsdB)
% generates a logarithmic sine sweep that starts at frequency f1 (Hz),
% stops at frequency f2 (Hz) and duration T (sec) at sample rate FS (Hz).
%
% usePlots indicates whether to show frequency characteristics of the
% sweep, and the optional AsdB is an amplitude suppression value in
% decibels to avoid clipping
%
% Developed at Oygo Sound LLC
%
% Equations from Muller and Massarani, "Transfer Function Measurement
% with Sweeps."
%
% Modified by Jacob Donley ([email protected]) Nov 2016
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% DO SOME PREPARATORY STUFF
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% number of samples / frequency bins
N = real(round(T*FS));
if nargin < 5
tail = 0;
end
if nargin < 6
AsdB = 0;
end
%%% make sure start frequency fits in the first fft bin
f1 = ceil( max(f1, FS/(2*N)) );
%%% set group delay of sweep's starting freq to one full period length of
%%% the starting frequency, or N/200 if thats too small, or N/10 if its too
%%% big
Gd_start = ceil(min(N/10,max(FS/f1, N/200)));
%%% set fadeout length
postfade = ceil(min(N/10,max(FS/f2,N/200)));
%%% find the length of the actual sweep when its between f1 and f2
Nsweep = N - tail - Gd_start - postfade;
%%% length in seconds of the actual sweep
tsweep = Nsweep/FS;
sweepRate = log2(f2/f1)/tsweep;
%%% make a frequency vector for calcs (This has length N+1) )
f = ([0:N]*FS)/(2*N);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% CALCULATE DESIRED MAGNITUDE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% create pink (-10dB per decade, or 1/(sqrt(f)) spectrum
mag = [sqrt(f1./f(1:end))];
mag(1) = mag(2);
%%% Create band pass magnitude to start and stop at desired frequencies
[B1, A1] = butter(2,f1/(FS/2),'high' ); %%% HP at f1
[B2, A2] = butter(2,f2/(FS/2)); %%% LP at f2
%%% convert filters to freq domain
H1 = freqz(B1,A1,N+1,FS);
H2 = freqz(B2,A2,N+1,FS);
%%% multiply mags to get final desired mag spectrum
mag = mag .* abs(H1)' .* abs(H2)';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% CALCULATE DESIRED GROUP DELAY
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% calc group delay for arbitrary mag spectrum with constant time envelope
%%% from Muller eq's 11 and 12
C = tsweep ./ sum(mag.^2);
Gd = C * cumsum(mag.^2);
Gd = Gd + Gd_start/FS; % add predelay
Gd = Gd*FS/2; % convert from secs to samps
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% CALCULATE DESIRED PHASE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% if (nargin > 6)
% mag = linspace(0.1, 1, length(mag));
% end
%%% integrate group delay to get phase
ph = grpdelay2phase(Gd);
%%% force the phase at FS/2 to be a multiple of 2pi using Muller eq 10
%%% (but ending with mod 2pi instead of zero ...)
ph = ph - (f/(FS/2))*mod(ph(end),2*pi);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% SYNTHESIZE COMPLEX FREQUENCY RESPONSE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cplx = mag.*exp(sqrt(-1)*ph); %%% put mag and phase together in polar form
cplx = [cplx conj(fliplr(cplx(2:end-1)))]; %%% conjugate, flip, append for whole spectrum
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% EXTRACT IMPULSE RESPONSE WITH IFFT AND WINDOW
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ir = real(ifft(cplx));
err = max(abs(imag(ifft(cplx)))); %%% if this is not really tiny then something is wrong
%%% create window for fade-in and apply
w = hann(2*Gd_start)';
I = 1:Gd_start;
ir(I) = ir(I).*w(I);
%%% create window for fade-out and apply
w = hann(2*postfade)';
I = Gd_start+Nsweep+1:Gd_start+Nsweep+postfade;
ir(I) = ir(I).*w(postfade+1:end);
%%% force the tail beyond the fadeout to zeros
I = Gd_start+Nsweep+postfade+1:length(ir);
ir(I) = zeros(1,length(I));
%%% cut the sweep down to its correct size
ir = ir(1:end/2);
%%% normalize
ir = ir / (max(abs(ir(:))));
%%% suppress output
ir = ir * db2mag( AsdB );
%%% get fft of sweep to verify that its okay and to use for inverse
irfft = fft(ir);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% CREATE INVERSE SPECTRUM
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% start with the true inverse of the sweep fft
%%% this includes the band-pass filtering, whos inverse could go to
%%% infinity!!!
invirfft = 1./irfft;
%%% so we need to re-apply the band pass here to get rid of that
H1 = freqz(B1,A1,length(irfft),FS,'whole');
H2 = freqz(B2,A2,length(irfft),FS,'whole');
%%% apply band pass filter to inverse magnitude
% invirfftmag = abs(invirfft).*abs(H1)'.*abs(H2)';
%
% %%% get inverse phase
% invirfftphase = angle(invirfft);
%
% %%% re-synthesis inverse fft in polar form
% invirfft = invirfftmag.*exp(sqrt(-1)*invirf
invirfft = invirfft .* abs(H1)' .* abs(H2)';
%%% assign outputs
invsweepfft = invirfft;
sweep = ir;