annotate src/libsamplerate-0.1.9/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 481f5f8c5634
children
rev   line source
Chris@41 1 /*
Chris@41 2 ** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
Chris@41 3 ** All rights reserved.
Chris@41 4 **
Chris@41 5 ** This code is released under 2-clause BSD license. Please see the
Chris@41 6 ** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
Chris@41 7 */
Chris@41 8
Chris@41 9 #include "config.h"
Chris@41 10
Chris@41 11 #include "util.h"
Chris@41 12
Chris@41 13 #if (HAVE_FFTW3 == 1)
Chris@41 14
Chris@41 15 #include <stdio.h>
Chris@41 16 #include <stdlib.h>
Chris@41 17 #include <string.h>
Chris@41 18 #include <math.h>
Chris@41 19
Chris@41 20 #include <fftw3.h>
Chris@41 21
Chris@41 22 #define MAX_SPEC_LEN (1<<18)
Chris@41 23 #define MAX_PEAKS 10
Chris@41 24
Chris@41 25 static void log_mag_spectrum (double *input, int len, double *magnitude) ;
Chris@41 26 static void smooth_mag_spectrum (double *magnitude, int len) ;
Chris@41 27 static double find_snr (const double *magnitude, int len, int expected_peaks) ;
Chris@41 28
Chris@41 29 typedef struct
Chris@41 30 { double peak ;
Chris@41 31 int index ;
Chris@41 32 } PEAK_DATA ;
Chris@41 33
Chris@41 34 double
Chris@41 35 calculate_snr (float *data, int len, int expected_peaks)
Chris@41 36 { static double magnitude [MAX_SPEC_LEN] ;
Chris@41 37 static double datacopy [MAX_SPEC_LEN] ;
Chris@41 38
Chris@41 39 double snr = 200.0 ;
Chris@41 40 int k ;
Chris@41 41
Chris@41 42 if (len > MAX_SPEC_LEN)
Chris@41 43 { printf ("%s : line %d : data length too large.\n", __FILE__, __LINE__) ;
Chris@41 44 exit (1) ;
Chris@41 45 } ;
Chris@41 46
Chris@41 47 for (k = 0 ; k < len ; k++)
Chris@41 48 datacopy [k] = data [k] ;
Chris@41 49
Chris@41 50 /* Pad the data just a little to speed up the FFT. */
Chris@41 51 while ((len & 0x1F) && len < MAX_SPEC_LEN)
Chris@41 52 { datacopy [len] = 0.0 ;
Chris@41 53 len ++ ;
Chris@41 54 } ;
Chris@41 55
Chris@41 56 log_mag_spectrum (datacopy, len, magnitude) ;
Chris@41 57 smooth_mag_spectrum (magnitude, len / 2) ;
Chris@41 58
Chris@41 59 snr = find_snr (magnitude, len, expected_peaks) ;
Chris@41 60
Chris@41 61 return snr ;
Chris@41 62 } /* calculate_snr */
Chris@41 63
Chris@41 64 /*==============================================================================
Chris@41 65 ** There is a slight problem with trying to measure SNR with the method used
Chris@41 66 ** here; the side lobes of the windowed FFT can look like a noise/aliasing peak.
Chris@41 67 ** The solution is to smooth the magnitude spectrum by wiping out troughs
Chris@41 68 ** between adjacent peaks as done here.
Chris@41 69 ** This removes side lobe peaks without affecting noise/aliasing peaks.
Chris@41 70 */
Chris@41 71
Chris@41 72 static void linear_smooth (double *mag, PEAK_DATA *larger, PEAK_DATA *smaller) ;
Chris@41 73
Chris@41 74 static void
Chris@41 75 smooth_mag_spectrum (double *mag, int len)
Chris@41 76 { PEAK_DATA peaks [2] ;
Chris@41 77
Chris@41 78 int k ;
Chris@41 79
Chris@41 80 memset (peaks, 0, sizeof (peaks)) ;
Chris@41 81
Chris@41 82 /* Find first peak. */
Chris@41 83 for (k = 1 ; k < len - 1 ; k++)
Chris@41 84 { if (mag [k - 1] < mag [k] && mag [k] >= mag [k + 1])
Chris@41 85 { peaks [0].peak = mag [k] ;
Chris@41 86 peaks [0].index = k ;
Chris@41 87 break ;
Chris@41 88 } ;
Chris@41 89 } ;
Chris@41 90
Chris@41 91 /* Find subsequent peaks ans smooth between peaks. */
Chris@41 92 for (k = peaks [0].index + 1 ; k < len - 1 ; k++)
Chris@41 93 { if (mag [k - 1] < mag [k] && mag [k] >= mag [k + 1])
Chris@41 94 { peaks [1].peak = mag [k] ;
Chris@41 95 peaks [1].index = k ;
Chris@41 96
Chris@41 97 if (peaks [1].peak > peaks [0].peak)
Chris@41 98 linear_smooth (mag, &peaks [1], &peaks [0]) ;
Chris@41 99 else
Chris@41 100 linear_smooth (mag, &peaks [0], &peaks [1]) ;
Chris@41 101 peaks [0] = peaks [1] ;
Chris@41 102 } ;
Chris@41 103 } ;
Chris@41 104
Chris@41 105 } /* smooth_mag_spectrum */
Chris@41 106
Chris@41 107 static void
Chris@41 108 linear_smooth (double *mag, PEAK_DATA *larger, PEAK_DATA *smaller)
Chris@41 109 { int k ;
Chris@41 110
Chris@41 111 if (smaller->index < larger->index)
Chris@41 112 { for (k = smaller->index + 1 ; k < larger->index ; k++)
Chris@41 113 mag [k] = (mag [k] < mag [k - 1]) ? 0.999 * mag [k - 1] : mag [k] ;
Chris@41 114 }
Chris@41 115 else
Chris@41 116 { for (k = smaller->index - 1 ; k >= larger->index ; k--)
Chris@41 117 mag [k] = (mag [k] < mag [k + 1]) ? 0.999 * mag [k + 1] : mag [k] ;
Chris@41 118 } ;
Chris@41 119
Chris@41 120 } /* linear_smooth */
Chris@41 121
Chris@41 122 /*==============================================================================
Chris@41 123 */
Chris@41 124
Chris@41 125 static int
Chris@41 126 peak_compare (const void *vp1, const void *vp2)
Chris@41 127 { const PEAK_DATA *peak1, *peak2 ;
Chris@41 128
Chris@41 129 peak1 = (const PEAK_DATA*) vp1 ;
Chris@41 130 peak2 = (const PEAK_DATA*) vp2 ;
Chris@41 131
Chris@41 132 return (peak1->peak < peak2->peak) ? 1 : -1 ;
Chris@41 133 } /* peak_compare */
Chris@41 134
Chris@41 135 static double
Chris@41 136 find_snr (const double *magnitude, int len, int expected_peaks)
Chris@41 137 { PEAK_DATA peaks [MAX_PEAKS] ;
Chris@41 138
Chris@41 139 int k, peak_count = 0 ;
Chris@41 140 double snr ;
Chris@41 141
Chris@41 142 memset (peaks, 0, sizeof (peaks)) ;
Chris@41 143
Chris@41 144 /* Find the MAX_PEAKS largest peaks. */
Chris@41 145 for (k = 1 ; k < len - 1 ; k++)
Chris@41 146 { if (magnitude [k - 1] < magnitude [k] && magnitude [k] >= magnitude [k + 1])
Chris@41 147 { if (peak_count < MAX_PEAKS)
Chris@41 148 { peaks [peak_count].peak = magnitude [k] ;
Chris@41 149 peaks [peak_count].index = k ;
Chris@41 150 peak_count ++ ;
Chris@41 151 qsort (peaks, peak_count, sizeof (PEAK_DATA), peak_compare) ;
Chris@41 152 }
Chris@41 153 else if (magnitude [k] > peaks [MAX_PEAKS - 1].peak)
Chris@41 154 { peaks [MAX_PEAKS - 1].peak = magnitude [k] ;
Chris@41 155 peaks [MAX_PEAKS - 1].index = k ;
Chris@41 156 qsort (peaks, MAX_PEAKS, sizeof (PEAK_DATA), peak_compare) ;
Chris@41 157 } ;
Chris@41 158 } ;
Chris@41 159 } ;
Chris@41 160
Chris@41 161 if (peak_count < expected_peaks)
Chris@41 162 { printf ("\n%s : line %d : bad peak_count (%d), expected %d.\n\n", __FILE__, __LINE__, peak_count, expected_peaks) ;
Chris@41 163 return -1.0 ;
Chris@41 164 } ;
Chris@41 165
Chris@41 166 /* Sort the peaks. */
Chris@41 167 qsort (peaks, peak_count, sizeof (PEAK_DATA), peak_compare) ;
Chris@41 168
Chris@41 169 snr = peaks [0].peak ;
Chris@41 170 for (k = 1 ; k < peak_count ; k++)
Chris@41 171 if (fabs (snr - peaks [k].peak) > 10.0)
Chris@41 172 return fabs (peaks [k].peak) ;
Chris@41 173
Chris@41 174 return snr ;
Chris@41 175 } /* find_snr */
Chris@41 176
Chris@41 177 static void
Chris@41 178 log_mag_spectrum (double *input, int len, double *magnitude)
Chris@41 179 { fftw_plan plan = NULL ;
Chris@41 180
Chris@41 181 double maxval ;
Chris@41 182 int k ;
Chris@41 183
Chris@41 184 if (input == NULL || magnitude == NULL)
Chris@41 185 return ;
Chris@41 186
Chris@41 187 plan = fftw_plan_r2r_1d (len, input, magnitude, FFTW_R2HC, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT) ;
Chris@41 188 if (plan == NULL)
Chris@41 189 { printf ("%s : line %d : create plan failed.\n", __FILE__, __LINE__) ;
Chris@41 190 exit (1) ;
Chris@41 191 } ;
Chris@41 192
Chris@41 193 fftw_execute (plan) ;
Chris@41 194
Chris@41 195 fftw_destroy_plan (plan) ;
Chris@41 196
Chris@41 197 maxval = 0.0 ;
Chris@41 198 for (k = 1 ; k < len / 2 ; k++)
Chris@41 199 { /*
Chris@41 200 ** From : http://www.fftw.org/doc/Real_002dto_002dReal-Transform-Kinds.html#Real_002dto_002dReal-Transform-Kinds
Chris@41 201 **
Chris@41 202 ** FFTW_R2HC computes a real-input DFT with output in “halfcomplex” format, i.e. real and imaginary parts
Chris@41 203 ** for a transform of size n stored as:
Chris@41 204 **
Chris@41 205 ** r0, r1, r2, ..., rn/2, i(n+1)/2-1, ..., i2, i1
Chris@41 206 */
Chris@41 207 double re = magnitude [k] ;
Chris@41 208 double im = magnitude [len - k] ;
Chris@41 209 magnitude [k] = sqrt (re * re + im * im) ;
Chris@41 210 maxval = (maxval < magnitude [k]) ? magnitude [k] : maxval ;
Chris@41 211 } ;
Chris@41 212
Chris@41 213 memset (magnitude + len / 2, 0, len / 2 * sizeof (magnitude [0])) ;
Chris@41 214
Chris@41 215 /* Don't care about DC component. Make it zero. */
Chris@41 216 magnitude [0] = 0.0 ;
Chris@41 217
Chris@41 218 /* log magnitude. */
Chris@41 219 for (k = 0 ; k < len ; k++)
Chris@41 220 { magnitude [k] = magnitude [k] / maxval ;
Chris@41 221 magnitude [k] = (magnitude [k] < 1e-15) ? -200.0 : 20.0 * log10 (magnitude [k]) ;
Chris@41 222 } ;
Chris@41 223
Chris@41 224 return ;
Chris@41 225 } /* log_mag_spectrum */
Chris@41 226
Chris@41 227 #else /* ! (HAVE_LIBFFTW && HAVE_LIBRFFTW) */
Chris@41 228
Chris@41 229 double
Chris@41 230 calculate_snr (float *data, int len, int expected_peaks)
Chris@41 231 { double snr = 200.0 ;
Chris@41 232
Chris@41 233 data = data ;
Chris@41 234 len = len ;
Chris@41 235 expected_peaks = expected_peaks ;
Chris@41 236
Chris@41 237 return snr ;
Chris@41 238 } /* calculate_snr */
Chris@41 239
Chris@41 240 #endif
Chris@41 241