ref: 105e2bc5e35b1b747b8a93da398ae34cac0a5dde
parent: 791cff8c4282427ea3e8c527af4ce40bf6056b7c
author: cbagwell <cbagwell>
date: Mon Dec 20 16:13:02 EST 2004
Adding new noise removal effect
--- /dev/null
+++ b/src/FFT.c
@@ -1,0 +1,401 @@
+/*
+ *
+ * FFT.c
+ *
+ * Based on FFT.cpp from Audacity, which contained the following text:
+ *
+ * This file contains a few FFT routines, including a real-FFT
+ * routine that is almost twice as fast as a normal complex FFT,
+ * and a power spectrum routine when you know you don't care
+ * about phase information.
+ *
+ * Some of this code was based on a free implementation of an FFT
+ * by Don Cross, available on the web at:
+ *
+ * http://www.intersrv.com/~dcross/fft.html
+ *
+ * The basic algorithm for his code was based on Numerican Recipes
+ * in Fortran. I optimized his code further by reducing array
+ * accesses, caching the bit reversal table, and eliminating
+ * float-to-double conversions, and I added the routines to
+ * calculate a real FFT and a real power spectrum.
+ *
+ * This file is now part of SoX, and is copyright Ian Turner and others.
+ *
+ * SoX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Foobar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SoX; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+#include "FFT.h"
+
+int **gFFTBitTable = NULL;
+const int MaxFastBits = 16;
+
+/* Declare Static functions */
+static int IsPowerOfTwo(int x);
+static int NumberOfBitsNeeded(int PowerOfTwo);
+static int ReverseBits(int index, int NumBits);
+static void InitFFT();
+
+int IsPowerOfTwo(int x)
+{
+ if (x < 2)
+ return 0;
+
+ return !(x & (x-1)); /* Thanks to 'byang' for this cute trick! */
+}
+
+int NumberOfBitsNeeded(int PowerOfTwo)
+{
+ int i;
+
+ if (PowerOfTwo < 2) {
+ fprintf(stderr, "Error: FFT called with size %d\n", PowerOfTwo);
+ exit(1);
+ }
+
+ for (i = 0;; i++)
+ if (PowerOfTwo & (1 << i))
+ return i;
+}
+
+int ReverseBits(int index, int NumBits)
+{
+ int i, rev;
+
+ for (i = rev = 0; i < NumBits; i++) {
+ rev = (rev << 1) | (index & 1);
+ index >>= 1;
+ }
+
+ return rev;
+}
+
+
+/* This function allocates about 250k (actually (2**16)-2 ints) which is never
+ * freed, to use as a lookup table for bit-reversal. The good news is that
+ * we bascially need this until the very end, so the fact that it's not freed
+ * is OK. */
+void InitFFT()
+{
+ int len, b;
+
+ gFFTBitTable = (int**)calloc(MaxFastBits, sizeof(*gFFTBitTable));
+
+ len = 2;
+ for (b = 1; b <= MaxFastBits; b++) {
+ int i;
+
+ gFFTBitTable[b - 1] = (int*)calloc(len, sizeof(**gFFTBitTable));
+
+ for (i = 0; i < len; i++) {
+ gFFTBitTable[b - 1][i] = ReverseBits(i, b);
+ }
+
+ len <<= 1;
+ }
+}
+
+inline int FastReverseBits(int i, int NumBits)
+{
+ if (NumBits <= MaxFastBits)
+ return gFFTBitTable[NumBits - 1][i];
+ else
+ return ReverseBits(i, NumBits);
+}
+
+/*
+ * Complex Fast Fourier Transform
+ */
+
+void FFT(int NumSamples,
+ int InverseTransform,
+ float *RealIn, float *ImagIn, float *RealOut, float *ImagOut)
+{
+ int NumBits; /* Number of bits needed to store indices */
+ int i, j, k, n;
+ int BlockSize, BlockEnd;
+
+ double angle_numerator = 2.0 * M_PI;
+ float tr, ti; /* temp real, temp imaginary */
+
+ if (!IsPowerOfTwo(NumSamples)) {
+ fprintf(stderr, "%d is not a power of two\n", NumSamples);
+ exit(1);
+ }
+
+ if (!gFFTBitTable)
+ InitFFT();
+
+ if (InverseTransform)
+ angle_numerator = -angle_numerator;
+
+ NumBits = NumberOfBitsNeeded(NumSamples);
+
+ /*
+ ** Do simultaneous data copy and bit-reversal ordering into outputs...
+ */
+
+ for (i = 0; i < NumSamples; i++) {
+ j = FastReverseBits(i, NumBits);
+ RealOut[j] = RealIn[i];
+ ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i];
+ }
+
+ /*
+ ** Do the FFT itself...
+ */
+
+ BlockEnd = 1;
+ for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1) {
+
+ double delta_angle = angle_numerator / (double) BlockSize;
+
+ float sm2 = sin(-2 * delta_angle);
+ float sm1 = sin(-delta_angle);
+ float cm2 = cos(-2 * delta_angle);
+ float cm1 = cos(-delta_angle);
+ float w = 2 * cm1;
+ float ar0, ar1, ar2, ai0, ai1, ai2;
+
+ for (i = 0; i < NumSamples; i += BlockSize) {
+ ar2 = cm2;
+ ar1 = cm1;
+
+ ai2 = sm2;
+ ai1 = sm1;
+
+ for (j = i, n = 0; n < BlockEnd; j++, n++) {
+ ar0 = w * ar1 - ar2;
+ ar2 = ar1;
+ ar1 = ar0;
+
+ ai0 = w * ai1 - ai2;
+ ai2 = ai1;
+ ai1 = ai0;
+
+ k = j + BlockEnd;
+ tr = ar0 * RealOut[k] - ai0 * ImagOut[k];
+ ti = ar0 * ImagOut[k] + ai0 * RealOut[k];
+
+ RealOut[k] = RealOut[j] - tr;
+ ImagOut[k] = ImagOut[j] - ti;
+
+ RealOut[j] += tr;
+ ImagOut[j] += ti;
+ }
+ }
+
+ BlockEnd = BlockSize;
+ }
+
+ /*
+ ** Need to normalize if inverse transform...
+ */
+
+ if (InverseTransform) {
+ float denom = (float) NumSamples;
+
+ for (i = 0; i < NumSamples; i++) {
+ RealOut[i] /= denom;
+ ImagOut[i] /= denom;
+ }
+ }
+}
+
+/*
+ * Real Fast Fourier Transform
+ *
+ * This function was based on the code in Numerical Recipes for C.
+ * In Num. Rec., the inner loop is based on a single 1-based array
+ * of interleaved real and imaginary numbers. Because we have two
+ * separate zero-based arrays, our indices are quite different.
+ * Here is the correspondence between Num. Rec. indices and our indices:
+ *
+ * i1 <-> real[i]
+ * i2 <-> imag[i]
+ * i3 <-> real[n/2-i]
+ * i4 <-> imag[n/2-i]
+ */
+
+void RealFFT(int NumSamples, float *RealIn, float *RealOut, float *ImagOut)
+{
+ int Half = NumSamples / 2;
+ int i;
+
+ float theta = M_PI / Half;
+ float wtemp = (float) sin(0.5 * theta);
+ float wpr = -2.0 * wtemp * wtemp;
+ float wpi = (float) sin(theta);
+ float wr = 1.0 + wpr;
+ float wi = wpi;
+
+ int i3;
+
+ float h1r, h1i, h2r, h2i;
+
+
+ float *tmpReal = (float*)calloc(Half, sizeof(float));
+ float *tmpImag = (float*)calloc(Half, sizeof(float));
+
+ for (i = 0; i < Half; i++) {
+ tmpReal[i] = RealIn[2 * i];
+ tmpImag[i] = RealIn[2 * i + 1];
+ }
+
+ FFT(Half, 0, tmpReal, tmpImag, RealOut, ImagOut);
+
+
+ for (i = 1; i < Half / 2; i++) {
+
+ i3 = Half - i;
+
+ h1r = 0.5 * (RealOut[i] + RealOut[i3]);
+ h1i = 0.5 * (ImagOut[i] - ImagOut[i3]);
+ h2r = 0.5 * (ImagOut[i] + ImagOut[i3]);
+ h2i = -0.5 * (RealOut[i] - RealOut[i3]);
+
+ RealOut[i] = h1r + wr * h2r - wi * h2i;
+ ImagOut[i] = h1i + wr * h2i + wi * h2r;
+ RealOut[i3] = h1r - wr * h2r + wi * h2i;
+ ImagOut[i3] = -h1i + wr * h2i + wi * h2r;
+
+ wr = (wtemp = wr) * wpr - wi * wpi + wr;
+ wi = wi * wpr + wtemp * wpi + wi;
+ }
+
+ RealOut[0] = (h1r = RealOut[0]) + ImagOut[0];
+ ImagOut[0] = h1r - ImagOut[0];
+
+ free(tmpReal);
+ free(tmpImag);
+}
+
+/*
+ * PowerSpectrum
+ *
+ * This function computes the same as RealFFT, above, but
+ * adds the squares of the real and imaginary part of each
+ * coefficient, extracting the power and throwing away the
+ * phase.
+ *
+ * For speed, it does not call RealFFT, but duplicates some
+ * of its code.
+ */
+
+void PowerSpectrum(int NumSamples, float *In, float *Out)
+{
+ int Half, i, i3;
+ float theta, wtemp, wpr, wpi, wr, wi;
+ float h1r, h1i, h2r, h2i, rt, it;
+ float *tmpReal;
+ float *tmpImag;
+ float *RealOut;
+ float *ImagOut;
+
+ Half = NumSamples / 2;
+
+ theta = M_PI / Half;
+
+ tmpReal = (float*)calloc(Half, sizeof(float));
+ tmpImag = (float*)calloc(Half, sizeof(float));
+ RealOut = (float*)calloc(Half, sizeof(float));
+ ImagOut = (float*)calloc(Half, sizeof(float));
+
+ for (i = 0; i < Half; i++) {
+ tmpReal[i] = In[2 * i];
+ tmpImag[i] = In[2 * i + 1];
+ }
+
+ FFT(Half, 0, tmpReal, tmpImag, RealOut, ImagOut);
+
+ wtemp = (float) sin(0.5 * theta);
+
+ wpr = -2.0 * wtemp * wtemp;
+ wpi = (float) sin(theta);
+ wr = 1.0 + wpr;
+ wi = wpi;
+
+ for (i = 1; i < Half / 2; i++) {
+
+ i3 = Half - i;
+
+ h1r = 0.5 * (RealOut[i] + RealOut[i3]);
+ h1i = 0.5 * (ImagOut[i] - ImagOut[i3]);
+ h2r = 0.5 * (ImagOut[i] + ImagOut[i3]);
+ h2i = -0.5 * (RealOut[i] - RealOut[i3]);
+
+ rt = h1r + wr * h2r - wi * h2i;
+ it = h1i + wr * h2i + wi * h2r;
+
+ Out[i] = rt * rt + it * it;
+
+ rt = h1r - wr * h2r + wi * h2i;
+ it = -h1i + wr * h2i + wi * h2r;
+
+ Out[i3] = rt * rt + it * it;
+
+ wr = (wtemp = wr) * wpr - wi * wpi + wr;
+ wi = wi * wpr + wtemp * wpi + wi;
+ }
+
+ rt = (h1r = RealOut[0]) + ImagOut[0];
+ it = h1r - ImagOut[0];
+ Out[0] = rt * rt + it * it;
+
+ rt = RealOut[Half / 2];
+ it = ImagOut[Half / 2];
+ Out[Half / 2] = rt * rt + it * it;
+
+ free(tmpReal);
+ free(tmpImag);
+ free(RealOut);
+ free(ImagOut);
+}
+
+/*
+ * Windowing Functions
+ */
+
+void WindowFunc(windowfunc_t whichFunction, int NumSamples, float *in)
+{
+ int i;
+
+ switch (whichFunction) {
+ case BARTLETT:
+ for (i = 0; i < NumSamples / 2; i++) {
+ in[i] *= (i / (float) (NumSamples / 2));
+ in[i + (NumSamples / 2)] *=
+ (1.0 - (i / (float) (NumSamples / 2)));
+ }
+ break;
+
+ case HAMMING:
+ for (i = 0; i < NumSamples; i++)
+ in[i] *= 0.54 - 0.46 * cos(2 * M_PI * i / (NumSamples - 1));
+ break;
+
+ case HANNING:
+ for (i = 0; i < NumSamples; i++)
+ in[i] *= 0.50 - 0.50 * cos(2 * M_PI * i / (NumSamples - 1));
+ break;
+ case RECTANGULAR:
+ break;
+ }
+}
--- /dev/null
+++ b/src/FFT.h
@@ -1,0 +1,96 @@
+/*
+ *
+ * FFT.h
+ *
+ * Based on FFT.h from Audacity, which contained the following text:
+ *
+ * This file contains a few FFT routines, including a real-FFT
+ * routine that is almost twice as fast as a normal complex FFT,
+ * and a power spectrum routine when you know you don't care
+ * about phase information.
+ *
+ * Some of this code was based on a free implementation of an FFT
+ * by Don Cross, available on the web at:
+ *
+ * http://www.intersrv.com/~dcross/fft.html
+ *
+ * The basic algorithm for his code was based on Numerican Recipes
+ * in Fortran. I optimized his code further by reducing array
+ * accesses, caching the bit reversal table, and eliminating
+ * float-to-double conversions, and I added the routines to
+ * calculate a real FFT and a real power spectrum.
+ *
+ * Note: all of these routines use single-precision floats.
+ * I have found that in practice, floats work well until you
+ * get above 8192 samples. If you need to do a larger FFT,
+ * you need to use doubles.
+ *
+ * This file is now part of SoX, and is copyright Ian Turner and others.
+ *
+ * SoX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Foobar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SoX; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This is the function you will use the most often.
+ * Given an array of floats, this will compute the power
+ * spectrum by doing a Real FFT and then computing the
+ * sum of the squares of the real and imaginary parts.
+ * Note that the output array is half the length of the
+ * input array, and that NumSamples must be a power of two.
+ */
+
+void PowerSpectrum(int NumSamples, float *In, float *Out);
+
+/*
+ * Computes an FFT when the input data is real but you still
+ * want complex data as output. The output arrays are half
+ * the length of the input, and NumSamples must be a power of
+ * two.
+ */
+
+void RealFFT(int NumSamples,
+ float *RealIn, float *RealOut, float *ImagOut);
+
+/*
+ * Computes a FFT of complex input and returns complex output.
+ * Currently this is the only function here that supports the
+ * inverse transform as well.
+ */
+
+void FFT(int NumSamples,
+ int InverseTransform,
+ float *RealIn, float *ImagIn, float *RealOut, float *ImagOut);
+
+/*
+ * Applies a windowing function to the data in place
+ */
+typedef enum {RECTANGULAR = 0, /* no window */
+ BARTLETT = 1, /* triangular */
+ HAMMING = 2,
+ HANNING = 3} windowfunc_t;
+
+void WindowFunc(windowfunc_t whichFunction, int NumSamples, float *data);
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+++ b/src/noiseprof.c
@@ -1,0 +1,212 @@
+/*
+ * noiseprof - Noise Profiling Effect.
+ *
+ * Written by Ian Turner (vectro@vectro.org)
+ *
+ * Copyright 1999 Ian Turner and others
+ * This file is part of SoX.
+
+ * SoX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "noisered.h"
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+typedef struct chandata {
+ float *sum;
+ int *profilecount;
+
+ float *window;
+} chandata_t;
+
+typedef struct profdata {
+ char* output_filename;
+ FILE* output_file;
+
+ chandata_t *chandata;
+ st_size_t bufdata;
+} * profdata_t;
+
+/*
+ * Get the filename, if any. We don't open it until st_noiseprof_start.
+ */
+int st_noiseprof_getopts(eff_t effp, int n, char **argv)
+{
+ profdata_t data = (profdata_t) effp->priv;
+
+ if (n == 1) {
+ data->output_filename = argv[0];
+ } else if (n > 1) {
+ st_fail("Usage: noiseprof [filename]");
+ return (ST_EOF);
+ }
+
+ return (ST_SUCCESS);
+}
+
+/*
+ * Prepare processing.
+ * Do all initializations.
+ */
+int st_noiseprof_start(eff_t effp)
+{
+ profdata_t data = (profdata_t) effp->priv;
+ int channels = effp->ininfo.channels;
+ int i;
+
+ if (data->output_filename != NULL) {
+ data->output_file = fopen(data->output_filename, "w");
+ if (data->output_file == NULL) {
+ st_fail("Couldn't open output file %s: %s",
+ data->output_filename, strerror(errno));
+ }
+ } else {
+ /* FIXME: We should detect output to stdout, and redirect to stderr. */
+ data->output_file = stderr;
+ }
+
+ data->chandata = (chandata_t*)calloc(channels, sizeof(*(data->chandata)));
+ for (i = 0; i < channels; i ++) {
+ data->chandata[i].sum =
+ (float*)calloc(FREQCOUNT, sizeof(float));
+ data->chandata[i].profilecount =
+ (int*)calloc(FREQCOUNT, sizeof(int));
+ data->chandata[i].window =
+ (float*)calloc(WINDOWSIZE, sizeof(float));
+ }
+ data->bufdata = 0;
+ return (ST_SUCCESS);
+}
+
+/* Collect statistics from the complete window on channel chan. */
+static void collect_data(profdata_t data, chandata_t* chan) {
+ float *out = (float*)calloc(FREQCOUNT, sizeof(float));
+
+ int i;
+
+ PowerSpectrum(WINDOWSIZE, chan->window, out);
+
+ for (i = 0; i < FREQCOUNT; i ++) {
+ float value = log(out[i]);
+ if (finite(value)) {
+ chan->sum[i] += value;
+ chan->profilecount[i] ++;
+ }
+ }
+
+ free(out);
+}
+
+/*
+ * Grab what we can from ibuf, and process if we have a whole window.
+ */
+int st_noiseprof_flow(eff_t effp, st_sample_t *ibuf, st_sample_t *obuf,
+ st_size_t *isamp, st_size_t *osamp)
+{
+ profdata_t data = (profdata_t) effp->priv;
+ int samp = min(*isamp, *osamp);
+ int tracks = effp->ininfo.channels;
+ int track_samples = samp / tracks;
+ int ncopy = 0;
+ int i;
+
+ assert(effp->ininfo.channels == effp->outinfo.channels);
+
+ /* How many samples per track to analyze? */
+ ncopy = min(track_samples, WINDOWSIZE-data->bufdata);
+
+ /* Collect data for every channel. */
+ for (i = 0; i < tracks; i ++) {
+ chandata_t* chan = &(data->chandata[i]);
+ int j;
+ for (j = 0; j < ncopy; j ++) {
+ chan->window[j+data->bufdata] =
+ ST_SAMPLE_TO_FLOAT_DWORD(ibuf[i+j*tracks]);
+ }
+ if (ncopy + data->bufdata == WINDOWSIZE) {
+ collect_data(data, chan);
+ }
+ }
+
+ data->bufdata += ncopy;
+ assert(data->bufdata <= WINDOWSIZE);
+ if (data->bufdata == WINDOWSIZE)
+ data->bufdata = 0;
+
+ memcpy(obuf, ibuf, ncopy*tracks);
+ *isamp = *osamp = ncopy*tracks;
+
+ return (ST_SUCCESS);
+}
+
+/*
+ * Finish off the last window.
+ */
+
+int st_noiseprof_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
+{
+ profdata_t data = (profdata_t) effp->priv;
+ int tracks = effp->ininfo.channels;
+ int i;
+
+ *osamp = 0;
+
+ if (data->bufdata == 0) {
+ return ST_SUCCESS;
+ }
+
+ for (i = 0; i < tracks; i ++) {
+ int j;
+ for (j = data->bufdata+1; j < WINDOWSIZE; j ++) {
+ data->chandata[i].window[j] = 0;
+ }
+ collect_data(data, &(data->chandata[i]));
+ }
+
+ return (ST_SUCCESS);
+}
+
+/*
+ * Print profile and clean up.
+ */
+int st_noiseprof_stop(eff_t effp)
+{
+ profdata_t data = (profdata_t) effp->priv;
+ int i;
+
+ for (i = 0; i < effp->ininfo.channels; i ++) {
+ chandata_t* chan = &(data->chandata[i]);
+ int j;
+
+ fprintf(data->output_file, "Channel %d: ", i);
+
+ for (j = 0; j < FREQCOUNT; j ++) {
+ fprintf(data->output_file, "%s%f", j == 0 ? "" : ", ",
+ chan->sum[j] / chan->profilecount[j]);
+ }
+ fprintf(data->output_file, "\n");
+
+ free(chan->sum);
+ free(chan->profilecount);
+ }
+
+ free(data->chandata);
+
+ if (data->output_file != stderr && data->output_file != stdout) {
+ fclose(data->output_file);
+ }
+
+ return (ST_SUCCESS);
+}
+
+/* For Emacs:
+ Local Variables:
+ c-basic-offset: 4
+*/
--- /dev/null
+++ b/src/noisered.c
@@ -1,0 +1,340 @@
+/*
+ * noiseprof - Noise Profiling Effect.
+ *
+ * Written by Ian Turner (vectro@vectro.org)
+ *
+ * Copyright 1999 Ian Turner
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Authors are not responsible for the consequences of using this software.
+ */
+
+#include "noisered.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+typedef struct chandata {
+ float *window;
+ float *lastwindow;
+ float *noisegate;
+ float *smoothing;
+} chandata_t;
+
+/* Holds profile information */
+typedef struct reddata {
+ char* profile_filename;
+ float threshold;
+
+ chandata_t *chandata;
+ st_size_t bufdata;
+} * reddata_t;
+
+/*
+ * Get the options. Filename is mandatory, though a reasonable default would
+ * be stdin (if the input file isn't coming from there, of course!)
+ */
+int st_noisered_getopts(eff_t effp, int n, char **argv)
+{
+ reddata_t data = (reddata_t) effp->priv;
+
+ if (n > 2 || n < 1) {
+ st_fail("Usage: noiseprof profile-file [threshold]");
+ return (ST_EOF);
+ }
+ data->threshold = 0.5;
+ data->profile_filename = argv[0];
+ if (n == 2)
+ {
+ data->threshold = atof(argv[1]);
+
+ if (data->threshold > 1)
+ {
+ data->threshold = 1;
+ } else if (data->threshold < 0)
+ {
+ data->threshold = 0;
+ }
+ }
+ return (ST_SUCCESS);
+}
+
+/*
+ * Prepare processing.
+ * Do all initializations.
+ */
+int st_noisered_start(eff_t effp)
+{
+ reddata_t data = (reddata_t) effp->priv;
+ int fchannels = 0;
+ int channels = effp->ininfo.channels;
+ int i;
+ FILE* ifd;
+
+ data->chandata = (chandata_t*)calloc(channels, sizeof(*(data->chandata)));
+ for (i = 0; i < channels; i ++) {
+ data->chandata[i].noisegate = (float*)calloc(FREQCOUNT, sizeof(float));
+ data->chandata[i].smoothing = (float*)calloc(FREQCOUNT, sizeof(float));
+ data->chandata[i].lastwindow = NULL;
+ }
+ data->bufdata = 0;
+
+ /* Here we actually open the input file. */
+ ifd = fopen(data->profile_filename, "r");
+ if (ifd == NULL) {
+ st_fail("Couldn't open profile file %s: %s",
+ data->profile_filename, strerror(errno));
+ return ST_EOF;
+ }
+
+ while (1) {
+ int i1;
+ float f1;
+ if (2 != fscanf(ifd, " Channel %d: %f", &i1, &f1))
+ break;
+ if (i1 != fchannels) {
+ st_fail("noisered: Got channel %d, expected channel %d.",
+ i1, fchannels);
+ return ST_EOF;
+ }
+
+ data->chandata[fchannels].noisegate[0] = f1;
+ for (i = 1; i < FREQCOUNT; i ++) {
+ if (1 != fscanf(ifd, ", %f", &f1)) {
+ st_fail("noisered: Not enough datums for channel %d "
+ "(expected %d, got %d)", fchannels, FREQCOUNT, i);
+ return ST_EOF;
+ }
+ data->chandata[fchannels].noisegate[i] = f1;
+ }
+ fchannels ++;
+ }
+ if (fchannels != channels) {
+ st_fail("noisered: channel mismatch: %d in input, %d in profile.\n",
+ channels, fchannels);
+ return ST_EOF;
+ }
+ fclose(ifd);
+
+ return (ST_SUCCESS);
+}
+
+/* Mangle a single window. Each output sample (except the first and last
+ * half-window) is the result of two distinct calls to this function,
+ * due to overlapping windows. */
+static void reduce_noise(chandata_t* chan, float* window, float level)
+{
+ float *inr = (float*)calloc(WINDOWSIZE, sizeof(float));
+ float *ini = (float*)calloc(WINDOWSIZE, sizeof(float));
+ float *outr = (float*)calloc(WINDOWSIZE, sizeof(float));
+ float *outi = (float*)calloc(WINDOWSIZE, sizeof(float));
+ float *power = (float*)calloc(WINDOWSIZE, sizeof(float));
+ float *smoothing = chan->smoothing;
+ static int callnum = 0;
+
+ callnum ++;
+
+ int i;
+
+ for (i = 0; i < FREQCOUNT; i ++) {
+ assert(smoothing[i] >= 0 && smoothing[i] <= 1);
+ }
+
+ memcpy(inr, window, WINDOWSIZE*sizeof(float));
+
+ FFT(WINDOWSIZE, 0, inr, NULL, outr, outi);
+
+ memcpy(inr, window, WINDOWSIZE*sizeof(float));
+ WindowFunc(HANNING, WINDOWSIZE, inr);
+ PowerSpectrum(WINDOWSIZE, inr, power);
+
+ for(i = 0; i < FREQCOUNT; i ++) {
+ float smooth;
+ float plog;
+ plog = log(power[i]);
+ if (power[i] != 0 && plog < chan->noisegate[i] + level*8.0)
+ smooth = 0.0;
+ else
+ smooth = 1.0;
+
+ smoothing[i] = smooth * 0.5 + smoothing[i] * 0.5;
+ }
+
+ /* Audacity says this code will eliminate tinkle bells.
+ * I have no idea what that means. */
+ for (i = 2; i < FREQCOUNT - 2; i ++) {
+ if (smoothing[i]>=0.5 &&
+ smoothing[i]<=0.55 &&
+ smoothing[i-1]<0.1 &&
+ smoothing[i-2]<0.1 &&
+ smoothing[i+1]<0.1 &&
+ smoothing[i+2]<0.1)
+ smoothing[i] = 0.0;
+ }
+
+ outr[0] *= smoothing[0];
+ outi[0] *= smoothing[0];
+ outr[FREQCOUNT-1] *= smoothing[FREQCOUNT-1];
+ outi[FREQCOUNT-1] *= smoothing[FREQCOUNT-1];
+
+ for (i = 1; i < FREQCOUNT-1; i ++) {
+ int j = WINDOWSIZE - i;
+ float smooth = smoothing[i];
+
+ outr[i] *= smooth;
+ outi[i] *= smooth;
+ outr[j] *= smooth;
+ outi[j] *= smooth;
+ }
+
+ FFT(WINDOWSIZE, 1, outr, outi, inr, ini);
+ WindowFunc(HANNING, WINDOWSIZE, inr);
+
+ memcpy(window, inr, WINDOWSIZE*sizeof(float));
+
+ free(inr);
+ free(ini);
+ free(outr);
+ free(outi);
+ free(power);
+
+ for (i = 0; i < FREQCOUNT; i ++) {
+ assert(smoothing[i] >= 0 && smoothing[i] <= 1);
+ }
+}
+
+/* Do window management once we have a complete window, including mangling
+ * the current window. */
+static int process_window(reddata_t data, int chan_num, int num_chans,
+ st_sample_t *obuf, int len) {
+ int j;
+ float* nextwindow;
+ int use = min(len, WINDOWSIZE)-(WINDOWSIZE/2);
+ chandata_t *chan = &(data->chandata[chan_num]);
+ int first = (chan->lastwindow == NULL);
+
+ nextwindow = (float*)calloc(WINDOWSIZE, sizeof(float));
+ memcpy(nextwindow, chan->window+WINDOWSIZE/2,
+ sizeof(float)*(WINDOWSIZE/2));
+
+ reduce_noise(chan, chan->window, data->threshold);
+
+ if (!first) {
+ for (j = 0; j < use; j ++) {
+ float s = chan->window[j] + chan->lastwindow[WINDOWSIZE/2 + j];
+ assert(s >= -1 && s <= 1);
+ obuf[chan_num + num_chans * j] =
+ ST_FLOAT_DWORD_TO_SAMPLE(s);
+ }
+ free(chan->lastwindow);
+ } else {
+ for (j = 0; j < use; j ++) {
+ assert(chan->window[j] >= -1 && chan->window[j] <= 1);
+ obuf[chan_num + num_chans * j] =
+ ST_FLOAT_DWORD_TO_SAMPLE(chan->window[j]);
+ }
+ }
+ chan->lastwindow = chan->window;
+ chan->window = nextwindow;
+
+ return use;
+}
+
+/*
+ * Read in windows, and call process_window once we get a whole one.
+ */
+int st_noisered_flow(eff_t effp, st_sample_t *ibuf, st_sample_t *obuf,
+ st_size_t *isamp, st_size_t *osamp)
+{
+ reddata_t data = (reddata_t) effp->priv;
+ int samp = min(*isamp, *osamp);
+ int tracks = effp->ininfo.channels;
+ int track_samples = samp / tracks;
+ int ncopy = min(track_samples, WINDOWSIZE-data->bufdata);
+ int whole_window = (ncopy + data->bufdata == WINDOWSIZE);
+ int oldbuf = data->bufdata;
+ int i;
+ assert(effp->ininfo.channels == effp->outinfo.channels);
+
+ if (whole_window) {
+ data->bufdata = WINDOWSIZE/2;
+ } else {
+ data->bufdata += ncopy;
+ }
+
+ /* Reduce noise on every channel. */
+ for (i = 0; i < tracks; i ++) {
+ chandata_t* chan = &(data->chandata[i]);
+ int j;
+ if (chan->window == NULL) {
+ chan->window = (float*)calloc(WINDOWSIZE, sizeof(float));
+ }
+
+ for (j = 0; j < ncopy; j ++) {
+ chan->window[oldbuf + j] =
+ ST_SAMPLE_TO_FLOAT_DWORD(ibuf[i + tracks * j]);
+ }
+
+ if (!whole_window)
+ continue;
+ else {
+ process_window(data, i, tracks, obuf, oldbuf + ncopy);
+ }
+ }
+
+ *isamp = tracks*ncopy;
+ if (whole_window) {
+ *osamp = tracks*(WINDOWSIZE/2);
+ } else {
+ *osamp = 0;
+ }
+
+ return (ST_SUCCESS);
+}
+
+/*
+ * We have up to half a window left to dump.
+ */
+
+int st_noisered_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
+{
+ reddata_t data = (reddata_t)effp->priv;
+ int i;
+ int tracks = effp->ininfo.channels;
+ for (i = 0; i < tracks; i ++) {
+ *osamp = process_window(data, i, tracks, obuf, data->bufdata);
+ }
+ return (ST_SUCCESS);
+}
+
+/*
+ * Clean up.
+ */
+int st_noisered_stop(eff_t effp)
+{
+ reddata_t data = (reddata_t) effp->priv;
+ int i;
+
+ for (i = 0; i < effp->ininfo.channels; i ++) {
+ chandata_t* chan = &(data->chandata[i]);
+ if (chan->lastwindow != NULL) {
+ free(chan->lastwindow);
+ }
+ if (chan->window != NULL) {
+ free(chan->window);
+ }
+ free(chan->smoothing);
+ free(chan->noisegate);
+ }
+
+ free(data->chandata);
+
+ return (ST_SUCCESS);
+}
+
+/* For Emacs:
+ Local Variables:
+ c-basic-offset: 4
+*/
--- /dev/null
+++ b/src/noisered.h
@@ -1,0 +1,26 @@
+/*
+ * noiseprof.h - Headers for Noise Profiling Effect.
+ *
+ * Written by Ian Turner (vectro@vectro.org)
+ *
+ * Copyright 1999 Ian Turner and others
+ * This file is part of SoX.
+
+ * SoX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "st_i.h"
+#include "FFT.h"
+
+#include <math.h>
+
+#define WINDOWSIZE 2048
+#define FREQCOUNT (WINDOWSIZE/2+1)
+
+static int min(int a, int b) {
+ if (a < b) return a;
+ else return b;
+}