| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- %% Simulate the Diffuse Field Response of Microphone Array
- % Aaron J. Heller (heller@ai.sri.com)
- % 28-May-2012, revised 22-Jun-2012, 12-Jan-2013
- % tested with MATLAB R2012b and GNU Octave, version 3.2.4
- %% Main function for simulator
- function [dfr, ffr] = mic_array_sim_dfr( M, output_file )
- % MIC_ARRAY_SIM_DFR simulate diffuse field response pf mic array M
- %
- % M.name ... name of array
- % M.o ... orientaton of capsules (direction cosines)
- % M.u ... positions of capsules (in meters)
- % M.alpha .. directivities of capsules, by order
- % M.a2b ... the A-to-B matrix
- % (Batke and Elko call this the mode matrix, \Psi)
- % see MIC_ARRAY_Tetra and _Velan functions below for examples
-
- %% array to be simulated, all array parameteters in structure M
- if ~exist('M', 'var')
- M = CalrecMkIV;
- %%M.a2b = A2B';
- end
- %display(M.name);
- %% Physical constants and simulation parameters
-
- % speed of sound
- c = 340.29; % meters/sec
-
- % number of frequencies for simulation
- n_freqs = 200;
- low_freq = 100;
- high_freq = 20000;
-
- % log or linear
- log_freqs = false;
-
- % indexes of free-field responses to write out
- ffr_save = 1:6;
-
- % file for output, set to false for no file
- if ~exist('output_file', 'var')
- output_file = ['sim_output_', M.name, '.csv'];
- end
-
- % draw a graph?
- graph_response = true;
-
-
- %% Integration on the Sphere
- % use order 20 Lebedev-Laikov quadrature
- a = LebedevGrid(20);
-
- % excitation directions,
- x = [a.x'; a.y'; a.z'];
-
- % number of outputs is number of rows in a2b matrix
- n_Bout = size(M.a2b,2);
-
- % quadrature weights, a.w is for full sphere, normalize to 1, duplicate
- % for each output
- qw = a.w(:,ones(1,n_Bout))' / (4*pi);
-
- % projections of capsule positions along the excitation directions
- d = M.u * x;
-
- % cosine of angle between capsule orientation and excitation directions
- cos_theta = (M.o * x);
- size_cos_theta = size(cos_theta); % for octave
-
- % directional gain for order i is the Legendre Polynomial, P_i
- % evaluated at cos_theta
- dir_gain = diag(M.alpha(:,1)) * ones(size(cos_theta)); % P_0(x) = 1
- for i = 2:size(M.alpha,2)
- if false
- P_i = legendre(i-1, cos_theta);
- P_i = squeeze(P_i(1,:,:));
- else % for octave
- P_i = reshape(legendre(i-1, cos_theta(:)'), [i,size_cos_theta]);
- P_i = squeeze(P_i(1,:,:));
- end
- dir_gain = dir_gain + diag(M.alpha(:,i)) * P_i;
- end
-
- % allocate array to hold diffuse-field frequency response
- dfr = zeros(n_freqs, n_Bout);
-
- % allocate array to hold free-field frequency response
- ffr = zeros(n_freqs, n_Bout, numel(ffr_save));
-
- % test frequencies
- if log_freqs
- freqs = logspace(log10(low_freq), log10(high_freq), n_freqs);
- else
- freqs = linspace(low_freq, high_freq, n_freqs);
- end
-
- i = 1;
- for k = freqs * (2*pi)/c % k is wavenumber
- % compute B-format outputs at this frequency
- B = M.a2b * ( dir_gain .* exp( +1j * k * d ) );
- ffr(i,:,:) = B(:,ffr_save);
-
- % compute RMS responses over surface of sphere
- dfr(i,:) = sqrt( sum(B .* conj(B) .* qw, 2) );
- i = i + 1;
- end
-
- if output_file
- dlmwrite(...
- output_file,...
- [freqs', ... % column 1
- dfr, ... % columns 2 ... n_Bout
- reshape(abs(ffr), numel(freqs), n_Bout*numel(ffr_save))],...
- 'precision', '%16g');
- end
-
- if graph_response
- % Graph it
- % dfr(:,1) is W, dfr(:,2) is X
- createfigure(M, freqs, 20*log10(dfr(:,1:2)));
- end
- end
- %% Generic tetrahedral array
- function M = MIC_ARRAY_Tetra(radius, alpha1)
- % mic_array_tetra return parameters for generic tetra microphone
- % RADIUS is the acoustic radius of the array, typically 10% larger
- % than physical radius.
- % ALPHA1 is the gain of the first-order parameter
- % 0 = omni,
- % 1/2 = cardioid,
- % 1 = fig-8
- % (3-sqrt(3))/2 = supercardioid (~0.63397)
- % 3/4 = hypercardoid
- %
- % The default values for RADIUS and ALPHA1 give the results shown in
- % Gerzon, "The Design of Precisely Coincident Microphone Arrays for
- % Stereo and Surround Sound", 50th AES Convention, London, 1975.
-
- M.name = 'Tetra';
-
- % Soundfield mic is four mics mounted on the faces of a tetrahedron.
- if ~exist('radius', 'var')
- M.r = 1.47 * 1e-2; % meters
- else
- M.r = radius;
- end
-
- % capsule directivities
- % for first-order mic: omni=[1,0]; cardioid=[0.5,0.5],fig8=[0,1];
- if ~exist('alpha1', 'var')
- alpha1 = 0.5;
- end
-
- M.alpha = [ ...
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1; ];
-
-
- % capsule orientations, direciton cosines
- M.o = [ ...
- -1 1 -1; % back-left-down .... LBD
- +1 1 1; % front-left-up ..... LFU
- +1 -1 -1; % front-right-down .. RFD
- -1 -1 1; % back-right-up ..... RBU
- ];
- % normalize
- M.o = diag(1./sqrt(diag(M.o * M.o'))) * M.o;
-
- % capsule positions for radial array, in meters
- M.u = M.o * M.r;
-
- % a2b matrix, Batke calls this the mode matrix, \Psi
- M.a2b = [ones(size(M.o,1),1), sign(M.o)]';
- end
- %% Calrec MkIV
- function M = MIC_ARRAY_CalrecMkIV()
- % original Calrec capsules 10.5 dB f/b ratio
- M = MIC_ARRAY_Tetra(1.47e-2, 0.35073087);
- M.name = 'Calrec_MkIV';
- end
- %% Soundfield MkV
- function M = MIC_ARRAY_SFRMkV()
- % new capsules used by Sounfield Research, 9.5 dB f/b ratio
- M = MIC_ARRAY_Tetra(1.47e-2, 0.33251727);
- M.name = 'Soundfield_Research_MkV';
- end
- %% Danish Pro Audio DPA-4
- function M = MIC_ARRAY_DPA()
- % DPA-4 array
- M = MIC_ARRAY_Tetra(2.5e-2, 0.5);
- M.name = 'DPA-4';
-
- % DPA uses different A format order and alternate tetrahedral config
- % M.a2b = ??????? (get data from Eric, fill in here )
- end
- %% Umashankar's Velan
- function M = MIC_ARRAY_Velan()
-
- M.name = 'Velan';
-
- % Umashankar's Velan is four TSB-140 cardioids with pairs pointed along
- % axis with separation 14.3 mm. See
- % http://www.shapeways.com/model/143678/velan-140-internals.html
- % http://www.firstpr.com.au/rwi/mics/2009-09-a/2011052109473129207TSB-140A25-GP.jpg
- % TSB-140 is 14mm diameter x 6.3mm depth. I assume the rings are 1 mm
- % thick. Acoustic radius is typically 20% larger than physical.
- M.r = 1.2*(14+2+2*6.3)/2 * 1e-3; % meters
-
- % capsule directivities
- % for first-order mic: omni=[1,0]; cardioid=[0.5,0.5],fig8=[0,1];
- M.alpha = [ ...
- 0.5 0.5;
- 0.5 0.5;
- 0.5 0.5;
- 0.5 0.5;
- 0.5 0.5;
- 0.5 0.5 ];
-
- % capsule orientations, direciton cosines
- % a row for each capsule, columns are x,y,z
- M.o = [ ...
- 1 0 0;
- -1 0 0;
- 0 1 0;
- 0 -1 0;
- 0 0 1;
- 0 0 -1];
-
- if false
- % specify in spherical (az, el, r), like this
- M.o = [...
- 0, 0, 1;
- 180, 0, 1;
- 90, 0, 1;
- -90, 0, 1;
- 0, 90, 1;
- 0,-90, 1 ];
- % convert to radians
- M.o(:,1:2) = M.o(:,1:2)*pi/180;
- % convert to cartesian
- [M.o(:,1), M.o(:,2), M.o(:,3)] = ...
- sph2cart(M.o(:,1),M.o(:,2),M.o(:,3));
- end
-
- % capsule positions for radial array, in meters
- M.u = M.o .* M.r;
-
- % a2b matrix, Batke calls this the mode matrix, \Psi
- % a column for each capsule, a row for each array output
- M.a2b = [ ...
- 0.2357 0.2357 0.2357 0.2357 0.2357 0.2357;
- 1.0000 -1.0000 0.0000 0.0000 0.0000 0.0000;
- 0.0000 0.0000 1.0000 -1.0000 0.0000 0.0000;
- 0 0 0 0 1.0000 -1.0000];
- end
- %% Plotting function
- function createfigure(M, X1,YMatrix1)
- % X1: vector of x data
- % YMATRIX1: matrix of y data
-
- % Auto-generated by MATLAB on 12-Mar-2011 11:02:34
-
- % Create figure
- figure1 = figure();
-
- % Create axes
- axes1 = axes('Parent',figure1,'YGrid','on','XGrid','on',...
- 'XMinorTick','on','YMinorTick','on','FontSize',12);
- box(axes1,'on');
- hold(axes1,'all');
-
- % Create multiple lines using matrix input to plot
- plot1 = plot(X1,YMatrix1,'Parent',axes1);
- set(plot1(1),'DisplayName','W');
- set(plot1(2),'DisplayName','XYZ');
- %set(plot1(3),'DisplayName','Y');
-
- % Create title
- name_sanitized = strrep(M.name, '_', '\_');
- if false
- title(sprintf(...
- ['Diffuse Field Response of simulated %s Mic Array' ...
- '\n(r=%0.3fcm, \\alpha=%0.3f)'], ...
- name_sanitized, M.r*100, M.alpha),...
- 'FontWeight','bold',...
- 'FontSize',12);
- else
- title(sprintf(...
- 'Diffuse Field Response of simulated %s Mic Array', ...
- name_sanitized), ...
- 'FontWeight','bold',...
- 'FontSize',12);
- end
-
- % Create xlabel
- xlabel('Frequency (Hz x 10^4)', 'FontAngle', 'italic');
- % Create ylabel
- ylabel('Relative Response (dB)', 'FontAngle', 'italic');
- % Create legend
- legend1 = legend(axes1,'show');
- set(legend1,'Location','SouthWest');
- end
- %% EMB's Octathingy
- function M = MIC_ARRAY_Octathingy(radius, alpha1)
- % mic_array_octathingy return parameters for generic tetra microphone
- % RADIUS is the acoustic radius of the array, typically 10% larger
- % than physical radius.
- % ALPHA1 is the gain of the first-order parameter
- % 0 = omni,
- % 1/2 = cardioid,
- % 1 = fig-8
- % (3-sqrt(3))/2 = supercardioid (~0.63397)
- % 3/4 = hypercardoid
- %
-
- M.name = 'Octathingy';
-
- % The octathingy is an octahedron (capsules at the vertices of a cube),
- % with the top rotated 45deg, so it can do 2nd-order horizontal,
- % 1st-order vertical.
-
- if ~exist('radius', 'var')
- M.r = 1.47 * 1e-2; % meters
- else
- M.r = radius;
- end
-
- % capsule directivities
- % for first-order mic: omni=[1,0]; cardioid=[0.5,0.5],fig8=[0,1];
- if ~exist('alpha1', 'var')
- alpha1 = 0.5;
- end
-
- M.alpha = [ ...
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1;
- 1-alpha1 alpha1 ];
-
-
- % capsule orientations, direciton cosines
- M.o = [ ...
- % bottom ring
- -1 1 -1; % back-left-down .... LBD
- 1 1 -1; % front-left-down
- +1 -1 -1; % front-right-down .. RFD
- -1 -1 -1; % back-right-down
-
- 0 1 1; % left-up
- +1 0 1; % front-up
- 0 -1 1; % right-up
- -1 0 1; % back-up
- ];
- % normalize
- M.o = diag(1./sqrt(diag(M.o * M.o'))) * M.o;
-
- % capsule positions for radial array, in meters
- M.u = M.o * M.r;
-
- % a2b matrix, Batke calls this the mode matrix, \Psi
- M.a2b = [ones(size(M.o,1),1), sign(M.o)]';
- end
|