1
0

mic_array_sim_dfr.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. %% Simulate the Diffuse Field Response of Microphone Array
  2. % Aaron J. Heller (heller@ai.sri.com)
  3. % 28-May-2012, revised 22-Jun-2012, 12-Jan-2013
  4. % tested with MATLAB R2012b and GNU Octave, version 3.2.4
  5. %% Main function for simulator
  6. function [dfr, ffr] = mic_array_sim_dfr( M, output_file )
  7. % MIC_ARRAY_SIM_DFR simulate diffuse field response pf mic array M
  8. %
  9. % M.name ... name of array
  10. % M.o ... orientaton of capsules (direction cosines)
  11. % M.u ... positions of capsules (in meters)
  12. % M.alpha .. directivities of capsules, by order
  13. % M.a2b ... the A-to-B matrix
  14. % (Batke and Elko call this the mode matrix, \Psi)
  15. % see MIC_ARRAY_Tetra and _Velan functions below for examples
  16. %% array to be simulated, all array parameteters in structure M
  17. if ~exist('M', 'var')
  18. M = CalrecMkIV;
  19. %%M.a2b = A2B';
  20. end
  21. %display(M.name);
  22. %% Physical constants and simulation parameters
  23. % speed of sound
  24. c = 340.29; % meters/sec
  25. % number of frequencies for simulation
  26. n_freqs = 200;
  27. low_freq = 100;
  28. high_freq = 20000;
  29. % log or linear
  30. log_freqs = false;
  31. % indexes of free-field responses to write out
  32. ffr_save = 1:6;
  33. % file for output, set to false for no file
  34. if ~exist('output_file', 'var')
  35. output_file = ['sim_output_', M.name, '.csv'];
  36. end
  37. % draw a graph?
  38. graph_response = true;
  39. %% Integration on the Sphere
  40. % use order 20 Lebedev-Laikov quadrature
  41. a = LebedevGrid(20);
  42. % excitation directions,
  43. x = [a.x'; a.y'; a.z'];
  44. % number of outputs is number of rows in a2b matrix
  45. n_Bout = size(M.a2b,2);
  46. % quadrature weights, a.w is for full sphere, normalize to 1, duplicate
  47. % for each output
  48. qw = a.w(:,ones(1,n_Bout))' / (4*pi);
  49. % projections of capsule positions along the excitation directions
  50. d = M.u * x;
  51. % cosine of angle between capsule orientation and excitation directions
  52. cos_theta = (M.o * x);
  53. size_cos_theta = size(cos_theta); % for octave
  54. % directional gain for order i is the Legendre Polynomial, P_i
  55. % evaluated at cos_theta
  56. dir_gain = diag(M.alpha(:,1)) * ones(size(cos_theta)); % P_0(x) = 1
  57. for i = 2:size(M.alpha,2)
  58. if false
  59. P_i = legendre(i-1, cos_theta);
  60. P_i = squeeze(P_i(1,:,:));
  61. else % for octave
  62. P_i = reshape(legendre(i-1, cos_theta(:)'), [i,size_cos_theta]);
  63. P_i = squeeze(P_i(1,:,:));
  64. end
  65. dir_gain = dir_gain + diag(M.alpha(:,i)) * P_i;
  66. end
  67. % allocate array to hold diffuse-field frequency response
  68. dfr = zeros(n_freqs, n_Bout);
  69. % allocate array to hold free-field frequency response
  70. ffr = zeros(n_freqs, n_Bout, numel(ffr_save));
  71. % test frequencies
  72. if log_freqs
  73. freqs = logspace(log10(low_freq), log10(high_freq), n_freqs);
  74. else
  75. freqs = linspace(low_freq, high_freq, n_freqs);
  76. end
  77. i = 1;
  78. for k = freqs * (2*pi)/c % k is wavenumber
  79. % compute B-format outputs at this frequency
  80. B = M.a2b * ( dir_gain .* exp( +1j * k * d ) );
  81. ffr(i,:,:) = B(:,ffr_save);
  82. % compute RMS responses over surface of sphere
  83. dfr(i,:) = sqrt( sum(B .* conj(B) .* qw, 2) );
  84. i = i + 1;
  85. end
  86. if output_file
  87. dlmwrite(...
  88. output_file,...
  89. [freqs', ... % column 1
  90. dfr, ... % columns 2 ... n_Bout
  91. reshape(abs(ffr), numel(freqs), n_Bout*numel(ffr_save))],...
  92. 'precision', '%16g');
  93. end
  94. if graph_response
  95. % Graph it
  96. % dfr(:,1) is W, dfr(:,2) is X
  97. createfigure(M, freqs, 20*log10(dfr(:,1:2)));
  98. end
  99. end
  100. %% Generic tetrahedral array
  101. function M = MIC_ARRAY_Tetra(radius, alpha1)
  102. % mic_array_tetra return parameters for generic tetra microphone
  103. % RADIUS is the acoustic radius of the array, typically 10% larger
  104. % than physical radius.
  105. % ALPHA1 is the gain of the first-order parameter
  106. % 0 = omni,
  107. % 1/2 = cardioid,
  108. % 1 = fig-8
  109. % (3-sqrt(3))/2 = supercardioid (~0.63397)
  110. % 3/4 = hypercardoid
  111. %
  112. % The default values for RADIUS and ALPHA1 give the results shown in
  113. % Gerzon, "The Design of Precisely Coincident Microphone Arrays for
  114. % Stereo and Surround Sound", 50th AES Convention, London, 1975.
  115. M.name = 'Tetra';
  116. % Soundfield mic is four mics mounted on the faces of a tetrahedron.
  117. if ~exist('radius', 'var')
  118. M.r = 1.47 * 1e-2; % meters
  119. else
  120. M.r = radius;
  121. end
  122. % capsule directivities
  123. % for first-order mic: omni=[1,0]; cardioid=[0.5,0.5],fig8=[0,1];
  124. if ~exist('alpha1', 'var')
  125. alpha1 = 0.5;
  126. end
  127. M.alpha = [ ...
  128. 1-alpha1 alpha1;
  129. 1-alpha1 alpha1;
  130. 1-alpha1 alpha1;
  131. 1-alpha1 alpha1; ];
  132. % capsule orientations, direciton cosines
  133. M.o = [ ...
  134. -1 1 -1; % back-left-down .... LBD
  135. +1 1 1; % front-left-up ..... LFU
  136. +1 -1 -1; % front-right-down .. RFD
  137. -1 -1 1; % back-right-up ..... RBU
  138. ];
  139. % normalize
  140. M.o = diag(1./sqrt(diag(M.o * M.o'))) * M.o;
  141. % capsule positions for radial array, in meters
  142. M.u = M.o * M.r;
  143. % a2b matrix, Batke calls this the mode matrix, \Psi
  144. M.a2b = [ones(size(M.o,1),1), sign(M.o)]';
  145. end
  146. %% Calrec MkIV
  147. function M = MIC_ARRAY_CalrecMkIV()
  148. % original Calrec capsules 10.5 dB f/b ratio
  149. M = MIC_ARRAY_Tetra(1.47e-2, 0.35073087);
  150. M.name = 'Calrec_MkIV';
  151. end
  152. %% Soundfield MkV
  153. function M = MIC_ARRAY_SFRMkV()
  154. % new capsules used by Sounfield Research, 9.5 dB f/b ratio
  155. M = MIC_ARRAY_Tetra(1.47e-2, 0.33251727);
  156. M.name = 'Soundfield_Research_MkV';
  157. end
  158. %% Danish Pro Audio DPA-4
  159. function M = MIC_ARRAY_DPA()
  160. % DPA-4 array
  161. M = MIC_ARRAY_Tetra(2.5e-2, 0.5);
  162. M.name = 'DPA-4';
  163. % DPA uses different A format order and alternate tetrahedral config
  164. % M.a2b = ??????? (get data from Eric, fill in here )
  165. end
  166. %% Umashankar's Velan
  167. function M = MIC_ARRAY_Velan()
  168. M.name = 'Velan';
  169. % Umashankar's Velan is four TSB-140 cardioids with pairs pointed along
  170. % axis with separation 14.3 mm. See
  171. % http://www.shapeways.com/model/143678/velan-140-internals.html
  172. % http://www.firstpr.com.au/rwi/mics/2009-09-a/2011052109473129207TSB-140A25-GP.jpg
  173. % TSB-140 is 14mm diameter x 6.3mm depth. I assume the rings are 1 mm
  174. % thick. Acoustic radius is typically 20% larger than physical.
  175. M.r = 1.2*(14+2+2*6.3)/2 * 1e-3; % meters
  176. % capsule directivities
  177. % for first-order mic: omni=[1,0]; cardioid=[0.5,0.5],fig8=[0,1];
  178. M.alpha = [ ...
  179. 0.5 0.5;
  180. 0.5 0.5;
  181. 0.5 0.5;
  182. 0.5 0.5;
  183. 0.5 0.5;
  184. 0.5 0.5 ];
  185. % capsule orientations, direciton cosines
  186. % a row for each capsule, columns are x,y,z
  187. M.o = [ ...
  188. 1 0 0;
  189. -1 0 0;
  190. 0 1 0;
  191. 0 -1 0;
  192. 0 0 1;
  193. 0 0 -1];
  194. if false
  195. % specify in spherical (az, el, r), like this
  196. M.o = [...
  197. 0, 0, 1;
  198. 180, 0, 1;
  199. 90, 0, 1;
  200. -90, 0, 1;
  201. 0, 90, 1;
  202. 0,-90, 1 ];
  203. % convert to radians
  204. M.o(:,1:2) = M.o(:,1:2)*pi/180;
  205. % convert to cartesian
  206. [M.o(:,1), M.o(:,2), M.o(:,3)] = ...
  207. sph2cart(M.o(:,1),M.o(:,2),M.o(:,3));
  208. end
  209. % capsule positions for radial array, in meters
  210. M.u = M.o .* M.r;
  211. % a2b matrix, Batke calls this the mode matrix, \Psi
  212. % a column for each capsule, a row for each array output
  213. M.a2b = [ ...
  214. 0.2357 0.2357 0.2357 0.2357 0.2357 0.2357;
  215. 1.0000 -1.0000 0.0000 0.0000 0.0000 0.0000;
  216. 0.0000 0.0000 1.0000 -1.0000 0.0000 0.0000;
  217. 0 0 0 0 1.0000 -1.0000];
  218. end
  219. %% Plotting function
  220. function createfigure(M, X1,YMatrix1)
  221. % X1: vector of x data
  222. % YMATRIX1: matrix of y data
  223. % Auto-generated by MATLAB on 12-Mar-2011 11:02:34
  224. % Create figure
  225. figure1 = figure();
  226. % Create axes
  227. axes1 = axes('Parent',figure1,'YGrid','on','XGrid','on',...
  228. 'XMinorTick','on','YMinorTick','on','FontSize',12);
  229. box(axes1,'on');
  230. hold(axes1,'all');
  231. % Create multiple lines using matrix input to plot
  232. plot1 = plot(X1,YMatrix1,'Parent',axes1);
  233. set(plot1(1),'DisplayName','W');
  234. set(plot1(2),'DisplayName','XYZ');
  235. %set(plot1(3),'DisplayName','Y');
  236. % Create title
  237. name_sanitized = strrep(M.name, '_', '\_');
  238. if false
  239. title(sprintf(...
  240. ['Diffuse Field Response of simulated %s Mic Array' ...
  241. '\n(r=%0.3fcm, \\alpha=%0.3f)'], ...
  242. name_sanitized, M.r*100, M.alpha),...
  243. 'FontWeight','bold',...
  244. 'FontSize',12);
  245. else
  246. title(sprintf(...
  247. 'Diffuse Field Response of simulated %s Mic Array', ...
  248. name_sanitized), ...
  249. 'FontWeight','bold',...
  250. 'FontSize',12);
  251. end
  252. % Create xlabel
  253. xlabel('Frequency (Hz x 10^4)', 'FontAngle', 'italic');
  254. % Create ylabel
  255. ylabel('Relative Response (dB)', 'FontAngle', 'italic');
  256. % Create legend
  257. legend1 = legend(axes1,'show');
  258. set(legend1,'Location','SouthWest');
  259. end
  260. %% EMB's Octathingy
  261. function M = MIC_ARRAY_Octathingy(radius, alpha1)
  262. % mic_array_octathingy return parameters for generic tetra microphone
  263. % RADIUS is the acoustic radius of the array, typically 10% larger
  264. % than physical radius.
  265. % ALPHA1 is the gain of the first-order parameter
  266. % 0 = omni,
  267. % 1/2 = cardioid,
  268. % 1 = fig-8
  269. % (3-sqrt(3))/2 = supercardioid (~0.63397)
  270. % 3/4 = hypercardoid
  271. %
  272. M.name = 'Octathingy';
  273. % The octathingy is an octahedron (capsules at the vertices of a cube),
  274. % with the top rotated 45deg, so it can do 2nd-order horizontal,
  275. % 1st-order vertical.
  276. if ~exist('radius', 'var')
  277. M.r = 1.47 * 1e-2; % meters
  278. else
  279. M.r = radius;
  280. end
  281. % capsule directivities
  282. % for first-order mic: omni=[1,0]; cardioid=[0.5,0.5],fig8=[0,1];
  283. if ~exist('alpha1', 'var')
  284. alpha1 = 0.5;
  285. end
  286. M.alpha = [ ...
  287. 1-alpha1 alpha1;
  288. 1-alpha1 alpha1;
  289. 1-alpha1 alpha1;
  290. 1-alpha1 alpha1;
  291. 1-alpha1 alpha1;
  292. 1-alpha1 alpha1;
  293. 1-alpha1 alpha1;
  294. 1-alpha1 alpha1 ];
  295. % capsule orientations, direciton cosines
  296. M.o = [ ...
  297. % bottom ring
  298. -1 1 -1; % back-left-down .... LBD
  299. 1 1 -1; % front-left-down
  300. +1 -1 -1; % front-right-down .. RFD
  301. -1 -1 -1; % back-right-down
  302. 0 1 1; % left-up
  303. +1 0 1; % front-up
  304. 0 -1 1; % right-up
  305. -1 0 1; % back-up
  306. ];
  307. % normalize
  308. M.o = diag(1./sqrt(diag(M.o * M.o'))) * M.o;
  309. % capsule positions for radial array, in meters
  310. M.u = M.o * M.r;
  311. % a2b matrix, Batke calls this the mode matrix, \Psi
  312. M.a2b = [ones(size(M.o,1),1), sign(M.o)]';
  313. end