alsabat: add noise detection
cover alsabat: add noise detection
From: "Lu, Han" <[email protected]>
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(-)
Signature · 1 line
2.5.0
PATCH 1/4 alsabat: align the data type on float
From: "Lu, Han" <[email protected]>
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.
bat/analyze.c +19 -18
@@ -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; }
bat/bat.c +8 -8
@@ -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
bat/common.h +4 -4
@@ -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 *);
bat/convert.c +4 -4
@@ -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;
bat/convert.h +4 -4
@@ -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 );
bat/signal.c +1 -1
@@ -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 */
configure.ac +1 -1
@@ -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
Signature · 1 line
2.5.0
PATCH 3/4 alsabat: add a single channel sine wave generator
In reply to [email protected]From: "Lu, Han" <[email protected]>
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.
bat/bat-signal.h +1 -0
@@ -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 );
bat/signal.c +18 -0
@@ -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; +}
Signature · 1 line
2.5.0
PATCH 2/4 alsabat: add channels parameter for adjust_waveform()
In reply to [email protected]From: "Lu, Han" <[email protected]>
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.
bat/signal.c +4 -3
@@ -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;
Signature · 1 line
2.5.0
PATCH 4/4 alsabat: add noise detection
In reply to [email protected]From: "Lu, Han" <[email protected]>
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.
bat/alsabat-test.sh +4 -0
@@ -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 }
bat/alsabat.1 +8 -0
@@ -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
bat/analyze.c +178 -1
191 lines not on this page · raw message
bat/bat.c +45 -1
88 lines not on this page · raw message
bat/common.h +28 -0
@@ -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 */
Signature · 1 line
2.5.0
Takashi Iwai
AppliedQuoted 23 lines · On Wed, 08 Jun 2016 21:42:45 +0200, [email protected] wrote:
From: "Lu, Han" <[email protected]>
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 foradjust_waveform()
3. add a single channel sine wave generator
4. add noise detection
Quoted 5 lines
Lu, Han (4):
alsabat: align the data type on float
alsabat: add channels parameter foradjust_waveform()
alsabat: add a single channel sine wave generator
alsabat: add noise detection
Applied all patches now. Thanks.
Takashi