[alsa-devel] [PATCH 0/4] alsabat: add noise detection
From: "Lu, Han" han.lu@intel.com
Add noise detection for alsabat. Alsabat reports error when noise above threshold be detected. Use either of the options below to designate the threshold. (e.g. if the ratio of noise to signal is 5%, the snr is about 26dB.) --snr-db <value in dB> --snr-pc <value in %>
The noise detection is performed in time domain. On each period of the sine wave being analyzed, alsabat substracts a clean sine wave from the source, calculates the RMS value of the residual, and compares the result with the threshold. At last, alsabat returns the number of periods with noise above threshold. 0 is returned when the source is clean.
patch content: 1. align the data type and process on float 2. add a parameter for adjust_waveform() 3. add a single channel sine wave generator 4. add noise detection
Lu, Han (4): alsabat: align the data type on float alsabat: add channels parameter for adjust_waveform() alsabat: add a single channel sine wave generator alsabat: add noise detection
bat/alsabat-test.sh | 4 + bat/alsabat.1 | 8 ++ bat/analyze.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++----- bat/bat-signal.h | 1 + bat/bat.c | 62 ++++++++++++--- bat/common.h | 36 ++++++++- bat/convert.c | 8 +- bat/convert.h | 8 +- bat/signal.c | 27 ++++++- configure.ac | 2 +- 10 files changed, 327 insertions(+), 45 deletions(-)
On Wed, 08 Jun 2016 21:42:45 +0200, han.lu@intel.com wrote:
From: "Lu, Han" han.lu@intel.com
Add noise detection for alsabat. Alsabat reports error when noise above threshold be detected. Use either of the options below to designate the threshold. (e.g. if the ratio of noise to signal is 5%, the snr is about 26dB.) --snr-db <value in dB> --snr-pc <value in %>
The noise detection is performed in time domain. On each period of the sine wave being analyzed, alsabat substracts a clean sine wave from the source, calculates the RMS value of the residual, and compares the result with the threshold. At last, alsabat returns the number of periods with noise above threshold. 0 is returned when the source is clean.
patch content:
- align the data type and process on float
- add a parameter for adjust_waveform()
- add a single channel sine wave generator
- add noise detection
Lu, Han (4): alsabat: align the data type on float alsabat: add channels parameter for adjust_waveform() alsabat: add a single channel sine wave generator alsabat: add noise detection
Applied all patches now. Thanks.
Takashi
From: "Lu, Han" han.lu@intel.com
Aligning the data type of fftw analyzer, sample converter and other components on float, because: 1. avoid unnecessary data type conversion; 2. using float is more efficient than using double; 3. the extra double accuracy is not required.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/bat/analyze.c b/bat/analyze.c index 58781d6..bdb1f83 100644 --- a/bat/analyze.c +++ b/bat/analyze.c @@ -26,10 +26,11 @@ #include "gettext.h"
#include "common.h" +#include "bat-signal.h"
-static void check_amplitude(struct bat *bat, double *buf) +static void check_amplitude(struct bat *bat, float *buf) { - double sum, average, amplitude; + float sum, average, amplitude; int i, percent;
/* calculate average value */ @@ -39,7 +40,7 @@ static void check_amplitude(struct bat *bat, double *buf)
/* calculate peak-to-average amplitude */ for (i = 0, sum = 0.0; i < bat->frames; i++) - sum += abs(buf[i] - average); + sum += fabsf(buf[i] - average); amplitude = sum / bat->frames * M_PI / 2.0;
/* calculate amplitude percentage against full range */ @@ -71,9 +72,9 @@ int check_peak(struct bat *bat, struct analyze *a, int end, int peak, float hz, float tolerance = (delta_rate > delta_HZ) ? delta_rate : delta_HZ;
fprintf(bat->log, _("Detected peak at %2.2f Hz of %2.2f dB\n"), hz_peak, - 10.0 * log10(a->mag[peak] / mean)); + 10.0 * log10f(a->mag[peak] / mean)); fprintf(bat->log, _(" Total %3.1f dB from %2.2f to %2.2f Hz\n"), - 10.0 * log10(p / mean), start * hz, end * hz); + 10.0 * log10f(p / mean), start * hz, end * hz);
if (hz_peak < DC_THRESHOLD) { fprintf(bat->err, _(" WARNING: Found low peak %2.2f Hz,"), @@ -161,7 +162,7 @@ static int check(struct bat *bat, struct analyze *a, int channel)
static void calc_magnitude(struct bat *bat, struct analyze *a, int N) { - double r2, i2; + float r2, i2; int i;
for (i = 1; i < N / 2; i++) { @@ -176,36 +177,36 @@ static void calc_magnitude(struct bat *bat, struct analyze *a, int N) static int find_and_check_harmonics(struct bat *bat, struct analyze *a, int channel) { - fftw_plan p; + fftwf_plan p; int err = -ENOMEM, N = bat->frames;
/* Allocate FFT buffers */ - a->in = (double *) fftw_malloc(sizeof(double) * bat->frames); + a->in = (float *) fftwf_malloc(sizeof(float) * bat->frames); if (a->in == NULL) goto out1;
- a->out = (double *) fftw_malloc(sizeof(double) * bat->frames); + a->out = (float *) fftwf_malloc(sizeof(float) * bat->frames); if (a->out == NULL) goto out2;
- a->mag = (double *) fftw_malloc(sizeof(double) * bat->frames); + a->mag = (float *) fftwf_malloc(sizeof(float) * bat->frames); if (a->mag == NULL) goto out3;
/* create FFT plan */ - p = fftw_plan_r2r_1d(N, a->in, a->out, FFTW_R2HC, + p = fftwf_plan_r2r_1d(N, a->in, a->out, FFTW_R2HC, FFTW_MEASURE | FFTW_PRESERVE_INPUT); if (p == NULL) goto out4;
- /* convert source PCM to doubles */ - bat->convert_sample_to_double(a->buf, a->in, bat->frames); + /* convert source PCM to floats */ + bat->convert_sample_to_float(a->buf, a->in, bat->frames);
/* check amplitude */ check_amplitude(bat, a->in);
/* run FFT */ - fftw_execute(p); + fftwf_execute(p);
/* FFT out is real and imaginary numbers - calc magnitude for each */ calc_magnitude(bat, a, N); @@ -213,14 +214,14 @@ static int find_and_check_harmonics(struct bat *bat, struct analyze *a, /* check data */ err = check(bat, a, channel);
- fftw_destroy_plan(p); + fftwf_destroy_plan(p);
out4: - fftw_free(a->mag); + fftwf_free(a->mag); out3: - fftw_free(a->out); + fftwf_free(a->out); out2: - fftw_free(a->in); + fftwf_free(a->in); out1: return err; } diff --git a/bat/bat.c b/bat/bat.c index cd4ea2d..d534d48 100644 --- a/bat/bat.c +++ b/bat/bat.c @@ -37,7 +37,7 @@ #include "alsa.h" #endif #include "convert.h" -#ifdef HAVE_LIBFFTW3 +#ifdef HAVE_LIBFFTW3F #include "analyze.h" #endif
@@ -67,7 +67,7 @@ static int get_duration(struct bat *bat)
if (bat->frames <= 0 || bat->frames > MAX_FRAMES) { fprintf(bat->err, _("Invalid duration. Range: (0, %d(%fs))\n"), - MAX_FRAMES, (double)MAX_FRAMES / bat->rate); + MAX_FRAMES, (float)MAX_FRAMES / bat->rate); return -EINVAL; }
@@ -317,7 +317,7 @@ static void set_defaults(struct bat *bat) bat->sample_size = 2; bat->format = BAT_PCM_FORMAT_S16_LE; bat->convert_float_to_sample = convert_float_to_int16; - bat->convert_sample_to_double = convert_int16_to_double; + bat->convert_sample_to_float = convert_int16_to_float; bat->frames = bat->rate * 2; bat->target_freq[0] = 997.0; bat->target_freq[1] = 997.0; @@ -569,19 +569,19 @@ static int bat_init(struct bat *bat) switch (bat->sample_size) { case 1: bat->convert_float_to_sample = convert_float_to_uint8; - bat->convert_sample_to_double = convert_uint8_to_double; + bat->convert_sample_to_float = convert_uint8_to_float; break; case 2: bat->convert_float_to_sample = convert_float_to_int16; - bat->convert_sample_to_double = convert_int16_to_double; + bat->convert_sample_to_float = convert_int16_to_float; break; case 3: bat->convert_float_to_sample = convert_float_to_int24; - bat->convert_sample_to_double = convert_int24_to_double; + bat->convert_sample_to_float = convert_int24_to_float; break; case 4: bat->convert_float_to_sample = convert_float_to_int32; - bat->convert_sample_to_double = convert_int32_to_double; + bat->convert_sample_to_float = convert_int32_to_float; break; default: fprintf(bat->err, _("Invalid PCM format: size=%d\n"), @@ -633,7 +633,7 @@ int main(int argc, char *argv[]) test_loopback(&bat);
analyze: -#ifdef HAVE_LIBFFTW3 +#ifdef HAVE_LIBFFTW3F if (!bat.standalone) err = analyze_capture(&bat); #else diff --git a/bat/common.h b/bat/common.h index ad02a5a..ad91270 100644 --- a/bat/common.h +++ b/bat/common.h @@ -182,7 +182,7 @@ struct bat { FILE *log; FILE *err;
- void (*convert_sample_to_double)(void *, double *, int); + void (*convert_sample_to_float)(void *, float *, int); void (*convert_float_to_sample)(float *, void *, int, int);
void *buf; /* PCM Buffer */ @@ -192,9 +192,9 @@ struct bat {
struct analyze { void *buf; - double *in; - double *out; - double *mag; + float *in; + float *out; + float *mag; };
void prepare_wav_info(struct wav_container *, struct bat *); diff --git a/bat/convert.c b/bat/convert.c index dcbe912..f555389 100644 --- a/bat/convert.c +++ b/bat/convert.c @@ -18,7 +18,7 @@ #include <stdlib.h> #include <stdint.h>
-void convert_uint8_to_double(void *buf, double *val, int samples) +void convert_uint8_to_float(void *buf, float *val, int samples) { int i;
@@ -26,7 +26,7 @@ void convert_uint8_to_double(void *buf, double *val, int samples) val[i] = ((uint8_t *) buf)[i]; }
-void convert_int16_to_double(void *buf, double *val, int samples) +void convert_int16_to_float(void *buf, float *val, int samples) { int i;
@@ -34,7 +34,7 @@ void convert_int16_to_double(void *buf, double *val, int samples) val[i] = ((int16_t *) buf)[i]; }
-void convert_int24_to_double(void *buf, double *val, int samples) +void convert_int24_to_float(void *buf, float *val, int samples) { int i; int32_t tmp; @@ -48,7 +48,7 @@ void convert_int24_to_double(void *buf, double *val, int samples) } }
-void convert_int32_to_double(void *buf, double *val, int samples) +void convert_int32_to_float(void *buf, float *val, int samples) { int i;
diff --git a/bat/convert.h b/bat/convert.h index 28828ba..699c07a 100644 --- a/bat/convert.h +++ b/bat/convert.h @@ -13,10 +13,10 @@ * */
-void convert_uint8_to_double(void *, double *, int); -void convert_int16_to_double(void *, double *, int); -void convert_int24_to_double(void *, double *, int); -void convert_int32_to_double(void *, double *, int); +void convert_uint8_to_float(void *, float *, int); +void convert_int16_to_float(void *, float *, int); +void convert_int24_to_float(void *, float *, int); +void convert_int32_to_float(void *, float *, int); void convert_float_to_uint8(float *, void *, int, int); void convert_float_to_int16(float *, void *, int, int); void convert_float_to_int24(float *, void *, int, int); diff --git a/bat/signal.c b/bat/signal.c index 61d2824..15bea0a 100644 --- a/bat/signal.c +++ b/bat/signal.c @@ -75,7 +75,7 @@ float sin_generator_next_sample(struct sin_generator *sg) sg->state_real = sr * pr - si * pi; sg->state_imag = sr * pi + pr * si; /* return the input value so sine wave starts at exactly 0.0 */ - return sr; + return (float)sr; }
/* fills a vector with a sine wave */ diff --git a/configure.ac b/configure.ac index eabbb22..05c3be1 100644 --- a/configure.ac +++ b/configure.ac @@ -91,7 +91,7 @@ if test x$bat = xtrue; then FFTW_CFLAGS="" dnl Check for libfftw3 have_libfftw3="yes" - AC_CHECK_LIB([fftw3], [fftw_malloc], , [have_libfftw3="no"]) + AC_CHECK_LIB([fftw3f], [fftwf_malloc], , [have_libfftw3="no"]) dnl Check for libtinyalsa have_libtinyalsa= if test x$alsabat_backend_tiny = xtrue; then
From: "Lu, Han" han.lu@intel.com
The function adjust_waveform() is a component of generate_sine_wave(), and depended on bat->channels parameter. Add parameter "channels" to remove the dependency, and then adjust_waveform() can be applied on other use cases, e.g. a single channel sine wave generator.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/bat/signal.c b/bat/signal.c index 15bea0a..2047f53 100644 --- a/bat/signal.c +++ b/bat/signal.c @@ -109,7 +109,8 @@ static int reorder(struct bat *bat, float *val, int frames) return 0; }
-static int adjust_waveform(struct bat *bat, float *val, int frames) +static int adjust_waveform(struct bat *bat, float *val, int frames, + int channels) { int i, nsamples, max; float factor, offset = 0.0; @@ -134,7 +135,7 @@ static int adjust_waveform(struct bat *bat, float *val, int frames) }
factor = max * RANGE_FACTOR; - nsamples = bat->channels * frames; + nsamples = channels * frames;
for (i = 0; i < nsamples; i++) val[i] = val[i] * factor + offset; @@ -171,7 +172,7 @@ int generate_sine_wave(struct bat *bat, int frames, void *buf) goto exit;
/* adjust amplitude and offset of waveform */ - err = adjust_waveform(bat, sinus_f, frames); + err = adjust_waveform(bat, sinus_f, frames, bat->channels); if (err != 0) goto exit;
From: "Lu, Han" han.lu@intel.com
Add function generate_sine_wave_raw_mono(). It serves as a single channel sine wave generator, to provide data for calculation (e.g. for noise analysis). The function is similar to generate_sine_wave(), but a lite revision. It has no dependency on bat channels and target frequency, no malloc inside, no data conversion from float to integer samples, and supports one channel only.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/bat/bat-signal.h b/bat/bat-signal.h index a295517..3c314a5 100644 --- a/bat/bat-signal.h +++ b/bat/bat-signal.h @@ -28,3 +28,4 @@ int sin_generator_init(struct sin_generator *, float, float, float); float sin_generator_next_sample(struct sin_generator *); void sin_generator_vfill(struct sin_generator *, float *, int); int generate_sine_wave(struct bat *, int, void *); +int generate_sine_wave_raw_mono(struct bat *, float *, float, int); diff --git a/bat/signal.c b/bat/signal.c index 2047f53..c9b5c7b 100644 --- a/bat/signal.c +++ b/bat/signal.c @@ -183,3 +183,21 @@ exit:
return err; } + +/* generate single channel sine waveform without sample conversion */ +int generate_sine_wave_raw_mono(struct bat *bat, float *buf, + float freq, int nsamples) +{ + int err = 0; + struct sin_generator sg; + + err = sin_generator_init(&sg, 1.0, freq, bat->rate); + if (err < 0) + return err; + sin_generator_vfill(&sg, buf, nsamples); + + /* adjust amplitude and offset of waveform */ + err = adjust_waveform(bat, buf, nsamples, 1); + + return err; +}
From: "Lu, Han" han.lu@intel.com
Alsabat reports error when noise above threshold be detected. Use either of the options below to designate the threshold. (e.g. if the ratio of noise to signal is 5%, the snr is about 26dB.) --snr-db <value in dB> --snr-pc <value in %>
The noise detection is performed in time domain. On each period of the sine wave being analyzed, alsabat substracts a clean sine wave from the source, calculates the RMS value of the residual, and compares the result with the threshold. At last, alsabat returns the number of periods with noise above threshold. 0 is returned when the source is clean.
Signed-off-by: Lu, Han han.lu@intel.com
diff --git a/bat/alsabat-test.sh b/bat/alsabat-test.sh index 2d31a77..7d024fb 100755 --- a/bat/alsabat-test.sh +++ b/bat/alsabat-test.sh @@ -81,6 +81,10 @@ feature_list_test () { latestfile=`ls -t1 /tmp/bat.wav.* | head -n 1` feature_test "--local -F $maxfreq --file $latestfile" \ "local mode: analyze local file" + feature_test "--snr-db 26" \ + "noise detect threshold in SNR(dB)" + feature_test "--snr-pc 5" \ + "noise detect threshold in noise percentage(%)"
print_result } diff --git a/bat/alsabat.1 b/bat/alsabat.1 index 3f9b767..6f6da5c 100644 --- a/bat/alsabat.1 +++ b/bat/alsabat.1 @@ -132,6 +132,14 @@ just like in normal mode, but will not be analyzed. The ALSABAT being built without libfftw3 support is always in standalone mode. The ALSABAT in normal mode can also bypass data analysis using option "--standalone". +.TP +\fI--snr-db=#\fP +Noise detection threshold in SNR (dB). 26dB indicates 5% noise in amplitude. +ALSABAT will return error if signal SNR is smaller than the threshold. +.TP +\fI--snr-pc=#\fP +Noise detection threshold in percentage of noise amplitude (%). +ALSABAT will return error if the noise amplitude is larger than the threshold.
.SH EXAMPLES
diff --git a/bat/analyze.c b/bat/analyze.c index bdb1f83..17fff50 100644 --- a/bat/analyze.c +++ b/bat/analyze.c @@ -226,6 +226,169 @@ out1: return err; }
+static int calculate_noise_one_period(struct bat *bat, + struct noise_analyzer *na, float *src, + int length, int channel) +{ + int i, shift = 0; + float tmp, rms, gain, residual; + float a = 0.0, b = 1.0; + + /* step 1. phase compensation */ + + if (length < 2 * na->nsamples) + return -EINVAL; + + /* search for the beginning of a sine period */ + for (i = 0, tmp = 0.0, shift = -1; i < na->nsamples; i++) { + /* find i where src[i] >= 0 && src[i+1] < 0 */ + if (src[i] < 0.0) + continue; + if (src[i + 1] < 0.0) { + tmp = src[i] - src[i + 1]; + a = src[i] / tmp; + b = -src[i + 1] / tmp; + shift = i; + break; + } + } + + /* didn't find the beginning of a sine period */ + if (shift == -1) + return -EINVAL; + + /* shift sine waveform to source[0] = 0.0 */ + for (i = 0; i < na->nsamples; i++) + na->source[i] = a * src[i + shift + 1] + b * src[i + shift]; + + /* step 2. gain compensation */ + + /* calculate rms of signal amplitude */ + for (i = 0, tmp = 0.0; i < na->nsamples; i++) + tmp += na->source[i] * na->source[i]; + rms = sqrtf(tmp / na->nsamples); + + gain = na->rms_tgt / rms; + + for (i = 0; i < na->nsamples; i++) + na->source[i] *= gain; + + /* step 3. calculate snr in dB */ + + for (i = 0, tmp = 0.0, residual = 0.0; i < na->nsamples; i++) { + tmp = fabsf(na->target[i] - na->source[i]); + residual += tmp * tmp; + } + + tmp = na->rms_tgt / sqrtf(residual / na->nsamples); + na->snr_db = 20.0 * log10f(tmp); + + return 0; +} + +static int calculate_noise(struct bat *bat, float *src, int channel) +{ + int err = 0; + struct noise_analyzer na; + float freq = bat->target_freq[channel]; + float tmp, sum_snr_pc, avg_snr_pc, avg_snr_db; + int offset, i, cnt_noise, cnt_clean; + /* num of samples in each sine period */ + int nsamples = (int) ceilf(bat->rate / freq); + /* each section has 2 sine periods, the first one for locating + * and the second one for noise calculating */ + int nsamples_per_section = nsamples * 2; + /* all sine periods will be calculated except the first one */ + int nsection = bat->frames / nsamples - 1; + + fprintf(bat->log, _("samples per period: %d\n"), nsamples); + fprintf(bat->log, _("total sections to detect: %d\n"), nsection); + na.source = (float *)malloc(sizeof(float) * nsamples); + if (!na.source) { + err = -ENOMEM; + goto out1; + } + + na.target = (float *)malloc(sizeof(float) * nsamples); + if (!na.target) { + err = -ENOMEM; + goto out2; + } + + /* generate standard single-tone signal */ + err = generate_sine_wave_raw_mono(bat, na.target, freq, nsamples); + if (err < 0) + goto out3; + + na.nsamples = nsamples; + + /* calculate rms of standard signal */ + for (i = 0, tmp = 0.0; i < nsamples; i++) + tmp += na.target[i] * na.target[i]; + na.rms_tgt = sqrtf(tmp / nsamples); + + /* calculate average noise level */ + sum_snr_pc = 0.0; + cnt_clean = cnt_noise = 0; + for (i = 0, offset = 0; i < nsection; i++) { + na.snr_db = SNR_DB_INVALID; + + err = calculate_noise_one_period(bat, &na, src + offset, + nsamples_per_section, channel); + if (err < 0) + goto out3; + + if (na.snr_db > bat->snr_thd_db) { + cnt_clean++; + sum_snr_pc += 100.0 / powf(10.0, na.snr_db / 20.0); + } else { + cnt_noise++; + } + offset += nsamples; + } + + if (cnt_noise > 0) { + fprintf(bat->err, _("Noise detected at %d points.\n"), + cnt_noise); + err = -cnt_noise; + if (cnt_clean == 0) + goto out3; + } else { + fprintf(bat->log, _("No noise detected.\n")); + } + + avg_snr_pc = sum_snr_pc / cnt_clean; + avg_snr_db = 20.0 * log10f(100.0 / avg_snr_pc); + fprintf(bat->log, _("Average SNR is %.2f dB (%.2f %%) at %d points.\n"), + avg_snr_db, avg_snr_pc, cnt_clean); + +out3: + free(na.target); +out2: + free(na.source); +out1: + return err; +} + +static int find_and_check_noise(struct bat *bat, void *buf, int channel) +{ + int err = 0; + float *source; + + source = (float *)malloc(sizeof(float) * bat->frames); + if (!source) + return -ENOMEM; + + /* convert source PCM to floats */ + bat->convert_sample_to_float(buf, source, bat->frames); + + /* adjust waveform and calculate noise */ + err = calculate_noise(bat, source, channel); + + free(source); + return err; +} + /** * Convert interleaved samples from channels in samples from a single channel */ @@ -324,7 +487,21 @@ int analyze_capture(struct bat *bat) a.buf = bat->buf + c * bat->frames * bat->frame_size / bat->channels; - err = find_and_check_harmonics(bat, &a, c); + if (!bat->standalone) { + err = find_and_check_harmonics(bat, &a, c); + if (err != 0) + goto exit2; + } + + if (snr_is_valid(bat->snr_thd_db)) { + fprintf(bat->log, _("\nChecking for SNR: ")); + fprintf(bat->log, _("Threshold is %.2f dB (%.2f%%)\n"), + bat->snr_thd_db, 100.0 + / powf(10.0, bat->snr_thd_db / 20.0)); + err = find_and_check_noise(bat, a.buf, c); + if (err != 0) + goto exit2; + } }
exit2: diff --git a/bat/bat.c b/bat/bat.c index d534d48..e650b01 100644 --- a/bat/bat.c +++ b/bat/bat.c @@ -24,6 +24,7 @@ #include <math.h> #include <limits.h> #include <locale.h> +#include <math.h>
#include "aconfig.h" #include "gettext.h" @@ -41,6 +42,38 @@ #include "analyze.h" #endif
+/* get snr threshold in dB */ +static void get_snr_thd_db(struct bat *bat, char *thd) +{ + int err; + float thd_db; + char *ptrf; + + thd_db = strtof(thd, &ptrf); + err = -errno; + if (!snr_is_valid(thd_db)) { + fprintf(bat->err, _("Invalid threshold '%s':%d\n"), thd, err); + exit(EXIT_FAILURE); + } + bat->snr_thd_db = thd_db; +} + +/* get snr threshold in %, and convert to dB */ +static void get_snr_thd_pc(struct bat *bat, char *thd) +{ + int err; + float thd_pc; + char *ptrf; + + thd_pc = strtof(thd, &ptrf); + err = -errno; + if (thd_pc <= 0.0 || thd_pc >= 100.0) { + fprintf(bat->err, _("Invalid threshold '%s':%d\n"), thd, err); + exit(EXIT_FAILURE); + } + bat->snr_thd_db = 20.0 * log10f(100.0 / thd_pc); +} + static int get_duration(struct bat *bat) { int err; @@ -299,6 +332,8 @@ _("Usage: alsabat [-options]...\n" " --saveplay=# file that storing playback content, for debug\n" " --local internal loop, set to bypass pcm hardware devices\n" " --standalone standalone mode, to bypass analysis\n" +" --snr-db=# noise detect threshold, in SNR(dB)\n" +" --snr-pc=# noise detect threshold, in noise percentage(%%)\n" )); fprintf(bat->log, _("Recognized sample formats are: ")); fprintf(bat->log, _("U8 S16_LE S24_3LE S32_LE\n")); @@ -322,6 +357,7 @@ static void set_defaults(struct bat *bat) bat->target_freq[0] = 997.0; bat->target_freq[1] = 997.0; bat->sigma_k = 3.0; + bat->snr_thd_db = SNR_DB_INVALID; bat->playback.device = NULL; bat->capture.device = NULL; bat->buf = NULL; @@ -355,6 +391,8 @@ static void parse_arguments(struct bat *bat, int argc, char *argv[]) {"saveplay", 1, 0, OPT_SAVEPLAY}, {"local", 0, 0, OPT_LOCAL}, {"standalone", 0, 0, OPT_STANDALONE}, + {"snr-db", 1, 0, OPT_SNRTHD_DB}, + {"snr-pc", 1, 0, OPT_SNRTHD_PC}, {0, 0, 0, 0} };
@@ -376,6 +414,12 @@ static void parse_arguments(struct bat *bat, int argc, char *argv[]) case OPT_STANDALONE: bat->standalone = true; break; + case OPT_SNRTHD_DB: + get_snr_thd_db(bat, optarg); + break; + case OPT_SNRTHD_PC: + get_snr_thd_pc(bat, optarg); + break; case 'D': if (bat->playback.device == NULL) bat->playback.device = optarg; @@ -634,7 +678,7 @@ int main(int argc, char *argv[])
analyze: #ifdef HAVE_LIBFFTW3F - if (!bat.standalone) + if (!bat.standalone || snr_is_valid(bat.snr_thd_db)) err = analyze_capture(&bat); #else fprintf(bat.log, _("No libfftw3 library. Exit without analysis.\n")); diff --git a/bat/common.h b/bat/common.h index ad91270..8214a03 100644 --- a/bat/common.h +++ b/bat/common.h @@ -22,6 +22,8 @@ #define OPT_SAVEPLAY (OPT_BASE + 3) #define OPT_LOCAL (OPT_BASE + 4) #define OPT_STANDALONE (OPT_BASE + 5) +#define OPT_SNRTHD_DB (OPT_BASE + 7) +#define OPT_SNRTHD_PC (OPT_BASE + 8)
#define COMPOSE(a, b, c, d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) #define WAV_RIFF COMPOSE('R', 'I', 'F', 'F') @@ -75,6 +77,23 @@ #define SHIFT_MAX (sizeof(int) * 8 - 2) #define SHIFT_MIN 8
+/* Define SNR range in dB. + * if the noise is equal to signal, SNR = 0.0dB; + * if the noise is zero, SNR is limited by RIFF wav data width: + * 8 bit --> 20.0 * log10f (powf(2.0, 8.0)) = 48.16 dB + * 16 bit --> 20.0 * log10f (powf(2.0, 16.0)) = 96.33 dB + * 24 bit --> 20.0 * log10f (powf(2.0, 24.0)) = 144.49 dB + * 32 bit --> 20.0 * log10f (powf(2.0, 32.0)) = 192.66 dB + * so define the SNR range (0.0, 200.0) dB, value out of range is invalid. */ +#define SNR_DB_INVALID -1.0 +#define SNR_DB_MIN 0.0 +#define SNR_DB_MAX 200.0 + +inline bool snr_is_valid(float db) +{ + return (db > SNR_DB_MIN && db < SNR_DB_MAX); +} + struct wav_header { unsigned int magic; /* 'RIFF' */ unsigned int length; /* file len */ @@ -151,6 +170,14 @@ struct sin_generator { float magnitude; };
+struct noise_analyzer { + int nsamples; /* number of sample */ + float *source; /* single-tone to be analyzed */ + float *target; /* target single-tone as standard */ + float rms_tgt; /* rms of target single-tone */ + float snr_db; /* snr in dB */ +}; + struct bat { unsigned int rate; /* sampling rate */ int channels; /* nb of channels */ @@ -162,6 +189,7 @@ struct bat { int period_size; /* period size in frames */
float sigma_k; /* threshold for peak detection */ + float snr_thd_db; /* threshold for noise detection (dB) */ float target_freq[MAX_CHANNELS];
int sinus_duration; /* number of frames for playback */
participants (2)
-
han.lu@intel.com
-
Takashi Iwai