1#!/usr/bin/awk -f
2#
3# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4#
5# Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27# SUCH DAMAGE.
28#
29# $FreeBSD$
30#
31
32#
33# Biquad coefficients generator for Parametric Software Equalizer. Not as ugly
34# as 'feeder_rate_mkfilter.awk'
35#
36# Based on:
37#
38#  "Cookbook formulae for audio EQ biquad filter coefficients"
39#    by Robert Bristow-Johnson  <rbj@audioimagination.com>
40#
41#    -  http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
42#
43
44
45
46#
47# Some basic Math functions.
48#
49function abs(x)
50{
51	return (((x < 0) ? -x : x) + 0);
52}
53
54function fabs(x)
55{
56	return (((x < 0.0) ? -x : x) + 0.0);
57}
58
59function floor(x, r)
60{
61	r = int(x);
62	if (r > x)
63		r--;
64	return (r + 0);
65}
66
67function pow(x, y)
68{
69	return (exp(1.0 * y * log(1.0 * x)));
70}
71
72#
73# What the hell...
74#
75function shl(x, y)
76{
77	while (y > 0) {
78		x *= 2;
79		y--;
80	}
81	return (x);
82}
83
84function feedeq_w0(fc, rate)
85{
86	return ((2.0 * M_PI * fc) / (1.0 * rate));
87}
88
89function feedeq_A(gain, A)
90{
91	if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ || FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
92		A = pow(10, gain / 40.0);
93	else
94		A = sqrt(pow(10, gain / 20.0));
95
96	return (A);
97}
98
99function feedeq_alpha(w0, A, QS)
100{
101	if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ)
102		alpha = sin(w0) / (2.0 * QS);
103	else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
104		alpha = sin(w0) * 0.5 * sqrt(A + ((1.0 / A) *		\
105		    ((1.0 / QS) - 1.0)) + 2.0);
106	else
107		alpha = 0.0;
108
109	return (alpha);
110}
111
112function feedeq_fx_floor(v, r)
113{
114	if (fabs(v) < fabs(smallest))
115		smallest = v;
116	if (fabs(v) > fabs(largest))
117		largest = v;
118
119	r = floor((v * FEEDEQ_COEFF_ONE) + 0.5);
120
121	if (r < INT32_MIN || r > INT32_MAX)
122		printf("\n#error overflow v=%f, "			\
123		    "please reduce FEEDEQ_COEFF_SHIFT\n", v);
124
125	return (r);
126}
127
128function feedeq_gen_biquad_coeffs(coeffs, rate, gain,			\
129    w0, A, alpha, a0, a1, a2, b0, b1, b2)
130{
131	w0    = feedeq_w0(FEEDEQ_TREBLE_SFREQ, 1.0 * rate);
132	A     = feedeq_A(1.0 * gain);
133	alpha = feedeq_alpha(w0, A, FEEDEQ_TREBLE_SLOPE);
134
135	if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
136		b0 =  1.0 + (alpha * A);
137		b1 = -2.0 * cos(w0);
138		b2 =  1.0 - (alpha * A);
139		a0 =  1.0 + (alpha / A);
140		a1 = -2.0 * cos(w0);
141		a2 =  1.0 - (alpha / A);
142	} else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
143		b0 =      A*((A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
144		b1 = -2.0*A*((A-1.0)+((A+1.0)*cos(w0))                    );
145		b2 =      A*((A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
146		a0 =         (A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
147		a1 =  2.0 * ((A-1.0)-((A+1.0)*cos(w0))                    );
148		a2 =         (A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
149	} else
150		b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
151
152	b0 /= a0;
153	b1 /= a0;
154	b2 /= a0;
155	a1 /= a0;
156	a2 /= a0;
157
158	coeffs["treble", gain, 0] = feedeq_fx_floor(a0);
159	coeffs["treble", gain, 1] = feedeq_fx_floor(a1);
160	coeffs["treble", gain, 2] = feedeq_fx_floor(a2);
161	coeffs["treble", gain, 3] = feedeq_fx_floor(b0);
162	coeffs["treble", gain, 4] = feedeq_fx_floor(b1);
163	coeffs["treble", gain, 5] = feedeq_fx_floor(b2);
164
165	w0    = feedeq_w0(FEEDEQ_BASS_SFREQ, 1.0 * rate);
166	A     = feedeq_A(1.0 * gain);
167	alpha = feedeq_alpha(w0, A, FEEDEQ_BASS_SLOPE);
168
169	if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
170		b0 =  1.0 + (alpha * A);
171		b1 = -2.0 * cos(w0);
172		b2 =  1.0 - (alpha * A);
173		a0 =  1.0 + (alpha / A);
174		a1 = -2.0 * cos(w0);
175		a2 =  1.0 - (alpha / A);
176	} else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
177		b0 =      A*((A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
178		b1 =  2.0*A*((A-1.0)-((A+1.0)*cos(w0))                    );
179		b2 =      A*((A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
180		a0 =         (A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
181		a1 = -2.0 * ((A-1.0)+((A+1.0)*cos(w0))                    );
182		a2 =         (A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
183	} else
184		b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
185
186	b0 /= a0;
187	b1 /= a0;
188	b2 /= a0;
189	a1 /= a0;
190	a2 /= a0;
191
192	coeffs["bass", gain, 0] = feedeq_fx_floor(a0);
193	coeffs["bass", gain, 1] = feedeq_fx_floor(a1);
194	coeffs["bass", gain, 2] = feedeq_fx_floor(a2);
195	coeffs["bass", gain, 3] = feedeq_fx_floor(b0);
196	coeffs["bass", gain, 4] = feedeq_fx_floor(b1);
197	coeffs["bass", gain, 5] = feedeq_fx_floor(b2);
198}
199
200function feedeq_gen_freq_coeffs(frq, g, i, v)
201{
202	coeffs[0] = 0;
203
204	for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV);			\
205	    g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV);			\
206	    g += FEEDEQ_GAIN_STEP) {
207		feedeq_gen_biquad_coeffs(coeffs, frq,			\
208		    g * FEEDEQ_GAIN_RECIPROCAL);
209	}
210
211	printf("\nstatic struct feed_eq_coeff eq_%d[%d] "		\
212	    "= {\n", frq, FEEDEQ_LEVELS);
213	for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV);			\
214	    g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV);			\
215	    g += FEEDEQ_GAIN_STEP) {
216		printf("     {{ ");
217		for (i = 1; i < 6; i++) {
218			v = coeffs["treble", g * FEEDEQ_GAIN_RECIPROCAL, i];
219			printf("%s0x%08x%s",				\
220			    (v < 0) ? "-" : " ", abs(v),		\
221			    (i == 5) ? " " : ", ");
222		}
223		printf("},\n      { ");
224		for (i = 1; i < 6; i++) {
225			v = coeffs["bass", g * FEEDEQ_GAIN_RECIPROCAL, i];
226			printf("%s0x%08x%s",				\
227			    (v < 0) ? "-" : " ", abs(v),		\
228			    (i == 5) ? " " : ", ");
229		}
230		printf("}}%s\n",					\
231		    (g < (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV)) ? "," : "");
232	}
233	printf("};\n");
234}
235
236function feedeq_calc_preamp(norm, gain, shift, mul, bit, attn)
237{
238	shift = FEEDEQ_PREAMP_SHIFT;
239
240	if (floor(FEEDEQ_PREAMP_BITDB) == 6 &&				\
241	    (1.0 * floor(gain)) == gain && (floor(gain) % 6) == 0) {
242		mul = 1;
243		shift = floor(floor(gain) / 6);
244	} else {
245		bit = 32.0 - ((1.0 * gain) / (1.0 * FEEDEQ_PREAMP_BITDB));
246		attn = pow(2.0, bit) / pow(2.0, 32.0);
247		mul = floor((attn * FEEDEQ_PREAMP_ONE) + 0.5);
248	}
249
250	while ((mul % 2) == 0 && shift > 0) {
251		mul = floor(mul / 2);
252		shift--;
253	}
254
255	norm["mul"] = mul;
256	norm["shift"] = shift;
257}
258
259BEGIN {
260	M_PI = atan2(0.0, -1.0);
261
262	INT32_MAX = 1 + ((shl(1, 30) - 1) * 2);
263	INT32_MIN = -1 - INT32_MAX;
264
265	FEEDEQ_TYPE_PEQ   = 0;
266	FEEDEQ_TYPE_SHELF = 1;
267
268	FEEDEQ_TYPE       = FEEDEQ_TYPE_PEQ;
269
270	FEEDEQ_COEFF_SHIFT = 24;
271	FEEDEQ_COEFF_ONE   = shl(1, FEEDEQ_COEFF_SHIFT);
272
273	FEEDEQ_PREAMP_SHIFT = 31;
274	FEEDEQ_PREAMP_ONE   = shl(1, FEEDEQ_PREAMP_SHIFT);
275	FEEDEQ_PREAMP_BITDB = 6; # 20.0 * (log(2.0) / log(10.0));
276
277	FEEDEQ_GAIN_DIV   = 10;
278	i = 0;
279	j = 1;
280	while (j < FEEDEQ_GAIN_DIV) {
281		j *= 2;
282		i++;
283	}
284	FEEDEQ_GAIN_SHIFT = i;
285	FEEDEQ_GAIN_FMASK = shl(1, FEEDEQ_GAIN_SHIFT) - 1;
286
287	FEEDEQ_GAIN_RECIPROCAL = 1.0 / FEEDEQ_GAIN_DIV;
288
289	if (ARGC == 2) {
290		i = 1;
291		split(ARGV[1], arg, ":");
292		while (match(arg[i], "^[^0-9]*$")) {
293			if (arg[i] == "PEQ") {
294				FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
295			} else if (arg[i] == "SHELF") {
296				FEEDEQ_TYPE = FEEDEQ_TYPE_SHELF;
297			}
298			i++;
299		}
300		split(arg[i++], subarg, ",");
301		FEEDEQ_TREBLE_SFREQ = 1.0 * subarg[1];
302		FEEDEQ_TREBLE_SLOPE = 1.0 * subarg[2];
303		FEEDEQ_BASS_SFREQ = 1.0 * subarg[3];
304		FEEDEQ_BASS_SLOPE = 1.0 * subarg[4];
305		split(arg[i++], subarg, ",");
306		FEEDEQ_GAIN_MIN = floor(1.0 * subarg[1]);
307		FEEDEQ_GAIN_MAX = floor(1.0 * subarg[2]);
308		if (length(subarg) > 2) {
309			j = floor(1.0 * FEEDEQ_GAIN_DIV * subarg[3]);
310			if (j < 2)
311				j = 1;
312			else if (j < 5)
313				j = 2;
314			else if (j < 10)
315				j = 5;
316			else
317				j = 10;
318			if (j > FEEDEQ_GAIN_DIV || (FEEDEQ_GAIN_DIV % j) != 0)
319				j = FEEDEQ_GAIN_DIV;
320			FEEDEQ_GAIN_STEP = j;
321		} else
322			FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
323		split(arg[i], subarg, ",");
324		for (i = 1; i <= length(subarg); i++)
325			allfreq[i - 1] = floor(1.0 * subarg[i]);
326	} else {
327		FEEDEQ_TREBLE_SFREQ  = 16000.0;
328		FEEDEQ_TREBLE_SLOPE  = 0.25;
329		FEEDEQ_BASS_SFREQ    = 62.0;
330		FEEDEQ_BASS_SLOPE    = 0.25;
331
332		FEEDEQ_GAIN_MIN  = -9;
333		FEEDEQ_GAIN_MAX  = 9;
334
335		FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
336
337
338		allfreq[0] = 44100;
339		allfreq[1] = 48000;
340		allfreq[2] = 88200;
341		allfreq[3] = 96000;
342		allfreq[4] = 176400;
343		allfreq[5] = 192000;
344	}
345
346	FEEDEQ_LEVELS = ((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) *		\
347	    floor(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1;
348
349	FEEDEQ_ERR_CLIP = 0;
350
351	smallest = 10.000000;
352	largest  =  0.000010;
353
354	printf("#ifndef _FEEDER_EQ_GEN_H_\n");
355	printf("#define _FEEDER_EQ_GEN_H_\n\n");
356	printf("/*\n");
357	printf(" * Generated using feeder_eq_mkfilter.awk, heaven, wind and awesome.\n");
358	printf(" *\n");
359	printf(" * DO NOT EDIT!\n");
360	printf(" */\n\n");
361	printf("/*\n");
362	printf(" * EQ: %s\n", (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ?	\
363	    "Shelving" : "Peaking EQ");
364	printf(" */\n");
365	printf("#define FEEDER_EQ_PRESETS\t\"");
366	printf("%s:%d,%.4f,%d,%.4f:%d,%d,%.1f:",			\
367	    (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? "SHELF" : "PEQ",	\
368	    FEEDEQ_TREBLE_SFREQ, FEEDEQ_TREBLE_SLOPE,			\
369	    FEEDEQ_BASS_SFREQ, FEEDEQ_BASS_SLOPE,			\
370	    FEEDEQ_GAIN_MIN, FEEDEQ_GAIN_MAX,				\
371	    FEEDEQ_GAIN_STEP * FEEDEQ_GAIN_RECIPROCAL);
372	for (i = 0; i < length(allfreq); i++) {
373		if (i != 0)
374			printf(",");
375		printf("%d", allfreq[i]);
376	}
377	printf("\"\n\n");
378	printf("struct feed_eq_coeff_tone {\n");
379	printf("\tint32_t a1, a2;\n");
380	printf("\tint32_t b0, b1, b2;\n");
381	printf("};\n\n");
382	printf("struct feed_eq_coeff {\n");
383	#printf("\tstruct {\n");
384	#printf("\t\tint32_t a1, a2;\n");
385	#printf("\t\tint32_t b0, b1, b2;\n");
386	#printf("\t} treble, bass;\n");
387	printf("\tstruct feed_eq_coeff_tone treble;\n");
388	printf("\tstruct feed_eq_coeff_tone bass;\n");
389	#printf("\tstruct {\n");
390	#printf("\t\tint32_t a1, a2;\n");
391	#printf("\t\tint32_t b0, b1, b2;\n");
392	#printf("\t} bass;\n");
393	printf("};\n");
394	for (i = 0; i < length(allfreq); i++)
395		feedeq_gen_freq_coeffs(allfreq[i]);
396	printf("\n");
397	printf("static const struct {\n");
398	printf("\tuint32_t rate;\n");
399	printf("\tstruct feed_eq_coeff *coeff;\n");
400	printf("} feed_eq_tab[] = {\n");
401	for (i = 0; i < length(allfreq); i++) {
402		printf("\t{ %6d, eq_%-6d },\n", allfreq[i], allfreq[i]);
403	}
404	printf("};\n");
405
406	printf("\n#define FEEDEQ_RATE_MIN\t\t%d\n", allfreq[0]);
407	printf("#define FEEDEQ_RATE_MAX\t\t%d\n", allfreq[length(allfreq) - 1]);
408	printf("\n#define FEEDEQ_TAB_SIZE\t\t\t\t\t\t\t\\\n");
409	printf("\t((int32_t)(sizeof(feed_eq_tab) / sizeof(feed_eq_tab[0])))\n");
410
411	printf("\nstatic const struct {\n");
412	printf("\tint32_t mul, shift;\n");
413	printf("} feed_eq_preamp[] = {\n");
414	for (i = (FEEDEQ_GAIN_MAX * 2 * FEEDEQ_GAIN_DIV); i >= 0;	\
415	    i -= FEEDEQ_GAIN_STEP) {
416		feedeq_calc_preamp(norm, i * FEEDEQ_GAIN_RECIPROCAL);
417		dbgain = ((FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV) - i) *	\
418		    FEEDEQ_GAIN_RECIPROCAL;
419		printf("\t{ 0x%08x, 0x%08x },\t/* %+5.1f dB */\n",	\
420		    norm["mul"], norm["shift"], dbgain);
421	}
422	printf("};\n");
423
424	printf("\n#define FEEDEQ_GAIN_MIN\t\t%d", FEEDEQ_GAIN_MIN);
425	printf("\n#define FEEDEQ_GAIN_MAX\t\t%d\n", FEEDEQ_GAIN_MAX);
426
427	printf("\n#define FEEDEQ_GAIN_SHIFT\t%d\n", FEEDEQ_GAIN_SHIFT);
428	printf("#define FEEDEQ_GAIN_DIV\t\t%d\n", FEEDEQ_GAIN_DIV);
429	printf("#define FEEDEQ_GAIN_FMASK\t0x%08x\n", FEEDEQ_GAIN_FMASK);
430	printf("#define FEEDEQ_GAIN_STEP\t%d\n", FEEDEQ_GAIN_STEP);
431
432	#printf("\n#define FEEDEQ_PREAMP_MIN\t-%d\n",			\
433	#    shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
434	#printf("#define FEEDEQ_PREAMP_MAX\t%d\n",			\
435	#    shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
436
437	printf("\n#define FEEDEQ_COEFF_SHIFT\t%d\n", FEEDEQ_COEFF_SHIFT);
438
439	#feedeq_calc_preamp(norm, FEEDEQ_GAIN_MAX);
440
441	#printf("#define FEEDEQ_COEFF_NORM(v)\t(");
442	#if (norm["mul"] == 1)
443	#	printf("(v) >> %d", norm["shift"]);
444	#else
445	#	printf("(0x%xLL * (v)) >> %d", norm["mul"], norm["shift"]);
446	#printf(")\n");
447
448	#printf("\n#define FEEDEQ_LEVELS\t\t%d\n", FEEDEQ_LEVELS);
449	if (FEEDEQ_ERR_CLIP != 0)
450		printf("\n#define FEEDEQ_ERR_CLIP\t\t%d\n", FEEDEQ_ERR_CLIP);
451	printf("\n/*\n");
452	printf(" * volume level mapping (0 - 100):\n");
453	printf(" *\n");
454
455	for (i = 0; i <= 100; i++) {
456		ind = floor((i * FEEDEQ_LEVELS) / 100);
457		if (ind >= FEEDEQ_LEVELS)
458			ind = FEEDEQ_LEVELS - 1;
459		printf(" *\t%3d  ->  %3d (%+5.1f dB)\n",		\
460		    i, ind, FEEDEQ_GAIN_MIN +				\
461		    (ind * (FEEDEQ_GAIN_RECIPROCAL * FEEDEQ_GAIN_STEP)));
462	}
463
464	printf(" */\n");
465	printf("\n/*\n * smallest: %.32f\n *  largest: %.32f\n */\n",	\
466	    smallest, largest);
467	printf("\n#endif\t/* !_FEEDER_EQ_GEN_H_ */\n");
468}
469