| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 1998 Juergen Mueller And Sundry Contributors | ||
| 3 | * This source code is freely redistributable and may be used for | ||
| 4 | * any purpose. This copyright notice must be maintained. | ||
| 5 | * Juergen Mueller And Sundry Contributors are not responsible for | ||
| 6 | * the consequences of using this software. | ||
| 7 | * | ||
| 8 | * Copyright (c) 2015 Paul B Mahol | ||
| 9 | * | ||
| 10 | * This file is part of FFmpeg. | ||
| 11 | * | ||
| 12 | * FFmpeg is free software; you can redistribute it and/or | ||
| 13 | * modify it under the terms of the GNU Lesser General Public | ||
| 14 | * License as published by the Free Software Foundation; either | ||
| 15 | * version 2.1 of the License, or (at your option) any later version. | ||
| 16 | * | ||
| 17 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 20 | * Lesser General Public License for more details. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU Lesser General Public | ||
| 23 | * License along with FFmpeg; if not, write to the Free Software | ||
| 24 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 25 | */ | ||
| 26 | |||
| 27 | /** | ||
| 28 | * @file | ||
| 29 | * chorus audio filter | ||
| 30 | */ | ||
| 31 | |||
| 32 | #include "libavutil/avstring.h" | ||
| 33 | #include "libavutil/mem.h" | ||
| 34 | #include "libavutil/opt.h" | ||
| 35 | #include "audio.h" | ||
| 36 | #include "avfilter.h" | ||
| 37 | #include "filters.h" | ||
| 38 | #include "generate_wave_table.h" | ||
| 39 | |||
| 40 | typedef struct ChorusContext { | ||
| 41 | const AVClass *class; | ||
| 42 | float in_gain, out_gain; | ||
| 43 | char *delays_str; | ||
| 44 | char *decays_str; | ||
| 45 | char *speeds_str; | ||
| 46 | char *depths_str; | ||
| 47 | float *delays; | ||
| 48 | float *decays; | ||
| 49 | float *speeds; | ||
| 50 | float *depths; | ||
| 51 | uint8_t **chorusbuf; | ||
| 52 | int **phase; | ||
| 53 | int *length; | ||
| 54 | int32_t **lookup_table; | ||
| 55 | int *counter; | ||
| 56 | int num_chorus; | ||
| 57 | int max_samples; | ||
| 58 | int channels; | ||
| 59 | int modulation; | ||
| 60 | int fade_out; | ||
| 61 | int64_t next_pts; | ||
| 62 | } ChorusContext; | ||
| 63 | |||
| 64 | #define OFFSET(x) offsetof(ChorusContext, x) | ||
| 65 | #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||
| 66 | |||
| 67 | static const AVOption chorus_options[] = { | ||
| 68 | { "in_gain", "set input gain", OFFSET(in_gain), AV_OPT_TYPE_FLOAT, {.dbl=.4}, 0, 1, A }, | ||
| 69 | { "out_gain", "set output gain", OFFSET(out_gain), AV_OPT_TYPE_FLOAT, {.dbl=.4}, 0, 1, A }, | ||
| 70 | { "delays", "set delays", OFFSET(delays_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A }, | ||
| 71 | { "decays", "set decays", OFFSET(decays_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A }, | ||
| 72 | { "speeds", "set speeds", OFFSET(speeds_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A }, | ||
| 73 | { "depths", "set depths", OFFSET(depths_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A }, | ||
| 74 | { NULL } | ||
| 75 | }; | ||
| 76 | |||
| 77 | AVFILTER_DEFINE_CLASS(chorus); | ||
| 78 | |||
| 79 | 8 | static void count_items(char *item_str, int *nb_items) | |
| 80 | { | ||
| 81 | char *p; | ||
| 82 | |||
| 83 | 8 | *nb_items = 1; | |
| 84 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 8 times.
|
58 | for (p = item_str; *p; p++) { |
| 85 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | if (*p == '|') |
| 86 | ✗ | (*nb_items)++; | |
| 87 | } | ||
| 88 | |||
| 89 | 8 | } | |
| 90 | |||
| 91 | 8 | static void fill_items(char *item_str, int *nb_items, float *items) | |
| 92 | { | ||
| 93 | 8 | char *p, *saveptr = NULL; | |
| 94 | 8 | int i, new_nb_items = 0; | |
| 95 | |||
| 96 | 8 | p = item_str; | |
| 97 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
16 | for (i = 0; i < *nb_items; i++) { |
| 98 | 8 | char *tstr = av_strtok(p, "|", &saveptr); | |
| 99 | 8 | p = NULL; | |
| 100 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (tstr) |
| 101 | 8 | new_nb_items += sscanf(tstr, "%f", &items[new_nb_items]) == 1; | |
| 102 | } | ||
| 103 | |||
| 104 | 8 | *nb_items = new_nb_items; | |
| 105 | 8 | } | |
| 106 | |||
| 107 | 2 | static av_cold int init(AVFilterContext *ctx) | |
| 108 | { | ||
| 109 | 2 | ChorusContext *s = ctx->priv; | |
| 110 | int nb_delays, nb_decays, nb_speeds, nb_depths; | ||
| 111 | |||
| 112 |
4/8✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
|
2 | if (!s->delays_str || !s->decays_str || !s->speeds_str || !s->depths_str) { |
| 113 | ✗ | av_log(ctx, AV_LOG_ERROR, "Both delays & decays & speeds & depths must be set.\n"); | |
| 114 | ✗ | return AVERROR(EINVAL); | |
| 115 | } | ||
| 116 | |||
| 117 | 2 | count_items(s->delays_str, &nb_delays); | |
| 118 | 2 | count_items(s->decays_str, &nb_decays); | |
| 119 | 2 | count_items(s->speeds_str, &nb_speeds); | |
| 120 | 2 | count_items(s->depths_str, &nb_depths); | |
| 121 | |||
| 122 | 2 | s->delays = av_realloc_f(s->delays, nb_delays, sizeof(*s->delays)); | |
| 123 | 2 | s->decays = av_realloc_f(s->decays, nb_decays, sizeof(*s->decays)); | |
| 124 | 2 | s->speeds = av_realloc_f(s->speeds, nb_speeds, sizeof(*s->speeds)); | |
| 125 | 2 | s->depths = av_realloc_f(s->depths, nb_depths, sizeof(*s->depths)); | |
| 126 | |||
| 127 |
4/8✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
|
2 | if (!s->delays || !s->decays || !s->speeds || !s->depths) |
| 128 | ✗ | return AVERROR(ENOMEM); | |
| 129 | |||
| 130 | 2 | fill_items(s->delays_str, &nb_delays, s->delays); | |
| 131 | 2 | fill_items(s->decays_str, &nb_decays, s->decays); | |
| 132 | 2 | fill_items(s->speeds_str, &nb_speeds, s->speeds); | |
| 133 | 2 | fill_items(s->depths_str, &nb_depths, s->depths); | |
| 134 | |||
| 135 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
2 | if (nb_delays != nb_decays && nb_delays != nb_speeds && nb_delays != nb_depths) { |
| 136 | ✗ | av_log(ctx, AV_LOG_ERROR, "Number of delays & decays & speeds & depths given must be same.\n"); | |
| 137 | ✗ | return AVERROR(EINVAL); | |
| 138 | } | ||
| 139 | |||
| 140 | 2 | s->num_chorus = nb_delays; | |
| 141 | |||
| 142 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (s->num_chorus < 1) { |
| 143 | ✗ | av_log(ctx, AV_LOG_ERROR, "At least one delay & decay & speed & depth must be set.\n"); | |
| 144 | ✗ | return AVERROR(EINVAL); | |
| 145 | } | ||
| 146 | |||
| 147 | 2 | s->length = av_calloc(s->num_chorus, sizeof(*s->length)); | |
| 148 | 2 | s->lookup_table = av_calloc(s->num_chorus, sizeof(*s->lookup_table)); | |
| 149 | |||
| 150 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | if (!s->length || !s->lookup_table) |
| 151 | ✗ | return AVERROR(ENOMEM); | |
| 152 | |||
| 153 | 2 | s->next_pts = AV_NOPTS_VALUE; | |
| 154 | |||
| 155 | 2 | return 0; | |
| 156 | } | ||
| 157 | |||
| 158 | 1 | static int config_output(AVFilterLink *outlink) | |
| 159 | { | ||
| 160 | 1 | AVFilterContext *ctx = outlink->src; | |
| 161 | 1 | ChorusContext *s = ctx->priv; | |
| 162 | 1 | float sum_in_volume = 1.0; | |
| 163 | int n; | ||
| 164 | |||
| 165 | 1 | s->channels = outlink->ch_layout.nb_channels; | |
| 166 | |||
| 167 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | for (n = 0; n < s->num_chorus; n++) { |
| 168 | 1 | int samples = (int) ((s->delays[n] + s->depths[n]) * outlink->sample_rate / 1000.0); | |
| 169 | 1 | int depth_samples = (int) (s->depths[n] * outlink->sample_rate / 1000.0); | |
| 170 | |||
| 171 | 1 | s->length[n] = outlink->sample_rate / s->speeds[n]; | |
| 172 | |||
| 173 | 1 | s->lookup_table[n] = av_malloc(sizeof(int32_t) * s->length[n]); | |
| 174 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!s->lookup_table[n]) |
| 175 | ✗ | return AVERROR(ENOMEM); | |
| 176 | |||
| 177 | 1 | ff_generate_wave_table(WAVE_SIN, AV_SAMPLE_FMT_S32, s->lookup_table[n], | |
| 178 | 1 | s->length[n], 0., depth_samples, 0); | |
| 179 | 1 | s->max_samples = FFMAX(s->max_samples, samples); | |
| 180 | } | ||
| 181 | |||
| 182 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | for (n = 0; n < s->num_chorus; n++) |
| 183 | 1 | sum_in_volume += s->decays[n]; | |
| 184 | |||
| 185 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (s->in_gain * (sum_in_volume) > 1.0 / s->out_gain) |
| 186 | ✗ | av_log(ctx, AV_LOG_WARNING, "output gain can cause saturation or clipping of output\n"); | |
| 187 | |||
| 188 | 1 | s->counter = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->counter)); | |
| 189 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!s->counter) |
| 190 | ✗ | return AVERROR(ENOMEM); | |
| 191 | |||
| 192 | 1 | s->phase = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->phase)); | |
| 193 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!s->phase) |
| 194 | ✗ | return AVERROR(ENOMEM); | |
| 195 | |||
| 196 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | for (n = 0; n < outlink->ch_layout.nb_channels; n++) { |
| 197 | 1 | s->phase[n] = av_calloc(s->num_chorus, sizeof(int)); | |
| 198 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!s->phase[n]) |
| 199 | ✗ | return AVERROR(ENOMEM); | |
| 200 | } | ||
| 201 | |||
| 202 | 1 | s->fade_out = s->max_samples; | |
| 203 | |||
| 204 | 1 | return av_samples_alloc_array_and_samples(&s->chorusbuf, NULL, | |
| 205 | outlink->ch_layout.nb_channels, | ||
| 206 | s->max_samples, | ||
| 207 | 1 | outlink->format, 0); | |
| 208 | } | ||
| 209 | |||
| 210 | #define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a)) | ||
| 211 | |||
| 212 | 11 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) | |
| 213 | { | ||
| 214 | 11 | AVFilterContext *ctx = inlink->dst; | |
| 215 | 11 | ChorusContext *s = ctx->priv; | |
| 216 | AVFrame *out_frame; | ||
| 217 | int c, i, n; | ||
| 218 | |||
| 219 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | if (av_frame_is_writable(frame)) { |
| 220 | 11 | out_frame = frame; | |
| 221 | } else { | ||
| 222 | ✗ | out_frame = ff_get_audio_buffer(ctx->outputs[0], frame->nb_samples); | |
| 223 | ✗ | if (!out_frame) { | |
| 224 | ✗ | av_frame_free(&frame); | |
| 225 | ✗ | return AVERROR(ENOMEM); | |
| 226 | } | ||
| 227 | ✗ | av_frame_copy_props(out_frame, frame); | |
| 228 | } | ||
| 229 | |||
| 230 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
|
22 | for (c = 0; c < inlink->ch_layout.nb_channels; c++) { |
| 231 | 11 | const float *src = (const float *)frame->extended_data[c]; | |
| 232 | 11 | float *dst = (float *)out_frame->extended_data[c]; | |
| 233 | 11 | float *chorusbuf = (float *)s->chorusbuf[c]; | |
| 234 | 11 | int *phase = s->phase[c]; | |
| 235 | |||
| 236 |
2/2✓ Branch 0 taken 22528 times.
✓ Branch 1 taken 11 times.
|
22539 | for (i = 0; i < frame->nb_samples; i++) { |
| 237 | 22528 | float out, in = src[i]; | |
| 238 | |||
| 239 | 22528 | out = in * s->in_gain; | |
| 240 | |||
| 241 |
2/2✓ Branch 0 taken 22528 times.
✓ Branch 1 taken 22528 times.
|
45056 | for (n = 0; n < s->num_chorus; n++) { |
| 242 |
2/2✓ Branch 0 taken 22149 times.
✓ Branch 1 taken 379 times.
|
22528 | out += chorusbuf[MOD(s->max_samples + s->counter[c] - |
| 243 | s->lookup_table[n][phase[n]], | ||
| 244 | 22528 | s->max_samples)] * s->decays[n]; | |
| 245 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22528 times.
|
22528 | phase[n] = MOD(phase[n] + 1, s->length[n]); |
| 246 | } | ||
| 247 | |||
| 248 | 22528 | out *= s->out_gain; | |
| 249 | |||
| 250 | 22528 | dst[i] = out; | |
| 251 | |||
| 252 | 22528 | chorusbuf[s->counter[c]] = in; | |
| 253 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 22513 times.
|
22528 | s->counter[c] = MOD(s->counter[c] + 1, s->max_samples); |
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | 11 | s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base); | |
| 258 | |||
| 259 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (frame != out_frame) |
| 260 | ✗ | av_frame_free(&frame); | |
| 261 | |||
| 262 | 11 | return ff_filter_frame(ctx->outputs[0], out_frame); | |
| 263 | } | ||
| 264 | |||
| 265 | 10 | static int request_frame(AVFilterLink *outlink) | |
| 266 | { | ||
| 267 | 10 | AVFilterContext *ctx = outlink->src; | |
| 268 | 10 | ChorusContext *s = ctx->priv; | |
| 269 | int ret; | ||
| 270 | |||
| 271 | 10 | ret = ff_request_frame(ctx->inputs[0]); | |
| 272 | |||
| 273 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
10 | if (ret == AVERROR_EOF && !ctx->is_disabled && s->fade_out) { |
| 274 | ✗ | int nb_samples = FFMIN(s->fade_out, 2048); | |
| 275 | AVFrame *frame; | ||
| 276 | |||
| 277 | ✗ | frame = ff_get_audio_buffer(outlink, nb_samples); | |
| 278 | ✗ | if (!frame) | |
| 279 | ✗ | return AVERROR(ENOMEM); | |
| 280 | ✗ | s->fade_out -= nb_samples; | |
| 281 | |||
| 282 | ✗ | av_samples_set_silence(frame->extended_data, 0, | |
| 283 | frame->nb_samples, | ||
| 284 | outlink->ch_layout.nb_channels, | ||
| 285 | ✗ | frame->format); | |
| 286 | |||
| 287 | ✗ | frame->pts = s->next_pts; | |
| 288 | ✗ | if (s->next_pts != AV_NOPTS_VALUE) | |
| 289 | ✗ | s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); | |
| 290 | |||
| 291 | ✗ | ret = filter_frame(ctx->inputs[0], frame); | |
| 292 | } | ||
| 293 | |||
| 294 | 10 | return ret; | |
| 295 | } | ||
| 296 | |||
| 297 | 2 | static av_cold void uninit(AVFilterContext *ctx) | |
| 298 | { | ||
| 299 | 2 | ChorusContext *s = ctx->priv; | |
| 300 | int n; | ||
| 301 | |||
| 302 | 2 | av_freep(&s->delays); | |
| 303 | 2 | av_freep(&s->decays); | |
| 304 | 2 | av_freep(&s->speeds); | |
| 305 | 2 | av_freep(&s->depths); | |
| 306 | |||
| 307 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (s->chorusbuf) |
| 308 | 1 | av_freep(&s->chorusbuf[0]); | |
| 309 | 2 | av_freep(&s->chorusbuf); | |
| 310 | |||
| 311 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (s->phase) |
| 312 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | for (n = 0; n < s->channels; n++) |
| 313 | 1 | av_freep(&s->phase[n]); | |
| 314 | 2 | av_freep(&s->phase); | |
| 315 | |||
| 316 | 2 | av_freep(&s->counter); | |
| 317 | 2 | av_freep(&s->length); | |
| 318 | |||
| 319 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (s->lookup_table) |
| 320 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | for (n = 0; n < s->num_chorus; n++) |
| 321 | 2 | av_freep(&s->lookup_table[n]); | |
| 322 | 2 | av_freep(&s->lookup_table); | |
| 323 | 2 | } | |
| 324 | |||
| 325 | static const AVFilterPad chorus_inputs[] = { | ||
| 326 | { | ||
| 327 | .name = "default", | ||
| 328 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 329 | .filter_frame = filter_frame, | ||
| 330 | }, | ||
| 331 | }; | ||
| 332 | |||
| 333 | static const AVFilterPad chorus_outputs[] = { | ||
| 334 | { | ||
| 335 | .name = "default", | ||
| 336 | .type = AVMEDIA_TYPE_AUDIO, | ||
| 337 | .request_frame = request_frame, | ||
| 338 | .config_props = config_output, | ||
| 339 | }, | ||
| 340 | }; | ||
| 341 | |||
| 342 | const FFFilter ff_af_chorus = { | ||
| 343 | .p.name = "chorus", | ||
| 344 | .p.description = NULL_IF_CONFIG_SMALL("Add a chorus effect to the audio."), | ||
| 345 | .p.priv_class = &chorus_class, | ||
| 346 | .priv_size = sizeof(ChorusContext), | ||
| 347 | .init = init, | ||
| 348 | .uninit = uninit, | ||
| 349 | FILTER_INPUTS(chorus_inputs), | ||
| 350 | FILTER_OUTPUTS(chorus_outputs), | ||
| 351 | FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), | ||
| 352 | }; | ||
| 353 |