From 9cb3d59d8efcc2f42287cec5a33e0140c1440a15 Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Wed, 8 Jul 2026 14:05:32 +0300 Subject: [PATCH 1/2] audio: eq_iir: tune: add example measurements for sof_ucm2_eq_generate Add two small example endpoint frequency-response measurements so the sof_ucm2_eq_generate script added in the following commit can be exercised without external input: sof_ucm2_eq_example.txt 1024x3 (freq Hz, response L, response R) sof_ucm2_eq_example.xlsx identical numeric grid in Excel form Both files share the same [frequency, magnitude...] layout the loader expects, so they also demonstrate the multi-column averaging code path. Signed-off-by: Seppo Ingalsuo --- src/audio/eq_iir/tune/sof_ucm2_eq_example.txt | 1024 +++++++++++++++++ .../eq_iir/tune/sof_ucm2_eq_example.xlsx | Bin 0 -> 30865 bytes 2 files changed, 1024 insertions(+) create mode 100644 src/audio/eq_iir/tune/sof_ucm2_eq_example.txt create mode 100644 src/audio/eq_iir/tune/sof_ucm2_eq_example.xlsx diff --git a/src/audio/eq_iir/tune/sof_ucm2_eq_example.txt b/src/audio/eq_iir/tune/sof_ucm2_eq_example.txt new file mode 100644 index 000000000000..1b47705f08bd --- /dev/null +++ b/src/audio/eq_iir/tune/sof_ucm2_eq_example.txt @@ -0,0 +1,1024 @@ + 100.000, -32.508, -34.491 + 100.519, -32.455, -34.372 + 101.041, -32.402, -34.252 + 101.566, -32.349, -34.130 + 102.093, -32.296, -34.008 + 102.623, -32.242, -33.885 + 103.156, -32.189, -33.761 + 103.692, -32.135, -33.636 + 104.230, -32.080, -33.512 + 104.772, -32.026, -33.387 + 105.316, -31.971, -33.261 + 105.863, -31.916, -33.136 + 106.412, -31.861, -33.011 + 106.965, -31.806, -32.885 + 107.520, -31.750, -32.760 + 108.079, -31.694, -32.635 + 108.640, -31.638, -32.510 + 109.204, -31.581, -32.386 + 109.771, -31.525, -32.262 + 110.341, -31.468, -32.138 + 110.914, -31.411, -32.015 + 111.490, -31.353, -31.893 + 112.069, -31.295, -31.771 + 112.651, -31.237, -31.649 + 113.236, -31.179, -31.528 + 113.824, -31.120, -31.407 + 114.415, -31.061, -31.288 + 115.009, -31.001, -31.168 + 115.606, -30.941, -31.050 + 116.206, -30.881, -30.932 + 116.810, -30.820, -30.814 + 117.416, -30.759, -30.698 + 118.026, -30.698, -30.582 + 118.639, -30.636, -30.466 + 119.255, -30.573, -30.351 + 119.874, -30.510, -30.237 + 120.497, -30.447, -30.124 + 121.122, -30.383, -30.011 + 121.751, -30.318, -29.899 + 122.383, -30.253, -29.787 + 123.019, -30.188, -29.676 + 123.658, -30.121, -29.565 + 124.300, -30.055, -29.456 + 124.945, -29.987, -29.346 + 125.594, -29.919, -29.238 + 126.246, -29.850, -29.129 + 126.902, -29.781, -29.022 + 127.561, -29.711, -28.915 + 128.223, -29.640, -28.808 + 128.889, -29.568, -28.702 + 129.558, -29.496, -28.596 + 130.231, -29.423, -28.491 + 130.907, -29.349, -28.387 + 131.587, -29.274, -28.283 + 132.270, -29.198, -28.179 + 132.957, -29.122, -28.076 + 133.647, -29.045, -27.973 + 134.341, -28.967, -27.870 + 135.039, -28.888, -27.768 + 135.740, -28.808, -27.666 + 136.445, -28.727, -27.565 + 137.154, -28.646, -27.464 + 137.866, -28.563, -27.363 + 138.582, -28.480, -27.263 + 139.301, -28.395, -27.163 + 140.025, -28.310, -27.063 + 140.752, -28.224, -26.963 + 141.483, -28.137, -26.864 + 142.217, -28.048, -26.765 + 142.956, -27.959, -26.666 + 143.698, -27.869, -26.567 + 144.444, -27.778, -26.468 + 145.194, -27.686, -26.370 + 145.948, -27.593, -26.272 + 146.706, -27.499, -26.174 + 147.468, -27.404, -26.076 + 148.234, -27.308, -25.978 + 149.003, -27.211, -25.880 + 149.777, -27.114, -25.782 + 150.555, -27.015, -25.685 + 151.337, -26.915, -25.587 + 152.122, -26.815, -25.489 + 152.912, -26.713, -25.392 + 153.706, -26.611, -25.294 + 154.504, -26.508, -25.196 + 155.307, -26.403, -25.099 + 156.113, -26.298, -25.001 + 156.924, -26.193, -24.903 + 157.739, -26.086, -24.805 + 158.558, -25.978, -24.707 + 159.381, -25.870, -24.609 + 160.209, -25.761, -24.511 + 161.041, -25.651, -24.412 + 161.877, -25.540, -24.314 + 162.717, -25.429, -24.215 + 163.562, -25.317, -24.116 + 164.412, -25.204, -24.017 + 165.265, -25.091, -23.918 + 166.123, -24.977, -23.818 + 166.986, -24.862, -23.718 + 167.853, -24.747, -23.618 + 168.725, -24.631, -23.518 + 169.601, -24.515, -23.417 + 170.482, -24.398, -23.316 + 171.367, -24.281, -23.215 + 172.257, -24.163, -23.113 + 173.151, -24.045, -23.012 + 174.050, -23.926, -22.909 + 174.954, -23.807, -22.807 + 175.863, -23.687, -22.704 + 176.776, -23.568, -22.601 + 177.694, -23.448, -22.497 + 178.616, -23.327, -22.393 + 179.544, -23.206, -22.289 + 180.476, -23.086, -22.184 + 181.413, -22.964, -22.079 + 182.355, -22.843, -21.973 + 183.302, -22.722, -21.867 + 184.254, -22.600, -21.761 + 185.211, -22.478, -21.654 + 186.173, -22.356, -21.547 + 187.139, -22.234, -21.439 + 188.111, -22.112, -21.331 + 189.088, -21.990, -21.223 + 190.070, -21.868, -21.114 + 191.057, -21.746, -21.005 + 192.049, -21.624, -20.895 + 193.046, -21.502, -20.785 + 194.048, -21.381, -20.675 + 195.056, -21.259, -20.565 + 196.069, -21.137, -20.453 + 197.087, -21.016, -20.342 + 198.110, -20.895, -20.230 + 199.139, -20.773, -20.118 + 200.173, -20.652, -20.006 + 201.213, -20.532, -19.893 + 202.257, -20.411, -19.780 + 203.308, -20.291, -19.667 + 204.363, -20.171, -19.554 + 205.424, -20.051, -19.440 + 206.491, -19.932, -19.326 + 207.563, -19.812, -19.212 + 208.641, -19.693, -19.097 + 209.725, -19.575, -18.983 + 210.814, -19.457, -18.868 + 211.908, -19.339, -18.753 + 213.009, -19.221, -18.638 + 214.115, -19.104, -18.523 + 215.227, -18.987, -18.408 + 216.344, -18.870, -18.293 + 217.468, -18.754, -18.178 + 218.597, -18.639, -18.063 + 219.732, -18.523, -17.947 + 220.873, -18.408, -17.832 + 222.020, -18.294, -17.717 + 223.173, -18.180, -17.603 + 224.331, -18.066, -17.488 + 225.496, -17.953, -17.373 + 226.667, -17.840, -17.259 + 227.844, -17.727, -17.145 + 229.027, -17.616, -17.031 + 230.217, -17.504, -16.918 + 231.412, -17.393, -16.804 + 232.614, -17.283, -16.691 + 233.822, -17.172, -16.579 + 235.036, -17.063, -16.467 + 236.256, -16.954, -16.355 + 237.483, -16.845, -16.244 + 238.716, -16.737, -16.133 + 239.956, -16.630, -16.023 + 241.202, -16.523, -15.914 + 242.454, -16.416, -15.805 + 243.713, -16.310, -15.696 + 244.979, -16.205, -15.588 + 246.251, -16.100, -15.481 + 247.529, -15.996, -15.375 + 248.815, -15.892, -15.269 + 250.107, -15.790, -15.164 + 251.405, -15.687, -15.060 + 252.711, -15.585, -14.957 + 254.023, -15.484, -14.854 + 255.342, -15.384, -14.753 + 256.668, -15.284, -14.652 + 258.001, -15.185, -14.552 + 259.341, -15.086, -14.453 + 260.687, -14.989, -14.355 + 262.041, -14.891, -14.258 + 263.402, -14.795, -14.162 + 264.769, -14.699, -14.067 + 266.144, -14.604, -13.973 + 267.526, -14.510, -13.880 + 268.915, -14.416, -13.788 + 270.312, -14.324, -13.697 + 271.715, -14.232, -13.607 + 273.126, -14.140, -13.518 + 274.545, -14.049, -13.430 + 275.970, -13.959, -13.344 + 277.403, -13.870, -13.258 + 278.844, -13.781, -13.174 + 280.292, -13.693, -13.090 + 281.747, -13.606, -13.008 + 283.210, -13.519, -12.926 + 284.681, -13.433, -12.846 + 286.159, -13.348, -12.767 + 287.645, -13.263, -12.688 + 289.138, -13.178, -12.611 + 290.640, -13.094, -12.535 + 292.149, -13.011, -12.459 + 293.666, -12.927, -12.385 + 295.191, -12.844, -12.311 + 296.724, -12.762, -12.238 + 298.264, -12.679, -12.166 + 299.813, -12.597, -12.095 + 301.370, -12.514, -12.024 + 302.935, -12.432, -11.955 + 304.508, -12.350, -11.885 + 306.089, -12.267, -11.817 + 307.679, -12.184, -11.748 + 309.276, -12.101, -11.681 + 310.882, -12.018, -11.613 + 312.497, -11.934, -11.546 + 314.119, -11.849, -11.479 + 315.750, -11.764, -11.412 + 317.390, -11.678, -11.346 + 319.038, -11.591, -11.279 + 320.695, -11.503, -11.212 + 322.360, -11.414, -11.145 + 324.034, -11.324, -11.078 + 325.716, -11.232, -11.010 + 327.408, -11.139, -10.942 + 329.108, -11.045, -10.874 + 330.817, -10.950, -10.805 + 332.535, -10.852, -10.735 + 334.261, -10.753, -10.664 + 335.997, -10.653, -10.592 + 337.742, -10.550, -10.519 + 339.495, -10.446, -10.446 + 341.258, -10.340, -10.370 + 343.030, -10.232, -10.294 + 344.812, -10.122, -10.216 + 346.602, -10.010, -10.137 + 348.402, -9.896, -10.057 + 350.211, -9.781, -9.974 + 352.029, -9.663, -9.890 + 353.857, -9.543, -9.804 + 355.695, -9.422, -9.717 + 357.542, -9.298, -9.627 + 359.398, -9.173, -9.535 + 361.265, -9.047, -9.442 + 363.141, -8.918, -9.346 + 365.026, -8.788, -9.249 + 366.922, -8.657, -9.149 + 368.827, -8.524, -9.048 + 370.742, -8.391, -8.944 + 372.667, -8.256, -8.838 + 374.602, -8.120, -8.730 + 376.548, -7.983, -8.620 + 378.503, -7.845, -8.508 + 380.468, -7.707, -8.394 + 382.444, -7.569, -8.278 + 384.430, -7.430, -8.160 + 386.426, -7.291, -8.040 + 388.433, -7.152, -7.918 + 390.450, -7.013, -7.794 + 392.477, -6.874, -7.669 + 394.515, -6.736, -7.543 + 396.564, -6.599, -7.414 + 398.623, -6.462, -7.284 + 400.693, -6.326, -7.153 + 402.773, -6.191, -7.021 + 404.865, -6.057, -6.887 + 406.967, -5.924, -6.752 + 409.080, -5.792, -6.616 + 411.204, -5.662, -6.478 + 413.340, -5.533, -6.340 + 415.486, -5.406, -6.201 + 417.644, -5.281, -6.062 + 419.812, -5.157, -5.921 + 421.992, -5.035, -5.780 + 424.183, -4.915, -5.638 + 426.386, -4.797, -5.495 + 428.600, -4.680, -5.352 + 430.826, -4.566, -5.209 + 433.063, -4.454, -5.065 + 435.311, -4.343, -4.921 + 437.572, -4.235, -4.776 + 439.844, -4.128, -4.631 + 442.128, -4.024, -4.486 + 444.424, -3.921, -4.341 + 446.732, -3.821, -4.195 + 449.051, -3.722, -4.049 + 451.383, -3.626, -3.904 + 453.727, -3.531, -3.758 + 456.083, -3.439, -3.612 + 458.451, -3.348, -3.466 + 460.832, -3.259, -3.321 + 463.225, -3.173, -3.176 + 465.630, -3.088, -3.031 + 468.048, -3.005, -2.886 + 470.478, -2.923, -2.742 + 472.921, -2.844, -2.599 + 475.377, -2.767, -2.456 + 477.845, -2.691, -2.314 + 480.327, -2.618, -2.174 + 482.821, -2.546, -2.034 + 485.328, -2.477, -1.896 + 487.848, -2.410, -1.759 + 490.381, -2.344, -1.623 + 492.928, -2.281, -1.489 + 495.487, -2.220, -1.358 + 498.060, -2.161, -1.228 + 500.646, -2.105, -1.101 + 503.246, -2.050, -0.976 + 505.859, -1.999, -0.854 + 508.486, -1.949, -0.734 + 511.126, -1.902, -0.618 + 513.780, -1.858, -0.505 + 516.448, -1.816, -0.395 + 519.130, -1.777, -0.288 + 521.826, -1.741, -0.186 + 524.535, -1.707, -0.087 + 527.259, -1.676, 0.008 + 529.997, -1.647, 0.099 + 532.749, -1.621, 0.185 + 535.515, -1.598, 0.267 + 538.296, -1.577, 0.345 + 541.091, -1.559, 0.418 + 543.901, -1.543, 0.486 + 546.725, -1.530, 0.550 + 549.564, -1.519, 0.608 + 552.418, -1.510, 0.662 + 555.286, -1.503, 0.711 + 558.170, -1.498, 0.755 + 561.068, -1.495, 0.795 + 563.982, -1.493, 0.829 + 566.910, -1.493, 0.858 + 569.854, -1.494, 0.882 + 572.813, -1.496, 0.901 + 575.787, -1.499, 0.915 + 578.777, -1.503, 0.924 + 581.783, -1.508, 0.927 + 584.804, -1.513, 0.926 + 587.840, -1.518, 0.920 + 590.893, -1.524, 0.908 + 593.961, -1.529, 0.891 + 597.045, -1.534, 0.869 + 600.145, -1.539, 0.842 + 603.262, -1.543, 0.809 + 606.394, -1.547, 0.771 + 609.543, -1.550, 0.727 + 612.708, -1.553, 0.679 + 615.890, -1.555, 0.624 + 619.088, -1.556, 0.565 + 622.303, -1.556, 0.500 + 625.534, -1.556, 0.429 + 628.782, -1.556, 0.353 + 632.047, -1.555, 0.272 + 635.329, -1.554, 0.185 + 638.628, -1.552, 0.093 + 641.944, -1.550, -0.004 + 645.278, -1.549, -0.106 + 648.628, -1.548, -0.212 + 651.996, -1.547, -0.324 + 655.382, -1.548, -0.439 + 658.785, -1.549, -0.559 + 662.206, -1.551, -0.682 + 665.645, -1.555, -0.809 + 669.101, -1.560, -0.938 + 672.575, -1.568, -1.071 + 676.068, -1.577, -1.205 + 679.578, -1.589, -1.342 + 683.107, -1.604, -1.480 + 686.654, -1.622, -1.619 + 690.220, -1.642, -1.759 + 693.804, -1.666, -1.900 + 697.407, -1.693, -2.041 + 701.028, -1.724, -2.181 + 704.668, -1.758, -2.321 + 708.327, -1.797, -2.460 + 712.005, -1.839, -2.599 + 715.703, -1.886, -2.736 + 719.419, -1.937, -2.872 + 723.155, -1.993, -3.006 + 726.910, -2.053, -3.139 + 730.684, -2.118, -3.270 + 734.478, -2.188, -3.399 + 738.292, -2.262, -3.526 + 742.126, -2.342, -3.651 + 745.980, -2.427, -3.772 + 749.853, -2.517, -3.890 + 753.747, -2.613, -4.004 + 757.661, -2.714, -4.113 + 761.595, -2.820, -4.217 + 765.550, -2.932, -4.313 + 769.525, -3.048, -4.402 + 773.521, -3.169, -4.482 + 777.537, -3.295, -4.551 + 781.575, -3.424, -4.609 + 785.633, -3.556, -4.654 + 789.713, -3.690, -4.685 + 793.813, -3.824, -4.702 + 797.935, -3.959, -4.704 + 802.079, -4.091, -4.690 + 806.244, -4.221, -4.662 + 810.430, -4.345, -4.619 + 814.639, -4.463, -4.563 + 818.869, -4.574, -4.495 + 823.121, -4.675, -4.417 + 827.395, -4.765, -4.331 + 831.691, -4.844, -4.239 + 836.010, -4.912, -4.143 + 840.351, -4.967, -4.044 + 844.715, -5.010, -3.945 + 849.101, -5.041, -3.846 + 853.510, -5.062, -3.751 + 857.942, -5.073, -3.659 + 862.397, -5.075, -3.571 + 866.875, -5.069, -3.488 + 871.376, -5.057, -3.409 + 875.901, -5.040, -3.336 + 880.449, -5.018, -3.266 + 885.021, -4.994, -3.200 + 889.617, -4.966, -3.135 + 894.236, -4.937, -3.071 + 898.880, -4.907, -3.006 + 903.547, -4.875, -2.938 + 908.239, -4.842, -2.866 + 912.955, -4.807, -2.787 + 917.696, -4.770, -2.699 + 922.461, -4.731, -2.602 + 927.251, -4.689, -2.494 + 932.066, -4.644, -2.374 + 936.906, -4.596, -2.243 + 941.771, -4.544, -2.100 + 946.661, -4.488, -1.947 + 951.577, -4.428, -1.785 + 956.518, -4.364, -1.615 + 961.485, -4.296, -1.440 + 966.477, -4.225, -1.262 + 971.496, -4.151, -1.083 + 976.541, -4.076, -0.905 + 981.611, -3.999, -0.731 + 986.709, -3.923, -0.563 + 991.832, -3.846, -0.402 + 996.982, -3.772, -0.249 + 1002.159, -3.699, -0.106 + 1007.363, -3.630, 0.026 + 1012.594, -3.564, 0.147 + 1017.852, -3.502, 0.257 + 1023.137, -3.444, 0.355 + 1028.450, -3.391, 0.442 + 1033.791, -3.343, 0.518 + 1039.159, -3.300, 0.582 + 1044.555, -3.260, 0.636 + 1049.979, -3.224, 0.680 + 1055.431, -3.192, 0.715 + 1060.911, -3.161, 0.740 + 1066.420, -3.130, 0.758 + 1071.958, -3.100, 0.768 + 1077.524, -3.067, 0.770 + 1083.119, -3.031, 0.766 + 1088.743, -2.990, 0.757 + 1094.397, -2.943, 0.742 + 1100.080, -2.888, 0.724 + 1105.792, -2.826, 0.702 + 1111.534, -2.755, 0.679 + 1117.306, -2.675, 0.656 + 1123.107, -2.587, 0.635 + 1128.939, -2.492, 0.617 + 1134.802, -2.391, 0.605 + 1140.694, -2.285, 0.601 + 1146.617, -2.176, 0.607 + 1152.571, -2.067, 0.624 + 1158.556, -1.958, 0.656 + 1164.572, -1.853, 0.702 + 1170.619, -1.753, 0.765 + 1176.698, -1.659, 0.844 + 1182.808, -1.572, 0.939 + 1188.950, -1.493, 1.048 + 1195.124, -1.424, 1.170 + 1201.330, -1.363, 1.303 + 1207.568, -1.310, 1.445 + 1213.838, -1.265, 1.591 + 1220.141, -1.226, 1.739 + 1226.477, -1.192, 1.885 + 1232.845, -1.162, 2.028 + 1239.247, -1.133, 2.163 + 1245.682, -1.103, 2.288 + 1252.151, -1.070, 2.401 + 1258.652, -1.033, 2.500 + 1265.188, -0.989, 2.584 + 1271.758, -0.938, 2.651 + 1278.362, -0.879, 2.701 + 1285.000, -0.812, 2.734 + 1291.672, -0.738, 2.750 + 1298.379, -0.657, 2.751 + 1305.121, -0.570, 2.737 + 1311.898, -0.480, 2.709 + 1318.711, -0.386, 2.669 + 1325.558, -0.291, 2.619 + 1332.441, -0.195, 2.561 + 1339.360, -0.097, 2.495 + 1346.315, 0.001, 2.423 + 1353.306, 0.102, 2.347 + 1360.333, 0.207, 2.268 + 1367.397, 0.317, 2.186 + 1374.497, 0.436, 2.104 + 1381.634, 0.566, 2.022 + 1388.809, 0.710, 1.942 + 1396.020, 0.869, 1.864 + 1403.269, 1.046, 1.791 + 1410.556, 1.241, 1.725 + 1417.881, 1.454, 1.668 + 1425.243, 1.684, 1.621 + 1432.644, 1.927, 1.587 + 1440.083, 2.182, 1.566 + 1447.561, 2.443, 1.560 + 1455.078, 2.706, 1.568 + 1462.633, 2.966, 1.588 + 1470.228, 3.219, 1.618 + 1477.862, 3.458, 1.655 + 1485.536, 3.682, 1.696 + 1493.250, 3.884, 1.737 + 1501.004, 4.064, 1.773 + 1508.798, 4.218, 1.802 + 1516.633, 4.345, 1.820 + 1524.508, 4.445, 1.825 + 1532.425, 4.519, 1.816 + 1540.382, 4.569, 1.793 + 1548.380, 4.598, 1.755 + 1556.421, 4.611, 1.706 + 1564.503, 4.611, 1.647 + 1572.626, 4.603, 1.582 + 1580.792, 4.593, 1.515 + 1589.001, 4.585, 1.449 + 1597.252, 4.580, 1.389 + 1605.546, 4.580, 1.336 + 1613.883, 4.585, 1.292 + 1622.263, 4.593, 1.256 + 1630.687, 4.600, 1.226 + 1639.155, 4.603, 1.199 + 1647.666, 4.597, 1.169 + 1656.222, 4.578, 1.131 + 1664.822, 4.542, 1.077 + 1673.467, 4.486, 1.003 + 1682.157, 4.410, 0.901 + 1690.891, 4.312, 0.770 + 1699.672, 4.194, 0.605 + 1708.497, 4.057, 0.408 + 1717.369, 3.904, 0.183 + 1726.287, 3.737, -0.062 + 1735.251, 3.559, -0.316 + 1744.261, 3.371, -0.561 + 1753.318, 3.175, -0.774 + 1762.423, 2.973, -0.933 + 1771.574, 2.765, -1.015 + 1780.773, 2.554, -1.007 + 1790.020, 2.341, -0.906 + 1799.315, 2.130, -0.722 + 1808.658, 1.925, -0.474 + 1818.050, 1.730, -0.186 + 1827.491, 1.548, 0.120 + 1836.980, 1.383, 0.423 + 1846.519, 1.233, 0.711 + 1856.107, 1.096, 0.976 + 1865.745, 0.962, 1.212 + 1875.433, 0.822, 1.420 + 1885.172, 0.662, 1.601 + 1894.961, 0.468, 1.757 + 1904.801, 0.228, 1.889 + 1914.692, -0.067, 1.999 + 1924.634, -0.418, 2.085 + 1934.628, -0.816, 2.147 + 1944.674, -1.236, 2.180 + 1954.772, -1.636, 2.182 + 1964.922, -1.951, 2.148 + 1975.125, -2.109, 2.078 + 1985.381, -2.054, 1.972 + 1995.691, -1.772, 1.834 + 2006.053, -1.300, 1.675 + 2016.470, -0.706, 1.511 + 2026.941, -0.065, 1.362 + 2037.466, 0.565, 1.252 + 2048.046, 1.144, 1.200 + 2058.681, 1.649, 1.220 + 2069.371, 2.069, 1.310 + 2080.116, 2.402, 1.461 + 2090.917, 2.651, 1.655 + 2101.775, 2.827, 1.875 + 2112.688, 2.942, 2.110 + 2123.659, 3.013, 2.356 + 2134.686, 3.057, 2.617 + 2145.771, 3.088, 2.903 + 2156.913, 3.121, 3.224 + 2168.113, 3.163, 3.585 + 2179.371, 3.221, 3.983 + 2190.688, 3.293, 4.409 + 2202.063, 3.376, 4.845 + 2213.498, 3.467, 5.275 + 2224.992, 3.560, 5.683 + 2236.545, 3.651, 6.058 + 2248.159, 3.739, 6.392 + 2259.833, 3.823, 6.681 + 2271.567, 3.904, 6.924 + 2283.363, 3.983, 7.120 + 2295.219, 4.061, 7.269 + 2307.138, 4.140, 7.373 + 2319.118, 4.222, 7.432 + 2331.160, 4.309, 7.448 + 2343.265, 4.401, 7.420 + 2355.433, 4.501, 7.351 + 2367.663, 4.609, 7.241 + 2379.958, 4.726, 7.095 + 2392.316, 4.854, 6.919 + 2404.739, 4.994, 6.727 + 2417.225, 5.150, 6.537 + 2429.777, 5.322, 6.372 + 2442.394, 5.514, 6.255 + 2455.077, 5.725, 6.205 + 2467.825, 5.950, 6.231 + 2480.639, 6.182, 6.326 + 2493.520, 6.411, 6.475 + 2506.468, 6.624, 6.656 + 2519.484, 6.811, 6.850 + 2532.566, 6.961, 7.043 + 2545.717, 7.071, 7.227 + 2558.936, 7.140, 7.399 + 2572.224, 7.171, 7.557 + 2585.580, 7.168, 7.696 + 2599.006, 7.140, 7.810 + 2612.502, 7.093, 7.891 + 2626.068, 7.036, 7.930 + 2639.704, 6.970, 7.920 + 2653.411, 6.897, 7.856 + 2667.189, 6.809, 7.741 + 2681.039, 6.695, 7.578 + 2694.960, 6.543, 7.374 + 2708.954, 6.342, 7.137 + 2723.021, 6.083, 6.875 + 2737.160, 5.768, 6.592 + 2751.374, 5.405, 6.291 + 2765.660, 5.012, 5.976 + 2780.021, 4.611, 5.644 + 2794.457, 4.228, 5.297 + 2808.968, 3.882, 4.934 + 2823.554, 3.579, 4.561 + 2838.215, 3.316, 4.189 + 2852.953, 3.080, 3.837 + 2867.767, 2.858, 3.527 + 2882.659, 2.639, 3.279 + 2897.627, 2.422, 3.105 + 2912.674, 2.209, 3.001 + 2927.798, 2.008, 2.948 + 2943.001, 1.829, 2.925 + 2958.283, 1.683, 2.904 + 2973.644, 1.577, 2.866 + 2989.085, 1.515, 2.798 + 3004.606, 1.486, 2.694 + 3020.208, 1.469, 2.558 + 3035.891, 1.435, 2.404 + 3051.655, 1.355, 2.251 + 3067.501, 1.208, 2.118 + 3083.430, 0.990, 2.019 + 3099.441, 0.726, 1.951 + 3115.535, 0.467, 1.889 + 3131.713, 0.280, 1.797 + 3147.975, 0.218, 1.632 + 3164.321, 0.288, 1.355 + 3180.752, 0.457, 0.938 + 3197.269, 0.677, 0.372 + 3213.871, 0.918, -0.336 + 3230.559, 1.170, -1.138 + 3247.335, 1.432, -1.928 + 3264.197, 1.698, -2.527 + 3281.147, 1.947, -2.742 + 3298.184, 2.146, -2.510 + 3315.311, 2.264, -1.959 + 3332.526, 2.285, -1.292 + 3349.830, 2.216, -0.668 + 3367.225, 2.081, -0.171 + 3384.710, 1.917, 0.171 + 3402.285, 1.757, 0.372 + 3419.952, 1.612, 0.464 + 3437.710, 1.465, 0.495 + 3455.561, 1.285, 0.502 + 3473.505, 1.051, 0.513 + 3491.541, 0.764, 0.543 + 3509.672, 0.452, 0.604 + 3527.896, 0.148, 0.703 + 3546.215, -0.134, 0.833 + 3564.629, -0.419, 0.961 + 3583.139, -0.749, 1.033 + 3601.745, -1.158, 0.988 + 3620.448, -1.629, 0.786 + 3639.247, -2.095, 0.420 + 3658.144, -2.470, -0.075 + 3677.140, -2.699, -0.628 + 3696.234, -2.803, -1.164 + 3715.427, -2.843, -1.626 + 3734.720, -2.847, -1.983 + 3754.113, -2.776, -2.195 + 3773.607, -2.564, -2.204 + 3793.202, -2.200, -1.968 + 3812.898, -1.779, -1.529 + 3832.697, -1.441, -1.010 + 3852.599, -1.297, -0.548 + 3872.604, -1.386, -0.256 + 3892.713, -1.676, -0.200 + 3912.927, -2.088, -0.391 + 3933.245, -2.529, -0.764 + 3953.669, -2.942, -1.180 + 3974.199, -3.338, -1.481 + 3994.835, -3.787, -1.589 + 4015.579, -4.352, -1.567 + 4036.430, -5.023, -1.565 + 4057.390, -5.679, -1.724 + 4078.459, -6.101, -2.132 + 4099.637, -6.069, -2.834 + 4120.925, -5.561, -3.859 + 4142.323, -4.789, -5.264 + 4163.833, -4.018, -7.179 + 4185.454, -3.441, -9.843 + 4207.187, -3.175, -13.476 + 4229.034, -3.291, -16.662 + 4250.993, -3.819, -15.263 + 4273.067, -4.728, -12.543 + 4295.256, -5.939, -10.774 + 4317.559, -7.337, -9.828 + 4339.979, -8.753, -9.406 + 4362.515, -9.868, -9.261 + 4385.168, -10.286, -9.180 + 4407.938, -10.048, -8.976 + 4430.827, -9.665, -8.512 + 4453.835, -9.483, -7.790 + 4476.962, -9.421, -6.959 + 4500.209, -9.119, -6.188 + 4523.577, -8.363, -5.571 + 4547.066, -7.338, -5.118 + 4570.678, -6.360, -4.781 + 4594.411, -5.614, -4.477 + 4618.268, -5.155, -4.149 + 4642.249, -4.983, -3.808 + 4666.355, -5.087, -3.525 + 4690.586, -5.407, -3.363 + 4714.942, -5.739, -3.307 + 4739.425, -5.714, -3.227 + 4764.035, -5.084, -2.947 + 4788.773, -4.053, -2.415 + 4813.639, -3.021, -1.776 + 4838.635, -2.223, -1.244 + 4863.760, -1.716, -0.947 + 4889.016, -1.472, -0.887 + 4914.403, -1.419, -0.968 + 4939.921, -1.466, -1.066 + 4965.572, -1.541, -1.123 + 4991.357, -1.614, -1.183 + 5017.275, -1.708, -1.327 + 5043.328, -1.870, -1.594 + 5069.516, -2.127, -1.955 + 5095.840, -2.441, -2.362 + 5122.301, -2.678, -2.771 + 5148.899, -2.625, -3.098 + 5175.636, -2.168, -3.156 + 5202.511, -1.448, -2.786 + 5229.525, -0.746, -2.056 + 5256.680, -0.276, -1.223 + 5283.976, -0.114, -0.509 + 5311.414, -0.222, -0.004 + 5338.994, -0.511, 0.313 + 5366.718, -0.897, 0.503 + 5394.585, -1.306, 0.622 + 5422.597, -1.659, 0.675 + 5450.755, -1.905, 0.629 + 5479.059, -2.070, 0.477 + 5507.509, -2.242, 0.247 + 5536.108, -2.518, -0.078 + 5564.855, -2.954, -0.599 + 5593.751, -3.515, -1.372 + 5622.797, -4.057, -2.256 + 5651.994, -4.428, -2.984 + 5681.343, -4.666, -3.456 + 5710.844, -4.998, -3.835 + 5740.499, -5.593, -4.217 + 5770.307, -6.404, -4.443 + 5800.270, -7.202, -4.380 + 5830.389, -7.699, -4.204 + 5860.664, -7.679, -4.240 + 5891.096, -7.167, -4.652 + 5921.686, -6.465, -5.360 + 5952.435, -6.012, -6.068 + 5983.344, -6.175, -6.427 + 6014.413, -7.093, -6.494 + 6045.644, -8.490, -6.699 + 6077.037, -9.407, -7.254 + 6108.593, -9.036, -7.923 + 6140.312, -8.078, -8.234 + 6172.197, -7.434, -8.040 + 6204.247, -7.359, -7.681 + 6236.463, -7.728, -7.472 + 6268.847, -8.259, -7.460 + 6301.399, -8.740, -7.533 + 6334.119, -9.255, -7.542 + 6367.010, -10.077, -7.421 + 6400.072, -11.221, -7.290 + 6433.305, -12.171, -7.449 + 6466.711, -12.438, -8.167 + 6500.290, -12.441, -9.431 + 6534.043, -12.716, -10.924 + 6567.972, -13.375, -12.318 + 6602.077, -14.522, -13.594 + 6636.360, -16.291, -14.726 + 6670.820, -18.101, -15.238 + 6705.459, -18.039, -15.202 + 6740.278, -16.402, -15.660 + 6775.278, -15.051, -17.365 + 6810.459, -14.271, -20.048 + 6845.823, -13.987, -21.818 + 6881.371, -14.444, -21.562 + 6917.104, -16.046, -20.989 + 6953.022, -18.976, -20.814 + 6989.126, -21.513, -20.848 + 7025.418, -19.935, -20.559 + 7061.898, -17.556, -19.332 + 7098.568, -16.478, -17.667 + 7135.428, -16.342, -16.496 + 7172.480, -16.694, -16.255 + 7209.724, -17.631, -16.975 + 7247.162, -19.119, -18.228 + 7284.793, -20.456, -19.398 + 7322.621, -20.687, -20.406 + 7360.644, -19.696, -21.304 + 7398.865, -18.155, -20.938 + 7437.285, -16.363, -18.936 + 7475.904, -15.019, -16.690 + 7514.724, -14.858, -15.023 + 7553.745, -15.810, -14.036 + 7592.969, -17.228, -13.553 + 7632.396, -17.894, -13.531 + 7672.028, -17.607, -13.920 + 7711.866, -17.483, -14.575 + 7751.911, -17.746, -15.310 + 7792.164, -17.784, -15.947 + 7832.626, -17.193, -16.403 + 7873.298, -16.241, -16.281 + 7914.181, -15.411, -15.424 + 7955.276, -15.321, -14.933 + 7996.585, -16.266, -15.499 + 8038.109, -17.631, -16.236 + 8079.847, -18.283, -16.140 + 8121.803, -18.228, -15.764 + 8163.977, -18.534, -15.990 + 8206.369, -19.440, -16.806 + 8248.982, -20.279, -17.370 + 8291.816, -21.029, -17.339 + 8334.872, -21.487, -16.961 + 8378.152, -20.743, -15.922 + 8421.657, -20.368, -15.269 + 8465.387, -22.097, -16.216 + 8509.345, -24.351, -17.655 + 8553.531, -25.517, -18.692 + 8597.946, -27.087, -20.607 + 8642.592, -27.679, -22.159 + 8687.470, -27.150, -21.397 + 8732.581, -28.437, -20.818 + 8777.926, -31.018, -21.232 + 8823.506, -33.644, -22.472 + 8869.323, -34.609, -24.474 + 8915.379, -33.982, -25.161 + 8961.673, -33.633, -25.372 + 9008.208, -33.089, -26.322 + 9054.984, -35.767, -28.407 + 9102.003, -41.735, -32.372 + 9149.266, -34.312, -33.912 + 9196.775, -32.116, -31.573 + 9244.531, -32.322, -29.695 + 9292.534, -35.271, -27.913 + 9340.787, -40.656, -27.612 + 9389.290, -43.153, -29.349 + 9438.045, -39.375, -30.869 + 9487.053, -32.993, -29.169 + 9536.316, -28.298, -30.903 + 9585.835, -25.244, -35.381 + 9635.610, -25.701, -35.362 + 9685.645, -26.142, -32.926 + 9735.939, -25.770, -30.588 + 9786.494, -27.603, -29.455 + 9837.311, -28.201, -30.668 + 9888.393, -26.653, -32.910 + 9939.740, -25.131, -34.270 + 9991.353, -24.468, -35.300 + 10043.235, -23.680, -30.777 + 10095.385, -24.079, -28.870 + 10147.807, -24.705, -27.825 + 10200.501, -21.853, -24.193 + 10253.468, -19.870, -20.828 + 10306.711, -20.054, -19.489 + 10360.230, -18.561, -19.124 + 10414.026, -16.977, -19.539 + 10468.103, -18.750, -22.053 + 10522.460, -19.384, -22.842 + 10577.099, -16.125, -20.135 + 10632.022, -14.385, -19.305 + 10687.230, -13.973, -19.471 + 10742.725, -13.647, -18.969 + 10798.508, -14.294, -19.198 + 10854.581, -14.520, -19.317 + 10910.944, -13.225, -18.710 + 10967.601, -12.438, -18.775 + 11024.552, -12.464, -19.154 + 11081.798, -12.716, -18.998 + 11139.342, -13.167, -18.335 + 11197.184, -13.560, -17.458 + 11255.327, -11.920, -15.783 + 11313.772, -10.510, -14.429 + 11372.520, -10.493, -13.845 + 11431.573, -10.929, -13.800 + 11490.933, -10.800, -13.789 + 11550.601, -11.014, -14.439 + 11610.579, -11.331, -14.387 + 11670.869, -11.260, -14.252 + 11731.471, -10.606, -13.932 + 11792.389, -10.766, -13.623 + 11853.622, -12.072, -13.561 + 11915.174, -13.699, -14.575 + 11977.045, -13.911, -14.760 + 12039.237, -13.612, -13.833 + 12101.752, -14.254, -15.248 + 12164.592, -14.255, -16.082 + 12227.759, -15.246, -15.230 + 12291.253, -17.082, -15.578 + 12355.077, -17.119, -16.278 + 12419.232, -18.754, -17.374 + 12483.721, -20.074, -18.117 + 12548.544, -19.721, -17.465 + 12613.704, -22.686, -17.832 + 12679.202, -21.220, -19.830 + 12745.041, -19.104, -21.238 + 12811.221, -19.327, -23.423 + 12877.745, -18.651, -29.805 + 12944.614, -17.624, -25.798 + 13011.831, -18.196, -21.551 + 13079.397, -18.380, -19.626 + 13147.313, -17.632, -19.430 + 13215.582, -17.013, -20.859 + 13284.206, -15.295, -20.423 + 13353.186, -14.005, -18.827 + 13422.524, -13.332, -18.428 + 13492.222, -14.533, -18.642 + 13562.282, -14.689, -17.755 + 13632.706, -13.633, -17.931 + 13703.496, -13.694, -17.529 + 13774.653, -14.440, -17.448 + 13846.180, -15.071, -18.632 + 13918.078, -15.652, -19.169 + 13990.349, -15.433, -18.595 + 14062.996, -15.518, -17.754 + 14136.020, -15.975, -16.881 + 14209.423, -15.197, -15.931 + 14283.207, -15.387, -16.473 + 14357.375, -16.436, -17.261 + 14431.927, -17.499, -17.709 + 14506.867, -17.735, -17.362 + 14582.196, -18.070, -17.592 + 14657.916, -17.755, -17.638 + 14734.029, -18.716, -17.712 + 14810.537, -18.667, -18.692 + 14887.443, -18.899, -21.009 + 14964.748, -19.456, -21.171 + 15042.454, -20.952, -21.413 + 15120.564, -21.670, -21.334 + 15199.080, -21.980, -22.066 + 15278.003, -25.323, -23.947 + 15357.336, -26.823, -24.928 + 15437.081, -25.991, -27.074 + 15517.240, -27.629, -30.389 + 15597.815, -30.773, -32.039 + 15678.809, -35.757, -31.495 + 15760.223, -29.072, -31.792 + 15842.060, -28.077, -35.229 + 15924.322, -27.585, -42.646 + 16007.012, -25.155, -34.823 + 16090.130, -25.171, -32.693 + 16173.680, -25.795, -32.305 + 16257.664, -25.643, -31.496 + 16342.084, -25.355, -32.089 + 16426.943, -25.736, -31.500 + 16512.242, -26.246, -30.598 + 16597.984, -25.192, -29.239 + 16684.171, -27.304, -30.238 + 16770.806, -28.107, -31.071 + 16857.890, -27.198, -30.781 + 16945.427, -27.967, -33.128 + 17033.418, -29.094, -33.147 + 17121.867, -30.431, -33.102 + 17210.774, -32.619, -34.209 + 17300.143, -34.268, -35.872 + 17389.977, -34.497, -36.592 + 17480.276, -31.588, -34.715 + 17571.045, -37.675, -40.715 + 17662.285, -37.569, -39.255 + 17753.999, -42.735, -42.000 + 17846.189, -49.213, -49.727 + 17938.857, -47.611, -50.239 + 18032.007, -45.206, -42.393 + 18125.641, -51.820, -50.161 + 18219.761, -55.094, -53.106 + 18314.369, -52.647, -50.265 + 18409.469, -53.595, -49.285 + 18505.062, -49.637, -61.368 + 18601.152, -52.866, -55.880 + 18697.741, -44.759, -50.507 + 18794.832, -43.593, -55.002 + 18892.426, -42.729, -53.713 + 18990.528, -42.671, -50.713 + 19089.139, -40.584, -49.937 + 19188.261, -34.787, -43.509 + 19287.899, -41.757, -48.445 + 19388.054, -32.058, -42.623 + 19488.729, -31.664, -39.266 + 19589.927, -31.893, -36.969 + 19691.650, -30.779, -35.801 + 19793.901, -31.022, -36.402 + 19896.684, -32.701, -36.436 + 20000.000, -36.437, -39.544 diff --git a/src/audio/eq_iir/tune/sof_ucm2_eq_example.xlsx b/src/audio/eq_iir/tune/sof_ucm2_eq_example.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6e8ece702ff7d639f9f258887269c42c68944806 GIT binary patch literal 30865 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 Date: Wed, 8 Jul 2026 14:05:32 +0300 Subject: [PATCH 2/2] audio: eq_iir: tune: add sof_ucm2_eq_generate Add a tuning script that turns a measured endpoint frequency response into a pair of IIR + FIR blobs and a UCM2 include file laid out the way alsa-ucm-conf expects. The IIR is a four-biquad parametric (HP2 + low-Q mid PN2 + LS2 + one fine PN2 corrector) fit stage-by-stage with fminsearch, and the residual mid-band delta is picked up by a short minimum-phase FIR. The script takes the DMI sys_vendor and product_name from /sys/devices/virtual/dmi/id and an endpoint name, and produces: ucm2_blobs_sof/ipc4/eq_iir/___iir.{txt,bin} ucm2_blobs_sof/ipc4/eq_fir/___fir.{txt,bin} ucm2_blobs_sof/product_configs//.conf The blob file names are lowercased and non-alphanumeric runs are folded to underscores; the vendor and product directory names are kept verbatim to match the existing alsa-ucm-conf tree. The .conf file follows the alsa-ucm-conf sof/product_configs style with a biquad summary in comments and the two Define.PostMixerPlayback{Iir,Fir}Blob keys pointing at the installed /usr/share/alsa/ucm2/blobs/sof/... paths. Measurement input is a numeric grid with column 1 = frequency in Hz and one or more magnitude columns in dB that are averaged. Plain text and Excel/ODS workbooks are both accepted, dispatched by file extension. The sof_ucm2_eq_example.{txt,xlsx} files added in the previous commit let the script be run without external data: sof_ucm2_eq_generate('example', 'example', 'speaker', \ 'sof_ucm2_eq_example.txt'); sof_ucm2_eq_generate('example', 'example', 'speaker', \ 'sof_ucm2_eq_example.xlsx'); Signed-off-by: Seppo Ingalsuo --- src/audio/eq_iir/tune/sof_ucm2_eq_generate.m | 437 +++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 src/audio/eq_iir/tune/sof_ucm2_eq_generate.m diff --git a/src/audio/eq_iir/tune/sof_ucm2_eq_generate.m b/src/audio/eq_iir/tune/sof_ucm2_eq_generate.m new file mode 100644 index 000000000000..5fe9f83481ea --- /dev/null +++ b/src/audio/eq_iir/tune/sof_ucm2_eq_generate.m @@ -0,0 +1,437 @@ +function sof_ucm2_eq_generate(sys_vendor, product_name, endpoint, meas_file) + +% SOF_UCM2_EQ_GENERATE Fit IIR + FIR endpoint EQ from a measurement. +% +% sof_ucm2_eq_generate(SYS_VENDOR, PRODUCT_NAME, ENDPOINT, MEAS_FILE) +% +% SYS_VENDOR DMI /sys/devices/virtual/dmi/id/sys_vendor value, e.g. +% 'Acme Ltd.'. Kept verbatim (with spaces and case) for the +% product_configs directory name. +% PRODUCT_NAME DMI product_name value, e.g. 'Model 100'. Kept +% verbatim for the .conf file name. +% ENDPOINT Endpoint being tuned, e.g. 'speaker' or 'headphone'. Used +% lower case in blob file names and capitalized in the +% Define.PostMixerPlayback... UCM keys. +% MEAS_FILE Path to a comma separated numbers text file whose first +% column is the measurement frequency in Hz and whose +% remaining columns are one or more measured magnitude +% traces in dB. Multiple traces are averaged. +% Such a file can be produced with sof_mls_freq_resp.m. +% Excel workbooks (.xls, .xlsx, .xlsm) and OpenDocument +% spreadsheets (.ods) are also accepted; the format is +% detected from the file extension and imported via +% xlsread from the Octave 'io' package. The workbook +% is expected to contain the same [freq, resp...] numeric +% layout with no header row, matching +% sof_ucm2_eq_example.xlsx. +% +% The IIR and FIR blobs are written to +% ./ucm2_blobs_sof/ipc4/eq_iir/___iir.{txt,bin} +% ./ucm2_blobs_sof/ipc4/eq_fir/___fir.{txt,bin} +% +% and a UCM include file that will be picked up automatically by UCM is +% written to +% ./ucm2_blobs_sof/product_configs//.conf +% +% Run this to see a design example and produced configuration for ALSA UCMv2 +% sof_ucm2_eq_generate('example', 'example', 'speaker', 'sof_ucm2_eq_example.txt'); +% or +% sof_ucm2_eq_generate('example', 'example', 'speaker', 'sof_ucm2_eq_example.xlsx'); + +% SPDX-License-Identifier: BSD-3-Clause +% +% Copyright (c) 2026, Intel Corporation. + +if nargin < 4 + help sof_ucm2_eq_generate + printf('\n'); + error('Usage: sof_ucm2_eq_generate(sys_vendor, product_name, endpoint, meas_file)'); +end + +%% Validate DMI and endpoint inputs before they are used to build any +%% path or UCM key. sys_vendor and product_name are kept verbatim as +%% directory / file name components under product_configs/, so they must +%% not contain path separators or traversal segments. endpoint is +%% capitalized and dropped into Define.PostMixerPlayback... +%% keys, so it has to be a plain letter identifier. +validate_dmi_field('sys_vendor', sys_vendor); +validate_dmi_field('product_name', product_name); +validate_endpoint(endpoint); + +%% Load the signal package up front so the script fails cleanly on a +%% system missing it, before any output directories are created. The io +%% package is only pulled in when the measurement file is a spreadsheet +%% (see load_measurement below), so text-only users do not need it. +pkg load signal; + +%% Derive blob base name and output paths from the DMI info + endpoint. +endpoint_lc = lower(endpoint); +base = sprintf('%s_%s_%s', endpoint_lc, sanitize_name(sys_vendor), ... + sanitize_name(product_name)); +out_root = 'ucm2_blobs_sof'; +cpath4 = fullfile(out_root, 'ipc4'); +iir_txt = fullfile('eq_iir', [base '_iir.txt']); +iir_bin = fullfile('eq_iir', [base '_iir.bin']); +fir_txt = fullfile('eq_fir', [base '_fir.txt']); +fir_bin = fullfile('eq_fir', [base '_fir.bin']); +conf_dir = fullfile(out_root, 'product_configs', sys_vendor); +conf_file = fullfile(conf_dir, [product_name '.conf']); +ensure_dir(fullfile(cpath4, 'eq_iir')); +ensure_dir(fullfile(cpath4, 'eq_fir')); +ensure_dir(conf_dir); + +sof_eq_paths(true); + +%% Base equalizer setup +eq = sof_eq_defaults(); +eq.fs = 48e3; +eq.logsmooth_plot = 1.0; +eq.logsmooth_eq = 1.0; +eq.enable_iir = 1; +eq.enable_fir = 1; +eq.iir_norm_type = 'loudness'; +eq.iir_norm_offs_db = -1; +eq.fir_norm_type = 'loudness'; +eq.fir_norm_offs_db = -1; +eq.p_fmin = 20; +eq.p_fmax = 20e3; + +%% Parametric target: keep the corrected response band-limited so the fit +%% does not chase noise below 100 Hz (below the speaker's useful range) or +%% brighten the top octave. HP2 100 Hz rolls off sub-bass in the target, +%% LP2 15 kHz tames the highs. Both the IIR stage fit (stage_rms) and the +%% FIR design in sof_eq_compute honor eq.t_db that this generates. +eq.parametric_target_response = [ ... + eq.PEQ_HP2 80 0 0; ... + eq.PEQ_LP2 10000 0 0; ... +]; + +%% Load measurement, fit IIR + FIR, compute the final response and export +eq = load_measurement(eq, meas_file); +eq = design_iir_stages(eq); +eq = configure_fir(eq); +eq = sof_eq_compute(eq); +sof_eq_plot(eq, 1); +export_blobs(eq, cpath4, iir_txt, iir_bin, fir_txt, fir_bin); +write_product_conf(conf_file, sys_vendor, product_name, endpoint, base, eq); + +sof_eq_paths(false); + +printf('\n'); +printf('To install these blobs into alsa-ucm-conf, copy the contents of\n'); +printf('%s/ recursively into alsa-ucm-conf/ucm2/blobs/sof:\n', out_root); +printf(' cp -r %s/* /ucm2/blobs/sof/\n', out_root); + +end + +%% ----------------------------------------------------------------------- +%% Measurement loading +%% ----------------------------------------------------------------------- +function eq = load_measurement(eq, meas_file) +if ~exist(meas_file, 'file') + error('Measurement file not found: %s', meas_file); +end +[~, ~, ext] = fileparts(meas_file); +switch lower(ext) +case {'.xls', '.xlsx', '.xlsm', '.ods'} + pkg load io; + meas = xlsread(meas_file); +otherwise + %% dlmread with no explicit delimiter auto-detects whitespace or + %% comma separation, which covers both the sof_mls_freq_resp.m + %% output and legacy whitespace-delimited measurement files. + meas = dlmread(meas_file); +end +if size(meas, 2) < 2 + error('%s must have at least a frequency column and one response column', ... + meas_file); +end +eq.raw_f = meas(:,1); +if size(meas, 2) == 2 + eq.raw_m_db = meas(:,2); +else + %% Average of the response columns + eq.raw_m_db = mean(meas(:, 2:end), 2); + fprintf('Averaged %d response columns from %s\n', size(meas, 2) - 1, meas_file); +end +end + +%% ----------------------------------------------------------------------- +%% Multi-stage IIR fit +%% ----------------------------------------------------------------------- +function eq = design_iir_stages(eq) + +%% Cap on the combined response shaping between the mid PN2 (stage 1) and +% the bass LS2 (stage 2). If stage 1 pulls the mids down by A dB, the +% LS2 upper bound is reduced so that bass_boost + |mid_atten| does not +% exceed this limit. This keeps the bass-to-mid tilt from becoming +% excessive (e.g. 26 dB total is already an aggressive shape). +max_bass_plus_mid_atten_db = 26; + +%% Filter budget (4 biquads max): +% 1) HP2 at 80 Hz to protect the speaker +% 2) PN2 mid shaper -> fc [2000, 4000], gain [-12, +6], Q [0.5, 1.0] +% 3) LS2 bass boost -> fc [120, 1000], gain [0, +12] +% 4) PN2 fine correction (low band) -> fc [200, 4000], gain [-6, +12], Q [0.5, 1.0] +% Fitting order matters: the mid PN2 is fit first so the subsequent low +% shelf can settle on top of an already-flattened midrange instead of +% chasing a mid bump with bass gain. The remaining high-band correction +% is left to the mid-band FIR that runs after the IIR, which keeps the +% IIR order low and avoids the audible ringing seen with a larger IIR +% budget. +hp_fc = 80; + +opts = optimset('Display', 'notify', 'MaxIter', 300, 'MaxFunEvals', 2000, ... + 'TolX', 1e-3, 'TolFun', 1e-3); + +peq_fixed = [eq.PEQ_HP2, hp_fc, 0, 0]; + +%% Stage 1: fit a mid PN2 peak/notch first, on top of the HP2. Doing this +% before the low shelf keeps the shelf from over-compensating for a +% broad midrange bump. The gain upper bound is kept low (+6 dB) so this +% stage stays a mid *attenuator* rather than a boost. +% Params: [fc, gain, Q]. fc [2000, 4000] Hz, gain [-12, +6] dB, Q [0.5, 1.0]. +fmin_fit = 400; +fmax_fit = 7000; +p1_0 = [2500, -6, 0.4]; +p1_bounds = [2000, 4000; -12, +6; 0.5, 1.0]; +p1 = fminsearch(@(p) stage_rms(peq_fixed, pn_row(p, p1_bounds, eq), eq, fmin_fit, fmax_fit), ... + p1_0, opts); +peq_fixed = [peq_fixed; pn_row(p1, p1_bounds, eq)]; +fprintf('Stage 1 (mid PN2): fc=%.1f Hz g=%.2f dB Q=%.2f\n', ... + clamp(p1(1), p1_bounds(1,1), p1_bounds(1,2)), ... + clamp(p1(2), p1_bounds(2,1), p1_bounds(2,2)), ... + clamp(p1(3), p1_bounds(3,1), p1_bounds(3,2))); + +%% Stage 2: fit the low shelf on top of the flattened midrange. +% Params: [ls_fc, ls_g]. fc [120, 1000] Hz, gain [0, +12] dB. +% The upper bass gain bound is shrunk when stage 1 attenuated the mids, +% so the total bass-to-mid shaping stays within max_bass_plus_mid_atten_db. +fmin_fit = 200; +fmax_fit = 2000; +ls0 = [200, 8]; +ls_bounds = [120, 1000; 0, 12]; +mid_atten_db = min(0, clamp(p1(2), p1_bounds(2,1), p1_bounds(2,2))); +ls_bounds(2,2) = min(ls_bounds(2,2), max(0, max_bass_plus_mid_atten_db - abs(mid_atten_db))); +if ls_bounds(2,2) < ls0(2) + ls0(2) = ls_bounds(2,2); +end +fprintf('Stage 2 bass gain upper bound = %.2f dB (mid atten %.2f dB, cap %.1f dB)\n', ... + ls_bounds(2,2), mid_atten_db, max_bass_plus_mid_atten_db); +ls = fminsearch(@(p) stage_rms(peq_fixed, shelf_row(eq.PEQ_LS2, p, ls_bounds), ... + eq, fmin_fit, fmax_fit), ls0, opts); +peq_fixed = [peq_fixed; shelf_row(eq.PEQ_LS2, ls, ls_bounds)]; +fprintf('Stage 2 (LS2): fc=%.1f Hz g=%.2f dB\n', clamp(ls(1), ls_bounds(1,1), ls_bounds(1,2)), ... + clamp(ls(2), ls_bounds(2,1), ls_bounds(2,2))); + +%% Widen the fit band for the fine correction stage: below 400 Hz the LS2 +% already dominates and above the mid we still want to shape the response +% out to 7 kHz. +fmin_fit = 400; +fmax_fit = 7000; + +%% Stage 3: single fine correction across the low/mid band. The higher +% frequencies are left to the mid-band FIR, so this biquad can focus on +% whatever residual the shelf + mid PN2 left behind. +% Params: [fc, gain, Q]. fc [200, 4000] Hz, gain [-6, +12] dB, Q [0.5, 1.0]. +p2_0 = [800, -3, 1.0]; +p2_bounds = [200, 4000; -6, 12; 0.5, 1.0]; +p2 = fminsearch(@(p) stage_rms(peq_fixed, pn_row(p, p2_bounds, eq), eq, fmin_fit, fmax_fit), ... + p2_0, opts); +peq_fixed = [peq_fixed; pn_row(p2, p2_bounds, eq)]; +fprintf('Stage 3 (fine PN2): fc=%.1f Hz g=%.2f dB Q=%.2f\n', ... + clamp(p2(1), p2_bounds(1,1), p2_bounds(1,2)), ... + clamp(p2(2), p2_bounds(2,1), p2_bounds(2,2)), ... + clamp(p2(3), p2_bounds(3,1), p2_bounds(3,2))); + +eq.peq = peq_fixed; +end + +%% ----------------------------------------------------------------------- +%% FIR configuration for mid-band residual correction +%% ----------------------------------------------------------------------- +function eq = configure_fir(eq) +%% The IIR takes care of the coarse bass shelf plus two low/mid PN2 +% shapers. Whatever residual vs. the target is left after the IIR +% (fir_compensate_iir = 1 in the defaults) is picked up here by a short +% minimum-phase FIR limited to [fmin_fir, fmax_fir], so it does not +% spend taps on the LF/HF regions the IIR already handles or where the +% measurement is unreliable. +eq.fir_length = 63; +eq.fir_beta = 10; +eq.fir_minph = 1; +eq.fir_autoband = 0; +eq.fmin_fir = 400; +eq.fmax_fir = 7000; +fprintf('FIR: length=%d taps, mid band [%d, %d] Hz\n', ... + eq.fir_length, eq.fmin_fir, eq.fmax_fir); +end + +%% ----------------------------------------------------------------------- +%% IIR + FIR blob packing and export +%% ----------------------------------------------------------------------- +function export_blobs(eq, cpath, iir_txt, iir_bin, fir_txt, fir_bin) +%% Two-channel blob with a single shared response. Both channels are +%% assigned to response 0, which suits identical L/R drivers on a +%% single endpoint. For endpoints with distinct per-channel tuning, +%% pass num_responses > 1 and adjust assign_response accordingly. +channels_in_config = 2; +num_responses = 1; +assign_response = [0 0]; + +%% IIR blob +bq_iir = sof_eq_iir_blob_quant(eq.p_z, eq.p_p, eq.p_k); +bm_iir = sof_eq_iir_blob_merge(channels_in_config, num_responses, ... + assign_response, bq_iir); +bp_iir = sof_eq_iir_blob_pack(bm_iir, 4); % IPC4 +sof_alsactl_write(fullfile(cpath, iir_txt), bp_iir); +sof_ucm_blob_write(fullfile(cpath, iir_bin), bp_iir); + +%% FIR blob +bq_fir = sof_eq_fir_blob_quant(eq.b_fir); +bm_fir = sof_eq_fir_blob_merge(channels_in_config, num_responses, ... + assign_response, bq_fir); +bp_fir = sof_eq_fir_blob_pack(bm_fir, 4); % IPC4 +sof_alsactl_write(fullfile(cpath, fir_txt), bp_fir); +sof_ucm_blob_write(fullfile(cpath, fir_bin), bp_fir); +end + +%% ----------------------------------------------------------------------- +%% Small helpers used by the IIR stages +%% ----------------------------------------------------------------------- +function peq = shelf_row(type, p, b) +peq = [type, clamp(p(1), b(1,1), b(1,2)), clamp(p(2), b(2,1), b(2,2)), 0]; +end + +function peq = pn_row(p, b, eq) +peq = [eq.PEQ_PN2, clamp(p(1), b(1,1), b(1,2)), ... + clamp(p(2), b(2,1), b(2,2)), ... + clamp(p(3), b(3,1), b(3,2))]; +end + +function e = stage_rms(peq_fixed, new_row, eq, fmin_fit, fmax_fit) +eq.peq = [peq_fixed; new_row]; +try + eq2 = sof_eq_compute(eq); +catch + e = 1e6; + return; +end +idx = eq2.f >= fmin_fit & eq2.f <= fmax_fit; +%% eq.err_db_s is the eq-smoothed (logsmooth_eq) target-minus-measurement +%% error the FIR will pick up. What remains for the FIR after the IIR is +%% err_db_s - iir_eq_db, so minimizing its shape here fits the IIR against +%% the same signal the FIR design consumes and applies logsmooth_eq (not +%% the plot-only logsmooth_plot). +residual = eq2.err_db_s(idx) - eq2.iir_eq_db(idx); +residual = residual - mean(residual); +e = sqrt(mean(residual .^ 2)); +end + +function y = clamp(x, lo, hi) +y = min(hi, max(lo, x)); +end + +%% ----------------------------------------------------------------------- +%% UCM product .conf generation and misc string / filesystem helpers +%% ----------------------------------------------------------------------- +function write_product_conf(conf_file, sys_vendor, product_name, endpoint, base, eq) +ep_cap = capitalize(endpoint); +iir_key = sprintf('Define.PostMixer%sPlaybackIirBlob', ep_cap); +fir_key = sprintf('Define.PostMixer%sPlaybackFirBlob', ep_cap); +iir_path = sprintf('/usr/share/alsa/ucm2/blobs/sof/ipc4/eq_iir/%s_iir.bin', base); +fir_path = sprintf('/usr/share/alsa/ucm2/blobs/sof/ipc4/eq_fir/%s_fir.bin', base); + +fid = fopen(conf_file, 'w'); +if fid < 0 + error('Could not open %s for writing', conf_file); +end +fprintf(fid, '# Add bespoke %s equalizer for %s %s\n', endpoint, sys_vendor, product_name); +fprintf(fid, '#\n'); +fprintf(fid, '# IIR is defined as parametric equalizer and FIR carries the mid-band residual\n'); +fprintf(fid, '# correction, see:\n'); +fprintf(fid, '# https://github.com/thesofproject/sof/tree/main/src/audio/eq_iir/tune\n'); +fprintf(fid, '#\n'); +for i = 1:size(eq.peq, 1) + fprintf(fid, '#\t%-10s %6.1f %+5.1f %4.2f\n', ... + peq_type_name(eq, eq.peq(i, 1)), eq.peq(i, 2), eq.peq(i, 3), eq.peq(i, 4)); +end +fprintf(fid, '\n'); +fprintf(fid, '%s "%s"\n', iir_key, iir_path); +fprintf(fid, '%s "%s"\n', fir_key, fir_path); +fclose(fid); +fprintf('Wrote %s\n', conf_file); +end + +function s = sanitize_name(s) +% Lower case, replace any non-alphanumeric run with a single underscore, and +% trim leading/trailing underscores. e.g. 'Acme Ltd.' -> 'acme_ltd', +% 'Model 100' -> 'model_100'. +s = lower(s); +s = regexprep(s, '[^a-z0-9]+', '_'); +s = regexprep(s, '^_+|_+$', ''); +end + +function validate_dmi_field(field_name, s) +% Reject DMI values that would escape the output tree or otherwise produce +% a bad directory / file name when used verbatim. Path separators and the +% special names '.' and '..' are refused; any other printable UTF-8 string +% is accepted because DMI content is noisy in the wild. +if ~ischar(s) || isempty(s) + error('%s must be a non-empty string', field_name); +end +if any(s == '/') || any(s == '\') + error('%s must not contain path separators: "%s"', field_name, s); +end +if strcmp(s, '.') || strcmp(s, '..') + error('%s must not be "." or "..": "%s"', field_name, s); +end +if any(s < 32) + error('%s must not contain control characters', field_name); +end +end + +function validate_endpoint(s) +% endpoint becomes part of the UCM key Define.PostMixerPlayback... +% and a component of blob file names, so restrict it to a plain letters-only +% identifier. Extend the pattern here if a new endpoint naming scheme is +% needed. +if ~ischar(s) || isempty(s) + error('endpoint must be a non-empty string'); +end +if isempty(regexp(s, '^[A-Za-z]+$', 'once')) + error('endpoint must be letters only (e.g. speaker, headphone): "%s"', s); +end +end + +function s = capitalize(s) +if isempty(s) + return; +end +s = [upper(s(1)), lower(s(2:end))]; +end + +function ensure_dir(d) +if ~exist(d, 'dir') + [ok, msg] = mkdir(d); + if ~ok + error('mkdir %s failed: %s', d, msg); + end +end +end + +function name = peq_type_name(eq, type_num) +% Look up the PEQ_* field name whose value matches type_num, using the +% constants that sof_eq_defaults() already stored on the eq struct. This +% keeps the mapping in sync with sof_eq_define_parametric_eq.m without +% duplicating the enum here. +fns = fieldnames(eq); +for k = 1:numel(fns) + if strncmp(fns{k}, 'PEQ_', 4) && isnumeric(eq.(fns{k})) && ... + isscalar(eq.(fns{k})) && eq.(fns{k}) == type_num + name = fns{k}; + return; + end +end +name = sprintf('PEQ_%d', type_num); +end