// FreqSpec.m #include #include #include #include #include #include #include "FreqSpec.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // for computing hanning window on FFT input data #define hanning(n, N) (0.5 - 0.5 * cos((2 * M_PI * n) / N)) // error macros #define PROGNAME "FreqSpec" #define fatal_error(s) do { \ fprintf(stderr, PROGNAME ":%s Error: %s: %s\n", \ __FUNCTION__, \ s, \ strerror(errno)); \ exit(EXIT_FAILURE); \ } while (0) // static inline int log2(double x); @implementation FreqSpec {} - init { // buggish: must be called after "N:" plan = rfftw_create_plan(N, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE); if (! (in = malloc(N * sizeof(fftw_real))) ) fatal_error("malloc"); if (! (out = malloc(N * sizeof(fftw_real))) ) fatal_error("malloc"); n_spectra = N / 2 + 1; if (! (spectrum = malloc(n_spectra * sizeof(fftw_real))) ) fatal_error("malloc"); // normalize the magnitudes by dividing by N / 2 normalize = YES; normalize_scale = N / 2; return self; } + newWithN: (size_t) N { self = [ super new ]; [ self N: N ]; [ self init ]; return self; } - (void)N: (size_t) n { N = n; } - use_hanning: (BOOL) yesno { use_hanning = yesno; if (use_hanning) normalize_scale = 1.3 * (N / 2); return self; } - analyze { int i, end; fftw_real magnitude; if (use_hanning) { // apply Hanning window to input data for (i = 0; i < N; ++i) in[i] *= hanning(i, N); } rfftw_one(plan, in, out); spectrum[0] = sqrt(out[0] * out[0]); /* DC component */ end = (N + 1) / 2; /* i < N/2 rounded up */ for (i = 1; i < end; ++i) { magnitude = sqrt(out[i] * out[i] + out[N - i] * out[N - i]); if (normalize) magnitude /= normalize_scale; spectrum[i] = magnitude; } if ((N % 2) == 0) { /* for even N */ magnitude = sqrt(out[N/2] * out[N/2]); /* Nyquist freq */ if (normalize) magnitude /= normalize_scale; spectrum[N/2] = magnitude; } return self; } - shortAnalyze: (short *) samples { int i; /* copy from shorts into floats */ for (i = 0; i < N; ++i) in[i] = samples[i]; return [ self analyze ]; } - intAnalyze: (int *) samples { int i; /* copy from shorts into floats */ for (i = 0; i < N; ++i) in[i] = samples[i]; return [ self analyze ]; } - (BOOL) freqs: (fftw_real *) frequencies withBaseFreq: (fftw_real) freq_spacing intoBands: (fftw_real *) bands { fftw_real avg; int n_bands = 0; int i, j, start, end, distance; // ---------count the number of requested frequencies in the // zero-terminated array while (frequencies[n_bands] != 0) ++n_bands; for (i = 0; i < n_bands - 1; ++i) { // start = log2(frequencies[i] * freq_spacing); start = frequencies[i] / freq_spacing; end = frequencies[i + 1] / freq_spacing; distance = end - start; if (distance < 0) { fprintf(stderr, "FreqSpec Error: malformed frequency array\n"); return NO; } for (j = start, avg = 0; j < end; ++j) avg += spectrum[j] / distance; // hopefully avoid overflow bands[i] = avg; } return YES; } - destroy { rfftw_destroy_plan(plan); if (in) free(in); if (out) free(out); if (spectrum) free(spectrum); return nil; } @end // static inline int log2(double x) // { // int exp; // frexp(x, &exp); // printf("log2 of %f is %d\n", x, exp - 1); // return --exp; // } /* - (fftw_real *)NBand: (int) n_bands { // pedants say don't cast malloc's return value in C, only C++ fftw_real *bands = xmalloc(n_bands * sizeof(fftw_real)); int per_band = n_spectra / n_bands; // spectra per band fftw_real sum, avg; int i, j; for (i = 0; i < n_bands; ++i) { for (j = 0, sum = 0; j < per_band; ++j) sum += spectrum[i * per_band + j]; avg = sum / per_band; bands[i] = avg; } return bands; } */