19cab9fdeSChristos Margiolis /*-
2*6d5a4280STuukka Pasanen * SPDX-License-Identifier: BSD-2-Clause
3*6d5a4280STuukka Pasanen *
49cab9fdeSChristos Margiolis * Copyright (c) 2019 Google LLC, written by Richard Kralovic <riso@google.com>
59cab9fdeSChristos Margiolis *
69cab9fdeSChristos Margiolis * Redistribution and use in source and binary forms, with or without
79cab9fdeSChristos Margiolis * modification, are permitted provided that the following conditions
89cab9fdeSChristos Margiolis * are met:
99cab9fdeSChristos Margiolis * 1. Redistributions of source code must retain the above copyright
109cab9fdeSChristos Margiolis * notice, this list of conditions and the following disclaimer.
119cab9fdeSChristos Margiolis * 2. Redistributions in binary form must reproduce the above copyright
129cab9fdeSChristos Margiolis * notice, this list of conditions and the following disclaimer in the
139cab9fdeSChristos Margiolis * documentation and/or other materials provided with the distribution.
149cab9fdeSChristos Margiolis *
159cab9fdeSChristos Margiolis * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
169cab9fdeSChristos Margiolis * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
179cab9fdeSChristos Margiolis * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
189cab9fdeSChristos Margiolis * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
199cab9fdeSChristos Margiolis * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
209cab9fdeSChristos Margiolis * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
219cab9fdeSChristos Margiolis * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
229cab9fdeSChristos Margiolis * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
239cab9fdeSChristos Margiolis * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
249cab9fdeSChristos Margiolis * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
259cab9fdeSChristos Margiolis * SUCH DAMAGE.
269cab9fdeSChristos Margiolis */
279cab9fdeSChristos Margiolis
289cab9fdeSChristos Margiolis #include <sys/ioctl.h>
299cab9fdeSChristos Margiolis #include <sys/socket.h>
309cab9fdeSChristos Margiolis #include <sys/soundcard.h>
319cab9fdeSChristos Margiolis #include <sys/types.h>
329cab9fdeSChristos Margiolis #include <sys/un.h>
339cab9fdeSChristos Margiolis
349cab9fdeSChristos Margiolis #include <err.h>
359cab9fdeSChristos Margiolis #include <errno.h>
369cab9fdeSChristos Margiolis #include <fcntl.h>
379cab9fdeSChristos Margiolis #include <fftw3.h>
389cab9fdeSChristos Margiolis #include <getopt.h>
399cab9fdeSChristos Margiolis #include <math.h>
409cab9fdeSChristos Margiolis #include <stdarg.h>
419cab9fdeSChristos Margiolis #include <stdint.h>
429cab9fdeSChristos Margiolis #include <stdlib.h>
439cab9fdeSChristos Margiolis #include <string.h>
449cab9fdeSChristos Margiolis #include <sysexits.h>
459cab9fdeSChristos Margiolis #include <unistd.h>
469cab9fdeSChristos Margiolis
479cab9fdeSChristos Margiolis #include "virtual_oss.h"
489cab9fdeSChristos Margiolis
499cab9fdeSChristos Margiolis struct Equalizer {
509cab9fdeSChristos Margiolis double rate;
519cab9fdeSChristos Margiolis int block_size;
529cab9fdeSChristos Margiolis int do_normalize;
539cab9fdeSChristos Margiolis
549cab9fdeSChristos Margiolis /* (block_size * 2) elements, time domain */
559cab9fdeSChristos Margiolis double *fftw_time;
569cab9fdeSChristos Margiolis
579cab9fdeSChristos Margiolis /* (block_size * 2) elements, half-complex, freq domain */
589cab9fdeSChristos Margiolis double *fftw_freq;
599cab9fdeSChristos Margiolis
609cab9fdeSChristos Margiolis fftw_plan forward;
619cab9fdeSChristos Margiolis fftw_plan inverse;
629cab9fdeSChristos Margiolis };
639cab9fdeSChristos Margiolis
649cab9fdeSChristos Margiolis static int be_silent = 0;
659cab9fdeSChristos Margiolis
669cab9fdeSChristos Margiolis static void
message(const char * fmt,...)679cab9fdeSChristos Margiolis message(const char *fmt,...)
689cab9fdeSChristos Margiolis {
699cab9fdeSChristos Margiolis va_list list;
709cab9fdeSChristos Margiolis
719cab9fdeSChristos Margiolis if (be_silent)
729cab9fdeSChristos Margiolis return;
739cab9fdeSChristos Margiolis va_start(list, fmt);
749cab9fdeSChristos Margiolis vfprintf(stderr, fmt, list);
759cab9fdeSChristos Margiolis va_end(list);
769cab9fdeSChristos Margiolis }
779cab9fdeSChristos Margiolis
789cab9fdeSChristos Margiolis /*
799cab9fdeSChristos Margiolis * Masking window value for -1 < x < 1.
809cab9fdeSChristos Margiolis *
819cab9fdeSChristos Margiolis * Window must be symmetric, thus, this function is queried for x >= 0
829cab9fdeSChristos Margiolis * only. Currently a Hann window.
839cab9fdeSChristos Margiolis */
849cab9fdeSChristos Margiolis static double
equalizer_get_window(double x)859cab9fdeSChristos Margiolis equalizer_get_window(double x)
869cab9fdeSChristos Margiolis {
879cab9fdeSChristos Margiolis return (0.5 + 0.5 * cos(M_PI * x));
889cab9fdeSChristos Margiolis }
899cab9fdeSChristos Margiolis
909cab9fdeSChristos Margiolis static int
equalizer_load_freq_amps(struct Equalizer * e,const char * config)919cab9fdeSChristos Margiolis equalizer_load_freq_amps(struct Equalizer *e, const char *config)
929cab9fdeSChristos Margiolis {
939cab9fdeSChristos Margiolis double prev_f = 0.0;
949cab9fdeSChristos Margiolis double prev_amp = 1.0;
959cab9fdeSChristos Margiolis double next_f = 0.0;
969cab9fdeSChristos Margiolis double next_amp = 1.0;
979cab9fdeSChristos Margiolis int i;
989cab9fdeSChristos Margiolis
999cab9fdeSChristos Margiolis if (strncasecmp(config, "normalize", 4) == 0) {
1009cab9fdeSChristos Margiolis while (*config != 0) {
1019cab9fdeSChristos Margiolis if (*config == '\n') {
1029cab9fdeSChristos Margiolis config++;
1039cab9fdeSChristos Margiolis break;
1049cab9fdeSChristos Margiolis }
1059cab9fdeSChristos Margiolis config++;
1069cab9fdeSChristos Margiolis }
1079cab9fdeSChristos Margiolis e->do_normalize = 1;
1089cab9fdeSChristos Margiolis } else {
1099cab9fdeSChristos Margiolis e->do_normalize = 0;
1109cab9fdeSChristos Margiolis }
1119cab9fdeSChristos Margiolis
1129cab9fdeSChristos Margiolis for (i = 0; i <= (e->block_size / 2); ++i) {
1139cab9fdeSChristos Margiolis const double f = (i * e->rate) / e->block_size;
1149cab9fdeSChristos Margiolis
1159cab9fdeSChristos Margiolis while (f >= next_f) {
1169cab9fdeSChristos Margiolis prev_f = next_f;
1179cab9fdeSChristos Margiolis prev_amp = next_amp;
1189cab9fdeSChristos Margiolis
1199cab9fdeSChristos Margiolis if (*config == 0) {
1209cab9fdeSChristos Margiolis next_f = e->rate;
1219cab9fdeSChristos Margiolis next_amp = prev_amp;
1229cab9fdeSChristos Margiolis } else {
1239cab9fdeSChristos Margiolis int len;
1249cab9fdeSChristos Margiolis
1259cab9fdeSChristos Margiolis if (sscanf(config, "%lf %lf %n", &next_f, &next_amp, &len) == 2) {
1269cab9fdeSChristos Margiolis config += len;
1279cab9fdeSChristos Margiolis if (next_f < prev_f) {
1289cab9fdeSChristos Margiolis message("Parse error: Nonincreasing sequence of frequencies.\n");
1299cab9fdeSChristos Margiolis return (0);
1309cab9fdeSChristos Margiolis }
1319cab9fdeSChristos Margiolis } else {
1329cab9fdeSChristos Margiolis message("Parse error.\n");
1339cab9fdeSChristos Margiolis return (0);
1349cab9fdeSChristos Margiolis }
1359cab9fdeSChristos Margiolis }
1369cab9fdeSChristos Margiolis if (prev_f == 0.0)
1379cab9fdeSChristos Margiolis prev_amp = next_amp;
1389cab9fdeSChristos Margiolis }
1399cab9fdeSChristos Margiolis e->fftw_freq[i] = ((f - prev_f) / (next_f - prev_f)) * (next_amp - prev_amp) + prev_amp;
1409cab9fdeSChristos Margiolis }
1419cab9fdeSChristos Margiolis return (1);
1429cab9fdeSChristos Margiolis }
1439cab9fdeSChristos Margiolis
1449cab9fdeSChristos Margiolis static void
equalizer_init(struct Equalizer * e,int rate,int block_size)1459cab9fdeSChristos Margiolis equalizer_init(struct Equalizer *e, int rate, int block_size)
1469cab9fdeSChristos Margiolis {
1479cab9fdeSChristos Margiolis size_t buffer_size;
1489cab9fdeSChristos Margiolis
1499cab9fdeSChristos Margiolis e->rate = rate;
1509cab9fdeSChristos Margiolis e->block_size = block_size;
1519cab9fdeSChristos Margiolis
1529cab9fdeSChristos Margiolis buffer_size = sizeof(double) * e->block_size;
1539cab9fdeSChristos Margiolis
1549cab9fdeSChristos Margiolis e->fftw_time = (double *)malloc(buffer_size);
1559cab9fdeSChristos Margiolis e->fftw_freq = (double *)malloc(buffer_size);
1569cab9fdeSChristos Margiolis
1579cab9fdeSChristos Margiolis e->forward = fftw_plan_r2r_1d(block_size, e->fftw_time, e->fftw_freq,
1589cab9fdeSChristos Margiolis FFTW_R2HC, FFTW_MEASURE);
1599cab9fdeSChristos Margiolis e->inverse = fftw_plan_r2r_1d(block_size, e->fftw_freq, e->fftw_time,
1609cab9fdeSChristos Margiolis FFTW_HC2R, FFTW_MEASURE);
1619cab9fdeSChristos Margiolis }
1629cab9fdeSChristos Margiolis
1639cab9fdeSChristos Margiolis static int
equalizer_load(struct Equalizer * eq,const char * config)1649cab9fdeSChristos Margiolis equalizer_load(struct Equalizer *eq, const char *config)
1659cab9fdeSChristos Margiolis {
1669cab9fdeSChristos Margiolis int retval = 0;
1679cab9fdeSChristos Margiolis int N = eq->block_size;
1689cab9fdeSChristos Margiolis int buffer_size = sizeof(double) * N;
1699cab9fdeSChristos Margiolis int i;
1709cab9fdeSChristos Margiolis
1719cab9fdeSChristos Margiolis memset(eq->fftw_freq, 0, buffer_size);
1729cab9fdeSChristos Margiolis
1739cab9fdeSChristos Margiolis message("\n\nReloading amplification specifications:\n%s\n", config);
1749cab9fdeSChristos Margiolis
1759cab9fdeSChristos Margiolis if (!equalizer_load_freq_amps(eq, config))
1769cab9fdeSChristos Margiolis goto end;
1779cab9fdeSChristos Margiolis
1789cab9fdeSChristos Margiolis double *requested_freq = (double *)malloc(buffer_size);
1799cab9fdeSChristos Margiolis
1809cab9fdeSChristos Margiolis memcpy(requested_freq, eq->fftw_freq, buffer_size);
1819cab9fdeSChristos Margiolis
1829cab9fdeSChristos Margiolis fftw_execute(eq->inverse);
1839cab9fdeSChristos Margiolis
1849cab9fdeSChristos Margiolis /* Multiply by symmetric window and shift */
1859cab9fdeSChristos Margiolis for (i = 0; i < (N / 2); ++i) {
1869cab9fdeSChristos Margiolis double weight = equalizer_get_window(i / (double)(N / 2)) / N;
1879cab9fdeSChristos Margiolis
1889cab9fdeSChristos Margiolis eq->fftw_time[N / 2 + i] = eq->fftw_time[i] * weight;
1899cab9fdeSChristos Margiolis }
1909cab9fdeSChristos Margiolis for (i = (N / 2 - 1); i > 0; --i) {
1919cab9fdeSChristos Margiolis eq->fftw_time[i] = eq->fftw_time[N - i];
1929cab9fdeSChristos Margiolis }
1939cab9fdeSChristos Margiolis eq->fftw_time[0] = 0;
1949cab9fdeSChristos Margiolis
1959cab9fdeSChristos Margiolis fftw_execute(eq->forward);
1969cab9fdeSChristos Margiolis for (i = 0; i < N; ++i) {
1979cab9fdeSChristos Margiolis eq->fftw_freq[i] /= (double)N;
1989cab9fdeSChristos Margiolis }
1999cab9fdeSChristos Margiolis
2009cab9fdeSChristos Margiolis /* Debug output */
2019cab9fdeSChristos Margiolis for (i = 0; i <= (N / 2); ++i) {
2029cab9fdeSChristos Margiolis double f = (eq->rate / N) * i;
2039cab9fdeSChristos Margiolis double a = sqrt(pow(eq->fftw_freq[i], 2.0) +
2049cab9fdeSChristos Margiolis ((i > 0 && i < N / 2) ? pow(eq->fftw_freq[N - i], 2.0) : 0));
2059cab9fdeSChristos Margiolis
2069cab9fdeSChristos Margiolis a *= N;
2079cab9fdeSChristos Margiolis double r = requested_freq[i];
2089cab9fdeSChristos Margiolis
2099cab9fdeSChristos Margiolis message("%3.1lf Hz: requested %2.2lf, got %2.7lf (log10 = %.2lf), %3.7lfdb\n",
2109cab9fdeSChristos Margiolis f, r, a, log(a) / log(10), (log(a / r) / log(10.0)) * 10.0);
2119cab9fdeSChristos Margiolis }
2129cab9fdeSChristos Margiolis
2139cab9fdeSChristos Margiolis /* Normalize FIR filter, if any */
2149cab9fdeSChristos Margiolis if (eq->do_normalize) {
2159cab9fdeSChristos Margiolis double sum = 0;
2169cab9fdeSChristos Margiolis
2179cab9fdeSChristos Margiolis for (i = 0; i < N; ++i)
2189cab9fdeSChristos Margiolis sum += fabs(eq->fftw_time[i]);
2199cab9fdeSChristos Margiolis if (sum != 0.0) {
2209cab9fdeSChristos Margiolis for (i = 0; i < N; ++i)
2219cab9fdeSChristos Margiolis eq->fftw_time[i] /= sum;
2229cab9fdeSChristos Margiolis }
2239cab9fdeSChristos Margiolis }
2249cab9fdeSChristos Margiolis for (i = 0; i < N; ++i) {
2259cab9fdeSChristos Margiolis message("%.3lf ms: %.10lf\n", 1000.0 * i / eq->rate, eq->fftw_time[i]);
2269cab9fdeSChristos Margiolis }
2279cab9fdeSChristos Margiolis
2289cab9fdeSChristos Margiolis /* End of debug */
2299cab9fdeSChristos Margiolis
2309cab9fdeSChristos Margiolis retval = 1;
2319cab9fdeSChristos Margiolis
2329cab9fdeSChristos Margiolis free(requested_freq);
2339cab9fdeSChristos Margiolis end:
2349cab9fdeSChristos Margiolis return (retval);
2359cab9fdeSChristos Margiolis }
2369cab9fdeSChristos Margiolis
2379cab9fdeSChristos Margiolis static void
equalizer_done(struct Equalizer * eq)2389cab9fdeSChristos Margiolis equalizer_done(struct Equalizer *eq)
2399cab9fdeSChristos Margiolis {
2409cab9fdeSChristos Margiolis
2419cab9fdeSChristos Margiolis fftw_destroy_plan(eq->forward);
2429cab9fdeSChristos Margiolis fftw_destroy_plan(eq->inverse);
2439cab9fdeSChristos Margiolis free(eq->fftw_time);
2449cab9fdeSChristos Margiolis free(eq->fftw_freq);
2459cab9fdeSChristos Margiolis }
2469cab9fdeSChristos Margiolis
2479cab9fdeSChristos Margiolis static struct option equalizer_opts[] = {
2489cab9fdeSChristos Margiolis {"device", required_argument, NULL, 'd'},
2499cab9fdeSChristos Margiolis {"part", required_argument, NULL, 'p'},
2509cab9fdeSChristos Margiolis {"channels", required_argument, NULL, 'c'},
2519cab9fdeSChristos Margiolis {"what", required_argument, NULL, 'w'},
2529cab9fdeSChristos Margiolis {"off", no_argument, NULL, 'o'},
2539cab9fdeSChristos Margiolis {"quiet", no_argument, NULL, 'q'},
2549cab9fdeSChristos Margiolis {"file", no_argument, NULL, 'f'},
2559cab9fdeSChristos Margiolis {"help", no_argument, NULL, 'h'},
2569cab9fdeSChristos Margiolis };
2579cab9fdeSChristos Margiolis
2589cab9fdeSChristos Margiolis static void
usage(void)2599cab9fdeSChristos Margiolis usage(void)
2609cab9fdeSChristos Margiolis {
2619cab9fdeSChristos Margiolis message("Usage: virtual_equalizer -d /dev/vdsp.ctl \n"
2629cab9fdeSChristos Margiolis "\t -d, --device [control device]\n"
2639cab9fdeSChristos Margiolis "\t -w, --what [rx_dev,tx_dev,rx_loop,tx_loop, default tx_dev]\n"
2649cab9fdeSChristos Margiolis "\t -p, --part [part number, default 0]\n"
2659cab9fdeSChristos Margiolis "\t -c, --channels [channels, default -1]\n"
2669cab9fdeSChristos Margiolis "\t -f, --file [read input from file, default standard input]\n"
2679cab9fdeSChristos Margiolis "\t -o, --off [disable equalizer]\n"
2689cab9fdeSChristos Margiolis "\t -q, --quiet\n"
2699cab9fdeSChristos Margiolis "\t -h, --help\n");
2709cab9fdeSChristos Margiolis exit(EX_USAGE);
2719cab9fdeSChristos Margiolis }
2729cab9fdeSChristos Margiolis
2739cab9fdeSChristos Margiolis int
main(int argc,char ** argv)2749cab9fdeSChristos Margiolis main(int argc, char **argv)
2759cab9fdeSChristos Margiolis {
2769cab9fdeSChristos Margiolis struct virtual_oss_fir_filter fir = {};
2779cab9fdeSChristos Margiolis struct virtual_oss_io_info info = {};
2789cab9fdeSChristos Margiolis
2799cab9fdeSChristos Margiolis struct Equalizer e;
2809cab9fdeSChristos Margiolis
2819cab9fdeSChristos Margiolis char buffer[65536];
2829cab9fdeSChristos Margiolis unsigned cmd_fir_set = VIRTUAL_OSS_SET_TX_DEV_FIR_FILTER;
2839cab9fdeSChristos Margiolis unsigned cmd_fir_get = VIRTUAL_OSS_GET_TX_DEV_FIR_FILTER;
2849cab9fdeSChristos Margiolis unsigned cmd_info = VIRTUAL_OSS_GET_DEV_INFO;
2859cab9fdeSChristos Margiolis const char *dsp = NULL;
2869cab9fdeSChristos Margiolis int rate;
2879cab9fdeSChristos Margiolis int channels = -1;
2889cab9fdeSChristos Margiolis int part = 0;
2899cab9fdeSChristos Margiolis int opt;
2909cab9fdeSChristos Margiolis int len;
2919cab9fdeSChristos Margiolis int offset;
2929cab9fdeSChristos Margiolis int disable = 0;
2939cab9fdeSChristos Margiolis int f = STDIN_FILENO;
2949cab9fdeSChristos Margiolis
2959cab9fdeSChristos Margiolis while ((opt = getopt_long(argc, argv, "d:c:f:op:w:qh",
2969cab9fdeSChristos Margiolis equalizer_opts, NULL)) != -1) {
2979cab9fdeSChristos Margiolis switch (opt) {
2989cab9fdeSChristos Margiolis case 'd':
2999cab9fdeSChristos Margiolis dsp = optarg;
3009cab9fdeSChristos Margiolis break;
3019cab9fdeSChristos Margiolis case 'c':
3029cab9fdeSChristos Margiolis channels = atoi(optarg);
3039cab9fdeSChristos Margiolis if (channels == 0) {
3049cab9fdeSChristos Margiolis message("Wrong number of channels\n");
3059cab9fdeSChristos Margiolis usage();
3069cab9fdeSChristos Margiolis }
3079cab9fdeSChristos Margiolis break;
3089cab9fdeSChristos Margiolis case 'p':
3099cab9fdeSChristos Margiolis part = atoi(optarg);
3109cab9fdeSChristos Margiolis if (part < 0) {
3119cab9fdeSChristos Margiolis message("Invalid part number\n");
3129cab9fdeSChristos Margiolis usage();
3139cab9fdeSChristos Margiolis }
3149cab9fdeSChristos Margiolis break;
3159cab9fdeSChristos Margiolis case 'w':
3169cab9fdeSChristos Margiolis if (strcmp(optarg, "rx_dev") == 0) {
3179cab9fdeSChristos Margiolis cmd_fir_set = VIRTUAL_OSS_SET_RX_DEV_FIR_FILTER;
3189cab9fdeSChristos Margiolis cmd_fir_get = VIRTUAL_OSS_GET_RX_DEV_FIR_FILTER;
3199cab9fdeSChristos Margiolis cmd_info = VIRTUAL_OSS_GET_DEV_INFO;
3209cab9fdeSChristos Margiolis } else if (strcmp(optarg, "tx_dev") == 0) {
3219cab9fdeSChristos Margiolis cmd_fir_set = VIRTUAL_OSS_SET_TX_DEV_FIR_FILTER;
3229cab9fdeSChristos Margiolis cmd_fir_get = VIRTUAL_OSS_GET_TX_DEV_FIR_FILTER;
3239cab9fdeSChristos Margiolis cmd_info = VIRTUAL_OSS_GET_DEV_INFO;
3249cab9fdeSChristos Margiolis } else if (strcmp(optarg, "rx_loop") == 0) {
3259cab9fdeSChristos Margiolis cmd_fir_set = VIRTUAL_OSS_SET_RX_LOOP_FIR_FILTER;
3269cab9fdeSChristos Margiolis cmd_fir_get = VIRTUAL_OSS_GET_RX_LOOP_FIR_FILTER;
3279cab9fdeSChristos Margiolis cmd_info = VIRTUAL_OSS_GET_LOOP_INFO;
3289cab9fdeSChristos Margiolis } else if (strcmp(optarg, "tx_loop") == 0) {
3299cab9fdeSChristos Margiolis cmd_fir_set = VIRTUAL_OSS_SET_TX_LOOP_FIR_FILTER;
3309cab9fdeSChristos Margiolis cmd_fir_get = VIRTUAL_OSS_GET_TX_LOOP_FIR_FILTER;
3319cab9fdeSChristos Margiolis cmd_info = VIRTUAL_OSS_GET_LOOP_INFO;
3329cab9fdeSChristos Margiolis } else {
3339cab9fdeSChristos Margiolis message("Bad -w argument not recognized\n");
3349cab9fdeSChristos Margiolis usage();
3359cab9fdeSChristos Margiolis }
3369cab9fdeSChristos Margiolis break;
3379cab9fdeSChristos Margiolis case 'f':
3389cab9fdeSChristos Margiolis if (f != STDIN_FILENO) {
3399cab9fdeSChristos Margiolis message("Can only specify one file\n");
3409cab9fdeSChristos Margiolis usage();
3419cab9fdeSChristos Margiolis }
3429cab9fdeSChristos Margiolis f = open(optarg, O_RDONLY);
3439cab9fdeSChristos Margiolis if (f < 0) {
3449cab9fdeSChristos Margiolis message("Cannot open specified file\n");
3459cab9fdeSChristos Margiolis usage();
3469cab9fdeSChristos Margiolis }
3479cab9fdeSChristos Margiolis break;
3489cab9fdeSChristos Margiolis case 'o':
3499cab9fdeSChristos Margiolis disable = 1;
3509cab9fdeSChristos Margiolis break;
3519cab9fdeSChristos Margiolis case 'q':
3529cab9fdeSChristos Margiolis be_silent = 1;
3539cab9fdeSChristos Margiolis break;
3549cab9fdeSChristos Margiolis default:
3559cab9fdeSChristos Margiolis usage();
3569cab9fdeSChristos Margiolis }
3579cab9fdeSChristos Margiolis }
3589cab9fdeSChristos Margiolis
3599cab9fdeSChristos Margiolis fir.number = part;
3609cab9fdeSChristos Margiolis info.number = part;
3619cab9fdeSChristos Margiolis
3629cab9fdeSChristos Margiolis int fd = open(dsp, O_RDWR);
3639cab9fdeSChristos Margiolis
3649cab9fdeSChristos Margiolis if (fd < 0) {
3659cab9fdeSChristos Margiolis message("Cannot open DSP device\n");
3669cab9fdeSChristos Margiolis return (EX_SOFTWARE);
3679cab9fdeSChristos Margiolis }
3689cab9fdeSChristos Margiolis if (ioctl(fd, VIRTUAL_OSS_GET_SAMPLE_RATE, &rate) < 0) {
3699cab9fdeSChristos Margiolis message("Cannot get sample rate\n");
3709cab9fdeSChristos Margiolis return (EX_SOFTWARE);
3719cab9fdeSChristos Margiolis }
3729cab9fdeSChristos Margiolis if (ioctl(fd, cmd_fir_get, &fir) < 0) {
3739cab9fdeSChristos Margiolis message("Cannot get current FIR filter\n");
3749cab9fdeSChristos Margiolis return (EX_SOFTWARE);
3759cab9fdeSChristos Margiolis }
3769cab9fdeSChristos Margiolis if (disable) {
3779cab9fdeSChristos Margiolis for (fir.channel = 0; fir.channel != channels; fir.channel++) {
3789cab9fdeSChristos Margiolis if (ioctl(fd, cmd_fir_set, &fir) < 0) {
3799cab9fdeSChristos Margiolis if (fir.channel == 0) {
3809cab9fdeSChristos Margiolis message("Cannot disable FIR filter\n");
3819cab9fdeSChristos Margiolis return (EX_SOFTWARE);
3829cab9fdeSChristos Margiolis }
3839cab9fdeSChristos Margiolis break;
3849cab9fdeSChristos Margiolis }
3859cab9fdeSChristos Margiolis }
3869cab9fdeSChristos Margiolis return (0);
3879cab9fdeSChristos Margiolis }
3889cab9fdeSChristos Margiolis equalizer_init(&e, rate, fir.filter_size);
3899cab9fdeSChristos Margiolis equalizer_load(&e, "");
3909cab9fdeSChristos Margiolis
3919cab9fdeSChristos Margiolis if (f == STDIN_FILENO) {
3929cab9fdeSChristos Margiolis if (ioctl(fd, cmd_info, &info) < 0) {
3939cab9fdeSChristos Margiolis message("Cannot read part information\n");
3949cab9fdeSChristos Margiolis return (EX_SOFTWARE);
3959cab9fdeSChristos Margiolis }
3969cab9fdeSChristos Margiolis message("Please enter EQ layout for %s, <freq> <gain>:\n", info.name);
3979cab9fdeSChristos Margiolis }
3989cab9fdeSChristos Margiolis offset = 0;
3999cab9fdeSChristos Margiolis while (1) {
4009cab9fdeSChristos Margiolis if (offset == (int)(sizeof(buffer) - 1)) {
4019cab9fdeSChristos Margiolis message("Too much input data\n");
4029cab9fdeSChristos Margiolis return (EX_SOFTWARE);
4039cab9fdeSChristos Margiolis }
4049cab9fdeSChristos Margiolis len = read(f, buffer + offset, sizeof(buffer) - 1 - offset);
4059cab9fdeSChristos Margiolis if (len <= 0)
4069cab9fdeSChristos Margiolis break;
4079cab9fdeSChristos Margiolis offset += len;
4089cab9fdeSChristos Margiolis }
4099cab9fdeSChristos Margiolis buffer[offset] = 0;
4109cab9fdeSChristos Margiolis close(f);
4119cab9fdeSChristos Margiolis
4129cab9fdeSChristos Margiolis if (f == STDIN_FILENO)
4139cab9fdeSChristos Margiolis message("Loading new EQ layout\n");
4149cab9fdeSChristos Margiolis
4159cab9fdeSChristos Margiolis if (equalizer_load(&e, buffer) == 0) {
4169cab9fdeSChristos Margiolis message("Invalid equalizer data\n");
4179cab9fdeSChristos Margiolis return (EX_SOFTWARE);
4189cab9fdeSChristos Margiolis }
4199cab9fdeSChristos Margiolis fir.filter_data = e.fftw_time;
4209cab9fdeSChristos Margiolis
4219cab9fdeSChristos Margiolis for (fir.channel = 0; fir.channel != channels; fir.channel++) {
4229cab9fdeSChristos Margiolis if (ioctl(fd, cmd_fir_set, &fir) < 0) {
4239cab9fdeSChristos Margiolis if (fir.channel == 0)
4249cab9fdeSChristos Margiolis message("Cannot set FIR filter on channel\n");
4259cab9fdeSChristos Margiolis break;
4269cab9fdeSChristos Margiolis }
4279cab9fdeSChristos Margiolis }
4289cab9fdeSChristos Margiolis
4299cab9fdeSChristos Margiolis close(fd);
4309cab9fdeSChristos Margiolis equalizer_done(&e);
4319cab9fdeSChristos Margiolis
4329cab9fdeSChristos Margiolis return (0);
4339cab9fdeSChristos Margiolis }
434