1
0

sphear_fir_design.m 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. %%
  2. %% *SpHEAR Project
  3. %%
  4. %% sphear_fir_design: design a matrix of FIR filters that equalizes the microphone
  5. %%
  6. %% usage:
  7. %% [FIR] = sphear_fir_design(F, A2BF, n, Fs, Fl, W, X, Y, Z)
  8. %%
  9. %% F: array of filter section center frequencies
  10. %% A2BF: vector of A2B matrixes, one for each frequency
  11. %% n: order of the FIR filter being created
  12. %% maxboost: max high and low frequency boost in dB of the final filter
  13. %% Fs: sampling rate
  14. %% Fl: frequency limit, below it use A2B matrixes, above it use W, X, Y, Z shapes
  15. %%
  16. %% for the first run do not supply W/X/Y/Z, render the filters and then run
  17. %% sphear_fir_extract to extract the W, X, Y and Z filter shapes, finally run
  18. %% this again with the extra parameters below
  19. %%
  20. %% W: extracted shape of W filter (used above Fl)
  21. %% X: extracted shape of X filter (used above Fl)
  22. %% Y: extracted shape of Y filter (used above Fl)
  23. %% Z: extracted shape of Z filter (used above Fl)
  24. %% Z is optional, if not supplied X and Y are averaged to obtain Z
  25. %%
  26. %% Copyright 2016, Fernando Lopez-Lezcano, All Rights Reserved
  27. %% nando@ccrma.stanford.edu
  28. %%
  29. %% This program is free software: you can redistribute it and/or modify
  30. %% it under the terms of the GNU General Public License as published by
  31. %% the Free Software Foundation, either version 3 of the License, or
  32. %% (at your option) any later version.
  33. %%
  34. %% This program is distributed in the hope that it will be useful,
  35. %% but WITHOUT ANY WARRANTY; without even the implied warranty of
  36. %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  37. %% GNU General Public License for more details.
  38. %%
  39. %% You should have received a copy of the GNU General Public License
  40. %% along with this program. If not, see <http://www.gnu.org/licenses/>.
  41. function [FIR] = sphear_fir_design(F, A2BF, n, maxboost, Fs, Fl, W, X, Y, Z)
  42. fig = figure;
  43. if (exist("W") == 1 && ! exist("Z"))
  44. %% Z was not supplied, just average X and Y data
  45. Z = (X + Y) / 2;
  46. end
  47. %% all b format components
  48. for b = 1:4
  49. %% all capsules
  50. for c = 1:4
  51. %% all frequency bands
  52. for d = 1:size(A2BF(:,1,1))(1)
  53. %% create an array of points to design the filter
  54. if (F(d) <= Fl)
  55. %% below the limit frequency we use the A2B coefficients
  56. vlast = A2BF(d, c, b);
  57. dlast = d;
  58. if (exist("W") == 1)
  59. wlast = W(d);
  60. xlast = X(d);
  61. ylast = Y(d);
  62. zlast = Z(d);
  63. end
  64. else
  65. %% above the limit frequency we insert the W, X, Y and Z
  66. %% data if they exist, otherwise keep the last valid value
  67. %% from the A2B coefficients (ie: flat above the transition
  68. %% frequency
  69. if (b == 1)
  70. if (exist("W") == 1)
  71. A2BF(d, c, b) = vlast * (W(d)/wlast);
  72. else
  73. A2BF(d, c, b) = vlast;
  74. end
  75. end
  76. if (b == 2)
  77. if (exist("X") == 1)
  78. A2BF(d, c, b) = vlast * (X(d)/xlast);
  79. else
  80. A2BF(d, c, b) = vlast;
  81. end
  82. end
  83. if (b == 3)
  84. if (exist("Y") == 1)
  85. A2BF(d, c, b) = vlast * (Y(d)/ylast);
  86. else
  87. A2BF(d, c, b) = vlast;
  88. end
  89. end
  90. if (b == 4)
  91. if (exist("Z") == 1)
  92. A2BF(d, c, b) = vlast * (Z(d)/zlast);
  93. else
  94. A2BF(d, c, b) = vlast;
  95. end
  96. end
  97. end
  98. end
  99. end
  100. end
  101. %% normalize all components to the average of W levels for all
  102. %% capsules around the transition frequency
  103. nrm = mean([mean(A2BF([dlast-1, dlast, dlast+1],1,1))
  104. mean(A2BF([dlast-1, dlast, dlast+1],2,1))
  105. mean(A2BF([dlast-1, dlast, dlast+1],3,1))
  106. mean(A2BF([dlast-1, dlast, dlast+1],4,1))]);
  107. for b = 1:4
  108. for c = 1:4
  109. %% normalize coefficients
  110. for dn = 1:size(A2BF(:,1,1))(1)
  111. A2BF(dn,c,b) = A2BF(dn,c,b) ./ nrm;
  112. end
  113. %% fit a spline to the W, X, Y and Z data and extrapolate
  114. %% highest frequency if it does not exist in the measurements
  115. %%
  116. %% the 200Hz spacing of the points is arbitrary, we should
  117. %% select something that makes more sense
  118. %%
  119. fh = (F(dlast-1)/(Fs/2):200/(Fs/2):1)';
  120. %% make the end really 1.0
  121. fh(end) = 1;
  122. rh = interp1(F(dlast-1:end)/(Fs/2), A2BF(dlast-1:end,c,b)', fh, "cubic", "extrap");
  123. %% we fit the spline taking into account one additional point
  124. %% on the bottom edge so that it will fit better with the existing
  125. %% low frequency points, now we need to find the exact transition
  126. %% frequency in the interpolated array
  127. for idx = 1:size(fh(:))
  128. if (fh(idx) <= F(dlast)/(Fs/2))
  129. istart = idx;
  130. end
  131. end
  132. %% extract the low frequency A2B coefficients
  133. fl = [0 F(1:dlast-1)']' ./ (Fs/2);
  134. rl = [A2BF(1,c,b) A2BF(1:dlast-1,c,b)']';
  135. %% concatenate low and high frequency arrays
  136. fi = cat(1, fl, fh(istart:end));
  137. ri = cat(1, rl, rh(istart:end));
  138. printf("low frequency boost %f, high frequency boost %f\n", 20*log10(ri(1)), 20*log10(ri(end)));
  139. %% force a max boost at low and high frequencies
  140. if (maxboost != 0)
  141. if (abs(ri(1)) > 10^(maxboost/20))
  142. printf("limit low freq boost from %f to %f dB (%f)\n", 20*log10(ri(1)), maxboost, ri(1));
  143. ri(1) = sign(ri(1)) * 10^(maxboost/20);
  144. printf("new limit %f\n", ri(1));
  145. end
  146. if (abs(ri(end)) > 10^(maxboost/20))
  147. printf("limit high freq boost from %f dB to %f dB (%f)\n", 20*log10(ri(end)), maxboost, ri(end));
  148. ri(end) = sign(ri(end)) * 10^(maxboost/20);
  149. printf("new limit %f\n", ri(end));
  150. end
  151. end
  152. axis([300, (Fs/2)]);
  153. tics('x', [300, 500, 1000, 2000, 3000, 5000, 7000, 10000, 15000, 20000]);
  154. grid on;
  155. title(sprintf("B format FIR filters (transition frequency: %d Hz)", Fl));
  156. hold on;
  157. %% plot real measured points
  158. ph = semilogx(F, 20*log10(abs(A2BF(:,c,b)))','.k');
  159. %% plot extrapolated filter
  160. if (b == 1)
  161. %% W
  162. wh = semilogx(fi*(Fs/2), 20*log10(abs(ri)), 'b');
  163. else
  164. if (b == 4)
  165. %% Z
  166. zh = semilogx(fi*(Fs/2), 20*log10(abs(ri)), 'g');
  167. else
  168. %% XY
  169. xyh = semilogx(fi*(Fs/2), 20*log10(abs(ri)), 'r');
  170. end
  171. end
  172. %% design the filter
  173. FIR(b, c, :) = fir2(n, fi, ri);
  174. end
  175. end
  176. l = legend([wh, zh, xyh], {'W', 'XY', 'Z'});
  177. legend(l, 'location', 'southwest');
  178. if (exist("W") == 1)
  179. print(fig, '-deps','-color',["figures/w_xyz_filter_full.eps"]);
  180. else
  181. print(fig, '-deps','-color',["figures/w_xyz_filter_low.eps"]);
  182. end
  183. end