annotate src/libsamplerate-0.1.8/tests/calc_snr.c @ 169:223a55898ab9 tip default

Add null config files
author Chris Cannam <cannam@all-day-breakfast.com>
date Mon, 02 Mar 2020 14:03:47 +0000
parents 545efbb81310
children
rev   line source
cannam@85 1 /*
cannam@85 2 ** Copyright (C) 2002-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
cannam@85 3 **
cannam@85 4 ** This program is free software; you can redistribute it and/or modify
cannam@85 5 ** it under the terms of the GNU General Public License as published by
cannam@85 6 ** the Free Software Foundation; either version 2 of the License, or
cannam@85 7 ** (at your option) any later version.
cannam@85 8 **
cannam@85 9 ** This program is distributed in the hope that it will be useful,
cannam@85 10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
cannam@85 11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
cannam@85 12 ** GNU General Public License for more details.
cannam@85 13 **
cannam@85 14 ** You should have received a copy of the GNU General Public License
cannam@85 15 ** along with this program; if not, write to the Free Software
cannam@85 16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
cannam@85 17 */
cannam@85 18
cannam@85 19 #include "config.h"
cannam@85 20
cannam@85 21 #include "util.h"
cannam@85 22
cannam@85 23 #if (HAVE_FFTW3 == 1)
cannam@85 24
cannam@85 25 #include <stdio.h>
cannam@85 26 #include <stdlib.h>
cannam@85 27 #include <string.h>
cannam@85 28 #include <math.h>
cannam@85 29
cannam@85 30 #include <fftw3.h>
cannam@85 31
cannam@85 32 #define MAX_SPEC_LEN (1<<18)
cannam@85 33 #define MAX_PEAKS 10
cannam@85 34
cannam@85 35 static void log_mag_spectrum (double *input, int len, double *magnitude) ;
cannam@85 36 static void smooth_mag_spectrum (double *magnitude, int len) ;
cannam@85 37 static double find_snr (const double *magnitude, int len, int expected_peaks) ;
cannam@85 38
cannam@85 39 typedef struct
cannam@85 40 { double peak ;
cannam@85 41 int index ;
cannam@85 42 } PEAK_DATA ;
cannam@85 43
cannam@85 44 double
cannam@85 45 calculate_snr (float *data, int len, int expected_peaks)
cannam@85 46 { static double magnitude [MAX_SPEC_LEN] ;
cannam@85 47 static double datacopy [MAX_SPEC_LEN] ;
cannam@85 48
cannam@85 49 double snr = 200.0 ;
cannam@85 50 int k ;
cannam@85 51
cannam@85 52 if (len > MAX_SPEC_LEN)
cannam@85 53 { printf ("%s : line %d : data length too large.\n", __FILE__, __LINE__) ;
cannam@85 54 exit (1) ;
cannam@85 55 } ;
cannam@85 56
cannam@85 57 for (k = 0 ; k < len ; k++)
cannam@85 58 datacopy [k] = data [k] ;
cannam@85 59
cannam@85 60 /* Pad the data just a little to speed up the FFT. */
cannam@85 61 while ((len & 0x1F) && len < MAX_SPEC_LEN)
cannam@85 62 { datacopy [len] = 0.0 ;
cannam@85 63 len ++ ;
cannam@85 64 } ;
cannam@85 65
cannam@85 66 log_mag_spectrum (datacopy, len, magnitude) ;
cannam@85 67 smooth_mag_spectrum (magnitude, len / 2) ;
cannam@85 68
cannam@85 69 snr = find_snr (magnitude, len, expected_peaks) ;
cannam@85 70
cannam@85 71 return snr ;
cannam@85 72 } /* calculate_snr */
cannam@85 73
cannam@85 74 /*==============================================================================
cannam@85 75 ** There is a slight problem with trying to measure SNR with the method used
cannam@85 76 ** here; the side lobes of the windowed FFT can look like a noise/aliasing peak.
cannam@85 77 ** The solution is to smooth the magnitude spectrum by wiping out troughs
cannam@85 78 ** between adjacent peaks as done here.
cannam@85 79 ** This removes side lobe peaks without affecting noise/aliasing peaks.
cannam@85 80 */
cannam@85 81
cannam@85 82 static void linear_smooth (double *mag, PEAK_DATA *larger, PEAK_DATA *smaller) ;
cannam@85 83
cannam@85 84 static void
cannam@85 85 smooth_mag_spectrum (double *mag, int len)
cannam@85 86 { PEAK_DATA peaks [2] ;
cannam@85 87
cannam@85 88 int k ;
cannam@85 89
cannam@85 90 memset (peaks, 0, sizeof (peaks)) ;
cannam@85 91
cannam@85 92 /* Find first peak. */
cannam@85 93 for (k = 1 ; k < len - 1 ; k++)
cannam@85 94 { if (mag [k - 1] < mag [k] && mag [k] >= mag [k + 1])
cannam@85 95 { peaks [0].peak = mag [k] ;
cannam@85 96 peaks [0].index = k ;
cannam@85 97 break ;
cannam@85 98 } ;
cannam@85 99 } ;
cannam@85 100
cannam@85 101 /* Find subsequent peaks ans smooth between peaks. */
cannam@85 102 for (k = peaks [0].index + 1 ; k < len - 1 ; k++)
cannam@85 103 { if (mag [k - 1] < mag [k] && mag [k] >= mag [k + 1])
cannam@85 104 { peaks [1].peak = mag [k] ;
cannam@85 105 peaks [1].index = k ;
cannam@85 106
cannam@85 107 if (peaks [1].peak > peaks [0].peak)
cannam@85 108 linear_smooth (mag, &peaks [1], &peaks [0]) ;
cannam@85 109 else
cannam@85 110 linear_smooth (mag, &peaks [0], &peaks [1]) ;
cannam@85 111 peaks [0] = peaks [1] ;
cannam@85 112 } ;
cannam@85 113 } ;
cannam@85 114
cannam@85 115 } /* smooth_mag_spectrum */
cannam@85 116
cannam@85 117 static void
cannam@85 118 linear_smooth (double *mag, PEAK_DATA *larger, PEAK_DATA *smaller)
cannam@85 119 { int k ;
cannam@85 120
cannam@85 121 if (smaller->index < larger->index)
cannam@85 122 { for (k = smaller->index + 1 ; k < larger->index ; k++)
cannam@85 123 mag [k] = (mag [k] < mag [k - 1]) ? 0.999 * mag [k - 1] : mag [k] ;
cannam@85 124 }
cannam@85 125 else
cannam@85 126 { for (k = smaller->index - 1 ; k >= larger->index ; k--)
cannam@85 127 mag [k] = (mag [k] < mag [k + 1]) ? 0.999 * mag [k + 1] : mag [k] ;
cannam@85 128 } ;
cannam@85 129
cannam@85 130 } /* linear_smooth */
cannam@85 131
cannam@85 132 /*==============================================================================
cannam@85 133 */
cannam@85 134
cannam@85 135 static int
cannam@85 136 peak_compare (const void *vp1, const void *vp2)
cannam@85 137 { const PEAK_DATA *peak1, *peak2 ;
cannam@85 138
cannam@85 139 peak1 = (const PEAK_DATA*) vp1 ;
cannam@85 140 peak2 = (const PEAK_DATA*) vp2 ;
cannam@85 141
cannam@85 142 return (peak1->peak < peak2->peak) ? 1 : -1 ;
cannam@85 143 } /* peak_compare */
cannam@85 144
cannam@85 145 static double
cannam@85 146 find_snr (const double *magnitude, int len, int expected_peaks)
cannam@85 147 { PEAK_DATA peaks [MAX_PEAKS] ;
cannam@85 148
cannam@85 149 int k, peak_count = 0 ;
cannam@85 150 double snr ;
cannam@85 151
cannam@85 152 memset (peaks, 0, sizeof (peaks)) ;
cannam@85 153
cannam@85 154 /* Find the MAX_PEAKS largest peaks. */
cannam@85 155 for (k = 1 ; k < len - 1 ; k++)
cannam@85 156 { if (magnitude [k - 1] < magnitude [k] && magnitude [k] >= magnitude [k + 1])
cannam@85 157 { if (peak_count < MAX_PEAKS)
cannam@85 158 { peaks [peak_count].peak = magnitude [k] ;
cannam@85 159 peaks [peak_count].index = k ;
cannam@85 160 peak_count ++ ;
cannam@85 161 qsort (peaks, peak_count, sizeof (PEAK_DATA), peak_compare) ;
cannam@85 162 }
cannam@85 163 else if (magnitude [k] > peaks [MAX_PEAKS - 1].peak)
cannam@85 164 { peaks [MAX_PEAKS - 1].peak = magnitude [k] ;
cannam@85 165 peaks [MAX_PEAKS - 1].index = k ;
cannam@85 166 qsort (peaks, MAX_PEAKS, sizeof (PEAK_DATA), peak_compare) ;
cannam@85 167 } ;
cannam@85 168 } ;
cannam@85 169 } ;
cannam@85 170
cannam@85 171 if (peak_count < expected_peaks)
cannam@85 172 { printf ("\n%s : line %d : bad peak_count (%d), expected %d.\n\n", __FILE__, __LINE__, peak_count, expected_peaks) ;
cannam@85 173 return -1.0 ;
cannam@85 174 } ;
cannam@85 175
cannam@85 176 /* Sort the peaks. */
cannam@85 177 qsort (peaks, peak_count, sizeof (PEAK_DATA), peak_compare) ;
cannam@85 178
cannam@85 179 snr = peaks [0].peak ;
cannam@85 180 for (k = 1 ; k < peak_count ; k++)
cannam@85 181 if (fabs (snr - peaks [k].peak) > 10.0)
cannam@85 182 return fabs (peaks [k].peak) ;
cannam@85 183
cannam@85 184 return snr ;
cannam@85 185 } /* find_snr */
cannam@85 186
cannam@85 187 static void
cannam@85 188 log_mag_spectrum (double *input, int len, double *magnitude)
cannam@85 189 { fftw_plan plan = NULL ;
cannam@85 190
cannam@85 191 double maxval ;
cannam@85 192 int k ;
cannam@85 193
cannam@85 194 if (input == NULL || magnitude == NULL)
cannam@85 195 return ;
cannam@85 196
cannam@85 197 plan = fftw_plan_r2r_1d (len, input, magnitude, FFTW_R2HC, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT) ;
cannam@85 198 if (plan == NULL)
cannam@85 199 { printf ("%s : line %d : create plan failed.\n", __FILE__, __LINE__) ;
cannam@85 200 exit (1) ;
cannam@85 201 } ;
cannam@85 202
cannam@85 203 fftw_execute (plan) ;
cannam@85 204
cannam@85 205 fftw_destroy_plan (plan) ;
cannam@85 206
cannam@85 207 /* (k < N/2 rounded up) */
cannam@85 208 maxval = 0.0 ;
cannam@85 209 for (k = 1 ; k < len / 2 ; k++)
cannam@85 210 { magnitude [k] = sqrt (magnitude [k] * magnitude [k] + magnitude [len - k - 1] * magnitude [len - k - 1]) ;
cannam@85 211 maxval = (maxval < magnitude [k]) ? magnitude [k] : maxval ;
cannam@85 212 } ;
cannam@85 213
cannam@85 214 memset (magnitude + len / 2, 0, len / 2 * sizeof (magnitude [0])) ;
cannam@85 215
cannam@85 216 /* Don't care about DC component. Make it zero. */
cannam@85 217 magnitude [0] = 0.0 ;
cannam@85 218
cannam@85 219 /* log magnitude. */
cannam@85 220 for (k = 0 ; k < len ; k++)
cannam@85 221 { magnitude [k] = magnitude [k] / maxval ;
cannam@85 222 magnitude [k] = (magnitude [k] < 1e-15) ? -200.0 : 20.0 * log10 (magnitude [k]) ;
cannam@85 223 } ;
cannam@85 224
cannam@85 225 return ;
cannam@85 226 } /* log_mag_spectrum */
cannam@85 227
cannam@85 228 #else /* ! (HAVE_LIBFFTW && HAVE_LIBRFFTW) */
cannam@85 229
cannam@85 230 double
cannam@85 231 calculate_snr (float *data, int len, int expected_peaks)
cannam@85 232 { double snr = 200.0 ;
cannam@85 233
cannam@85 234 data = data ;
cannam@85 235 len = len ;
cannam@85 236 expected_peaks = expected_peaks ;
cannam@85 237
cannam@85 238 return snr ;
cannam@85 239 } /* calculate_snr */
cannam@85 240
cannam@85 241 #endif
cannam@85 242