| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2019 The FFmpeg Project | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include "libavutil/avassert.h" | ||
| 22 | #include "libavutil/channel_layout.h" | ||
| 23 | #include "libavutil/opt.h" | ||
| 24 | #include "avfilter.h" | ||
| 25 | #include "audio.h" | ||
| 26 | #include "filters.h" | ||
| 27 | |||
| 28 | #define MAX_OVERSAMPLE 64 | ||
| 29 | |||
| 30 | enum ASoftClipTypes { | ||
| 31 | ASC_HARD = -1, | ||
| 32 | ASC_TANH, | ||
| 33 | ASC_ATAN, | ||
| 34 | ASC_CUBIC, | ||
| 35 | ASC_EXP, | ||
| 36 | ASC_ALG, | ||
| 37 | ASC_QUINTIC, | ||
| 38 | ASC_SIN, | ||
| 39 | ASC_ERF, | ||
| 40 | NB_TYPES, | ||
| 41 | }; | ||
| 42 | |||
| 43 | typedef struct Lowpass { | ||
| 44 | float fb0, fb1, fb2; | ||
| 45 | float fa0, fa1, fa2; | ||
| 46 | |||
| 47 | double db0, db1, db2; | ||
| 48 | double da0, da1, da2; | ||
| 49 | } Lowpass; | ||
| 50 | |||
| 51 | typedef struct ASoftClipContext { | ||
| 52 | const AVClass *class; | ||
| 53 | |||
| 54 | int type; | ||
| 55 | int oversample; | ||
| 56 | int64_t delay; | ||
| 57 | double threshold; | ||
| 58 | double output; | ||
| 59 | double param; | ||
| 60 | |||
| 61 | Lowpass lowpass[MAX_OVERSAMPLE]; | ||
| 62 | AVFrame *frame[2]; | ||
| 63 | |||
| 64 | void (*filter)(struct ASoftClipContext *s, void **dst, const void **src, | ||
| 65 | int nb_samples, int channels, int start, int end); | ||
| 66 | } ASoftClipContext; | ||
| 67 | |||
| 68 | #define OFFSET(x) offsetof(ASoftClipContext, x) | ||
| 69 | #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM | ||
| 70 | |||
| 71 | static const AVOption asoftclip_options[] = { | ||
| 72 | { "type", "set softclip type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, -1, NB_TYPES-1, A, .unit = "types" }, | ||
| 73 | { "hard", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_HARD}, 0, 0, A, .unit = "types" }, | ||
| 74 | { "tanh", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_TANH}, 0, 0, A, .unit = "types" }, | ||
| 75 | { "atan", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ATAN}, 0, 0, A, .unit = "types" }, | ||
| 76 | { "cubic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_CUBIC}, 0, 0, A, .unit = "types" }, | ||
| 77 | { "exp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_EXP}, 0, 0, A, .unit = "types" }, | ||
| 78 | { "alg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ALG}, 0, 0, A, .unit = "types" }, | ||
| 79 | { "quintic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_QUINTIC},0, 0, A, .unit = "types" }, | ||
| 80 | { "sin", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_SIN}, 0, 0, A, .unit = "types" }, | ||
| 81 | { "erf", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ERF}, 0, 0, A, .unit = "types" }, | ||
| 82 | { "threshold", "set softclip threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.000001, 1, A }, | ||
| 83 | { "output", "set softclip output gain", OFFSET(output), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.000001, 16, A }, | ||
| 84 | { "param", "set softclip parameter", OFFSET(param), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 3, A }, | ||
| 85 | { "oversample", "set oversample factor", OFFSET(oversample), AV_OPT_TYPE_INT, {.i64=1}, 1, MAX_OVERSAMPLE, A }, | ||
| 86 | { NULL } | ||
| 87 | }; | ||
| 88 | |||
| 89 | AVFILTER_DEFINE_CLASS(asoftclip); | ||
| 90 | |||
| 91 | ✗ | static void get_lowpass(Lowpass *s, | |
| 92 | double frequency, | ||
| 93 | double sample_rate) | ||
| 94 | { | ||
| 95 | ✗ | double w0 = 2 * M_PI * frequency / sample_rate; | |
| 96 | ✗ | double alpha = sin(w0) / (2 * 0.8); | |
| 97 | double factor; | ||
| 98 | |||
| 99 | ✗ | s->da0 = 1 + alpha; | |
| 100 | ✗ | s->da1 = -2 * cos(w0); | |
| 101 | ✗ | s->da2 = 1 - alpha; | |
| 102 | ✗ | s->db0 = (1 - cos(w0)) / 2; | |
| 103 | ✗ | s->db1 = 1 - cos(w0); | |
| 104 | ✗ | s->db2 = (1 - cos(w0)) / 2; | |
| 105 | |||
| 106 | ✗ | s->da1 /= s->da0; | |
| 107 | ✗ | s->da2 /= s->da0; | |
| 108 | ✗ | s->db0 /= s->da0; | |
| 109 | ✗ | s->db1 /= s->da0; | |
| 110 | ✗ | s->db2 /= s->da0; | |
| 111 | ✗ | s->da0 /= s->da0; | |
| 112 | |||
| 113 | ✗ | factor = (s->da0 + s->da1 + s->da2) / (s->db0 + s->db1 + s->db2); | |
| 114 | ✗ | s->db0 *= factor; | |
| 115 | ✗ | s->db1 *= factor; | |
| 116 | ✗ | s->db2 *= factor; | |
| 117 | |||
| 118 | ✗ | s->fa0 = s->da0; | |
| 119 | ✗ | s->fa1 = s->da1; | |
| 120 | ✗ | s->fa2 = s->da2; | |
| 121 | ✗ | s->fb0 = s->db0; | |
| 122 | ✗ | s->fb1 = s->db1; | |
| 123 | ✗ | s->fb2 = s->db2; | |
| 124 | ✗ | } | |
| 125 | |||
| 126 | ✗ | static inline float run_lowpassf(const Lowpass *const s, | |
| 127 | float src, float *w) | ||
| 128 | { | ||
| 129 | float dst; | ||
| 130 | |||
| 131 | ✗ | dst = src * s->fb0 + w[0]; | |
| 132 | ✗ | w[0] = s->fb1 * src + w[1] - s->fa1 * dst; | |
| 133 | ✗ | w[1] = s->fb2 * src - s->fa2 * dst; | |
| 134 | |||
| 135 | ✗ | return dst; | |
| 136 | } | ||
| 137 | |||
| 138 | ✗ | static void filter_flt(ASoftClipContext *s, | |
| 139 | void **dptr, const void **sptr, | ||
| 140 | int nb_samples, int channels, | ||
| 141 | int start, int end) | ||
| 142 | { | ||
| 143 | ✗ | const int oversample = s->oversample; | |
| 144 | ✗ | const int nb_osamples = nb_samples * oversample; | |
| 145 | ✗ | const float scale = oversample > 1 ? oversample * 0.5f : 1.f; | |
| 146 | ✗ | float threshold = s->threshold; | |
| 147 | ✗ | float gain = s->output * threshold; | |
| 148 | ✗ | float factor = 1.f / threshold; | |
| 149 | ✗ | float param = s->param; | |
| 150 | |||
| 151 | ✗ | for (int c = start; c < end; c++) { | |
| 152 | ✗ | float *w = (float *)(s->frame[0]->extended_data[c]) + 2 * (oversample - 1); | |
| 153 | ✗ | const float *src = sptr[c]; | |
| 154 | ✗ | float *dst = dptr[c]; | |
| 155 | |||
| 156 | ✗ | for (int n = 0; n < nb_samples; n++) { | |
| 157 | ✗ | dst[oversample * n] = src[n]; | |
| 158 | |||
| 159 | ✗ | for (int m = 1; m < oversample; m++) | |
| 160 | ✗ | dst[oversample * n + m] = 0.f; | |
| 161 | } | ||
| 162 | |||
| 163 | ✗ | for (int n = 0; n < nb_osamples && oversample > 1; n++) | |
| 164 | ✗ | dst[n] = run_lowpassf(&s->lowpass[oversample - 1], dst[n], w); | |
| 165 | |||
| 166 | ✗ | switch (s->type) { | |
| 167 | ✗ | case ASC_HARD: | |
| 168 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 169 | ✗ | dst[n] = av_clipf(dst[n] * factor, -1.f, 1.f); | |
| 170 | ✗ | dst[n] *= gain; | |
| 171 | } | ||
| 172 | ✗ | break; | |
| 173 | ✗ | case ASC_TANH: | |
| 174 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 175 | ✗ | dst[n] = tanhf(dst[n] * factor * param); | |
| 176 | ✗ | dst[n] *= gain; | |
| 177 | } | ||
| 178 | ✗ | break; | |
| 179 | ✗ | case ASC_ATAN: | |
| 180 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 181 | ✗ | dst[n] = 2.f / M_PI * atanf(dst[n] * factor * param); | |
| 182 | ✗ | dst[n] *= gain; | |
| 183 | } | ||
| 184 | ✗ | break; | |
| 185 | ✗ | case ASC_CUBIC: | |
| 186 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 187 | ✗ | float sample = dst[n] * factor; | |
| 188 | |||
| 189 | ✗ | if (FFABS(sample) >= 1.5f) | |
| 190 | ✗ | dst[n] = FFSIGN(sample); | |
| 191 | else | ||
| 192 | ✗ | dst[n] = sample - 0.1481f * powf(sample, 3.f); | |
| 193 | ✗ | dst[n] *= gain; | |
| 194 | } | ||
| 195 | ✗ | break; | |
| 196 | ✗ | case ASC_EXP: | |
| 197 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 198 | ✗ | dst[n] = 2.f / (1.f + expf(-2.f * dst[n] * factor)) - 1.; | |
| 199 | ✗ | dst[n] *= gain; | |
| 200 | } | ||
| 201 | ✗ | break; | |
| 202 | ✗ | case ASC_ALG: | |
| 203 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 204 | ✗ | float sample = dst[n] * factor; | |
| 205 | |||
| 206 | ✗ | dst[n] = sample / (sqrtf(param + sample * sample)); | |
| 207 | ✗ | dst[n] *= gain; | |
| 208 | } | ||
| 209 | ✗ | break; | |
| 210 | ✗ | case ASC_QUINTIC: | |
| 211 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 212 | ✗ | float sample = dst[n] * factor; | |
| 213 | |||
| 214 | ✗ | if (FFABS(sample) >= 1.25) | |
| 215 | ✗ | dst[n] = FFSIGN(sample); | |
| 216 | else | ||
| 217 | ✗ | dst[n] = sample - 0.08192f * powf(sample, 5.f); | |
| 218 | ✗ | dst[n] *= gain; | |
| 219 | } | ||
| 220 | ✗ | break; | |
| 221 | ✗ | case ASC_SIN: | |
| 222 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 223 | ✗ | float sample = dst[n] * factor; | |
| 224 | |||
| 225 | ✗ | if (FFABS(sample) >= M_PI_2) | |
| 226 | ✗ | dst[n] = FFSIGN(sample); | |
| 227 | else | ||
| 228 | ✗ | dst[n] = sinf(sample); | |
| 229 | ✗ | dst[n] *= gain; | |
| 230 | } | ||
| 231 | ✗ | break; | |
| 232 | ✗ | case ASC_ERF: | |
| 233 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 234 | ✗ | dst[n] = erff(dst[n] * factor); | |
| 235 | ✗ | dst[n] *= gain; | |
| 236 | } | ||
| 237 | ✗ | break; | |
| 238 | ✗ | default: | |
| 239 | ✗ | av_assert0(0); | |
| 240 | } | ||
| 241 | |||
| 242 | ✗ | w = (float *)(s->frame[1]->extended_data[c]) + 2 * (oversample - 1); | |
| 243 | ✗ | for (int n = 0; n < nb_osamples && oversample > 1; n++) | |
| 244 | ✗ | dst[n] = run_lowpassf(&s->lowpass[oversample - 1], dst[n], w); | |
| 245 | |||
| 246 | ✗ | for (int n = 0; n < nb_samples; n++) | |
| 247 | ✗ | dst[n] = dst[n * oversample] * scale; | |
| 248 | } | ||
| 249 | ✗ | } | |
| 250 | |||
| 251 | ✗ | static inline double run_lowpassd(const Lowpass *const s, | |
| 252 | double src, double *w) | ||
| 253 | { | ||
| 254 | double dst; | ||
| 255 | |||
| 256 | ✗ | dst = src * s->db0 + w[0]; | |
| 257 | ✗ | w[0] = s->db1 * src + w[1] - s->da1 * dst; | |
| 258 | ✗ | w[1] = s->db2 * src - s->da2 * dst; | |
| 259 | |||
| 260 | ✗ | return dst; | |
| 261 | } | ||
| 262 | |||
| 263 | ✗ | static void filter_dbl(ASoftClipContext *s, | |
| 264 | void **dptr, const void **sptr, | ||
| 265 | int nb_samples, int channels, | ||
| 266 | int start, int end) | ||
| 267 | { | ||
| 268 | ✗ | const int oversample = s->oversample; | |
| 269 | ✗ | const int nb_osamples = nb_samples * oversample; | |
| 270 | ✗ | const double scale = oversample > 1 ? oversample * 0.5 : 1.; | |
| 271 | ✗ | double threshold = s->threshold; | |
| 272 | ✗ | double gain = s->output * threshold; | |
| 273 | ✗ | double factor = 1. / threshold; | |
| 274 | ✗ | double param = s->param; | |
| 275 | |||
| 276 | ✗ | for (int c = start; c < end; c++) { | |
| 277 | ✗ | double *w = (double *)(s->frame[0]->extended_data[c]) + 2 * (oversample - 1); | |
| 278 | ✗ | const double *src = sptr[c]; | |
| 279 | ✗ | double *dst = dptr[c]; | |
| 280 | |||
| 281 | ✗ | for (int n = 0; n < nb_samples; n++) { | |
| 282 | ✗ | dst[oversample * n] = src[n]; | |
| 283 | |||
| 284 | ✗ | for (int m = 1; m < oversample; m++) | |
| 285 | ✗ | dst[oversample * n + m] = 0.f; | |
| 286 | } | ||
| 287 | |||
| 288 | ✗ | for (int n = 0; n < nb_osamples && oversample > 1; n++) | |
| 289 | ✗ | dst[n] = run_lowpassd(&s->lowpass[oversample - 1], dst[n], w); | |
| 290 | |||
| 291 | ✗ | switch (s->type) { | |
| 292 | ✗ | case ASC_HARD: | |
| 293 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 294 | ✗ | dst[n] = av_clipd(dst[n] * factor, -1., 1.); | |
| 295 | ✗ | dst[n] *= gain; | |
| 296 | } | ||
| 297 | ✗ | break; | |
| 298 | ✗ | case ASC_TANH: | |
| 299 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 300 | ✗ | dst[n] = tanh(dst[n] * factor * param); | |
| 301 | ✗ | dst[n] *= gain; | |
| 302 | } | ||
| 303 | ✗ | break; | |
| 304 | ✗ | case ASC_ATAN: | |
| 305 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 306 | ✗ | dst[n] = 2. / M_PI * atan(dst[n] * factor * param); | |
| 307 | ✗ | dst[n] *= gain; | |
| 308 | } | ||
| 309 | ✗ | break; | |
| 310 | ✗ | case ASC_CUBIC: | |
| 311 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 312 | ✗ | double sample = dst[n] * factor; | |
| 313 | |||
| 314 | ✗ | if (FFABS(sample) >= 1.5) | |
| 315 | ✗ | dst[n] = FFSIGN(sample); | |
| 316 | else | ||
| 317 | ✗ | dst[n] = sample - 0.1481 * pow(sample, 3.); | |
| 318 | ✗ | dst[n] *= gain; | |
| 319 | } | ||
| 320 | ✗ | break; | |
| 321 | ✗ | case ASC_EXP: | |
| 322 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 323 | ✗ | dst[n] = 2. / (1. + exp(-2. * dst[n] * factor)) - 1.; | |
| 324 | ✗ | dst[n] *= gain; | |
| 325 | } | ||
| 326 | ✗ | break; | |
| 327 | ✗ | case ASC_ALG: | |
| 328 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 329 | ✗ | double sample = dst[n] * factor; | |
| 330 | |||
| 331 | ✗ | dst[n] = sample / (sqrt(param + sample * sample)); | |
| 332 | ✗ | dst[n] *= gain; | |
| 333 | } | ||
| 334 | ✗ | break; | |
| 335 | ✗ | case ASC_QUINTIC: | |
| 336 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 337 | ✗ | double sample = dst[n] * factor; | |
| 338 | |||
| 339 | ✗ | if (FFABS(sample) >= 1.25) | |
| 340 | ✗ | dst[n] = FFSIGN(sample); | |
| 341 | else | ||
| 342 | ✗ | dst[n] = sample - 0.08192 * pow(sample, 5.); | |
| 343 | ✗ | dst[n] *= gain; | |
| 344 | } | ||
| 345 | ✗ | break; | |
| 346 | ✗ | case ASC_SIN: | |
| 347 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 348 | ✗ | double sample = dst[n] * factor; | |
| 349 | |||
| 350 | ✗ | if (FFABS(sample) >= M_PI_2) | |
| 351 | ✗ | dst[n] = FFSIGN(sample); | |
| 352 | else | ||
| 353 | ✗ | dst[n] = sin(sample); | |
| 354 | ✗ | dst[n] *= gain; | |
| 355 | } | ||
| 356 | ✗ | break; | |
| 357 | ✗ | case ASC_ERF: | |
| 358 | ✗ | for (int n = 0; n < nb_osamples; n++) { | |
| 359 | ✗ | dst[n] = erf(dst[n] * factor); | |
| 360 | ✗ | dst[n] *= gain; | |
| 361 | } | ||
| 362 | ✗ | break; | |
| 363 | ✗ | default: | |
| 364 | ✗ | av_assert0(0); | |
| 365 | } | ||
| 366 | |||
| 367 | ✗ | w = (double *)(s->frame[1]->extended_data[c]) + 2 * (oversample - 1); | |
| 368 | ✗ | for (int n = 0; n < nb_osamples && oversample > 1; n++) | |
| 369 | ✗ | dst[n] = run_lowpassd(&s->lowpass[oversample - 1], dst[n], w); | |
| 370 | |||
| 371 | ✗ | for (int n = 0; n < nb_samples; n++) | |
| 372 | ✗ | dst[n] = dst[n * oversample] * scale; | |
| 373 | } | ||
| 374 | ✗ | } | |
| 375 | |||
| 376 | ✗ | static int config_input(AVFilterLink *inlink) | |
| 377 | { | ||
| 378 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 379 | ✗ | ASoftClipContext *s = ctx->priv; | |
| 380 | |||
| 381 | ✗ | switch (inlink->format) { | |
| 382 | ✗ | case AV_SAMPLE_FMT_FLTP: s->filter = filter_flt; break; | |
| 383 | ✗ | case AV_SAMPLE_FMT_DBLP: s->filter = filter_dbl; break; | |
| 384 | ✗ | default: av_assert0(0); | |
| 385 | } | ||
| 386 | |||
| 387 | ✗ | s->frame[0] = ff_get_audio_buffer(inlink, 2 * MAX_OVERSAMPLE); | |
| 388 | ✗ | s->frame[1] = ff_get_audio_buffer(inlink, 2 * MAX_OVERSAMPLE); | |
| 389 | ✗ | if (!s->frame[0] || !s->frame[1]) | |
| 390 | ✗ | return AVERROR(ENOMEM); | |
| 391 | |||
| 392 | ✗ | for (int i = 0; i < MAX_OVERSAMPLE; i++) { | |
| 393 | ✗ | get_lowpass(&s->lowpass[i], inlink->sample_rate / 2, inlink->sample_rate * (i + 1)); | |
| 394 | } | ||
| 395 | |||
| 396 | ✗ | return 0; | |
| 397 | } | ||
| 398 | |||
| 399 | typedef struct ThreadData { | ||
| 400 | AVFrame *in, *out; | ||
| 401 | int nb_samples; | ||
| 402 | int channels; | ||
| 403 | } ThreadData; | ||
| 404 | |||
| 405 | ✗ | static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | |
| 406 | { | ||
| 407 | ✗ | ASoftClipContext *s = ctx->priv; | |
| 408 | ✗ | ThreadData *td = arg; | |
| 409 | ✗ | AVFrame *out = td->out; | |
| 410 | ✗ | AVFrame *in = td->in; | |
| 411 | ✗ | const int channels = td->channels; | |
| 412 | ✗ | const int nb_samples = td->nb_samples; | |
| 413 | ✗ | const int start = (channels * jobnr) / nb_jobs; | |
| 414 | ✗ | const int end = (channels * (jobnr+1)) / nb_jobs; | |
| 415 | |||
| 416 | ✗ | s->filter(s, (void **)out->extended_data, (const void **)in->extended_data, | |
| 417 | nb_samples, channels, start, end); | ||
| 418 | |||
| 419 | ✗ | return 0; | |
| 420 | } | ||
| 421 | |||
| 422 | ✗ | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 423 | { | ||
| 424 | ✗ | AVFilterContext *ctx = inlink->dst; | |
| 425 | ✗ | ASoftClipContext *s = ctx->priv; | |
| 426 | ✗ | AVFilterLink *outlink = ctx->outputs[0]; | |
| 427 | int nb_samples, channels; | ||
| 428 | ThreadData td; | ||
| 429 | AVFrame *out; | ||
| 430 | |||
| 431 | ✗ | if (av_frame_is_writable(in) && s->oversample == 1) { | |
| 432 | ✗ | out = in; | |
| 433 | } else { | ||
| 434 | ✗ | out = ff_get_audio_buffer(outlink, in->nb_samples * s->oversample); | |
| 435 | ✗ | if (!out) { | |
| 436 | ✗ | av_frame_free(&in); | |
| 437 | ✗ | return AVERROR(ENOMEM); | |
| 438 | } | ||
| 439 | ✗ | av_frame_copy_props(out, in); | |
| 440 | } | ||
| 441 | |||
| 442 | ✗ | nb_samples = in->nb_samples; | |
| 443 | ✗ | channels = in->ch_layout.nb_channels; | |
| 444 | |||
| 445 | ✗ | td.in = in; | |
| 446 | ✗ | td.out = out; | |
| 447 | ✗ | td.nb_samples = nb_samples; | |
| 448 | ✗ | td.channels = channels; | |
| 449 | ✗ | ff_filter_execute(ctx, filter_channels, &td, NULL, | |
| 450 | ✗ | FFMIN(channels, ff_filter_get_nb_threads(ctx))); | |
| 451 | |||
| 452 | ✗ | if (out != in) | |
| 453 | ✗ | av_frame_free(&in); | |
| 454 | |||
| 455 | ✗ | out->nb_samples /= s->oversample; | |
| 456 | ✗ | return ff_filter_frame(outlink, out); | |
| 457 | } | ||
| 458 | |||
| 459 | ✗ | static av_cold void uninit(AVFilterContext *ctx) | |
| 460 | { | ||
| 461 | ✗ | ASoftClipContext *s = ctx->priv; | |
| 462 | |||
| 463 | ✗ | av_frame_free(&s->frame[0]); | |
| 464 | ✗ | av_frame_free(&s->frame[1]); | |
| 465 | ✗ | } | |
| 466 | |||
| 467 | static const AVFilterPad inputs[] = { | ||
| 468 | { | ||
| 469 | .name = "default", | ||
| 470 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 471 | .filter_frame = filter_frame, | ||
| 472 | .config_props = config_input, | ||
| 473 | }, | ||
| 474 | }; | ||
| 475 | |||
| 476 | const FFFilter ff_af_asoftclip = { | ||
| 477 | .p.name = "asoftclip", | ||
| 478 | .p.description = NULL_IF_CONFIG_SMALL("Audio Soft Clipper."), | ||
| 479 | .p.priv_class = &asoftclip_class, | ||
| 480 | .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | | ||
| 481 | AVFILTER_FLAG_SLICE_THREADS, | ||
| 482 | .priv_size = sizeof(ASoftClipContext), | ||
| 483 | FILTER_INPUTS(inputs), | ||
| 484 | FILTER_OUTPUTS(ff_audio_default_filterpad), | ||
| 485 | FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), | ||
| 486 | .uninit = uninit, | ||
| 487 | .process_command = ff_filter_process_command, | ||
| 488 | }; | ||
| 489 |