| /* |
| * Copyright (c) 2018 The FFmpeg Project |
| * |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg 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.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <float.h> |
| |
| #include "libavutil/audio_fifo.h" |
| #include "libavutil/avstring.h" |
| #include "libavutil/channel_layout.h" |
| #include "libavutil/opt.h" |
| #include "libavcodec/avfft.h" |
| #include "avfilter.h" |
| #include "audio.h" |
| #include "formats.h" |
| #include "filters.h" |
| |
| #define C (M_LN10 * 0.1) |
| #define RATIO 0.98 |
| #define RRATIO (1.0 - RATIO) |
| |
| enum OutModes { |
| IN_MODE, |
| OUT_MODE, |
| NOISE_MODE, |
| NB_MODES |
| }; |
| |
| enum NoiseType { |
| WHITE_NOISE, |
| VINYL_NOISE, |
| SHELLAC_NOISE, |
| CUSTOM_NOISE, |
| NB_NOISE |
| }; |
| |
| typedef struct DeNoiseChannel { |
| int band_noise[15]; |
| double noise_band_auto_var[15]; |
| double noise_band_sample[15]; |
| double *amt; |
| double *band_amt; |
| double *band_excit; |
| double *gain; |
| double *prior; |
| double *prior_band_excit; |
| double *clean_data; |
| double *noisy_data; |
| double *out_samples; |
| double *spread_function; |
| double *abs_var; |
| double *rel_var; |
| double *min_abs_var; |
| FFTComplex *fft_data; |
| FFTContext *fft, *ifft; |
| |
| double noise_band_norm[15]; |
| double noise_band_avr[15]; |
| double noise_band_avi[15]; |
| double noise_band_var[15]; |
| |
| double sfm_threshold; |
| double sfm_alpha; |
| double sfm_results[3]; |
| int sfm_fail_flags[512]; |
| int sfm_fail_total; |
| } DeNoiseChannel; |
| |
| typedef struct AudioFFTDeNoiseContext { |
| const AVClass *class; |
| |
| float noise_reduction; |
| float noise_floor; |
| int noise_type; |
| char *band_noise_str; |
| float residual_floor; |
| int track_noise; |
| int track_residual; |
| int output_mode; |
| |
| float last_residual_floor; |
| float last_noise_floor; |
| float last_noise_reduction; |
| float last_noise_balance; |
| int64_t block_count; |
| |
| int64_t pts; |
| int channels; |
| int sample_noise; |
| int sample_noise_start; |
| int sample_noise_end; |
| float sample_rate; |
| int buffer_length; |
| int fft_length; |
| int fft_length2; |
| int bin_count; |
| int window_length; |
| int sample_advance; |
| int number_of_bands; |
| |
| int band_centre[15]; |
| |
| int *bin2band; |
| double *window; |
| double *band_alpha; |
| double *band_beta; |
| |
| DeNoiseChannel *dnch; |
| |
| double max_gain; |
| double max_var; |
| double gain_scale; |
| double window_weight; |
| double floor; |
| double sample_floor; |
| double auto_floor; |
| |
| int noise_band_edge[17]; |
| int noise_band_count; |
| double matrix_a[25]; |
| double vector_b[5]; |
| double matrix_b[75]; |
| double matrix_c[75]; |
| |
| AVAudioFifo *fifo; |
| } AudioFFTDeNoiseContext; |
| |
| #define OFFSET(x) offsetof(AudioFFTDeNoiseContext, x) |
| #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
| #define AFR AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM |
| |
| static const AVOption afftdn_options[] = { |
| { "nr", "set the noise reduction", OFFSET(noise_reduction), AV_OPT_TYPE_FLOAT, {.dbl = 12}, .01, 97, AFR }, |
| { "nf", "set the noise floor", OFFSET(noise_floor), AV_OPT_TYPE_FLOAT, {.dbl =-50}, -80,-20, AFR }, |
| { "nt", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, AF, "type" }, |
| { "w", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, AF, "type" }, |
| { "v", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, AF, "type" }, |
| { "s", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, AF, "type" }, |
| { "c", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, AF, "type" }, |
| { "bn", "set the custom bands noise", OFFSET(band_noise_str), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, AF }, |
| { "rf", "set the residual floor", OFFSET(residual_floor), AV_OPT_TYPE_FLOAT, {.dbl =-38}, -80,-20, AFR }, |
| { "tn", "track noise", OFFSET(track_noise), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, |
| { "tr", "track residual", OFFSET(track_residual), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, |
| { "om", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64 = OUT_MODE}, 0, NB_MODES-1, AFR, "mode" }, |
| { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64 = IN_MODE}, 0, 0, AFR, "mode" }, |
| { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64 = OUT_MODE}, 0, 0, AFR, "mode" }, |
| { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, AFR, "mode" }, |
| { NULL } |
| }; |
| |
| AVFILTER_DEFINE_CLASS(afftdn); |
| |
| static int get_band_noise(AudioFFTDeNoiseContext *s, |
| int band, double a, |
| double b, double c) |
| { |
| double d1, d2, d3; |
| |
| d1 = a / s->band_centre[band]; |
| d1 = 10.0 * log(1.0 + d1 * d1) / M_LN10; |
| d2 = b / s->band_centre[band]; |
| d2 = 10.0 * log(1.0 + d2 * d2) / M_LN10; |
| d3 = s->band_centre[band] / c; |
| d3 = 10.0 * log(1.0 + d3 * d3) / M_LN10; |
| |
| return lrint(-d1 + d2 - d3); |
| } |
| |
| static void factor(double *array, int size) |
| { |
| for (int i = 0; i < size - 1; i++) { |
| for (int j = i + 1; j < size; j++) { |
| double d = array[j + i * size] / array[i + i * size]; |
| |
| array[j + i * size] = d; |
| for (int k = i + 1; k < size; k++) { |
| array[j + k * size] -= d * array[i + k * size]; |
| } |
| } |
| } |
| } |
| |
| static void solve(double *matrix, double *vector, int size) |
| { |
| for (int i = 0; i < size - 1; i++) { |
| for (int j = i + 1; j < size; j++) { |
| double d = matrix[j + i * size]; |
| vector[j] -= d * vector[i]; |
| } |
| } |
| |
| vector[size - 1] /= matrix[size * size - 1]; |
| |
| for (int i = size - 2; i >= 0; i--) { |
| double d = vector[i]; |
| for (int j = i + 1; j < size; j++) |
| d -= matrix[i + j * size] * vector[j]; |
| vector[i] = d / matrix[i + i * size]; |
| } |
| } |
| |
| static int process_get_band_noise(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch, |
| int band) |
| { |
| double product, sum, f; |
| int i = 0; |
| |
| if (band < 15) |
| return dnch->band_noise[band]; |
| |
| for (int j = 0; j < 5; j++) { |
| sum = 0.0; |
| for (int k = 0; k < 15; k++) |
| sum += s->matrix_b[i++] * dnch->band_noise[k]; |
| s->vector_b[j] = sum; |
| } |
| |
| solve(s->matrix_a, s->vector_b, 5); |
| f = (0.5 * s->sample_rate) / s->band_centre[14]; |
| f = 15.0 + log(f / 1.5) / log(1.5); |
| sum = 0.0; |
| product = 1.0; |
| for (int j = 0; j < 5; j++) { |
| sum += product * s->vector_b[j]; |
| product *= f; |
| } |
| |
| return lrint(sum); |
| } |
| |
| static void calculate_sfm(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch, |
| int start, int end) |
| { |
| double d1 = 0.0, d2 = 1.0; |
| int i = 0, j = 0; |
| |
| for (int k = start; k < end; k++) { |
| if (dnch->noisy_data[k] > s->sample_floor) { |
| j++; |
| d1 += dnch->noisy_data[k]; |
| d2 *= dnch->noisy_data[k]; |
| if (d2 > 1.0E100) { |
| d2 *= 1.0E-100; |
| i++; |
| } else if (d2 < 1.0E-100) { |
| d2 *= 1.0E100; |
| i--; |
| } |
| } |
| } |
| if (j > 1) { |
| d1 /= j; |
| dnch->sfm_results[0] = d1; |
| d2 = log(d2) + 230.2585 * i; |
| d2 /= j; |
| d1 = log(d1); |
| dnch->sfm_results[1] = d1; |
| dnch->sfm_results[2] = d1 - d2; |
| } else { |
| dnch->sfm_results[0] = s->auto_floor; |
| dnch->sfm_results[1] = dnch->sfm_threshold; |
| dnch->sfm_results[2] = dnch->sfm_threshold; |
| } |
| } |
| |
| static double limit_gain(double a, double b) |
| { |
| if (a > 1.0) |
| return (b * a - 1.0) / (b + a - 2.0); |
| if (a < 1.0) |
| return (b * a - 2.0 * a + 1.0) / (b - a); |
| return 1.0; |
| } |
| |
| static void process_frame(AudioFFTDeNoiseContext *s, DeNoiseChannel *dnch, |
| FFTComplex *fft_data, |
| double *prior, double *prior_band_excit, int track_noise) |
| { |
| double d1, d2, d3, gain; |
| int n, i1; |
| |
| d1 = fft_data[0].re * fft_data[0].re; |
| dnch->noisy_data[0] = d1; |
| d2 = d1 / dnch->abs_var[0]; |
| d3 = RATIO * prior[0] + RRATIO * fmax(d2 - 1.0, 0.0); |
| gain = d3 / (1.0 + d3); |
| gain *= (gain + M_PI_4 / fmax(d2, 1.0E-6)); |
| prior[0] = (d2 * gain); |
| dnch->clean_data[0] = (d1 * gain); |
| gain = sqrt(gain); |
| dnch->gain[0] = gain; |
| n = 0; |
| for (int i = 1; i < s->fft_length2; i++) { |
| d1 = fft_data[i].re * fft_data[i].re + fft_data[i].im * fft_data[i].im; |
| if (d1 > s->sample_floor) |
| n = i; |
| |
| dnch->noisy_data[i] = d1; |
| d2 = d1 / dnch->abs_var[i]; |
| d3 = RATIO * prior[i] + RRATIO * fmax(d2 - 1.0, 0.0); |
| gain = d3 / (1.0 + d3); |
| gain *= (gain + M_PI_4 / fmax(d2, 1.0E-6)); |
| prior[i] = d2 * gain; |
| dnch->clean_data[i] = d1 * gain; |
| gain = sqrt(gain); |
| dnch->gain[i] = gain; |
| } |
| d1 = fft_data[0].im * fft_data[0].im; |
| if (d1 > s->sample_floor) |
| n = s->fft_length2; |
| |
| dnch->noisy_data[s->fft_length2] = d1; |
| d2 = d1 / dnch->abs_var[s->fft_length2]; |
| d3 = RATIO * prior[s->fft_length2] + RRATIO * fmax(d2 - 1.0, 0.0); |
| gain = d3 / (1.0 + d3); |
| gain *= gain + M_PI_4 / fmax(d2, 1.0E-6); |
| prior[s->fft_length2] = d2 * gain; |
| dnch->clean_data[s->fft_length2] = d1 * gain; |
| gain = sqrt(gain); |
| dnch->gain[s->fft_length2] = gain; |
| if (n > s->fft_length2 - 2) { |
| n = s->bin_count; |
| i1 = s->noise_band_count; |
| } else { |
| i1 = 0; |
| for (int i = 0; i <= s->noise_band_count; i++) { |
| if (n > 1.1 * s->noise_band_edge[i]) { |
| i1 = i; |
| } |
| } |
| } |
| |
| if (track_noise && (i1 > s->noise_band_count / 2)) { |
| int j = FFMIN(n, s->noise_band_edge[i1]); |
| int m = 3, k; |
| |
| for (k = i1 - 1; k >= 0; k--) { |
| int i = s->noise_band_edge[k]; |
| calculate_sfm(s, dnch, i, j); |
| dnch->noise_band_sample[k] = dnch->sfm_results[0]; |
| if (dnch->sfm_results[2] + 0.013 * m * fmax(0.0, dnch->sfm_results[1] - 20.53) >= dnch->sfm_threshold) { |
| break; |
| } |
| j = i; |
| m++; |
| } |
| |
| if (k < i1 - 1) { |
| double sum = 0.0, min, max; |
| int i; |
| |
| for (i = i1 - 1; i > k; i--) { |
| min = log(dnch->noise_band_sample[i] / dnch->noise_band_auto_var[i]); |
| sum += min; |
| } |
| |
| i = i1 - k - 1; |
| if (i < 5) { |
| min = 3.0E-4 * i * i; |
| } else { |
| min = 3.0E-4 * (8 * i - 16); |
| } |
| if (i < 3) { |
| max = 2.0E-4 * i * i; |
| } else { |
| max = 2.0E-4 * (4 * i - 4); |
| } |
| |
| if (s->track_residual) { |
| if (s->last_noise_floor > s->last_residual_floor + 9) { |
| min *= 0.5; |
| max *= 0.75; |
| } else if (s->last_noise_floor > s->last_residual_floor + 6) { |
| min *= 0.4; |
| max *= 1.0; |
| } else if (s->last_noise_floor > s->last_residual_floor + 4) { |
| min *= 0.3; |
| max *= 1.3; |
| } else if (s->last_noise_floor > s->last_residual_floor + 2) { |
| min *= 0.2; |
| max *= 1.6; |
| } else if (s->last_noise_floor > s->last_residual_floor) { |
| min *= 0.1; |
| max *= 2.0; |
| } else { |
| min = 0.0; |
| max *= 2.5; |
| } |
| } |
| |
| sum = av_clipd(sum, -min, max); |
| sum = exp(sum); |
| for (int i = 0; i < 15; i++) |
| dnch->noise_band_auto_var[i] *= sum; |
| } else if (dnch->sfm_results[2] >= dnch->sfm_threshold) { |
| dnch->sfm_fail_flags[s->block_count & 0x1FF] = 1; |
| dnch->sfm_fail_total += 1; |
| } |
| } |
| |
| for (int i = 0; i < s->number_of_bands; i++) { |
| dnch->band_excit[i] = 0.0; |
| dnch->band_amt[i] = 0.0; |
| } |
| |
| for (int i = 0; i < s->bin_count; i++) { |
| dnch->band_excit[s->bin2band[i]] += dnch->clean_data[i]; |
| } |
| |
| for (int i = 0; i < s->number_of_bands; i++) { |
| dnch->band_excit[i] = fmax(dnch->band_excit[i], |
| s->band_alpha[i] * dnch->band_excit[i] + |
| s->band_beta[i] * prior_band_excit[i]); |
| prior_band_excit[i] = dnch->band_excit[i]; |
| } |
| |
| for (int j = 0, i = 0; j < s->number_of_bands; j++) { |
| for (int k = 0; k < s->number_of_bands; k++) { |
| dnch->band_amt[j] += dnch->spread_function[i++] * dnch->band_excit[k]; |
| } |
| } |
| |
| for (int i = 0; i < s->bin_count; i++) |
| dnch->amt[i] = dnch->band_amt[s->bin2band[i]]; |
| |
| if (dnch->amt[0] > dnch->abs_var[0]) { |
| dnch->gain[0] = 1.0; |
| } else if (dnch->amt[0] > dnch->min_abs_var[0]) { |
| double limit = sqrt(dnch->abs_var[0] / dnch->amt[0]); |
| dnch->gain[0] = limit_gain(dnch->gain[0], limit); |
| } else { |
| dnch->gain[0] = limit_gain(dnch->gain[0], s->max_gain); |
| } |
| if (dnch->amt[s->fft_length2] > dnch->abs_var[s->fft_length2]) { |
| dnch->gain[s->fft_length2] = 1.0; |
| } else if (dnch->amt[s->fft_length2] > dnch->min_abs_var[s->fft_length2]) { |
| double limit = sqrt(dnch->abs_var[s->fft_length2] / dnch->amt[s->fft_length2]); |
| dnch->gain[s->fft_length2] = limit_gain(dnch->gain[s->fft_length2], limit); |
| } else { |
| dnch->gain[s->fft_length2] = limit_gain(dnch->gain[s->fft_length2], s->max_gain); |
| } |
| |
| for (int i = 1; i < s->fft_length2; i++) { |
| if (dnch->amt[i] > dnch->abs_var[i]) { |
| dnch->gain[i] = 1.0; |
| } else if (dnch->amt[i] > dnch->min_abs_var[i]) { |
| double limit = sqrt(dnch->abs_var[i] / dnch->amt[i]); |
| dnch->gain[i] = limit_gain(dnch->gain[i], limit); |
| } else { |
| dnch->gain[i] = limit_gain(dnch->gain[i], s->max_gain); |
| } |
| } |
| |
| gain = dnch->gain[0]; |
| dnch->clean_data[0] = (gain * gain * dnch->noisy_data[0]); |
| fft_data[0].re *= gain; |
| gain = dnch->gain[s->fft_length2]; |
| dnch->clean_data[s->fft_length2] = (gain * gain * dnch->noisy_data[s->fft_length2]); |
| fft_data[0].im *= gain; |
| for (int i = 1; i < s->fft_length2; i++) { |
| gain = dnch->gain[i]; |
| dnch->clean_data[i] = (gain * gain * dnch->noisy_data[i]); |
| fft_data[i].re *= gain; |
| fft_data[i].im *= gain; |
| } |
| } |
| |
| static double freq2bark(double x) |
| { |
| double d = x / 7500.0; |
| |
| return 13.0 * atan(7.6E-4 * x) + 3.5 * atan(d * d); |
| } |
| |
| static int get_band_centre(AudioFFTDeNoiseContext *s, int band) |
| { |
| if (band == -1) |
| return lrint(s->band_centre[0] / 1.5); |
| |
| return s->band_centre[band]; |
| } |
| |
| static int get_band_edge(AudioFFTDeNoiseContext *s, int band) |
| { |
| int i; |
| |
| if (band == 15) { |
| i = lrint(s->band_centre[14] * 1.224745); |
| } else { |
| i = lrint(s->band_centre[band] / 1.224745); |
| } |
| |
| return FFMIN(i, s->sample_rate / 2); |
| } |
| |
| static void set_band_parameters(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch) |
| { |
| double band_noise, d2, d3, d4, d5; |
| int i = 0, j = 0, k = 0; |
| |
| d5 = 0.0; |
| band_noise = process_get_band_noise(s, dnch, 0); |
| for (int m = j; m <= s->fft_length2; m++) { |
| if (m == j) { |
| i = j; |
| d5 = band_noise; |
| if (k == 15) { |
| j = s->bin_count; |
| } else { |
| j = s->fft_length * get_band_centre(s, k) / s->sample_rate; |
| } |
| d2 = j - i; |
| band_noise = process_get_band_noise(s, dnch, k); |
| k++; |
| } |
| d3 = (j - m) / d2; |
| d4 = (m - i) / d2; |
| dnch->rel_var[m] = exp((d5 * d3 + band_noise * d4) * C); |
| } |
| dnch->rel_var[s->fft_length2] = exp(band_noise * C); |
| |
| for (i = 0; i < 15; i++) |
| dnch->noise_band_auto_var[i] = s->max_var * exp((process_get_band_noise(s, dnch, i) - 2.0) * C); |
| |
| for (i = 0; i <= s->fft_length2; i++) { |
| dnch->abs_var[i] = fmax(s->max_var * dnch->rel_var[i], 1.0); |
| dnch->min_abs_var[i] = s->gain_scale * dnch->abs_var[i]; |
| } |
| } |
| |
| static void read_custom_noise(AudioFFTDeNoiseContext *s, int ch) |
| { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| char *p, *arg, *saveptr = NULL; |
| int i, ret, band_noise[15] = { 0 }; |
| |
| if (!s->band_noise_str) |
| return; |
| |
| p = av_strdup(s->band_noise_str); |
| if (!p) |
| return; |
| |
| for (i = 0; i < 15; i++) { |
| if (!(arg = av_strtok(p, "| ", &saveptr))) |
| break; |
| |
| p = NULL; |
| |
| ret = av_sscanf(arg, "%d", &band_noise[i]); |
| if (ret != 1) { |
| av_log(s, AV_LOG_ERROR, "Custom band noise must be integer.\n"); |
| break; |
| } |
| |
| band_noise[i] = av_clip(band_noise[i], -24, 24); |
| } |
| |
| av_free(p); |
| memcpy(dnch->band_noise, band_noise, sizeof(band_noise)); |
| } |
| |
| static void set_parameters(AudioFFTDeNoiseContext *s) |
| { |
| if (s->last_noise_floor != s->noise_floor) |
| s->last_noise_floor = s->noise_floor; |
| |
| if (s->track_residual) |
| s->last_noise_floor = fmaxf(s->last_noise_floor, s->residual_floor); |
| |
| s->max_var = s->floor * exp((100.0 + s->last_noise_floor) * C); |
| |
| if (s->track_residual) { |
| s->last_residual_floor = s->residual_floor; |
| s->last_noise_reduction = fmax(s->last_noise_floor - s->last_residual_floor, 0); |
| s->max_gain = exp(s->last_noise_reduction * (0.5 * C)); |
| } else if (s->noise_reduction != s->last_noise_reduction) { |
| s->last_noise_reduction = s->noise_reduction; |
| s->last_residual_floor = av_clipf(s->last_noise_floor - s->last_noise_reduction, -80, -20); |
| s->max_gain = exp(s->last_noise_reduction * (0.5 * C)); |
| } |
| |
| s->gain_scale = 1.0 / (s->max_gain * s->max_gain); |
| |
| for (int ch = 0; ch < s->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| |
| set_band_parameters(s, dnch); |
| } |
| } |
| |
| static int config_input(AVFilterLink *inlink) |
| { |
| AVFilterContext *ctx = inlink->dst; |
| AudioFFTDeNoiseContext *s = ctx->priv; |
| double wscale, sar, sum, sdiv; |
| int i, j, k, m, n; |
| |
| s->dnch = av_calloc(inlink->channels, sizeof(*s->dnch)); |
| if (!s->dnch) |
| return AVERROR(ENOMEM); |
| |
| s->pts = AV_NOPTS_VALUE; |
| s->channels = inlink->channels; |
| s->sample_rate = inlink->sample_rate; |
| s->sample_advance = s->sample_rate / 80; |
| s->window_length = 3 * s->sample_advance; |
| s->fft_length2 = 1 << (32 - ff_clz(s->window_length)); |
| s->fft_length = s->fft_length2 * 2; |
| s->buffer_length = s->fft_length * 2; |
| s->bin_count = s->fft_length2 + 1; |
| |
| s->band_centre[0] = 80; |
| for (i = 1; i < 15; i++) { |
| s->band_centre[i] = lrint(1.5 * s->band_centre[i - 1] + 5.0); |
| if (s->band_centre[i] < 1000) { |
| s->band_centre[i] = 10 * (s->band_centre[i] / 10); |
| } else if (s->band_centre[i] < 5000) { |
| s->band_centre[i] = 50 * ((s->band_centre[i] + 20) / 50); |
| } else if (s->band_centre[i] < 15000) { |
| s->band_centre[i] = 100 * ((s->band_centre[i] + 45) / 100); |
| } else { |
| s->band_centre[i] = 1000 * ((s->band_centre[i] + 495) / 1000); |
| } |
| } |
| |
| for (j = 0; j < 5; j++) { |
| for (k = 0; k < 5; k++) { |
| s->matrix_a[j + k * 5] = 0.0; |
| for (m = 0; m < 15; m++) |
| s->matrix_a[j + k * 5] += pow(m, j + k); |
| } |
| } |
| |
| factor(s->matrix_a, 5); |
| |
| i = 0; |
| for (j = 0; j < 5; j++) |
| for (k = 0; k < 15; k++) |
| s->matrix_b[i++] = pow(k, j); |
| |
| i = 0; |
| for (j = 0; j < 15; j++) |
| for (k = 0; k < 5; k++) |
| s->matrix_c[i++] = pow(j, k); |
| |
| s->window = av_calloc(s->window_length, sizeof(*s->window)); |
| s->bin2band = av_calloc(s->bin_count, sizeof(*s->bin2band)); |
| if (!s->window || !s->bin2band) |
| return AVERROR(ENOMEM); |
| |
| sdiv = s->sample_rate / 17640.0; |
| for (i = 0; i <= s->fft_length2; i++) |
| s->bin2band[i] = lrint(sdiv * freq2bark((0.5 * i * s->sample_rate) / s->fft_length2)); |
| |
| s->number_of_bands = s->bin2band[s->fft_length2] + 1; |
| |
| s->band_alpha = av_calloc(s->number_of_bands, sizeof(*s->band_alpha)); |
| s->band_beta = av_calloc(s->number_of_bands, sizeof(*s->band_beta)); |
| if (!s->band_alpha || !s->band_beta) |
| return AVERROR(ENOMEM); |
| |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| |
| switch (s->noise_type) { |
| case WHITE_NOISE: |
| for (i = 0; i < 15; i++) |
| dnch->band_noise[i] = 0; |
| break; |
| case VINYL_NOISE: |
| for (i = 0; i < 15; i++) |
| dnch->band_noise[i] = get_band_noise(s, i, 50.0, 500.5, 2125.0) + FFMAX(i - 7, 0); |
| break; |
| case SHELLAC_NOISE: |
| for (i = 0; i < 15; i++) |
| dnch->band_noise[i] = get_band_noise(s, i, 1.0, 500.0, 1.0E10) + FFMAX(i - 12, -5); |
| break; |
| case CUSTOM_NOISE: |
| read_custom_noise(s, ch); |
| break; |
| default: |
| return AVERROR_BUG; |
| } |
| |
| |
| dnch->sfm_threshold = 0.8; |
| dnch->sfm_alpha = 0.05; |
| for (i = 0; i < 512; i++) |
| dnch->sfm_fail_flags[i] = 0; |
| |
| dnch->sfm_fail_total = 0; |
| j = FFMAX((int)(10.0 * (1.3 - dnch->sfm_threshold)), 1); |
| |
| for (i = 0; i < 512; i += j) { |
| dnch->sfm_fail_flags[i] = 1; |
| dnch->sfm_fail_total += 1; |
| } |
| |
| dnch->amt = av_calloc(s->bin_count, sizeof(*dnch->amt)); |
| dnch->band_amt = av_calloc(s->number_of_bands, sizeof(*dnch->band_amt)); |
| dnch->band_excit = av_calloc(s->number_of_bands, sizeof(*dnch->band_excit)); |
| dnch->gain = av_calloc(s->bin_count, sizeof(*dnch->gain)); |
| dnch->prior = av_calloc(s->bin_count, sizeof(*dnch->prior)); |
| dnch->prior_band_excit = av_calloc(s->number_of_bands, sizeof(*dnch->prior_band_excit)); |
| dnch->clean_data = av_calloc(s->bin_count, sizeof(*dnch->clean_data)); |
| dnch->noisy_data = av_calloc(s->bin_count, sizeof(*dnch->noisy_data)); |
| dnch->out_samples = av_calloc(s->buffer_length, sizeof(*dnch->out_samples)); |
| dnch->abs_var = av_calloc(s->bin_count, sizeof(*dnch->abs_var)); |
| dnch->rel_var = av_calloc(s->bin_count, sizeof(*dnch->rel_var)); |
| dnch->min_abs_var = av_calloc(s->bin_count, sizeof(*dnch->min_abs_var)); |
| dnch->fft_data = av_calloc(s->fft_length2 + 1, sizeof(*dnch->fft_data)); |
| dnch->fft = av_fft_init(av_log2(s->fft_length2), 0); |
| dnch->ifft = av_fft_init(av_log2(s->fft_length2), 1); |
| dnch->spread_function = av_calloc(s->number_of_bands * s->number_of_bands, |
| sizeof(*dnch->spread_function)); |
| |
| if (!dnch->amt || |
| !dnch->band_amt || |
| !dnch->band_excit || |
| !dnch->gain || |
| !dnch->prior || |
| !dnch->prior_band_excit || |
| !dnch->clean_data || |
| !dnch->noisy_data || |
| !dnch->out_samples || |
| !dnch->fft_data || |
| !dnch->abs_var || |
| !dnch->rel_var || |
| !dnch->min_abs_var || |
| !dnch->spread_function || |
| !dnch->fft || |
| !dnch->ifft) |
| return AVERROR(ENOMEM); |
| } |
| |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| double *prior_band_excit = dnch->prior_band_excit; |
| double *prior = dnch->prior; |
| double min, max; |
| double p1, p2; |
| |
| p1 = pow(0.1, 2.5 / sdiv); |
| p2 = pow(0.1, 1.0 / sdiv); |
| j = 0; |
| for (m = 0; m < s->number_of_bands; m++) { |
| for (n = 0; n < s->number_of_bands; n++) { |
| if (n < m) { |
| dnch->spread_function[j++] = pow(p2, m - n); |
| } else if (n > m) { |
| dnch->spread_function[j++] = pow(p1, n - m); |
| } else { |
| dnch->spread_function[j++] = 1.0; |
| } |
| } |
| } |
| |
| for (m = 0; m < s->number_of_bands; m++) { |
| dnch->band_excit[m] = 0.0; |
| prior_band_excit[m] = 0.0; |
| } |
| |
| for (m = 0; m <= s->fft_length2; m++) |
| dnch->band_excit[s->bin2band[m]] += 1.0; |
| |
| j = 0; |
| for (m = 0; m < s->number_of_bands; m++) { |
| for (n = 0; n < s->number_of_bands; n++) |
| prior_band_excit[m] += dnch->spread_function[j++] * dnch->band_excit[n]; |
| } |
| |
| min = pow(0.1, 2.5); |
| max = pow(0.1, 1.0); |
| for (int i = 0; i < s->number_of_bands; i++) { |
| if (i < lrint(12.0 * sdiv)) { |
| dnch->band_excit[i] = pow(0.1, 1.45 + 0.1 * i / sdiv); |
| } else { |
| dnch->band_excit[i] = pow(0.1, 2.5 - 0.2 * (i / sdiv - 14.0)); |
| } |
| dnch->band_excit[i] = av_clipd(dnch->band_excit[i], min, max); |
| } |
| |
| for (int i = 0; i <= s->fft_length2; i++) |
| prior[i] = RRATIO; |
| for (int i = 0; i < s->buffer_length; i++) |
| dnch->out_samples[i] = 0; |
| |
| j = 0; |
| for (int i = 0; i < s->number_of_bands; i++) |
| for (int k = 0; k < s->number_of_bands; k++) |
| dnch->spread_function[j++] *= dnch->band_excit[i] / prior_band_excit[i]; |
| } |
| |
| j = 0; |
| sar = s->sample_advance / s->sample_rate; |
| for (int i = 0; i <= s->fft_length2; i++) { |
| if ((i == s->fft_length2) || (s->bin2band[i] > j)) { |
| double d6 = (i - 1) * s->sample_rate / s->fft_length; |
| double d7 = fmin(0.008 + 2.2 / d6, 0.03); |
| s->band_alpha[j] = exp(-sar / d7); |
| s->band_beta[j] = 1.0 - s->band_alpha[j]; |
| j = s->bin2band[i]; |
| } |
| } |
| |
| wscale = sqrt(16.0 / (9.0 * s->fft_length)); |
| sum = 0.0; |
| for (int i = 0; i < s->window_length; i++) { |
| double d10 = sin(i * M_PI / s->window_length); |
| d10 *= wscale * d10; |
| s->window[i] = d10; |
| sum += d10 * d10; |
| } |
| |
| s->window_weight = 0.5 * sum; |
| s->floor = (1LL << 48) * exp(-23.025558369790467) * s->window_weight; |
| s->sample_floor = s->floor * exp(4.144600506562284); |
| s->auto_floor = s->floor * exp(6.907667510937141); |
| |
| set_parameters(s); |
| |
| s->noise_band_edge[0] = FFMIN(s->fft_length2, s->fft_length * get_band_edge(s, 0) / s->sample_rate); |
| i = 0; |
| for (int j = 1; j < 16; j++) { |
| s->noise_band_edge[j] = FFMIN(s->fft_length2, s->fft_length * get_band_edge(s, j) / s->sample_rate); |
| if (s->noise_band_edge[j] > lrint(1.1 * s->noise_band_edge[j - 1])) |
| i++; |
| s->noise_band_edge[16] = i; |
| } |
| s->noise_band_count = s->noise_band_edge[16]; |
| |
| s->fifo = av_audio_fifo_alloc(inlink->format, inlink->channels, s->fft_length); |
| if (!s->fifo) |
| return AVERROR(ENOMEM); |
| |
| return 0; |
| } |
| |
| static void preprocess(FFTComplex *in, int len) |
| { |
| double d1, d2, d3, d4, d5, d6, d7, d8, d9, d10; |
| int n, i, k; |
| |
| d5 = 2.0 * M_PI / len; |
| d8 = sin(0.5 * d5); |
| d8 = -2.0 * d8 * d8; |
| d7 = sin(d5); |
| d9 = 1.0 + d8; |
| d6 = d7; |
| n = len / 2; |
| |
| for (i = 1; i < len / 4; i++) { |
| k = n - i; |
| d2 = 0.5 * (in[i].re + in[k].re); |
| d1 = 0.5 * (in[i].im - in[k].im); |
| d4 = 0.5 * (in[i].im + in[k].im); |
| d3 = 0.5 * (in[k].re - in[i].re); |
| in[i].re = d2 + d9 * d4 + d6 * d3; |
| in[i].im = d1 + d9 * d3 - d6 * d4; |
| in[k].re = d2 - d9 * d4 - d6 * d3; |
| in[k].im = -d1 + d9 * d3 - d6 * d4; |
| d10 = d9; |
| d9 += d9 * d8 - d6 * d7; |
| d6 += d6 * d8 + d10 * d7; |
| } |
| |
| d2 = in[0].re; |
| in[0].re = d2 + in[0].im; |
| in[0].im = d2 - in[0].im; |
| } |
| |
| static void postprocess(FFTComplex *in, int len) |
| { |
| double d1, d2, d3, d4, d5, d6, d7, d8, d9, d10; |
| int n, i, k; |
| |
| d5 = 2.0 * M_PI / len; |
| d8 = sin(0.5 * d5); |
| d8 = -2.0 * d8 * d8; |
| d7 = sin(d5); |
| d9 = 1.0 + d8; |
| d6 = d7; |
| n = len / 2; |
| for (i = 1; i < len / 4; i++) { |
| k = n - i; |
| d2 = 0.5 * (in[i].re + in[k].re); |
| d1 = 0.5 * (in[i].im - in[k].im); |
| d4 = 0.5 * (in[i].re - in[k].re); |
| d3 = 0.5 * (in[i].im + in[k].im); |
| in[i].re = d2 - d9 * d3 - d6 * d4; |
| in[i].im = d1 + d9 * d4 - d6 * d3; |
| in[k].re = d2 + d9 * d3 + d6 * d4; |
| in[k].im = -d1 + d9 * d4 - d6 * d3; |
| d10 = d9; |
| d9 += d9 * d8 - d6 * d7; |
| d6 += d6 * d8 + d10 * d7; |
| } |
| d2 = in[0].re; |
| in[0].re = 0.5 * (d2 + in[0].im); |
| in[0].im = 0.5 * (d2 - in[0].im); |
| } |
| |
| static void init_sample_noise(DeNoiseChannel *dnch) |
| { |
| for (int i = 0; i < 15; i++) { |
| dnch->noise_band_norm[i] = 0.0; |
| dnch->noise_band_avr[i] = 0.0; |
| dnch->noise_band_avi[i] = 0.0; |
| dnch->noise_band_var[i] = 0.0; |
| } |
| } |
| |
| static void sample_noise_block(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch, |
| AVFrame *in, int ch) |
| { |
| float *src = (float *)in->extended_data[ch]; |
| double mag2, var = 0.0, avr = 0.0, avi = 0.0; |
| int edge, j, k, n, edgemax; |
| |
| for (int i = 0; i < s->window_length; i++) { |
| dnch->fft_data[i].re = s->window[i] * src[i] * (1LL << 24); |
| dnch->fft_data[i].im = 0.0; |
| } |
| |
| for (int i = s->window_length; i < s->fft_length2; i++) { |
| dnch->fft_data[i].re = 0.0; |
| dnch->fft_data[i].im = 0.0; |
| } |
| |
| av_fft_permute(dnch->fft, dnch->fft_data); |
| av_fft_calc(dnch->fft, dnch->fft_data); |
| |
| preprocess(dnch->fft_data, s->fft_length); |
| |
| edge = s->noise_band_edge[0]; |
| j = edge; |
| k = 0; |
| n = j; |
| edgemax = fmin(s->fft_length2, s->noise_band_edge[15]); |
| dnch->fft_data[s->fft_length2].re = dnch->fft_data[0].im; |
| dnch->fft_data[0].im = 0.0; |
| dnch->fft_data[s->fft_length2].im = 0.0; |
| |
| for (int i = j; i <= edgemax; i++) { |
| if ((i == j) && (i < edgemax)) { |
| if (j > edge) { |
| dnch->noise_band_norm[k - 1] += j - edge; |
| dnch->noise_band_avr[k - 1] += avr; |
| dnch->noise_band_avi[k - 1] += avi; |
| dnch->noise_band_var[k - 1] += var; |
| } |
| k++; |
| edge = j; |
| j = s->noise_band_edge[k]; |
| if (k == 15) { |
| j++; |
| } |
| var = 0.0; |
| avr = 0.0; |
| avi = 0.0; |
| } |
| avr += dnch->fft_data[n].re; |
| avi += dnch->fft_data[n].im; |
| mag2 = dnch->fft_data[n].re * dnch->fft_data[n].re + |
| dnch->fft_data[n].im * dnch->fft_data[n].im; |
| |
| mag2 = fmax(mag2, s->sample_floor); |
| |
| dnch->noisy_data[i] = mag2; |
| var += mag2; |
| n++; |
| } |
| |
| dnch->noise_band_norm[k - 1] += j - edge; |
| dnch->noise_band_avr[k - 1] += avr; |
| dnch->noise_band_avi[k - 1] += avi; |
| dnch->noise_band_var[k - 1] += var; |
| } |
| |
| static void finish_sample_noise(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch, |
| double *sample_noise) |
| { |
| for (int i = 0; i < s->noise_band_count; i++) { |
| dnch->noise_band_avr[i] /= dnch->noise_band_norm[i]; |
| dnch->noise_band_avi[i] /= dnch->noise_band_norm[i]; |
| dnch->noise_band_var[i] /= dnch->noise_band_norm[i]; |
| dnch->noise_band_var[i] -= dnch->noise_band_avr[i] * dnch->noise_band_avr[i] + |
| dnch->noise_band_avi[i] * dnch->noise_band_avi[i]; |
| dnch->noise_band_auto_var[i] = dnch->noise_band_var[i]; |
| sample_noise[i] = (1.0 / C) * log(dnch->noise_band_var[i] / s->floor) - 100.0; |
| } |
| if (s->noise_band_count < 15) { |
| for (int i = s->noise_band_count; i < 15; i++) |
| sample_noise[i] = sample_noise[i - 1]; |
| } |
| } |
| |
| static void set_noise_profile(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch, |
| double *sample_noise, |
| int new_profile) |
| { |
| int new_band_noise[15]; |
| double temp[15]; |
| double sum = 0.0, d1; |
| float new_noise_floor; |
| int i, n; |
| |
| for (int m = 0; m < 15; m++) |
| temp[m] = sample_noise[m]; |
| |
| if (new_profile) { |
| i = 0; |
| for (int m = 0; m < 5; m++) { |
| sum = 0.0; |
| for (n = 0; n < 15; n++) |
| sum += s->matrix_b[i++] * temp[n]; |
| s->vector_b[m] = sum; |
| } |
| solve(s->matrix_a, s->vector_b, 5); |
| i = 0; |
| for (int m = 0; m < 15; m++) { |
| sum = 0.0; |
| for (n = 0; n < 5; n++) |
| sum += s->matrix_c[i++] * s->vector_b[n]; |
| temp[m] = sum; |
| } |
| } |
| |
| sum = 0.0; |
| for (int m = 0; m < 15; m++) |
| sum += temp[m]; |
| |
| d1 = (int)(sum / 15.0 - 0.5); |
| if (!new_profile) |
| i = lrint(temp[7] - d1); |
| |
| for (d1 -= dnch->band_noise[7] - i; d1 > -20.0; d1 -= 1.0) |
| ; |
| |
| for (int m = 0; m < 15; m++) |
| temp[m] -= d1; |
| |
| new_noise_floor = d1 + 2.5; |
| |
| if (new_profile) { |
| av_log(s, AV_LOG_INFO, "bn="); |
| for (int m = 0; m < 15; m++) { |
| new_band_noise[m] = lrint(temp[m]); |
| new_band_noise[m] = av_clip(new_band_noise[m], -24, 24); |
| av_log(s, AV_LOG_INFO, "%d ", new_band_noise[m]); |
| } |
| av_log(s, AV_LOG_INFO, "\n"); |
| memcpy(dnch->band_noise, new_band_noise, sizeof(new_band_noise)); |
| } |
| |
| if (s->track_noise) |
| s->noise_floor = new_noise_floor; |
| } |
| |
| typedef struct ThreadData { |
| AVFrame *in; |
| } ThreadData; |
| |
| static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
| { |
| AudioFFTDeNoiseContext *s = ctx->priv; |
| ThreadData *td = arg; |
| AVFrame *in = td->in; |
| const int start = (in->channels * jobnr) / nb_jobs; |
| const int end = (in->channels * (jobnr+1)) / nb_jobs; |
| |
| for (int ch = start; ch < end; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| const float *src = (const float *)in->extended_data[ch]; |
| double *dst = dnch->out_samples; |
| |
| if (s->track_noise) { |
| int i = s->block_count & 0x1FF; |
| |
| if (dnch->sfm_fail_flags[i]) |
| dnch->sfm_fail_total--; |
| dnch->sfm_fail_flags[i] = 0; |
| dnch->sfm_threshold *= 1.0 - dnch->sfm_alpha; |
| dnch->sfm_threshold += dnch->sfm_alpha * (0.5 + (1.0 / 640) * dnch->sfm_fail_total); |
| } |
| |
| for (int m = 0; m < s->window_length; m++) { |
| dnch->fft_data[m].re = s->window[m] * src[m] * (1LL << 24); |
| dnch->fft_data[m].im = 0; |
| } |
| |
| for (int m = s->window_length; m < s->fft_length2; m++) { |
| dnch->fft_data[m].re = 0; |
| dnch->fft_data[m].im = 0; |
| } |
| |
| av_fft_permute(dnch->fft, dnch->fft_data); |
| av_fft_calc(dnch->fft, dnch->fft_data); |
| |
| preprocess(dnch->fft_data, s->fft_length); |
| process_frame(s, dnch, dnch->fft_data, |
| dnch->prior, |
| dnch->prior_band_excit, |
| s->track_noise); |
| postprocess(dnch->fft_data, s->fft_length); |
| |
| av_fft_permute(dnch->ifft, dnch->fft_data); |
| av_fft_calc(dnch->ifft, dnch->fft_data); |
| |
| for (int m = 0; m < s->window_length; m++) |
| dst[m] += s->window[m] * dnch->fft_data[m].re / (1LL << 24); |
| } |
| |
| return 0; |
| } |
| |
| static void get_auto_noise_levels(AudioFFTDeNoiseContext *s, |
| DeNoiseChannel *dnch, |
| double *levels) |
| { |
| if (s->noise_band_count > 0) { |
| for (int i = 0; i < s->noise_band_count; i++) { |
| levels[i] = (1.0 / C) * log(dnch->noise_band_auto_var[i] / s->floor) - 100.0; |
| } |
| if (s->noise_band_count < 15) { |
| for (int i = s->noise_band_count; i < 15; i++) |
| levels[i] = levels[i - 1]; |
| } |
| } else { |
| for (int i = 0; i < 15; i++) { |
| levels[i] = -100.0; |
| } |
| } |
| } |
| |
| static int output_frame(AVFilterLink *inlink) |
| { |
| AVFilterContext *ctx = inlink->dst; |
| AVFilterLink *outlink = ctx->outputs[0]; |
| AudioFFTDeNoiseContext *s = ctx->priv; |
| AVFrame *out = NULL, *in = NULL; |
| ThreadData td; |
| int ret = 0; |
| |
| in = ff_get_audio_buffer(outlink, s->window_length); |
| if (!in) |
| return AVERROR(ENOMEM); |
| |
| ret = av_audio_fifo_peek(s->fifo, (void **)in->extended_data, s->window_length); |
| if (ret < 0) |
| goto end; |
| |
| if (s->track_noise) { |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| double levels[15]; |
| |
| get_auto_noise_levels(s, dnch, levels); |
| set_noise_profile(s, dnch, levels, 0); |
| } |
| |
| if (s->noise_floor != s->last_noise_floor) |
| set_parameters(s); |
| } |
| |
| if (s->sample_noise_start) { |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| |
| init_sample_noise(dnch); |
| } |
| s->sample_noise_start = 0; |
| s->sample_noise = 1; |
| } |
| |
| if (s->sample_noise) { |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| |
| sample_noise_block(s, dnch, in, ch); |
| } |
| } |
| |
| if (s->sample_noise_end) { |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| double sample_noise[15]; |
| |
| finish_sample_noise(s, dnch, sample_noise); |
| set_noise_profile(s, dnch, sample_noise, 1); |
| set_band_parameters(s, dnch); |
| } |
| s->sample_noise = 0; |
| s->sample_noise_end = 0; |
| } |
| |
| s->block_count++; |
| td.in = in; |
| ctx->internal->execute(ctx, filter_channel, &td, NULL, |
| FFMIN(outlink->channels, ff_filter_get_nb_threads(ctx))); |
| |
| out = ff_get_audio_buffer(outlink, s->sample_advance); |
| if (!out) { |
| ret = AVERROR(ENOMEM); |
| goto end; |
| } |
| |
| for (int ch = 0; ch < inlink->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| double *src = dnch->out_samples; |
| float *orig = (float *)in->extended_data[ch]; |
| float *dst = (float *)out->extended_data[ch]; |
| |
| switch (s->output_mode) { |
| case IN_MODE: |
| for (int m = 0; m < s->sample_advance; m++) |
| dst[m] = orig[m]; |
| break; |
| case OUT_MODE: |
| for (int m = 0; m < s->sample_advance; m++) |
| dst[m] = src[m]; |
| break; |
| case NOISE_MODE: |
| for (int m = 0; m < s->sample_advance; m++) |
| dst[m] = orig[m] - src[m]; |
| break; |
| default: |
| av_frame_free(&out); |
| ret = AVERROR_BUG; |
| goto end; |
| } |
| memmove(src, src + s->sample_advance, (s->window_length - s->sample_advance) * sizeof(*src)); |
| memset(src + (s->window_length - s->sample_advance), 0, s->sample_advance * sizeof(*src)); |
| } |
| |
| av_audio_fifo_drain(s->fifo, s->sample_advance); |
| |
| out->pts = s->pts; |
| ret = ff_filter_frame(outlink, out); |
| if (ret < 0) |
| goto end; |
| s->pts += av_rescale_q(s->sample_advance, (AVRational){1, outlink->sample_rate}, outlink->time_base); |
| end: |
| av_frame_free(&in); |
| |
| return ret; |
| } |
| |
| static int activate(AVFilterContext *ctx) |
| { |
| AVFilterLink *inlink = ctx->inputs[0]; |
| AVFilterLink *outlink = ctx->outputs[0]; |
| AudioFFTDeNoiseContext *s = ctx->priv; |
| AVFrame *frame = NULL; |
| int ret; |
| |
| FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); |
| |
| ret = ff_inlink_consume_frame(inlink, &frame); |
| if (ret < 0) |
| return ret; |
| |
| if (ret > 0) { |
| if (s->pts == AV_NOPTS_VALUE) |
| s->pts = frame->pts; |
| |
| ret = av_audio_fifo_write(s->fifo, (void **)frame->extended_data, frame->nb_samples); |
| av_frame_free(&frame); |
| if (ret < 0) |
| return ret; |
| } |
| |
| if (av_audio_fifo_size(s->fifo) >= s->window_length) |
| return output_frame(inlink); |
| |
| FF_FILTER_FORWARD_STATUS(inlink, outlink); |
| if (ff_outlink_frame_wanted(outlink) && |
| av_audio_fifo_size(s->fifo) < s->window_length) { |
| ff_inlink_request_frame(inlink); |
| return 0; |
| } |
| |
| return FFERROR_NOT_READY; |
| } |
| |
| static av_cold void uninit(AVFilterContext *ctx) |
| { |
| AudioFFTDeNoiseContext *s = ctx->priv; |
| |
| av_freep(&s->window); |
| av_freep(&s->bin2band); |
| av_freep(&s->band_alpha); |
| av_freep(&s->band_beta); |
| |
| if (s->dnch) { |
| for (int ch = 0; ch < s->channels; ch++) { |
| DeNoiseChannel *dnch = &s->dnch[ch]; |
| av_freep(&dnch->amt); |
| av_freep(&dnch->band_amt); |
| av_freep(&dnch->band_excit); |
| av_freep(&dnch->gain); |
| av_freep(&dnch->prior); |
| av_freep(&dnch->prior_band_excit); |
| av_freep(&dnch->clean_data); |
| av_freep(&dnch->noisy_data); |
| av_freep(&dnch->out_samples); |
| av_freep(&dnch->spread_function); |
| av_freep(&dnch->abs_var); |
| av_freep(&dnch->rel_var); |
| av_freep(&dnch->min_abs_var); |
| av_freep(&dnch->fft_data); |
| av_fft_end(dnch->fft); |
| dnch->fft = NULL; |
| av_fft_end(dnch->ifft); |
| dnch->ifft = NULL; |
| } |
| av_freep(&s->dnch); |
| } |
| |
| av_audio_fifo_free(s->fifo); |
| } |
| |
| static int query_formats(AVFilterContext *ctx) |
| { |
| AVFilterFormats *formats = NULL; |
| AVFilterChannelLayouts *layouts = NULL; |
| static const enum AVSampleFormat sample_fmts[] = { |
| AV_SAMPLE_FMT_FLTP, |
| AV_SAMPLE_FMT_NONE |
| }; |
| int ret; |
| |
| formats = ff_make_format_list(sample_fmts); |
| if (!formats) |
| return AVERROR(ENOMEM); |
| ret = ff_set_common_formats(ctx, formats); |
| if (ret < 0) |
| return ret; |
| |
| layouts = ff_all_channel_counts(); |
| if (!layouts) |
| return AVERROR(ENOMEM); |
| |
| ret = ff_set_common_channel_layouts(ctx, layouts); |
| if (ret < 0) |
| return ret; |
| |
| formats = ff_all_samplerates(); |
| return ff_set_common_samplerates(ctx, formats); |
| } |
| |
| static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
| char *res, int res_len, int flags) |
| { |
| AudioFFTDeNoiseContext *s = ctx->priv; |
| int need_reset = 0; |
| int ret = 0; |
| |
| if (!strcmp(cmd, "sample_noise") || |
| !strcmp(cmd, "sn")) { |
| if (!strcmp(args, "start")) { |
| s->sample_noise_start = 1; |
| s->sample_noise_end = 0; |
| } else if (!strcmp(args, "end") || |
| !strcmp(args, "stop")) { |
| s->sample_noise_start = 0; |
| s->sample_noise_end = 1; |
| } |
| } else { |
| ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags); |
| if (ret < 0) |
| return ret; |
| need_reset = 1; |
| } |
| |
| if (need_reset) |
| set_parameters(s); |
| |
| return 0; |
| } |
| |
| static const AVFilterPad inputs[] = { |
| { |
| .name = "default", |
| .type = AVMEDIA_TYPE_AUDIO, |
| .config_props = config_input, |
| }, |
| { NULL } |
| }; |
| |
| static const AVFilterPad outputs[] = { |
| { |
| .name = "default", |
| .type = AVMEDIA_TYPE_AUDIO, |
| }, |
| { NULL } |
| }; |
| |
| AVFilter ff_af_afftdn = { |
| .name = "afftdn", |
| .description = NULL_IF_CONFIG_SMALL("Denoise audio samples using FFT."), |
| .query_formats = query_formats, |
| .priv_size = sizeof(AudioFFTDeNoiseContext), |
| .priv_class = &afftdn_class, |
| .activate = activate, |
| .uninit = uninit, |
| .inputs = inputs, |
| .outputs = outputs, |
| .process_command = process_command, |
| .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | |
| AVFILTER_FLAG_SLICE_THREADS, |
| }; |