xref: /third_party/alsa-utils/bat/analyze.c (revision c72fcc34)
1c72fcc34Sopenharmony_ci/*
2c72fcc34Sopenharmony_ci * Copyright (C) 2013-2015 Intel Corporation
3c72fcc34Sopenharmony_ci *
4c72fcc34Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
5c72fcc34Sopenharmony_ci * it under the terms of the GNU General Public License as published by
6c72fcc34Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
7c72fcc34Sopenharmony_ci * (at your option) any later version.
8c72fcc34Sopenharmony_ci *
9c72fcc34Sopenharmony_ci * This program is distributed in the hope that it will be useful,
10c72fcc34Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
11c72fcc34Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12c72fcc34Sopenharmony_ci * GNU General Public License for more details.
13c72fcc34Sopenharmony_ci *
14c72fcc34Sopenharmony_ci */
15c72fcc34Sopenharmony_ci
16c72fcc34Sopenharmony_ci#include "aconfig.h"
17c72fcc34Sopenharmony_ci
18c72fcc34Sopenharmony_ci#include <stdio.h>
19c72fcc34Sopenharmony_ci#include <stdlib.h>
20c72fcc34Sopenharmony_ci#include <errno.h>
21c72fcc34Sopenharmony_ci#include <stdbool.h>
22c72fcc34Sopenharmony_ci#include <stdint.h>
23c72fcc34Sopenharmony_ci
24c72fcc34Sopenharmony_ci#include <math.h>
25c72fcc34Sopenharmony_ci#include <fftw3.h>
26c72fcc34Sopenharmony_ci
27c72fcc34Sopenharmony_ci#include "gettext.h"
28c72fcc34Sopenharmony_ci
29c72fcc34Sopenharmony_ci#include "common.h"
30c72fcc34Sopenharmony_ci#include "bat-signal.h"
31c72fcc34Sopenharmony_ci
32c72fcc34Sopenharmony_cistatic void check_amplitude(struct bat *bat, float *buf)
33c72fcc34Sopenharmony_ci{
34c72fcc34Sopenharmony_ci	float sum, average, amplitude;
35c72fcc34Sopenharmony_ci	int i, percent;
36c72fcc34Sopenharmony_ci
37c72fcc34Sopenharmony_ci	/* calculate average value */
38c72fcc34Sopenharmony_ci	for (i = 0, sum = 0.0, average = 0.0; i < bat->frames; i++)
39c72fcc34Sopenharmony_ci		sum += buf[i];
40c72fcc34Sopenharmony_ci	average = sum / bat->frames;
41c72fcc34Sopenharmony_ci
42c72fcc34Sopenharmony_ci	/* calculate peak-to-average amplitude */
43c72fcc34Sopenharmony_ci	for (i = 0, sum = 0.0; i < bat->frames; i++)
44c72fcc34Sopenharmony_ci		sum += fabsf(buf[i] - average);
45c72fcc34Sopenharmony_ci	amplitude = sum / bat->frames * M_PI / 2.0;
46c72fcc34Sopenharmony_ci
47c72fcc34Sopenharmony_ci	/* calculate amplitude percentage against full range */
48c72fcc34Sopenharmony_ci	percent = amplitude * 100 / ((1 << ((bat->sample_size << 3) - 1)) - 1);
49c72fcc34Sopenharmony_ci
50c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Amplitude: %.1f; Percentage: [%d]\n"),
51c72fcc34Sopenharmony_ci			amplitude, percent);
52c72fcc34Sopenharmony_ci	if (percent < 0)
53c72fcc34Sopenharmony_ci		fprintf(bat->err, _("ERROR: Amplitude can't be negative!\n"));
54c72fcc34Sopenharmony_ci	else if (percent < 1)
55c72fcc34Sopenharmony_ci		fprintf(bat->err, _("WARNING: Signal too weak!\n"));
56c72fcc34Sopenharmony_ci	else if (percent > 100)
57c72fcc34Sopenharmony_ci		fprintf(bat->err, _("WARNING: Signal overflow!\n"));
58c72fcc34Sopenharmony_ci}
59c72fcc34Sopenharmony_ci
60c72fcc34Sopenharmony_ci/**
61c72fcc34Sopenharmony_ci *
62c72fcc34Sopenharmony_ci * @return 0 if peak detected at right frequency,
63c72fcc34Sopenharmony_ci *         1 if peak detected somewhere else
64c72fcc34Sopenharmony_ci *         2 if DC detected
65c72fcc34Sopenharmony_ci */
66c72fcc34Sopenharmony_ciint check_peak(struct bat *bat, struct analyze *a, int end, int peak, float hz,
67c72fcc34Sopenharmony_ci		float mean, float p, int channel, int start)
68c72fcc34Sopenharmony_ci{
69c72fcc34Sopenharmony_ci	int err;
70c72fcc34Sopenharmony_ci	float hz_peak = (float) (peak) * hz;
71c72fcc34Sopenharmony_ci	float delta_rate = DELTA_RATE * bat->target_freq[channel];
72c72fcc34Sopenharmony_ci	float delta_HZ = DELTA_HZ;
73c72fcc34Sopenharmony_ci	float tolerance = (delta_rate > delta_HZ) ? delta_rate : delta_HZ;
74c72fcc34Sopenharmony_ci
75c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Detected peak at %2.2f Hz of %2.2f dB\n"), hz_peak,
76c72fcc34Sopenharmony_ci			10.0 * log10f(a->mag[peak] / mean));
77c72fcc34Sopenharmony_ci	fprintf(bat->log, _(" Total %3.1f dB from %2.2f to %2.2f Hz\n"),
78c72fcc34Sopenharmony_ci			10.0 * log10f(p / mean), start * hz, end * hz);
79c72fcc34Sopenharmony_ci
80c72fcc34Sopenharmony_ci	if (hz_peak < DC_THRESHOLD) {
81c72fcc34Sopenharmony_ci		fprintf(bat->err, _(" WARNING: Found low peak %2.2f Hz,"),
82c72fcc34Sopenharmony_ci				hz_peak);
83c72fcc34Sopenharmony_ci		fprintf(bat->err, _(" very close to DC\n"));
84c72fcc34Sopenharmony_ci		err = FOUND_DC;
85c72fcc34Sopenharmony_ci	} else if (hz_peak < bat->target_freq[channel] - tolerance) {
86c72fcc34Sopenharmony_ci		fprintf(bat->err, _(" FAIL: Peak freq too low %2.2f Hz\n"),
87c72fcc34Sopenharmony_ci				hz_peak);
88c72fcc34Sopenharmony_ci		err = FOUND_WRONG_PEAK;
89c72fcc34Sopenharmony_ci	} else if (hz_peak > bat->target_freq[channel] + tolerance) {
90c72fcc34Sopenharmony_ci		fprintf(bat->err, _(" FAIL: Peak freq too high %2.2f Hz\n"),
91c72fcc34Sopenharmony_ci				hz_peak);
92c72fcc34Sopenharmony_ci		err = FOUND_WRONG_PEAK;
93c72fcc34Sopenharmony_ci	} else {
94c72fcc34Sopenharmony_ci		fprintf(bat->log, _(" PASS: Peak detected"));
95c72fcc34Sopenharmony_ci		fprintf(bat->log, _(" at target frequency\n"));
96c72fcc34Sopenharmony_ci		err = 0;
97c72fcc34Sopenharmony_ci	}
98c72fcc34Sopenharmony_ci
99c72fcc34Sopenharmony_ci	return err;
100c72fcc34Sopenharmony_ci}
101c72fcc34Sopenharmony_ci
102c72fcc34Sopenharmony_ci/**
103c72fcc34Sopenharmony_ci * Search for main frequencies in fft results and compare it to target
104c72fcc34Sopenharmony_ci */
105c72fcc34Sopenharmony_cistatic int check(struct bat *bat, struct analyze *a, int channel)
106c72fcc34Sopenharmony_ci{
107c72fcc34Sopenharmony_ci	float hz = 1.0 / ((float) bat->frames / (float) bat->rate);
108c72fcc34Sopenharmony_ci	float mean = 0.0, t, sigma = 0.0, p = 0.0;
109c72fcc34Sopenharmony_ci	int i, start = -1, end = -1, peak = 0, signals = 0;
110c72fcc34Sopenharmony_ci	int err = 0, N = bat->frames / 2;
111c72fcc34Sopenharmony_ci
112c72fcc34Sopenharmony_ci	/* calculate mean */
113c72fcc34Sopenharmony_ci	for (i = 0; i < N; i++)
114c72fcc34Sopenharmony_ci		mean += a->mag[i];
115c72fcc34Sopenharmony_ci	mean /= (float) N;
116c72fcc34Sopenharmony_ci
117c72fcc34Sopenharmony_ci	/* calculate standard deviation */
118c72fcc34Sopenharmony_ci	for (i = 0; i < N; i++) {
119c72fcc34Sopenharmony_ci		t = a->mag[i] - mean;
120c72fcc34Sopenharmony_ci		t *= t;
121c72fcc34Sopenharmony_ci		sigma += t;
122c72fcc34Sopenharmony_ci	}
123c72fcc34Sopenharmony_ci	sigma /= (float) N;
124c72fcc34Sopenharmony_ci	sigma = sqrtf(sigma);
125c72fcc34Sopenharmony_ci
126c72fcc34Sopenharmony_ci	/* clip any data less than k sigma + mean */
127c72fcc34Sopenharmony_ci	for (i = 0; i < N; i++) {
128c72fcc34Sopenharmony_ci		if (a->mag[i] > mean + bat->sigma_k * sigma) {
129c72fcc34Sopenharmony_ci
130c72fcc34Sopenharmony_ci			/* find peak start points */
131c72fcc34Sopenharmony_ci			if (start == -1) {
132c72fcc34Sopenharmony_ci				start = peak = end = i;
133c72fcc34Sopenharmony_ci				signals++;
134c72fcc34Sopenharmony_ci			} else {
135c72fcc34Sopenharmony_ci				if (a->mag[i] > a->mag[peak])
136c72fcc34Sopenharmony_ci					peak = i;
137c72fcc34Sopenharmony_ci				end = i;
138c72fcc34Sopenharmony_ci			}
139c72fcc34Sopenharmony_ci			p += a->mag[i];
140c72fcc34Sopenharmony_ci		} else if (start != -1) {
141c72fcc34Sopenharmony_ci			/* Check if peak is as expected */
142c72fcc34Sopenharmony_ci			err |= check_peak(bat, a, end, peak, hz, mean,
143c72fcc34Sopenharmony_ci					p, channel, start);
144c72fcc34Sopenharmony_ci			end = start = -1;
145c72fcc34Sopenharmony_ci			if (signals == MAX_PEAKS)
146c72fcc34Sopenharmony_ci				break;
147c72fcc34Sopenharmony_ci		}
148c72fcc34Sopenharmony_ci	}
149c72fcc34Sopenharmony_ci	if (signals == 0)
150c72fcc34Sopenharmony_ci		err = -ENOPEAK; /* No peak detected */
151c72fcc34Sopenharmony_ci	else if ((err == FOUND_DC) && (signals == 1))
152c72fcc34Sopenharmony_ci		err = -EONLYDC; /* Only DC detected */
153c72fcc34Sopenharmony_ci	else if ((err & FOUND_WRONG_PEAK) == FOUND_WRONG_PEAK)
154c72fcc34Sopenharmony_ci		err = -EBADPEAK; /* Bad peak detected */
155c72fcc34Sopenharmony_ci	else
156c72fcc34Sopenharmony_ci		err = 0; /* Correct peak detected */
157c72fcc34Sopenharmony_ci
158c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Detected at least %d signal(s) in total\n"),
159c72fcc34Sopenharmony_ci			signals);
160c72fcc34Sopenharmony_ci
161c72fcc34Sopenharmony_ci	return err;
162c72fcc34Sopenharmony_ci}
163c72fcc34Sopenharmony_ci
164c72fcc34Sopenharmony_cistatic void calc_magnitude(struct bat *bat, struct analyze *a, int N)
165c72fcc34Sopenharmony_ci{
166c72fcc34Sopenharmony_ci	float r2, i2;
167c72fcc34Sopenharmony_ci	int i;
168c72fcc34Sopenharmony_ci
169c72fcc34Sopenharmony_ci	for (i = 1; i < N / 2; i++) {
170c72fcc34Sopenharmony_ci		r2 = a->out[i] * a->out[i];
171c72fcc34Sopenharmony_ci		i2 = a->out[N - i] * a->out[N - i];
172c72fcc34Sopenharmony_ci
173c72fcc34Sopenharmony_ci		a->mag[i] = sqrtf(r2 + i2);
174c72fcc34Sopenharmony_ci	}
175c72fcc34Sopenharmony_ci	a->mag[0] = 0.0;
176c72fcc34Sopenharmony_ci}
177c72fcc34Sopenharmony_ci
178c72fcc34Sopenharmony_cistatic int find_and_check_harmonics(struct bat *bat, struct analyze *a,
179c72fcc34Sopenharmony_ci		int channel)
180c72fcc34Sopenharmony_ci{
181c72fcc34Sopenharmony_ci	fftwf_plan p;
182c72fcc34Sopenharmony_ci	int err = -ENOMEM, N = bat->frames;
183c72fcc34Sopenharmony_ci
184c72fcc34Sopenharmony_ci	/* Allocate FFT buffers */
185c72fcc34Sopenharmony_ci	a->in = (float *) fftwf_malloc(sizeof(float) * bat->frames);
186c72fcc34Sopenharmony_ci	if (a->in == NULL)
187c72fcc34Sopenharmony_ci		goto out1;
188c72fcc34Sopenharmony_ci
189c72fcc34Sopenharmony_ci	a->out = (float *) fftwf_malloc(sizeof(float) * bat->frames);
190c72fcc34Sopenharmony_ci	if (a->out == NULL)
191c72fcc34Sopenharmony_ci		goto out2;
192c72fcc34Sopenharmony_ci
193c72fcc34Sopenharmony_ci	a->mag = (float *) fftwf_malloc(sizeof(float) * bat->frames);
194c72fcc34Sopenharmony_ci	if (a->mag == NULL)
195c72fcc34Sopenharmony_ci		goto out3;
196c72fcc34Sopenharmony_ci
197c72fcc34Sopenharmony_ci	/* create FFT plan */
198c72fcc34Sopenharmony_ci	p = fftwf_plan_r2r_1d(N, a->in, a->out, FFTW_R2HC,
199c72fcc34Sopenharmony_ci			FFTW_MEASURE | FFTW_PRESERVE_INPUT);
200c72fcc34Sopenharmony_ci	if (p == NULL)
201c72fcc34Sopenharmony_ci		goto out4;
202c72fcc34Sopenharmony_ci
203c72fcc34Sopenharmony_ci	/* convert source PCM to floats */
204c72fcc34Sopenharmony_ci	bat->convert_sample_to_float(a->buf, a->in, bat->frames);
205c72fcc34Sopenharmony_ci
206c72fcc34Sopenharmony_ci	/* check amplitude */
207c72fcc34Sopenharmony_ci	check_amplitude(bat, a->in);
208c72fcc34Sopenharmony_ci
209c72fcc34Sopenharmony_ci	/* run FFT */
210c72fcc34Sopenharmony_ci	fftwf_execute(p);
211c72fcc34Sopenharmony_ci
212c72fcc34Sopenharmony_ci	/* FFT out is real and imaginary numbers - calc magnitude for each */
213c72fcc34Sopenharmony_ci	calc_magnitude(bat, a, N);
214c72fcc34Sopenharmony_ci
215c72fcc34Sopenharmony_ci	/* check data */
216c72fcc34Sopenharmony_ci	err = check(bat, a, channel);
217c72fcc34Sopenharmony_ci
218c72fcc34Sopenharmony_ci	fftwf_destroy_plan(p);
219c72fcc34Sopenharmony_ci
220c72fcc34Sopenharmony_ciout4:
221c72fcc34Sopenharmony_ci	fftwf_free(a->mag);
222c72fcc34Sopenharmony_ciout3:
223c72fcc34Sopenharmony_ci	fftwf_free(a->out);
224c72fcc34Sopenharmony_ciout2:
225c72fcc34Sopenharmony_ci	fftwf_free(a->in);
226c72fcc34Sopenharmony_ciout1:
227c72fcc34Sopenharmony_ci	return err;
228c72fcc34Sopenharmony_ci}
229c72fcc34Sopenharmony_ci
230c72fcc34Sopenharmony_cistatic int calculate_noise_one_period(struct bat *bat,
231c72fcc34Sopenharmony_ci		struct noise_analyzer *na, float *src,
232c72fcc34Sopenharmony_ci		int length, int channel)
233c72fcc34Sopenharmony_ci{
234c72fcc34Sopenharmony_ci	int i, shift = 0;
235c72fcc34Sopenharmony_ci	float tmp, rms, gain, residual;
236c72fcc34Sopenharmony_ci	float a = 0.0, b = 1.0;
237c72fcc34Sopenharmony_ci
238c72fcc34Sopenharmony_ci	/* step 1. phase compensation */
239c72fcc34Sopenharmony_ci
240c72fcc34Sopenharmony_ci	if (length < 2 * na->nsamples)
241c72fcc34Sopenharmony_ci		return -EINVAL;
242c72fcc34Sopenharmony_ci
243c72fcc34Sopenharmony_ci	/* search for the beginning of a sine period */
244c72fcc34Sopenharmony_ci	for (i = 0, tmp = 0.0, shift = -1; i < na->nsamples; i++) {
245c72fcc34Sopenharmony_ci		/* find i where src[i] >= 0 && src[i+1] < 0 */
246c72fcc34Sopenharmony_ci		if (src[i] < 0.0)
247c72fcc34Sopenharmony_ci			continue;
248c72fcc34Sopenharmony_ci		if (src[i + 1] < 0.0) {
249c72fcc34Sopenharmony_ci			tmp = src[i] - src[i + 1];
250c72fcc34Sopenharmony_ci			a = src[i] / tmp;
251c72fcc34Sopenharmony_ci			b = -src[i + 1] / tmp;
252c72fcc34Sopenharmony_ci			shift = i;
253c72fcc34Sopenharmony_ci			break;
254c72fcc34Sopenharmony_ci		}
255c72fcc34Sopenharmony_ci	}
256c72fcc34Sopenharmony_ci
257c72fcc34Sopenharmony_ci	/* didn't find the beginning of a sine period */
258c72fcc34Sopenharmony_ci	if (shift == -1)
259c72fcc34Sopenharmony_ci		return -EINVAL;
260c72fcc34Sopenharmony_ci
261c72fcc34Sopenharmony_ci	/* shift sine waveform to source[0] = 0.0 */
262c72fcc34Sopenharmony_ci	for (i = 0; i < na->nsamples; i++)
263c72fcc34Sopenharmony_ci		na->source[i] = a * src[i + shift + 1] + b * src[i + shift];
264c72fcc34Sopenharmony_ci
265c72fcc34Sopenharmony_ci	/* step 2. gain compensation */
266c72fcc34Sopenharmony_ci
267c72fcc34Sopenharmony_ci	/* calculate rms of signal amplitude */
268c72fcc34Sopenharmony_ci	for (i = 0, tmp = 0.0; i < na->nsamples; i++)
269c72fcc34Sopenharmony_ci		tmp += na->source[i] * na->source[i];
270c72fcc34Sopenharmony_ci	rms = sqrtf(tmp / na->nsamples);
271c72fcc34Sopenharmony_ci
272c72fcc34Sopenharmony_ci	gain = na->rms_tgt / rms;
273c72fcc34Sopenharmony_ci
274c72fcc34Sopenharmony_ci	for (i = 0; i < na->nsamples; i++)
275c72fcc34Sopenharmony_ci		na->source[i] *= gain;
276c72fcc34Sopenharmony_ci
277c72fcc34Sopenharmony_ci	/* step 3. calculate snr in dB */
278c72fcc34Sopenharmony_ci
279c72fcc34Sopenharmony_ci	for (i = 0, tmp = 0.0, residual = 0.0; i < na->nsamples; i++) {
280c72fcc34Sopenharmony_ci		tmp = fabsf(na->target[i] - na->source[i]);
281c72fcc34Sopenharmony_ci		residual += tmp * tmp;
282c72fcc34Sopenharmony_ci	}
283c72fcc34Sopenharmony_ci
284c72fcc34Sopenharmony_ci	tmp = na->rms_tgt / sqrtf(residual / na->nsamples);
285c72fcc34Sopenharmony_ci	na->snr_db = 20.0 * log10f(tmp);
286c72fcc34Sopenharmony_ci
287c72fcc34Sopenharmony_ci	return 0;
288c72fcc34Sopenharmony_ci}
289c72fcc34Sopenharmony_ci
290c72fcc34Sopenharmony_cistatic int calculate_noise(struct bat *bat, float *src, int channel)
291c72fcc34Sopenharmony_ci{
292c72fcc34Sopenharmony_ci	int err = 0;
293c72fcc34Sopenharmony_ci	struct noise_analyzer na;
294c72fcc34Sopenharmony_ci	float freq = bat->target_freq[channel];
295c72fcc34Sopenharmony_ci	float tmp, sum_snr_pc, avg_snr_pc, avg_snr_db;
296c72fcc34Sopenharmony_ci	int offset, i, cnt_noise, cnt_clean;
297c72fcc34Sopenharmony_ci	/* num of samples in each sine period */
298c72fcc34Sopenharmony_ci	int nsamples = (int) ceilf(bat->rate / freq);
299c72fcc34Sopenharmony_ci	/* each section has 2 sine periods, the first one for locating
300c72fcc34Sopenharmony_ci	 * and the second one for noise calculating */
301c72fcc34Sopenharmony_ci	int nsamples_per_section = nsamples * 2;
302c72fcc34Sopenharmony_ci	/* all sine periods will be calculated except the first and last one */
303c72fcc34Sopenharmony_ci	int nsection = bat->frames / nsamples - 1;
304c72fcc34Sopenharmony_ci
305c72fcc34Sopenharmony_ci	fprintf(bat->log, _("samples per period: %d\n"), nsamples);
306c72fcc34Sopenharmony_ci	fprintf(bat->log, _("total sections to detect: %d\n"), nsection);
307c72fcc34Sopenharmony_ci	na.source = (float *)malloc(sizeof(float) * nsamples);
308c72fcc34Sopenharmony_ci	if (!na.source) {
309c72fcc34Sopenharmony_ci		err = -ENOMEM;
310c72fcc34Sopenharmony_ci		goto out1;
311c72fcc34Sopenharmony_ci	}
312c72fcc34Sopenharmony_ci
313c72fcc34Sopenharmony_ci	na.target = (float *)malloc(sizeof(float) * nsamples);
314c72fcc34Sopenharmony_ci	if (!na.target) {
315c72fcc34Sopenharmony_ci		err = -ENOMEM;
316c72fcc34Sopenharmony_ci		goto out2;
317c72fcc34Sopenharmony_ci	}
318c72fcc34Sopenharmony_ci
319c72fcc34Sopenharmony_ci	/* generate standard single-tone signal */
320c72fcc34Sopenharmony_ci	err = generate_sine_wave_raw_mono(bat, na.target, freq, nsamples);
321c72fcc34Sopenharmony_ci	if (err < 0)
322c72fcc34Sopenharmony_ci		goto out3;
323c72fcc34Sopenharmony_ci
324c72fcc34Sopenharmony_ci	na.nsamples = nsamples;
325c72fcc34Sopenharmony_ci
326c72fcc34Sopenharmony_ci	/* calculate rms of standard signal */
327c72fcc34Sopenharmony_ci	for (i = 0, tmp = 0.0; i < nsamples; i++)
328c72fcc34Sopenharmony_ci		tmp += na.target[i] * na.target[i];
329c72fcc34Sopenharmony_ci	na.rms_tgt = sqrtf(tmp / nsamples);
330c72fcc34Sopenharmony_ci
331c72fcc34Sopenharmony_ci	/* calculate average noise level */
332c72fcc34Sopenharmony_ci	sum_snr_pc = 0.0;
333c72fcc34Sopenharmony_ci	cnt_clean = cnt_noise = 0;
334c72fcc34Sopenharmony_ci	for (i = 1, offset = nsamples; i < nsection; i++) {
335c72fcc34Sopenharmony_ci		na.snr_db = SNR_DB_INVALID;
336c72fcc34Sopenharmony_ci
337c72fcc34Sopenharmony_ci		err = calculate_noise_one_period(bat, &na, src + offset,
338c72fcc34Sopenharmony_ci				nsamples_per_section, channel);
339c72fcc34Sopenharmony_ci		if (err < 0)
340c72fcc34Sopenharmony_ci			goto out3;
341c72fcc34Sopenharmony_ci
342c72fcc34Sopenharmony_ci		if (na.snr_db > bat->snr_thd_db) {
343c72fcc34Sopenharmony_ci			cnt_clean++;
344c72fcc34Sopenharmony_ci			sum_snr_pc += 100.0 / powf(10.0, na.snr_db / 20.0);
345c72fcc34Sopenharmony_ci		} else {
346c72fcc34Sopenharmony_ci			cnt_noise++;
347c72fcc34Sopenharmony_ci		}
348c72fcc34Sopenharmony_ci		offset += nsamples;
349c72fcc34Sopenharmony_ci	}
350c72fcc34Sopenharmony_ci
351c72fcc34Sopenharmony_ci	if (cnt_noise > 0) {
352c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Noise detected at %d points.\n"),
353c72fcc34Sopenharmony_ci				cnt_noise);
354c72fcc34Sopenharmony_ci		err = -cnt_noise;
355c72fcc34Sopenharmony_ci		if (cnt_clean == 0)
356c72fcc34Sopenharmony_ci			goto out3;
357c72fcc34Sopenharmony_ci	} else {
358c72fcc34Sopenharmony_ci		fprintf(bat->log, _("No noise detected.\n"));
359c72fcc34Sopenharmony_ci	}
360c72fcc34Sopenharmony_ci
361c72fcc34Sopenharmony_ci	avg_snr_pc = sum_snr_pc / cnt_clean;
362c72fcc34Sopenharmony_ci	avg_snr_db = 20.0 * log10f(100.0 / avg_snr_pc);
363c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Average SNR is %.2f dB (%.2f %%) at %d points.\n"),
364c72fcc34Sopenharmony_ci			avg_snr_db, avg_snr_pc, cnt_clean);
365c72fcc34Sopenharmony_ci
366c72fcc34Sopenharmony_ciout3:
367c72fcc34Sopenharmony_ci	free(na.target);
368c72fcc34Sopenharmony_ciout2:
369c72fcc34Sopenharmony_ci	free(na.source);
370c72fcc34Sopenharmony_ciout1:
371c72fcc34Sopenharmony_ci	return err;
372c72fcc34Sopenharmony_ci}
373c72fcc34Sopenharmony_ci
374c72fcc34Sopenharmony_cistatic int find_and_check_noise(struct bat *bat, void *buf, int channel)
375c72fcc34Sopenharmony_ci{
376c72fcc34Sopenharmony_ci	int err = 0;
377c72fcc34Sopenharmony_ci	float *source;
378c72fcc34Sopenharmony_ci
379c72fcc34Sopenharmony_ci	source = (float *)malloc(sizeof(float) * bat->frames);
380c72fcc34Sopenharmony_ci	if (!source)
381c72fcc34Sopenharmony_ci		return -ENOMEM;
382c72fcc34Sopenharmony_ci
383c72fcc34Sopenharmony_ci	/* convert source PCM to floats */
384c72fcc34Sopenharmony_ci	bat->convert_sample_to_float(buf, source, bat->frames);
385c72fcc34Sopenharmony_ci
386c72fcc34Sopenharmony_ci	/* adjust waveform and calculate noise */
387c72fcc34Sopenharmony_ci	err = calculate_noise(bat, source, channel);
388c72fcc34Sopenharmony_ci
389c72fcc34Sopenharmony_ci	free(source);
390c72fcc34Sopenharmony_ci	return err;
391c72fcc34Sopenharmony_ci}
392c72fcc34Sopenharmony_ci
393c72fcc34Sopenharmony_ci/**
394c72fcc34Sopenharmony_ci * Convert interleaved samples from channels in samples from a single channel
395c72fcc34Sopenharmony_ci */
396c72fcc34Sopenharmony_cistatic int reorder_data(struct bat *bat)
397c72fcc34Sopenharmony_ci{
398c72fcc34Sopenharmony_ci	char *p, *new_bat_buf;
399c72fcc34Sopenharmony_ci	int ch, i, j;
400c72fcc34Sopenharmony_ci
401c72fcc34Sopenharmony_ci	if (bat->channels == 1)
402c72fcc34Sopenharmony_ci		return 0; /* No need for reordering */
403c72fcc34Sopenharmony_ci
404c72fcc34Sopenharmony_ci	p = malloc(bat->frames * bat->frame_size);
405c72fcc34Sopenharmony_ci	new_bat_buf = p;
406c72fcc34Sopenharmony_ci	if (p == NULL)
407c72fcc34Sopenharmony_ci		return -ENOMEM;
408c72fcc34Sopenharmony_ci
409c72fcc34Sopenharmony_ci	for (ch = 0; ch < bat->channels; ch++) {
410c72fcc34Sopenharmony_ci		for (j = 0; j < bat->frames; j++) {
411c72fcc34Sopenharmony_ci			for (i = 0; i < bat->sample_size; i++) {
412c72fcc34Sopenharmony_ci				*p++ = ((char *) (bat->buf))[j * bat->frame_size
413c72fcc34Sopenharmony_ci						+ ch * bat->sample_size + i];
414c72fcc34Sopenharmony_ci			}
415c72fcc34Sopenharmony_ci		}
416c72fcc34Sopenharmony_ci	}
417c72fcc34Sopenharmony_ci
418c72fcc34Sopenharmony_ci	free(bat->buf);
419c72fcc34Sopenharmony_ci	bat->buf = new_bat_buf;
420c72fcc34Sopenharmony_ci
421c72fcc34Sopenharmony_ci	return 0;
422c72fcc34Sopenharmony_ci}
423c72fcc34Sopenharmony_ci
424c72fcc34Sopenharmony_ci/* truncate sample frames for faster FFT analysis process */
425c72fcc34Sopenharmony_cistatic int truncate_frames(struct bat *bat)
426c72fcc34Sopenharmony_ci{
427c72fcc34Sopenharmony_ci	int shift = SHIFT_MAX;
428c72fcc34Sopenharmony_ci
429c72fcc34Sopenharmony_ci	for (; shift > SHIFT_MIN; shift--)
430c72fcc34Sopenharmony_ci		if (bat->frames & (1 << shift)) {
431c72fcc34Sopenharmony_ci			bat->frames = 1 << shift;
432c72fcc34Sopenharmony_ci			return 0;
433c72fcc34Sopenharmony_ci		}
434c72fcc34Sopenharmony_ci
435c72fcc34Sopenharmony_ci	return -EINVAL;
436c72fcc34Sopenharmony_ci}
437c72fcc34Sopenharmony_ci
438c72fcc34Sopenharmony_ciint analyze_capture(struct bat *bat)
439c72fcc34Sopenharmony_ci{
440c72fcc34Sopenharmony_ci	int err = 0;
441c72fcc34Sopenharmony_ci	size_t items;
442c72fcc34Sopenharmony_ci	int c;
443c72fcc34Sopenharmony_ci	struct analyze a;
444c72fcc34Sopenharmony_ci
445c72fcc34Sopenharmony_ci	err = truncate_frames(bat);
446c72fcc34Sopenharmony_ci	if (err < 0) {
447c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Invalid frame number for analysis: %d\n"),
448c72fcc34Sopenharmony_ci				bat->frames);
449c72fcc34Sopenharmony_ci		return err;
450c72fcc34Sopenharmony_ci	}
451c72fcc34Sopenharmony_ci
452c72fcc34Sopenharmony_ci	fprintf(bat->log, _("\nBAT analysis: signal has %d frames at %d Hz,"),
453c72fcc34Sopenharmony_ci			bat->frames, bat->rate);
454c72fcc34Sopenharmony_ci	fprintf(bat->log, _(" %d channels, %d bytes per sample.\n"),
455c72fcc34Sopenharmony_ci			bat->channels, bat->sample_size);
456c72fcc34Sopenharmony_ci
457c72fcc34Sopenharmony_ci	bat->buf = malloc(bat->frames * bat->frame_size);
458c72fcc34Sopenharmony_ci	if (bat->buf == NULL)
459c72fcc34Sopenharmony_ci		return -ENOMEM;
460c72fcc34Sopenharmony_ci
461c72fcc34Sopenharmony_ci	bat->fp = fopen(bat->capture.file, "rb");
462c72fcc34Sopenharmony_ci	err = -errno;
463c72fcc34Sopenharmony_ci	if (bat->fp == NULL) {
464c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Cannot open file: %s %d\n"),
465c72fcc34Sopenharmony_ci				bat->capture.file, err);
466c72fcc34Sopenharmony_ci		goto exit1;
467c72fcc34Sopenharmony_ci	}
468c72fcc34Sopenharmony_ci
469c72fcc34Sopenharmony_ci	/* Skip header */
470c72fcc34Sopenharmony_ci	err = read_wav_header(bat, bat->capture.file, bat->fp, true);
471c72fcc34Sopenharmony_ci	if (err != 0)
472c72fcc34Sopenharmony_ci		goto exit2;
473c72fcc34Sopenharmony_ci
474c72fcc34Sopenharmony_ci	items = fread(bat->buf, bat->frame_size, bat->frames, bat->fp);
475c72fcc34Sopenharmony_ci	if (items != bat->frames) {
476c72fcc34Sopenharmony_ci		err = -EIO;
477c72fcc34Sopenharmony_ci		goto exit2;
478c72fcc34Sopenharmony_ci	}
479c72fcc34Sopenharmony_ci
480c72fcc34Sopenharmony_ci	err = reorder_data(bat);
481c72fcc34Sopenharmony_ci	if (err != 0)
482c72fcc34Sopenharmony_ci		goto exit2;
483c72fcc34Sopenharmony_ci
484c72fcc34Sopenharmony_ci	for (c = 0; c < bat->channels; c++) {
485c72fcc34Sopenharmony_ci		fprintf(bat->log, _("\nChannel %i - "), c + 1);
486c72fcc34Sopenharmony_ci		fprintf(bat->log, _("Checking for target frequency %2.2f Hz\n"),
487c72fcc34Sopenharmony_ci				bat->target_freq[c]);
488c72fcc34Sopenharmony_ci		a.buf = bat->buf +
489c72fcc34Sopenharmony_ci				c * bat->frames * bat->frame_size
490c72fcc34Sopenharmony_ci				/ bat->channels;
491c72fcc34Sopenharmony_ci		if (!bat->standalone) {
492c72fcc34Sopenharmony_ci			err = find_and_check_harmonics(bat, &a, c);
493c72fcc34Sopenharmony_ci			if (err != 0)
494c72fcc34Sopenharmony_ci				goto exit2;
495c72fcc34Sopenharmony_ci		}
496c72fcc34Sopenharmony_ci
497c72fcc34Sopenharmony_ci		if (snr_is_valid(bat->snr_thd_db)) {
498c72fcc34Sopenharmony_ci			fprintf(bat->log, _("\nChecking for SNR: "));
499c72fcc34Sopenharmony_ci			fprintf(bat->log, _("Threshold is %.2f dB (%.2f%%)\n"),
500c72fcc34Sopenharmony_ci					bat->snr_thd_db, 100.0
501c72fcc34Sopenharmony_ci					/ powf(10.0, bat->snr_thd_db / 20.0));
502c72fcc34Sopenharmony_ci			err = find_and_check_noise(bat, a.buf, c);
503c72fcc34Sopenharmony_ci			if (err != 0)
504c72fcc34Sopenharmony_ci				goto exit2;
505c72fcc34Sopenharmony_ci		}
506c72fcc34Sopenharmony_ci	}
507c72fcc34Sopenharmony_ci
508c72fcc34Sopenharmony_ciexit2:
509c72fcc34Sopenharmony_ci	fclose(bat->fp);
510c72fcc34Sopenharmony_ciexit1:
511c72fcc34Sopenharmony_ci	free(bat->buf);
512c72fcc34Sopenharmony_ci
513c72fcc34Sopenharmony_ci	return err;
514c72fcc34Sopenharmony_ci}
515