xref: /freebsd/usr.sbin/virtual_oss/virtual_equalizer/equalizer.c (revision 9cab9fde5edad9b409dd2317a2aec7815e6d6bed)
1*9cab9fdeSChristos Margiolis /*-
2*9cab9fdeSChristos Margiolis  * Copyright (c) 2019 Google LLC, written by Richard Kralovic <riso@google.com>
3*9cab9fdeSChristos Margiolis  *
4*9cab9fdeSChristos Margiolis  * Redistribution and use in source and binary forms, with or without
5*9cab9fdeSChristos Margiolis  * modification, are permitted provided that the following conditions
6*9cab9fdeSChristos Margiolis  * are met:
7*9cab9fdeSChristos Margiolis  * 1. Redistributions of source code must retain the above copyright
8*9cab9fdeSChristos Margiolis  *    notice, this list of conditions and the following disclaimer.
9*9cab9fdeSChristos Margiolis  * 2. Redistributions in binary form must reproduce the above copyright
10*9cab9fdeSChristos Margiolis  *    notice, this list of conditions and the following disclaimer in the
11*9cab9fdeSChristos Margiolis  *    documentation and/or other materials provided with the distribution.
12*9cab9fdeSChristos Margiolis  *
13*9cab9fdeSChristos Margiolis  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14*9cab9fdeSChristos Margiolis  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15*9cab9fdeSChristos Margiolis  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16*9cab9fdeSChristos Margiolis  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17*9cab9fdeSChristos Margiolis  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18*9cab9fdeSChristos Margiolis  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19*9cab9fdeSChristos Margiolis  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20*9cab9fdeSChristos Margiolis  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21*9cab9fdeSChristos Margiolis  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22*9cab9fdeSChristos Margiolis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23*9cab9fdeSChristos Margiolis  * SUCH DAMAGE.
24*9cab9fdeSChristos Margiolis  */
25*9cab9fdeSChristos Margiolis 
26*9cab9fdeSChristos Margiolis #include <sys/ioctl.h>
27*9cab9fdeSChristos Margiolis #include <sys/socket.h>
28*9cab9fdeSChristos Margiolis #include <sys/soundcard.h>
29*9cab9fdeSChristos Margiolis #include <sys/types.h>
30*9cab9fdeSChristos Margiolis #include <sys/un.h>
31*9cab9fdeSChristos Margiolis 
32*9cab9fdeSChristos Margiolis #include <err.h>
33*9cab9fdeSChristos Margiolis #include <errno.h>
34*9cab9fdeSChristos Margiolis #include <fcntl.h>
35*9cab9fdeSChristos Margiolis #include <fftw3.h>
36*9cab9fdeSChristos Margiolis #include <getopt.h>
37*9cab9fdeSChristos Margiolis #include <math.h>
38*9cab9fdeSChristos Margiolis #include <stdarg.h>
39*9cab9fdeSChristos Margiolis #include <stdint.h>
40*9cab9fdeSChristos Margiolis #include <stdlib.h>
41*9cab9fdeSChristos Margiolis #include <string.h>
42*9cab9fdeSChristos Margiolis #include <sysexits.h>
43*9cab9fdeSChristos Margiolis #include <unistd.h>
44*9cab9fdeSChristos Margiolis 
45*9cab9fdeSChristos Margiolis #include "virtual_oss.h"
46*9cab9fdeSChristos Margiolis 
47*9cab9fdeSChristos Margiolis struct Equalizer {
48*9cab9fdeSChristos Margiolis 	double	rate;
49*9cab9fdeSChristos Margiolis 	int	block_size;
50*9cab9fdeSChristos Margiolis 	int	do_normalize;
51*9cab9fdeSChristos Margiolis 
52*9cab9fdeSChristos Margiolis 	/* (block_size * 2) elements, time domain */
53*9cab9fdeSChristos Margiolis 	double *fftw_time;
54*9cab9fdeSChristos Margiolis 
55*9cab9fdeSChristos Margiolis 	/* (block_size * 2) elements, half-complex, freq domain */
56*9cab9fdeSChristos Margiolis 	double *fftw_freq;
57*9cab9fdeSChristos Margiolis 
58*9cab9fdeSChristos Margiolis 	fftw_plan forward;
59*9cab9fdeSChristos Margiolis 	fftw_plan inverse;
60*9cab9fdeSChristos Margiolis };
61*9cab9fdeSChristos Margiolis 
62*9cab9fdeSChristos Margiolis static int be_silent = 0;
63*9cab9fdeSChristos Margiolis 
64*9cab9fdeSChristos Margiolis static void
message(const char * fmt,...)65*9cab9fdeSChristos Margiolis message(const char *fmt,...)
66*9cab9fdeSChristos Margiolis {
67*9cab9fdeSChristos Margiolis 	va_list list;
68*9cab9fdeSChristos Margiolis 
69*9cab9fdeSChristos Margiolis 	if (be_silent)
70*9cab9fdeSChristos Margiolis 		return;
71*9cab9fdeSChristos Margiolis 	va_start(list, fmt);
72*9cab9fdeSChristos Margiolis 	vfprintf(stderr, fmt, list);
73*9cab9fdeSChristos Margiolis 	va_end(list);
74*9cab9fdeSChristos Margiolis }
75*9cab9fdeSChristos Margiolis 
76*9cab9fdeSChristos Margiolis /*
77*9cab9fdeSChristos Margiolis  * Masking window value for -1 < x < 1.
78*9cab9fdeSChristos Margiolis  *
79*9cab9fdeSChristos Margiolis  * Window must be symmetric, thus, this function is queried for x >= 0
80*9cab9fdeSChristos Margiolis  * only. Currently a Hann window.
81*9cab9fdeSChristos Margiolis  */
82*9cab9fdeSChristos Margiolis static double
equalizer_get_window(double x)83*9cab9fdeSChristos Margiolis equalizer_get_window(double x)
84*9cab9fdeSChristos Margiolis {
85*9cab9fdeSChristos Margiolis 	return (0.5 + 0.5 * cos(M_PI * x));
86*9cab9fdeSChristos Margiolis }
87*9cab9fdeSChristos Margiolis 
88*9cab9fdeSChristos Margiolis static int
equalizer_load_freq_amps(struct Equalizer * e,const char * config)89*9cab9fdeSChristos Margiolis equalizer_load_freq_amps(struct Equalizer *e, const char *config)
90*9cab9fdeSChristos Margiolis {
91*9cab9fdeSChristos Margiolis 	double prev_f = 0.0;
92*9cab9fdeSChristos Margiolis 	double prev_amp = 1.0;
93*9cab9fdeSChristos Margiolis 	double next_f = 0.0;
94*9cab9fdeSChristos Margiolis 	double next_amp = 1.0;
95*9cab9fdeSChristos Margiolis 	int i;
96*9cab9fdeSChristos Margiolis 
97*9cab9fdeSChristos Margiolis 	if (strncasecmp(config, "normalize", 4) == 0) {
98*9cab9fdeSChristos Margiolis 		while (*config != 0) {
99*9cab9fdeSChristos Margiolis 			if (*config == '\n') {
100*9cab9fdeSChristos Margiolis 				config++;
101*9cab9fdeSChristos Margiolis 				break;
102*9cab9fdeSChristos Margiolis 			}
103*9cab9fdeSChristos Margiolis 			config++;
104*9cab9fdeSChristos Margiolis 		}
105*9cab9fdeSChristos Margiolis 		e->do_normalize = 1;
106*9cab9fdeSChristos Margiolis 	} else {
107*9cab9fdeSChristos Margiolis 		e->do_normalize = 0;
108*9cab9fdeSChristos Margiolis 	}
109*9cab9fdeSChristos Margiolis 
110*9cab9fdeSChristos Margiolis 	for (i = 0; i <= (e->block_size / 2); ++i) {
111*9cab9fdeSChristos Margiolis 		const double f = (i * e->rate) / e->block_size;
112*9cab9fdeSChristos Margiolis 
113*9cab9fdeSChristos Margiolis 		while (f >= next_f) {
114*9cab9fdeSChristos Margiolis 			prev_f = next_f;
115*9cab9fdeSChristos Margiolis 			prev_amp = next_amp;
116*9cab9fdeSChristos Margiolis 
117*9cab9fdeSChristos Margiolis 			if (*config == 0) {
118*9cab9fdeSChristos Margiolis 				next_f = e->rate;
119*9cab9fdeSChristos Margiolis 				next_amp = prev_amp;
120*9cab9fdeSChristos Margiolis 			} else {
121*9cab9fdeSChristos Margiolis 				int len;
122*9cab9fdeSChristos Margiolis 
123*9cab9fdeSChristos Margiolis 				if (sscanf(config, "%lf %lf %n", &next_f, &next_amp, &len) == 2) {
124*9cab9fdeSChristos Margiolis 					config += len;
125*9cab9fdeSChristos Margiolis 					if (next_f < prev_f) {
126*9cab9fdeSChristos Margiolis 						message("Parse error: Nonincreasing sequence of frequencies.\n");
127*9cab9fdeSChristos Margiolis 						return (0);
128*9cab9fdeSChristos Margiolis 					}
129*9cab9fdeSChristos Margiolis 				} else {
130*9cab9fdeSChristos Margiolis 					message("Parse error.\n");
131*9cab9fdeSChristos Margiolis 					return (0);
132*9cab9fdeSChristos Margiolis 				}
133*9cab9fdeSChristos Margiolis 			}
134*9cab9fdeSChristos Margiolis 			if (prev_f == 0.0)
135*9cab9fdeSChristos Margiolis 				prev_amp = next_amp;
136*9cab9fdeSChristos Margiolis 		}
137*9cab9fdeSChristos Margiolis 		e->fftw_freq[i] = ((f - prev_f) / (next_f - prev_f)) * (next_amp - prev_amp) + prev_amp;
138*9cab9fdeSChristos Margiolis 	}
139*9cab9fdeSChristos Margiolis 	return (1);
140*9cab9fdeSChristos Margiolis }
141*9cab9fdeSChristos Margiolis 
142*9cab9fdeSChristos Margiolis static void
equalizer_init(struct Equalizer * e,int rate,int block_size)143*9cab9fdeSChristos Margiolis equalizer_init(struct Equalizer *e, int rate, int block_size)
144*9cab9fdeSChristos Margiolis {
145*9cab9fdeSChristos Margiolis 	size_t buffer_size;
146*9cab9fdeSChristos Margiolis 
147*9cab9fdeSChristos Margiolis 	e->rate = rate;
148*9cab9fdeSChristos Margiolis 	e->block_size = block_size;
149*9cab9fdeSChristos Margiolis 
150*9cab9fdeSChristos Margiolis 	buffer_size = sizeof(double) * e->block_size;
151*9cab9fdeSChristos Margiolis 
152*9cab9fdeSChristos Margiolis 	e->fftw_time = (double *)malloc(buffer_size);
153*9cab9fdeSChristos Margiolis 	e->fftw_freq = (double *)malloc(buffer_size);
154*9cab9fdeSChristos Margiolis 
155*9cab9fdeSChristos Margiolis 	e->forward = fftw_plan_r2r_1d(block_size, e->fftw_time, e->fftw_freq,
156*9cab9fdeSChristos Margiolis 	    FFTW_R2HC, FFTW_MEASURE);
157*9cab9fdeSChristos Margiolis 	e->inverse = fftw_plan_r2r_1d(block_size, e->fftw_freq, e->fftw_time,
158*9cab9fdeSChristos Margiolis 	    FFTW_HC2R, FFTW_MEASURE);
159*9cab9fdeSChristos Margiolis }
160*9cab9fdeSChristos Margiolis 
161*9cab9fdeSChristos Margiolis static int
equalizer_load(struct Equalizer * eq,const char * config)162*9cab9fdeSChristos Margiolis equalizer_load(struct Equalizer *eq, const char *config)
163*9cab9fdeSChristos Margiolis {
164*9cab9fdeSChristos Margiolis 	int retval = 0;
165*9cab9fdeSChristos Margiolis 	int N = eq->block_size;
166*9cab9fdeSChristos Margiolis 	int buffer_size = sizeof(double) * N;
167*9cab9fdeSChristos Margiolis 	int i;
168*9cab9fdeSChristos Margiolis 
169*9cab9fdeSChristos Margiolis 	memset(eq->fftw_freq, 0, buffer_size);
170*9cab9fdeSChristos Margiolis 
171*9cab9fdeSChristos Margiolis 	message("\n\nReloading amplification specifications:\n%s\n", config);
172*9cab9fdeSChristos Margiolis 
173*9cab9fdeSChristos Margiolis 	if (!equalizer_load_freq_amps(eq, config))
174*9cab9fdeSChristos Margiolis 		goto end;
175*9cab9fdeSChristos Margiolis 
176*9cab9fdeSChristos Margiolis 	double *requested_freq = (double *)malloc(buffer_size);
177*9cab9fdeSChristos Margiolis 
178*9cab9fdeSChristos Margiolis 	memcpy(requested_freq, eq->fftw_freq, buffer_size);
179*9cab9fdeSChristos Margiolis 
180*9cab9fdeSChristos Margiolis 	fftw_execute(eq->inverse);
181*9cab9fdeSChristos Margiolis 
182*9cab9fdeSChristos Margiolis 	/* Multiply by symmetric window and shift */
183*9cab9fdeSChristos Margiolis 	for (i = 0; i < (N / 2); ++i) {
184*9cab9fdeSChristos Margiolis 		double weight = equalizer_get_window(i / (double)(N / 2)) / N;
185*9cab9fdeSChristos Margiolis 
186*9cab9fdeSChristos Margiolis 		eq->fftw_time[N / 2 + i] = eq->fftw_time[i] * weight;
187*9cab9fdeSChristos Margiolis 	}
188*9cab9fdeSChristos Margiolis 	for (i = (N / 2 - 1); i > 0; --i) {
189*9cab9fdeSChristos Margiolis 		eq->fftw_time[i] = eq->fftw_time[N - i];
190*9cab9fdeSChristos Margiolis 	}
191*9cab9fdeSChristos Margiolis 	eq->fftw_time[0] = 0;
192*9cab9fdeSChristos Margiolis 
193*9cab9fdeSChristos Margiolis 	fftw_execute(eq->forward);
194*9cab9fdeSChristos Margiolis 	for (i = 0; i < N; ++i) {
195*9cab9fdeSChristos Margiolis 		eq->fftw_freq[i] /= (double)N;
196*9cab9fdeSChristos Margiolis 	}
197*9cab9fdeSChristos Margiolis 
198*9cab9fdeSChristos Margiolis 	/* Debug output */
199*9cab9fdeSChristos Margiolis 	for (i = 0; i <= (N / 2); ++i) {
200*9cab9fdeSChristos Margiolis 		double f = (eq->rate / N) * i;
201*9cab9fdeSChristos Margiolis 		double a = sqrt(pow(eq->fftw_freq[i], 2.0) +
202*9cab9fdeSChristos Margiolis 		    ((i > 0 && i < N / 2) ? pow(eq->fftw_freq[N - i], 2.0) : 0));
203*9cab9fdeSChristos Margiolis 
204*9cab9fdeSChristos Margiolis 		a *= N;
205*9cab9fdeSChristos Margiolis 		double r = requested_freq[i];
206*9cab9fdeSChristos Margiolis 
207*9cab9fdeSChristos Margiolis 		message("%3.1lf Hz: requested %2.2lf, got %2.7lf (log10 = %.2lf), %3.7lfdb\n",
208*9cab9fdeSChristos Margiolis 		    f, r, a, log(a) / log(10), (log(a / r) / log(10.0)) * 10.0);
209*9cab9fdeSChristos Margiolis 	}
210*9cab9fdeSChristos Margiolis 
211*9cab9fdeSChristos Margiolis 	/* Normalize FIR filter, if any */
212*9cab9fdeSChristos Margiolis 	if (eq->do_normalize) {
213*9cab9fdeSChristos Margiolis 		double sum = 0;
214*9cab9fdeSChristos Margiolis 
215*9cab9fdeSChristos Margiolis 		for (i = 0; i < N; ++i)
216*9cab9fdeSChristos Margiolis 			sum += fabs(eq->fftw_time[i]);
217*9cab9fdeSChristos Margiolis 		if (sum != 0.0) {
218*9cab9fdeSChristos Margiolis 			for (i = 0; i < N; ++i)
219*9cab9fdeSChristos Margiolis 				eq->fftw_time[i] /= sum;
220*9cab9fdeSChristos Margiolis 		}
221*9cab9fdeSChristos Margiolis 	}
222*9cab9fdeSChristos Margiolis 	for (i = 0; i < N; ++i) {
223*9cab9fdeSChristos Margiolis 		message("%.3lf ms: %.10lf\n", 1000.0 * i / eq->rate, eq->fftw_time[i]);
224*9cab9fdeSChristos Margiolis 	}
225*9cab9fdeSChristos Margiolis 
226*9cab9fdeSChristos Margiolis 	/* End of debug */
227*9cab9fdeSChristos Margiolis 
228*9cab9fdeSChristos Margiolis 	retval = 1;
229*9cab9fdeSChristos Margiolis 
230*9cab9fdeSChristos Margiolis 	free(requested_freq);
231*9cab9fdeSChristos Margiolis end:
232*9cab9fdeSChristos Margiolis 	return (retval);
233*9cab9fdeSChristos Margiolis }
234*9cab9fdeSChristos Margiolis 
235*9cab9fdeSChristos Margiolis static void
equalizer_done(struct Equalizer * eq)236*9cab9fdeSChristos Margiolis equalizer_done(struct Equalizer *eq)
237*9cab9fdeSChristos Margiolis {
238*9cab9fdeSChristos Margiolis 
239*9cab9fdeSChristos Margiolis 	fftw_destroy_plan(eq->forward);
240*9cab9fdeSChristos Margiolis 	fftw_destroy_plan(eq->inverse);
241*9cab9fdeSChristos Margiolis 	free(eq->fftw_time);
242*9cab9fdeSChristos Margiolis 	free(eq->fftw_freq);
243*9cab9fdeSChristos Margiolis }
244*9cab9fdeSChristos Margiolis 
245*9cab9fdeSChristos Margiolis static struct option equalizer_opts[] = {
246*9cab9fdeSChristos Margiolis 	{"device", required_argument, NULL, 'd'},
247*9cab9fdeSChristos Margiolis 	{"part", required_argument, NULL, 'p'},
248*9cab9fdeSChristos Margiolis 	{"channels", required_argument, NULL, 'c'},
249*9cab9fdeSChristos Margiolis 	{"what", required_argument, NULL, 'w'},
250*9cab9fdeSChristos Margiolis 	{"off", no_argument, NULL, 'o'},
251*9cab9fdeSChristos Margiolis 	{"quiet", no_argument, NULL, 'q'},
252*9cab9fdeSChristos Margiolis 	{"file", no_argument, NULL, 'f'},
253*9cab9fdeSChristos Margiolis 	{"help", no_argument, NULL, 'h'},
254*9cab9fdeSChristos Margiolis };
255*9cab9fdeSChristos Margiolis 
256*9cab9fdeSChristos Margiolis static void
usage(void)257*9cab9fdeSChristos Margiolis usage(void)
258*9cab9fdeSChristos Margiolis {
259*9cab9fdeSChristos Margiolis 	message("Usage: virtual_equalizer -d /dev/vdsp.ctl \n"
260*9cab9fdeSChristos Margiolis 	    "\t -d, --device [control device]\n"
261*9cab9fdeSChristos Margiolis 	    "\t -w, --what [rx_dev,tx_dev,rx_loop,tx_loop, default tx_dev]\n"
262*9cab9fdeSChristos Margiolis 	    "\t -p, --part [part number, default 0]\n"
263*9cab9fdeSChristos Margiolis 	    "\t -c, --channels [channels, default -1]\n"
264*9cab9fdeSChristos Margiolis 	    "\t -f, --file [read input from file, default standard input]\n"
265*9cab9fdeSChristos Margiolis 	    "\t -o, --off [disable equalizer]\n"
266*9cab9fdeSChristos Margiolis 	    "\t -q, --quiet\n"
267*9cab9fdeSChristos Margiolis 	    "\t -h, --help\n");
268*9cab9fdeSChristos Margiolis 	exit(EX_USAGE);
269*9cab9fdeSChristos Margiolis }
270*9cab9fdeSChristos Margiolis 
271*9cab9fdeSChristos Margiolis int
main(int argc,char ** argv)272*9cab9fdeSChristos Margiolis main(int argc, char **argv)
273*9cab9fdeSChristos Margiolis {
274*9cab9fdeSChristos Margiolis 	struct virtual_oss_fir_filter fir = {};
275*9cab9fdeSChristos Margiolis 	struct virtual_oss_io_info info = {};
276*9cab9fdeSChristos Margiolis 
277*9cab9fdeSChristos Margiolis 	struct Equalizer e;
278*9cab9fdeSChristos Margiolis 
279*9cab9fdeSChristos Margiolis 	char buffer[65536];
280*9cab9fdeSChristos Margiolis 	unsigned cmd_fir_set = VIRTUAL_OSS_SET_TX_DEV_FIR_FILTER;
281*9cab9fdeSChristos Margiolis 	unsigned cmd_fir_get = VIRTUAL_OSS_GET_TX_DEV_FIR_FILTER;
282*9cab9fdeSChristos Margiolis 	unsigned cmd_info = VIRTUAL_OSS_GET_DEV_INFO;
283*9cab9fdeSChristos Margiolis 	const char *dsp = NULL;
284*9cab9fdeSChristos Margiolis 	int rate;
285*9cab9fdeSChristos Margiolis 	int channels = -1;
286*9cab9fdeSChristos Margiolis 	int part = 0;
287*9cab9fdeSChristos Margiolis 	int opt;
288*9cab9fdeSChristos Margiolis 	int len;
289*9cab9fdeSChristos Margiolis 	int offset;
290*9cab9fdeSChristos Margiolis 	int disable = 0;
291*9cab9fdeSChristos Margiolis 	int f = STDIN_FILENO;
292*9cab9fdeSChristos Margiolis 
293*9cab9fdeSChristos Margiolis 	while ((opt = getopt_long(argc, argv, "d:c:f:op:w:qh",
294*9cab9fdeSChristos Margiolis 	    equalizer_opts, NULL)) != -1) {
295*9cab9fdeSChristos Margiolis 		switch (opt) {
296*9cab9fdeSChristos Margiolis 		case 'd':
297*9cab9fdeSChristos Margiolis 			dsp = optarg;
298*9cab9fdeSChristos Margiolis 			break;
299*9cab9fdeSChristos Margiolis 		case 'c':
300*9cab9fdeSChristos Margiolis 			channels = atoi(optarg);
301*9cab9fdeSChristos Margiolis 			if (channels == 0) {
302*9cab9fdeSChristos Margiolis 				message("Wrong number of channels\n");
303*9cab9fdeSChristos Margiolis 				usage();
304*9cab9fdeSChristos Margiolis 			}
305*9cab9fdeSChristos Margiolis 			break;
306*9cab9fdeSChristos Margiolis 		case 'p':
307*9cab9fdeSChristos Margiolis 			part = atoi(optarg);
308*9cab9fdeSChristos Margiolis 			if (part < 0) {
309*9cab9fdeSChristos Margiolis 				message("Invalid part number\n");
310*9cab9fdeSChristos Margiolis 				usage();
311*9cab9fdeSChristos Margiolis 			}
312*9cab9fdeSChristos Margiolis 			break;
313*9cab9fdeSChristos Margiolis 		case 'w':
314*9cab9fdeSChristos Margiolis 			if (strcmp(optarg, "rx_dev") == 0) {
315*9cab9fdeSChristos Margiolis 				cmd_fir_set = VIRTUAL_OSS_SET_RX_DEV_FIR_FILTER;
316*9cab9fdeSChristos Margiolis 				cmd_fir_get = VIRTUAL_OSS_GET_RX_DEV_FIR_FILTER;
317*9cab9fdeSChristos Margiolis 				cmd_info = VIRTUAL_OSS_GET_DEV_INFO;
318*9cab9fdeSChristos Margiolis 			} else if (strcmp(optarg, "tx_dev") == 0) {
319*9cab9fdeSChristos Margiolis 				cmd_fir_set = VIRTUAL_OSS_SET_TX_DEV_FIR_FILTER;
320*9cab9fdeSChristos Margiolis 				cmd_fir_get = VIRTUAL_OSS_GET_TX_DEV_FIR_FILTER;
321*9cab9fdeSChristos Margiolis 				cmd_info = VIRTUAL_OSS_GET_DEV_INFO;
322*9cab9fdeSChristos Margiolis 			} else if (strcmp(optarg, "rx_loop") == 0) {
323*9cab9fdeSChristos Margiolis 				cmd_fir_set = VIRTUAL_OSS_SET_RX_LOOP_FIR_FILTER;
324*9cab9fdeSChristos Margiolis 				cmd_fir_get = VIRTUAL_OSS_GET_RX_LOOP_FIR_FILTER;
325*9cab9fdeSChristos Margiolis 				cmd_info = VIRTUAL_OSS_GET_LOOP_INFO;
326*9cab9fdeSChristos Margiolis 			} else if (strcmp(optarg, "tx_loop") == 0) {
327*9cab9fdeSChristos Margiolis 				cmd_fir_set = VIRTUAL_OSS_SET_TX_LOOP_FIR_FILTER;
328*9cab9fdeSChristos Margiolis 				cmd_fir_get = VIRTUAL_OSS_GET_TX_LOOP_FIR_FILTER;
329*9cab9fdeSChristos Margiolis 				cmd_info = VIRTUAL_OSS_GET_LOOP_INFO;
330*9cab9fdeSChristos Margiolis 			} else {
331*9cab9fdeSChristos Margiolis 				message("Bad -w argument not recognized\n");
332*9cab9fdeSChristos Margiolis 				usage();
333*9cab9fdeSChristos Margiolis 			}
334*9cab9fdeSChristos Margiolis 			break;
335*9cab9fdeSChristos Margiolis 		case 'f':
336*9cab9fdeSChristos Margiolis 			if (f != STDIN_FILENO) {
337*9cab9fdeSChristos Margiolis 				message("Can only specify one file\n");
338*9cab9fdeSChristos Margiolis 				usage();
339*9cab9fdeSChristos Margiolis 			}
340*9cab9fdeSChristos Margiolis 			f = open(optarg, O_RDONLY);
341*9cab9fdeSChristos Margiolis 			if (f < 0) {
342*9cab9fdeSChristos Margiolis 				message("Cannot open specified file\n");
343*9cab9fdeSChristos Margiolis 				usage();
344*9cab9fdeSChristos Margiolis 			}
345*9cab9fdeSChristos Margiolis 			break;
346*9cab9fdeSChristos Margiolis 		case 'o':
347*9cab9fdeSChristos Margiolis 			disable = 1;
348*9cab9fdeSChristos Margiolis 			break;
349*9cab9fdeSChristos Margiolis 		case 'q':
350*9cab9fdeSChristos Margiolis 			be_silent = 1;
351*9cab9fdeSChristos Margiolis 			break;
352*9cab9fdeSChristos Margiolis 		default:
353*9cab9fdeSChristos Margiolis 			usage();
354*9cab9fdeSChristos Margiolis 		}
355*9cab9fdeSChristos Margiolis 	}
356*9cab9fdeSChristos Margiolis 
357*9cab9fdeSChristos Margiolis 	fir.number = part;
358*9cab9fdeSChristos Margiolis 	info.number = part;
359*9cab9fdeSChristos Margiolis 
360*9cab9fdeSChristos Margiolis 	int fd = open(dsp, O_RDWR);
361*9cab9fdeSChristos Margiolis 
362*9cab9fdeSChristos Margiolis 	if (fd < 0) {
363*9cab9fdeSChristos Margiolis 		message("Cannot open DSP device\n");
364*9cab9fdeSChristos Margiolis 		return (EX_SOFTWARE);
365*9cab9fdeSChristos Margiolis 	}
366*9cab9fdeSChristos Margiolis 	if (ioctl(fd, VIRTUAL_OSS_GET_SAMPLE_RATE, &rate) < 0) {
367*9cab9fdeSChristos Margiolis 		message("Cannot get sample rate\n");
368*9cab9fdeSChristos Margiolis 		return (EX_SOFTWARE);
369*9cab9fdeSChristos Margiolis 	}
370*9cab9fdeSChristos Margiolis 	if (ioctl(fd, cmd_fir_get, &fir) < 0) {
371*9cab9fdeSChristos Margiolis 		message("Cannot get current FIR filter\n");
372*9cab9fdeSChristos Margiolis 		return (EX_SOFTWARE);
373*9cab9fdeSChristos Margiolis 	}
374*9cab9fdeSChristos Margiolis 	if (disable) {
375*9cab9fdeSChristos Margiolis 	  	for (fir.channel = 0; fir.channel != channels; fir.channel++) {
376*9cab9fdeSChristos Margiolis 			if (ioctl(fd, cmd_fir_set, &fir) < 0) {
377*9cab9fdeSChristos Margiolis 				if (fir.channel == 0) {
378*9cab9fdeSChristos Margiolis 					message("Cannot disable FIR filter\n");
379*9cab9fdeSChristos Margiolis 					return (EX_SOFTWARE);
380*9cab9fdeSChristos Margiolis 				}
381*9cab9fdeSChristos Margiolis 				break;
382*9cab9fdeSChristos Margiolis 			}
383*9cab9fdeSChristos Margiolis 		}
384*9cab9fdeSChristos Margiolis 		return (0);
385*9cab9fdeSChristos Margiolis 	}
386*9cab9fdeSChristos Margiolis 	equalizer_init(&e, rate, fir.filter_size);
387*9cab9fdeSChristos Margiolis 	equalizer_load(&e, "");
388*9cab9fdeSChristos Margiolis 
389*9cab9fdeSChristos Margiolis 	if (f == STDIN_FILENO) {
390*9cab9fdeSChristos Margiolis 		if (ioctl(fd, cmd_info, &info) < 0) {
391*9cab9fdeSChristos Margiolis 			message("Cannot read part information\n");
392*9cab9fdeSChristos Margiolis 			return (EX_SOFTWARE);
393*9cab9fdeSChristos Margiolis 		}
394*9cab9fdeSChristos Margiolis 		message("Please enter EQ layout for %s, <freq> <gain>:\n", info.name);
395*9cab9fdeSChristos Margiolis 	}
396*9cab9fdeSChristos Margiolis 	offset = 0;
397*9cab9fdeSChristos Margiolis 	while (1) {
398*9cab9fdeSChristos Margiolis 		if (offset == (int)(sizeof(buffer) - 1)) {
399*9cab9fdeSChristos Margiolis 			message("Too much input data\n");
400*9cab9fdeSChristos Margiolis 			return (EX_SOFTWARE);
401*9cab9fdeSChristos Margiolis 		}
402*9cab9fdeSChristos Margiolis 		len = read(f, buffer + offset, sizeof(buffer) - 1 - offset);
403*9cab9fdeSChristos Margiolis 		if (len <= 0)
404*9cab9fdeSChristos Margiolis 			break;
405*9cab9fdeSChristos Margiolis 		offset += len;
406*9cab9fdeSChristos Margiolis 	}
407*9cab9fdeSChristos Margiolis 	buffer[offset] = 0;
408*9cab9fdeSChristos Margiolis 	close(f);
409*9cab9fdeSChristos Margiolis 
410*9cab9fdeSChristos Margiolis 	if (f == STDIN_FILENO)
411*9cab9fdeSChristos Margiolis 		message("Loading new EQ layout\n");
412*9cab9fdeSChristos Margiolis 
413*9cab9fdeSChristos Margiolis 	if (equalizer_load(&e, buffer) == 0) {
414*9cab9fdeSChristos Margiolis 		message("Invalid equalizer data\n");
415*9cab9fdeSChristos Margiolis 		return (EX_SOFTWARE);
416*9cab9fdeSChristos Margiolis 	}
417*9cab9fdeSChristos Margiolis 	fir.filter_data = e.fftw_time;
418*9cab9fdeSChristos Margiolis 
419*9cab9fdeSChristos Margiolis 	for (fir.channel = 0; fir.channel != channels; fir.channel++) {
420*9cab9fdeSChristos Margiolis 		if (ioctl(fd, cmd_fir_set, &fir) < 0) {
421*9cab9fdeSChristos Margiolis 			if (fir.channel == 0)
422*9cab9fdeSChristos Margiolis 				message("Cannot set FIR filter on channel\n");
423*9cab9fdeSChristos Margiolis 			break;
424*9cab9fdeSChristos Margiolis 		}
425*9cab9fdeSChristos Margiolis 	}
426*9cab9fdeSChristos Margiolis 
427*9cab9fdeSChristos Margiolis 	close(fd);
428*9cab9fdeSChristos Margiolis 	equalizer_done(&e);
429*9cab9fdeSChristos Margiolis 
430*9cab9fdeSChristos Margiolis 	return (0);
431*9cab9fdeSChristos Margiolis }
432