1 /* noisered - Noise Reduction Effect.
3 * Written by Ian Turner (vectro@vectro.org)
5 * Copyright 1999 Ian Turner
6 * This source code is freely redistributable and may be used for
7 * any purpose. This copyright notice must be maintained.
8 * Authors are not responsible for the consequences of using this software.
25 /* Holds profile information */
27 char* profile_filename
;
34 static void FFT(unsigned NumSamples
,
36 const float *RealIn
, float *ImagIn
, float *RealOut
, float *ImagOut
)
39 double * work
= malloc(2 * NumSamples
* sizeof(*work
));
40 for (i
= 0; i
< 2 * NumSamples
; i
+= 2) {
41 work
[i
] = RealIn
[i
>> 1];
42 work
[i
+ 1] = ImagIn
? ImagIn
[i
>> 1] : 0;
44 lsx_safe_cdft(2 * (int)NumSamples
, InverseTransform
? -1 : 1, work
);
45 if (InverseTransform
) for (i
= 0; i
< 2 * NumSamples
; i
+= 2) {
46 RealOut
[i
>> 1] = work
[i
] / NumSamples
;
47 ImagOut
[i
>> 1] = work
[i
+ 1] / NumSamples
;
49 else for (i
= 0; i
< 2 * NumSamples
; i
+= 2) {
50 RealOut
[i
>> 1] = work
[i
];
51 ImagOut
[i
>> 1] = work
[i
+ 1];
57 * Get the options. Default file is stdin (if the audio
58 * input file isn't coming from there, of course!)
60 static int sox_noisered_getopts(sox_effect_t
* effp
, int argc
, char **argv
)
62 priv_t
* p
= (priv_t
*) effp
->priv
;
66 p
->profile_filename
= argv
[0];
72 do { /* break-able block */
73 NUMERIC_PARAMETER(threshold
, 0, 1);
76 return argc
? lsx_usage(effp
) : SOX_SUCCESS
;
81 * Do all initializations.
83 static int sox_noisered_start(sox_effect_t
* effp
)
85 priv_t
* data
= (priv_t
*) effp
->priv
;
87 size_t channels
= effp
->in_signal
.channels
;
89 FILE * ifp
= lsx_open_input_file(effp
, data
->profile_filename
, sox_false
);
94 data
->chandata
= lsx_calloc(channels
, sizeof(*(data
->chandata
)));
96 for (i
= 0; i
< channels
; i
++) {
97 data
->chandata
[i
].noisegate
= lsx_calloc(FREQCOUNT
, sizeof(float));
98 data
->chandata
[i
].smoothing
= lsx_calloc(FREQCOUNT
, sizeof(float));
99 data
->chandata
[i
].lastwindow
= NULL
;
105 if (2 != fscanf(ifp
, " Channel %lu: %f", &i1_ul
, &f1
))
108 if (i1
!= fchannels
) {
109 lsx_fail("noisered: Got channel %lu, expected channel %lu.",
110 (unsigned long)i1
, (unsigned long)fchannels
);
114 data
->chandata
[fchannels
].noisegate
[0] = f1
;
115 for (i
= 1; i
< FREQCOUNT
; i
++) {
116 if (1 != fscanf(ifp
, ", %f", &f1
)) {
117 lsx_fail("noisered: Not enough data for channel %lu "
118 "(expected %d, got %lu)", (unsigned long)fchannels
, FREQCOUNT
, (unsigned long)i
);
121 data
->chandata
[fchannels
].noisegate
[i
] = f1
;
125 if (fchannels
!= channels
) {
126 lsx_fail("noisered: channel mismatch: %lu in input, %lu in profile.",
127 (unsigned long)channels
, (unsigned long)fchannels
);
133 effp
->out_signal
.length
= SOX_UNKNOWN_LEN
; /* TODO: calculate actual length */
135 return (SOX_SUCCESS
);
138 /* Mangle a single window. Each output sample (except the first and last
139 * half-window) is the result of two distinct calls to this function,
140 * due to overlapping windows. */
141 static void reduce_noise(chandata_t
* chan
, float* window
, double level
)
143 float *inr
, *ini
, *outr
, *outi
, *power
;
144 float *smoothing
= chan
->smoothing
;
147 inr
= lsx_calloc(WINDOWSIZE
* 5, sizeof(float));
148 ini
= inr
+ WINDOWSIZE
;
149 outr
= ini
+ WINDOWSIZE
;
150 outi
= outr
+ WINDOWSIZE
;
151 power
= outi
+ WINDOWSIZE
;
153 for (i
= 0; i
< FREQCOUNT
; i
++)
154 assert(smoothing
[i
] >= 0 && smoothing
[i
] <= 1);
156 memcpy(inr
, window
, WINDOWSIZE
*sizeof(float));
158 FFT(WINDOWSIZE
, 0, inr
, NULL
, outr
, outi
);
160 memcpy(inr
, window
, WINDOWSIZE
*sizeof(float));
161 lsx_apply_hann_f(inr
, WINDOWSIZE
);
162 lsx_power_spectrum_f(WINDOWSIZE
, inr
, power
);
164 for (i
= 0; i
< FREQCOUNT
; i
++) {
167 plog
= log(power
[i
]);
168 if (power
[i
] != 0 && plog
< chan
->noisegate
[i
] + level
*8.0)
173 smoothing
[i
] = smooth
* 0.5 + smoothing
[i
] * 0.5;
176 /* Audacity says this code will eliminate tinkle bells.
177 * I have no idea what that means. */
178 for (i
= 2; i
< FREQCOUNT
- 2; i
++) {
179 if (smoothing
[i
]>=0.5 &&
180 smoothing
[i
]<=0.55 &&
181 smoothing
[i
-1]<0.1 &&
182 smoothing
[i
-2]<0.1 &&
183 smoothing
[i
+1]<0.1 &&
188 outr
[0] *= smoothing
[0];
189 outi
[0] *= smoothing
[0];
190 outr
[FREQCOUNT
-1] *= smoothing
[FREQCOUNT
-1];
191 outi
[FREQCOUNT
-1] *= smoothing
[FREQCOUNT
-1];
193 for (i
= 1; i
< FREQCOUNT
-1; i
++) {
194 int j
= WINDOWSIZE
- i
;
195 float smooth
= smoothing
[i
];
203 FFT(WINDOWSIZE
, 1, outr
, outi
, inr
, ini
);
204 lsx_apply_hann_f(inr
, WINDOWSIZE
);
206 memcpy(window
, inr
, WINDOWSIZE
*sizeof(float));
208 for (i
= 0; i
< FREQCOUNT
; i
++)
209 assert(smoothing
[i
] >= 0 && smoothing
[i
] <= 1);
214 /* Do window management once we have a complete window, including mangling
215 * the current window. */
216 static int process_window(sox_effect_t
* effp
, priv_t
* data
, unsigned chan_num
, unsigned num_chans
,
217 sox_sample_t
*obuf
, unsigned len
) {
220 int use
= min(len
, WINDOWSIZE
)-min(len
,(WINDOWSIZE
/2));
221 chandata_t
*chan
= &(data
->chandata
[chan_num
]);
222 int first
= (chan
->lastwindow
== NULL
);
225 if ((nextwindow
= lsx_calloc(WINDOWSIZE
, sizeof(float))) == NULL
)
228 memcpy(nextwindow
, chan
->window
+WINDOWSIZE
/2,
229 sizeof(float)*(WINDOWSIZE
/2));
231 reduce_noise(chan
, chan
->window
, data
->threshold
);
233 for (j
= 0; j
< use
; j
++) {
234 float s
= chan
->window
[j
] + chan
->lastwindow
[WINDOWSIZE
/2 + j
];
235 obuf
[chan_num
+ num_chans
* j
] =
236 SOX_FLOAT_32BIT_TO_SAMPLE(s
, effp
->clips
);
238 free(chan
->lastwindow
);
240 for (j
= 0; j
< use
; j
++) {
241 assert(chan
->window
[j
] >= -1 && chan
->window
[j
] <= 1);
242 obuf
[chan_num
+ num_chans
* j
] =
243 SOX_FLOAT_32BIT_TO_SAMPLE(chan
->window
[j
], effp
->clips
);
246 chan
->lastwindow
= chan
->window
;
247 chan
->window
= nextwindow
;
253 * Read in windows, and call process_window once we get a whole one.
255 static int sox_noisered_flow(sox_effect_t
* effp
, const sox_sample_t
*ibuf
, sox_sample_t
*obuf
,
256 size_t *isamp
, size_t *osamp
)
258 priv_t
* data
= (priv_t
*) effp
->priv
;
259 size_t samp
= min(*isamp
, *osamp
);
260 size_t tracks
= effp
->in_signal
.channels
;
261 size_t track_samples
= samp
/ tracks
;
262 size_t ncopy
= min(track_samples
, WINDOWSIZE
-data
->bufdata
);
263 size_t whole_window
= (ncopy
+ data
->bufdata
== WINDOWSIZE
);
264 int oldbuf
= data
->bufdata
;
267 /* FIXME: Make this automatic for all effects */
268 assert(effp
->in_signal
.channels
== effp
->out_signal
.channels
);
271 data
->bufdata
= WINDOWSIZE
/2;
273 data
->bufdata
+= ncopy
;
275 /* Reduce noise on every channel. */
276 for (i
= 0; i
< tracks
; i
++) {
278 chandata_t
* chan
= &(data
->chandata
[i
]);
281 if (chan
->window
== NULL
)
282 chan
->window
= lsx_calloc(WINDOWSIZE
, sizeof(float));
284 for (j
= 0; j
< ncopy
; j
++)
285 chan
->window
[oldbuf
+ j
] =
286 SOX_SAMPLE_TO_FLOAT_32BIT(ibuf
[i
+ tracks
* j
], effp
->clips
);
291 process_window(effp
, data
, (unsigned) i
, (unsigned) tracks
, obuf
, (unsigned) (oldbuf
+ ncopy
));
294 *isamp
= tracks
*ncopy
;
296 *osamp
= tracks
*(WINDOWSIZE
/2);
304 * We have up to half a window left to dump.
307 static int sox_noisered_drain(sox_effect_t
* effp
, sox_sample_t
*obuf
, size_t *osamp
)
309 priv_t
* data
= (priv_t
*)effp
->priv
;
311 unsigned tracks
= effp
->in_signal
.channels
;
312 for (i
= 0; i
< tracks
; i
++)
313 *osamp
= process_window(effp
, data
, i
, tracks
, obuf
, (unsigned) data
->bufdata
);
315 /* FIXME: This is very picky. osamp needs to be big enough to get all
316 * remaining data or it will be discarded.
324 static int sox_noisered_stop(sox_effect_t
* effp
)
326 priv_t
* data
= (priv_t
*) effp
->priv
;
329 for (i
= 0; i
< effp
->in_signal
.channels
; i
++) {
330 chandata_t
* chan
= &(data
->chandata
[i
]);
331 free(chan
->lastwindow
);
333 free(chan
->smoothing
);
334 free(chan
->noisegate
);
337 free(data
->chandata
);
339 return (SOX_SUCCESS
);
342 static sox_effect_handler_t sox_noisered_effect
= {
344 "[profile-file [amount]]",
345 SOX_EFF_MCHAN
|SOX_EFF_LENGTH
,
346 sox_noisered_getopts
,
354 const sox_effect_handler_t
*lsx_noisered_effect_fn(void)
356 return &sox_noisered_effect
;