| @@ -39,13 +39,13 @@ · 38 unmodified lines |
| 39 | 39 | */ |
| 40 | 40 | |
| 41 | 41 | #include <float.h> |
| 42 | | −#include "libavcodec/avfft.h" |
| 43 | 42 | #include "libavutil/avassert.h" |
| 44 | 43 | #include "libavutil/avstring.h" |
| 45 | 44 | #include "libavutil/channel_layout.h" |
| 46 | 45 | #include "libavutil/eval.h" |
| 47 | 46 | #include "libavutil/opt.h" |
| 48 | 47 | #include "libavutil/samplefmt.h" |
| 48 | +#include "libavutil/tx.h" |
| 49 | 49 | #include "avfilter.h" |
| 50 | 50 | #include "audio.h" |
| 51 | 51 | #include "internal.h" |
| @@ -67,7 +67,8 @@ typedef struct AudioFragment { · 15 unmodified lines |
| 67 | 67 | |
| 68 | 68 | // rDFT transform of the down-mixed mono fragment, used for |
| 69 | 69 | // fast waveform alignment via correlation in frequency domain: |
| 70 | | − FFTSample *xdat; |
| 70 | + float *xdat_in; |
| 71 | + float *xdat; |
| 71 | 72 | } AudioFragment; |
| 72 | 73 | |
| 73 | 74 | /** |
| @@ -140,9 +141,11 @@ typedef struct ATempoContext { · 66 unmodified lines |
| 140 | 141 | FilterState state; |
| 141 | 142 | |
| 142 | 143 | // for fast correlation calculation in frequency domain: |
| 143 | | − RDFTContext *real_to_complex; |
| 144 | | − RDFTContext *complex_to_real; |
| 145 | | − FFTSample *correlation; |
| 144 | + AVTXContext *real_to_complex; |
| 145 | + AVTXContext *complex_to_real; |
| 146 | + av_tx_fn r2c_fn, c2r_fn; |
| 147 | + float *correlation_in; |
| 148 | + float *correlation; |
| 146 | 149 | |
| 147 | 150 | // for managing AVFilterPad.request_frame and AVFilterPad.filter_frame |
| 148 | 151 | AVFrame *dst_buffer; |
| @@ -228,18 +231,18 @@ static void yae_release_buffers(ATempoContext *atempo) · 79 unmodified lines |
| 228 | 231 | |
| 229 | 232 | av_freep(&atempo->frag[0].data); |
| 230 | 233 | av_freep(&atempo->frag[1].data); |
| 234 | + av_freep(&atempo->frag[0].xdat_in); |
| 235 | + av_freep(&atempo->frag[1].xdat_in); |
| 231 | 236 | av_freep(&atempo->frag[0].xdat); |
| 232 | 237 | av_freep(&atempo->frag[1].xdat); |
| 233 | 238 | |
| 234 | 239 | av_freep(&atempo->buffer); |
| 235 | 240 | av_freep(&atempo->hann); |
| 241 | + av_freep(&atempo->correlation_in); |
| 236 | 242 | av_freep(&atempo->correlation); |
| 237 | 243 | |
| 238 | | − av_rdft_end(atempo->real_to_complex); |
| 239 | | − atempo->real_to_complex = NULL; |
| 240 | | − |
| 241 | | − av_rdft_end(atempo->complex_to_real); |
| 242 | | − atempo->complex_to_real = NULL; |
| 244 | + av_tx_uninit(&atempo->real_to_complex); |
| 245 | + av_tx_uninit(&atempo->complex_to_real); |
| 243 | 246 | } |
| 244 | 247 | |
| 245 | 248 | /* av_realloc is not aligned enough; fortunately, the data does not need to |
| @@ -247,7 +250,7 @@ static void yae_release_buffers(ATempoContext *atempo) · 1 unmodified line |
| 247 | 250 | #define RE_MALLOC_OR_FAIL(field, field_size) \ |
| 248 | 251 | do { \ |
| 249 | 252 | av_freep(&field); \ |
| 250 | | − field = av_malloc(field_size); \ |
| 253 | + field = av_calloc(field_size, 1); \ |
| 251 | 254 | if (!field) { \ |
| 252 | 255 | yae_release_buffers(atempo); \ |
| 253 | 256 | return AVERROR(ENOMEM); \ |
| @@ -265,6 +268,7 @@ static int yae_reset(ATempoContext *atempo, · 11 unmodified lines |
| 265 | 268 | { |
| 266 | 269 | const int sample_size = av_get_bytes_per_sample(format); |
| 267 | 270 | uint32_t nlevels = 0; |
| 271 | + float scale = 1.f, iscale = 1.f; |
| 268 | 272 | uint32_t pot; |
| 269 | 273 | int i; |
| 270 | 274 | |
| @@ -288,29 +292,29 @@ static int yae_reset(ATempoContext *atempo, · 17 unmodified lines |
| 288 | 292 | // initialize audio fragment buffers: |
| 289 | 293 | RE_MALLOC_OR_FAIL(atempo->frag[0].data, atempo->window * atempo->stride); |
| 290 | 294 | RE_MALLOC_OR_FAIL(atempo->frag[1].data, atempo->window * atempo->stride); |
| 291 | | − RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, atempo->window * sizeof(FFTComplex)); |
| 292 | | − RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, atempo->window * sizeof(FFTComplex)); |
| 295 | + RE_MALLOC_OR_FAIL(atempo->frag[0].xdat_in, (atempo->window + 1) * sizeof(AVComplexFloat)); |
| 296 | + RE_MALLOC_OR_FAIL(atempo->frag[1].xdat_in, (atempo->window + 1) * sizeof(AVComplexFloat)); |
| 297 | + RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, (atempo->window + 1) * sizeof(AVComplexFloat)); |
| 298 | + RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, (atempo->window + 1) * sizeof(AVComplexFloat)); |
| 293 | 299 | |
| 294 | 300 | // initialize rDFT contexts: |
| 295 | | − av_rdft_end(atempo->real_to_complex); |
| 296 | | − atempo->real_to_complex = NULL; |
| 297 | | − |
| 298 | | − av_rdft_end(atempo->complex_to_real); |
| 299 | | − atempo->complex_to_real = NULL; |
| 301 | + av_tx_uninit(&atempo->real_to_complex); |
| 302 | + av_tx_uninit(&atempo->complex_to_real); |
| 300 | 303 | |
| 301 | | − atempo->real_to_complex = av_rdft_init(nlevels + 1, DFT_R2C); |
| 304 | + av_tx_init(&atempo->real_to_complex, &atempo->r2c_fn, AV_TX_FLOAT_RDFT, 0, 1 << (nlevels + 1), &scale, 0); |
| 302 | 305 | if (!atempo->real_to_complex) { |
| 303 | 306 | yae_release_buffers(atempo); |
| 304 | 307 | return AVERROR(ENOMEM); |
| 305 | 308 | } |
| 306 | 309 | |
| 307 | | − atempo->complex_to_real = av_rdft_init(nlevels + 1, IDFT_C2R); |
| 310 | + av_tx_init(&atempo->complex_to_real, &atempo->c2r_fn, AV_TX_FLOAT_RDFT, 1, 1 << (nlevels + 1), &iscale, 0); |
| 308 | 311 | if (!atempo->complex_to_real) { |
| 309 | 312 | yae_release_buffers(atempo); |
| 310 | 313 | return AVERROR(ENOMEM); |
| 311 | 314 | } |
| 312 | 315 | |
| 313 | | − RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window * sizeof(FFTComplex)); |
| 316 | + RE_MALLOC_OR_FAIL(atempo->correlation_in, (atempo->window + 1) * sizeof(AVComplexFloat)); |
| 317 | + RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window * sizeof(AVComplexFloat)); |
| 314 | 318 | |
| 315 | 319 | atempo->ring = atempo->window * 3; |
| 316 | 320 | RE_MALLOC_OR_FAIL(atempo->buffer, atempo->ring * atempo->stride); |
| @@ -348,7 +352,7 @@ static int yae_update(AVFilterContext *ctx) · 31 unmodified lines |
| 348 | 352 | const uint8_t *src_end = src + \ |
| 349 | 353 | frag->nsamples * atempo->channels * sizeof(scalar_type); \ |
| 350 | 354 | \ |
| 351 | | − FFTSample *xdat = frag->xdat; \ |
| 355 | + float *xdat = frag->xdat_in; \ |
| 352 | 356 | scalar_type tmp; \ |
| 353 | 357 | \ |
| 354 | 358 | if (atempo->channels == 1) { \ |
| @@ -356,27 +360,27 @@ static int yae_update(AVFilterContext *ctx) · 1 unmodified line |
| 356 | 360 | tmp = *(const scalar_type *)src; \ |
| 357 | 361 | src += sizeof(scalar_type); \ |
| 358 | 362 | \ |
| 359 | | − *xdat = (FFTSample)tmp; \ |
| 363 | + *xdat = (float)tmp; \ |
| 360 | 364 | } \ |
| 361 | 365 | } else { \ |
| 362 | | − FFTSample s, max, ti, si; \ |
| 366 | + float s, max, ti, si; \ |
| 363 | 367 | int i; \ |
| 364 | 368 | \ |
| 365 | 369 | for (; src < src_end; xdat++) { \ |
| 366 | 370 | tmp = *(const scalar_type *)src; \ |
| 367 | 371 | src += sizeof(scalar_type); \ |
| 368 | 372 | \ |
| 369 | | − max = (FFTSample)tmp; \ |
| 370 | | − s = FFMIN((FFTSample)scalar_max, \ |
| 371 | | − (FFTSample)fabsf(max)); \ |
| 373 | + max = (float)tmp; \ |
| 374 | + s = FFMIN((float)scalar_max, \ |
| 375 | + (float)fabsf(max)); \ |
| 372 | 376 | \ |
| 373 | 377 | for (i = 1; i < atempo->channels; i++) { \ |
| 374 | 378 | tmp = *(const scalar_type *)src; \ |
| 375 | 379 | src += sizeof(scalar_type); \ |
| 376 | 380 | \ |
| 377 | | − ti = (FFTSample)tmp; \ |
| 378 | | − si = FFMIN((FFTSample)scalar_max, \ |
| 379 | | − (FFTSample)fabsf(ti)); \ |
| 381 | + ti = (float)tmp; \ |
| 382 | + si = FFMIN((float)scalar_max, \ |
| 383 | + (float)fabsf(ti)); \ |
| 380 | 384 | \ |
| 381 | 385 | if (s < si) { \ |
| 382 | 386 | s = si; \ |
| @@ -399,7 +403,7 @@ static void yae_downmix(ATempoContext *atempo, AudioFragment *frag) · 16 unmodified lines |
| 399 | 403 | const uint8_t *src = frag->data; |
| 400 | 404 | |
| 401 | 405 | // init complex data buffer used for FFT and Correlation: |
| 402 | | − memset(frag->xdat, 0, sizeof(FFTComplex) * atempo->window); |
| 406 | + memset(frag->xdat_in, 0, sizeof(AVComplexFloat) * (atempo->window + 1)); |
| 403 | 407 | |
| 404 | 408 | if (atempo->format == AV_SAMPLE_FMT_U8) { |
| 405 | 409 | yae_init_xdat(uint8_t, 127); |
| @@ -598,32 +602,24 @@ static void yae_advance_to_next_frag(ATempoContext *atempo) · 192 unmodified lines |
| 598 | 602 | * Multiply two vectors of complex numbers (result of real_to_complex rDFT) |
| 599 | 603 | * and transform back via complex_to_real rDFT. |
| 600 | 604 | */ |
| 601 | | −static void yae_xcorr_via_rdft(FFTSample *xcorr, |
| 602 | | − RDFTContext *complex_to_real, |
| 603 | | − const FFTComplex *xa, |
| 604 | | − const FFTComplex *xb, |
| 605 | +static void yae_xcorr_via_rdft(float *xcorr_in, |
| 606 | + float *xcorr, |
| 607 | + AVTXContext *complex_to_real, |
| 608 | + av_tx_fn c2r_fn, |
| 609 | + const AVComplexFloat *xa, |
| 610 | + const AVComplexFloat *xb, |
| 605 | 611 | const int window) |
| 606 | 612 | { |
| 607 | | − FFTComplex *xc = (FFTComplex *)xcorr; |
| 613 | + AVComplexFloat *xc = (AVComplexFloat *)xcorr_in; |
| 608 | 614 | int i; |
| 609 | 615 | |
| 610 | | − // NOTE: first element requires special care -- Given Y = rDFT(X), |
| 611 | | − // Im(Y[0]) and Im(Y[N/2]) are always zero, therefore av_rdft_calc |
| 612 | | − // stores Re(Y[N/2]) in place of Im(Y[0]). |
| 613 | | − |
| 614 | | − xc->re = xa->re * xb->re; |
| 615 | | − xc->im = xa->im * xb->im; |
| 616 | | − xa++; |
| 617 | | − xb++; |
| 618 | | − xc++; |
| 619 | | − |
| 620 | | − for (i = 1; i < window; i++, xa++, xb++, xc++) { |
| 616 | + for (i = 0; i <= window; i++, xa++, xb++, xc++) { |
| 621 | 617 | xc->re = (xa->re * xb->re + xa->im * xb->im); |
| 622 | 618 | xc->im = (xa->im * xb->re - xa->re * xb->im); |
| 623 | 619 | } |
| 624 | 620 | |
| 625 | 621 | // apply inverse rDFT: |
| 626 | | − av_rdft_calc(complex_to_real, xcorr); |
| 622 | + c2r_fn(complex_to_real, xcorr, xcorr_in, sizeof(float)); |
| 627 | 623 | } |
| 628 | 624 | |
| 629 | 625 | /** |
| @@ -637,21 +633,25 @@ static int yae_align(AudioFragment *frag, · 7 unmodified lines |
| 637 | 633 | const int window, |
| 638 | 634 | const int delta_max, |
| 639 | 635 | const int drift, |
| 640 | | − FFTSample *correlation, |
| 641 | | − RDFTContext *complex_to_real) |
| 636 | + float *correlation_in, |
| 637 | + float *correlation, |
| 638 | + AVTXContext *complex_to_real, |
| 639 | + av_tx_fn c2r_fn) |
| 642 | 640 | { |
| 643 | 641 | int best_offset = -drift; |
| 644 | | − FFTSample best_metric = -FLT_MAX; |
| 645 | | − FFTSample *xcorr; |
| 642 | + float best_metric = -FLT_MAX; |
| 643 | + float *xcorr; |
| 646 | 644 | |
| 647 | 645 | int i0; |
| 648 | 646 | int i1; |
| 649 | 647 | int i; |
| 650 | 648 | |
| 651 | | − yae_xcorr_via_rdft(correlation, |
| 649 | + yae_xcorr_via_rdft(correlation_in, |
| 650 | + correlation, |
| 652 | 651 | complex_to_real, |
| 653 | | − (const FFTComplex *)prev->xdat, |
| 654 | | − (const FFTComplex *)frag->xdat, |
| 652 | + c2r_fn, |
| 653 | + (const AVComplexFloat *)prev->xdat, |
| 654 | + (const AVComplexFloat *)frag->xdat, |
| 655 | 655 | window); |
| 656 | 656 | |
| 657 | 657 | // identify search window boundaries: |
| @@ -665,11 +665,11 @@ static int yae_align(AudioFragment *frag, · 7 unmodified lines |
| 665 | 665 | xcorr = correlation + i0; |
| 666 | 666 | |
| 667 | 667 | for (i = i0; i < i1; i++, xcorr++) { |
| 668 | | − FFTSample metric = *xcorr; |
| 668 | + float metric = *xcorr; |
| 669 | 669 | |
| 670 | 670 | // normalize: |
| 671 | | − FFTSample drifti = (FFTSample)(drift + i); |
| 672 | | − metric *= drifti * (FFTSample)(i - i0) * (FFTSample)(i1 - i); |
| 671 | + float drifti = (float)(drift + i); |
| 672 | + metric *= drifti * (float)(i - i0) * (float)(i1 - i); |
| 673 | 673 | |
| 674 | 674 | if (metric > best_metric) { |
| 675 | 675 | best_metric = metric; |
| @@ -706,8 +706,10 @@ static int yae_adjust_position(ATempoContext *atempo) · 30 unmodified lines |
| 706 | 706 | atempo->window, |
| 707 | 707 | delta_max, |
| 708 | 708 | drift, |
| 709 | + atempo->correlation_in, |
| 709 | 710 | atempo->correlation, |
| 710 | | − atempo->complex_to_real); |
| 711 | + atempo->complex_to_real, |
| 712 | + atempo->c2r_fn); |
| 711 | 713 | |
| 712 | 714 | if (correction) { |
| 713 | 715 | // adjust fragment position: |
| @@ -833,7 +835,7 @@ yae_apply(ATempoContext *atempo, · 119 unmodified lines |
| 833 | 835 | yae_downmix(atempo, yae_curr_frag(atempo)); |
| 834 | 836 | |
| 835 | 837 | // apply rDFT: |
| 836 | | − av_rdft_calc(atempo->real_to_complex, yae_curr_frag(atempo)->xdat); |
| 838 | + atempo->r2c_fn(atempo->real_to_complex, yae_curr_frag(atempo)->xdat, yae_curr_frag(atempo)->xdat_in, sizeof(float)); |
| 837 | 839 | |
| 838 | 840 | // must load the second fragment before alignment can start: |
| 839 | 841 | if (!atempo->nfrag) { |
| @@ -865,7 +867,7 @@ yae_apply(ATempoContext *atempo, · 25 unmodified lines |
| 865 | 867 | yae_downmix(atempo, yae_curr_frag(atempo)); |
| 866 | 868 | |
| 867 | 869 | // apply rDFT: |
| 868 | | − av_rdft_calc(atempo->real_to_complex, yae_curr_frag(atempo)->xdat); |
| 870 | + atempo->r2c_fn(atempo->real_to_complex, yae_curr_frag(atempo)->xdat, yae_curr_frag(atempo)->xdat_in, sizeof(float)); |
| 869 | 871 | |
| 870 | 872 | atempo->state = YAE_OUTPUT_OVERLAP_ADD; |
| 871 | 873 | } |
| @@ -929,7 +931,7 @@ static int yae_flush(ATempoContext *atempo, · 57 unmodified lines |
| 929 | 931 | yae_downmix(atempo, frag); |
| 930 | 932 | |
| 931 | 933 | // apply rDFT: |
| 932 | | − av_rdft_calc(atempo->real_to_complex, frag->xdat); |
| 934 | + atempo->r2c_fn(atempo->real_to_complex, frag->xdat, frag->xdat_in, sizeof(float)); |
| 933 | 935 | |
| 934 | 936 | // align current fragment to previous fragment: |
| 935 | 937 | if (yae_adjust_position(atempo)) { |