2 * Copyright (C) 2013-2015 Intel Corporation
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
30 static void check_amplitude(struct bat *bat, double *buf)
32 double sum, average, amplitude;
35 /* calculate average value */
36 for (i = 0, sum = 0.0; i < bat->frames; i++)
38 average = sum / bat->frames;
40 /* calculate peak-to-average amplitude */
41 for (i = 0, sum = 0.0; i < bat->frames; i++)
42 sum += abs(buf[i] - average);
43 amplitude = sum / bat->frames * M_PI / 2.0;
45 /* calculate amplitude percentage against full range */
46 percent = amplitude * 100 / ((1 << ((bat->sample_size << 3) - 1)) - 1);
48 fprintf(bat->log, _("Amplitude: %.1f; Percentage: [%d]\n"),
51 fprintf(bat->err, _("ERROR: Amplitude can't be negative!\n"));
53 fprintf(bat->err, _("WARNING: Signal too weak!\n"));
54 else if (percent > 100)
55 fprintf(bat->err, _("WARNING: Signal overflow!\n"));
60 * @return 0 if peak detected at right frequency,
61 * 1 if peak detected somewhere else
64 int check_peak(struct bat *bat, struct analyze *a, int end, int peak, float hz,
65 float mean, float p, int channel, int start)
68 float hz_peak = (float) (peak) * hz;
69 float delta_rate = DELTA_RATE * bat->target_freq[channel];
70 float delta_HZ = DELTA_HZ;
71 float tolerance = (delta_rate > delta_HZ) ? delta_rate : delta_HZ;
73 fprintf(bat->log, _("Detected peak at %2.2f Hz of %2.2f dB\n"), hz_peak,
74 10.0 * log10(a->mag[peak] / mean));
75 fprintf(bat->log, _(" Total %3.1f dB from %2.2f to %2.2f Hz\n"),
76 10.0 * log10(p / mean), start * hz, end * hz);
78 if (hz_peak < DC_THRESHOLD) {
79 fprintf(bat->err, _(" WARNING: Found low peak %2.2f Hz,"),
81 fprintf(bat->err, _(" very close to DC\n"));
83 } else if (hz_peak < bat->target_freq[channel] - tolerance) {
84 fprintf(bat->err, _(" FAIL: Peak freq too low %2.2f Hz\n"),
86 err = FOUND_WRONG_PEAK;
87 } else if (hz_peak > bat->target_freq[channel] + tolerance) {
88 fprintf(bat->err, _(" FAIL: Peak freq too high %2.2f Hz\n"),
90 err = FOUND_WRONG_PEAK;
92 fprintf(bat->log, _(" PASS: Peak detected"));
93 fprintf(bat->log, _(" at target frequency\n"));
101 * Search for main frequencies in fft results and compare it to target
103 static int check(struct bat *bat, struct analyze *a, int channel)
105 float hz = 1.0 / ((float) bat->frames / (float) bat->rate);
106 float mean = 0.0, t, sigma = 0.0, p = 0.0;
107 int i, start = -1, end = -1, peak = 0, signals = 0;
108 int err = 0, N = bat->frames / 2;
111 for (i = 0; i < N; i++)
115 /* calculate standard deviation */
116 for (i = 0; i < N; i++) {
117 t = a->mag[i] - mean;
122 sigma = sqrtf(sigma);
124 /* clip any data less than k sigma + mean */
125 for (i = 0; i < N; i++) {
126 if (a->mag[i] > mean + bat->sigma_k * sigma) {
128 /* find peak start points */
130 start = peak = end = i;
133 if (a->mag[i] > a->mag[peak])
138 } else if (start != -1) {
139 /* Check if peak is as expected */
140 err |= check_peak(bat, a, end, peak, hz, mean,
143 if (signals == MAX_PEAKS)
148 err = -ENOPEAK; /* No peak detected */
149 else if ((err == FOUND_DC) && (signals == 1))
150 err = -EONLYDC; /* Only DC detected */
151 else if ((err & FOUND_WRONG_PEAK) == FOUND_WRONG_PEAK)
152 err = -EBADPEAK; /* Bad peak detected */
154 err = 0; /* Correct peak detected */
156 fprintf(bat->log, _("Detected at least %d signal(s) in total\n"),
162 static void calc_magnitude(struct bat *bat, struct analyze *a, int N)
167 for (i = 1; i < N / 2; i++) {
168 r2 = a->out[i] * a->out[i];
169 i2 = a->out[N - i] * a->out[N - i];
171 a->mag[i] = sqrtf(r2 + i2);
176 static int find_and_check_harmonics(struct bat *bat, struct analyze *a,
180 int err = -ENOMEM, N = bat->frames;
182 /* Allocate FFT buffers */
183 a->in = (double *) fftw_malloc(sizeof(double) * bat->frames);
187 a->out = (double *) fftw_malloc(sizeof(double) * bat->frames);
191 a->mag = (double *) fftw_malloc(sizeof(double) * bat->frames);
195 /* create FFT plan */
196 p = fftw_plan_r2r_1d(N, a->in, a->out, FFTW_R2HC,
197 FFTW_MEASURE | FFTW_PRESERVE_INPUT);
201 /* convert source PCM to doubles */
202 bat->convert_sample_to_double(a->buf, a->in, bat->frames);
204 /* check amplitude */
205 check_amplitude(bat, a->in);
210 /* FFT out is real and imaginary numbers - calc magnitude for each */
211 calc_magnitude(bat, a, N);
214 err = check(bat, a, channel);
216 fftw_destroy_plan(p);
229 * Convert interleaved samples from channels in samples from a single channel
231 static int reorder_data(struct bat *bat)
233 char *p, *new_bat_buf;
236 if (bat->channels == 1)
237 return 0; /* No need for reordering */
239 p = malloc(bat->frames * bat->frame_size);
244 for (ch = 0; ch < bat->channels; ch++) {
245 for (j = 0; j < bat->frames; j++) {
246 for (i = 0; i < bat->sample_size; i++) {
247 *p++ = ((char *) (bat->buf))[j * bat->frame_size
248 + ch * bat->sample_size + i];
254 bat->buf = new_bat_buf;
259 /* truncate sample frames for faster FFT analysis process */
260 static int truncate_frames(struct bat *bat)
262 int shift = SHIFT_MAX;
264 for (; shift > SHIFT_MIN; shift--)
265 if (bat->frames & (1 << shift)) {
266 bat->frames = 1 << shift;
273 int analyze_capture(struct bat *bat)
280 err = truncate_frames(bat);
282 fprintf(bat->err, _("Invalid frame number for analysis: %d\n"),
287 fprintf(bat->log, _("\nBAT analysis: signal has %d frames at %d Hz,"),
288 bat->frames, bat->rate);
289 fprintf(bat->log, _(" %d channels, %d bytes per sample.\n"),
290 bat->channels, bat->sample_size);
292 bat->buf = malloc(bat->frames * bat->frame_size);
293 if (bat->buf == NULL)
296 bat->fp = fopen(bat->capture.file, "rb");
298 if (bat->fp == NULL) {
299 fprintf(bat->err, _("Cannot open file: %s %d\n"),
300 bat->capture.file, err);
305 err = read_wav_header(bat, bat->capture.file, bat->fp, true);
309 items = fread(bat->buf, bat->frame_size, bat->frames, bat->fp);
310 if (items != bat->frames) {
315 err = reorder_data(bat);
319 for (c = 0; c < bat->channels; c++) {
320 fprintf(bat->log, _("\nChannel %i - "), c + 1);
321 fprintf(bat->log, _("Checking for target frequency %2.2f Hz\n"),
322 bat->target_freq[c]);
324 c * bat->frames * bat->frame_size
326 err = find_and_check_harmonics(bat, &a, c);