annotate src/libsamplerate-0.1.8/tests/calc_snr.c @ 83:ae30d91d2ffe

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