yading@10: /* yading@10: * (c) 2002 Fabrice Bellard yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /** yading@10: * @file yading@10: * FFT and MDCT tests. yading@10: */ yading@10: yading@10: #include "libavutil/cpu.h" yading@10: #include "libavutil/mathematics.h" yading@10: #include "libavutil/lfg.h" yading@10: #include "libavutil/log.h" yading@10: #include "libavutil/time.h" yading@10: #include "fft.h" yading@10: #if CONFIG_FFT_FLOAT yading@10: #include "dct.h" yading@10: #include "rdft.h" yading@10: #endif yading@10: #include yading@10: #if HAVE_UNISTD_H yading@10: #include yading@10: #endif yading@10: #include yading@10: #include yading@10: yading@10: /* reference fft */ yading@10: yading@10: #define MUL16(a,b) ((a) * (b)) yading@10: yading@10: #define CMAC(pre, pim, are, aim, bre, bim) \ yading@10: {\ yading@10: pre += (MUL16(are, bre) - MUL16(aim, bim));\ yading@10: pim += (MUL16(are, bim) + MUL16(bre, aim));\ yading@10: } yading@10: yading@10: #if CONFIG_FFT_FLOAT yading@10: # define RANGE 1.0 yading@10: # define REF_SCALE(x, bits) (x) yading@10: # define FMT "%10.6f" yading@10: #else yading@10: # define RANGE 16384 yading@10: # define REF_SCALE(x, bits) ((x) / (1<<(bits))) yading@10: # define FMT "%6d" yading@10: #endif yading@10: yading@10: struct { yading@10: float re, im; yading@10: } *exptab; yading@10: yading@10: static void fft_ref_init(int nbits, int inverse) yading@10: { yading@10: int n, i; yading@10: double c1, s1, alpha; yading@10: yading@10: n = 1 << nbits; yading@10: exptab = av_malloc((n / 2) * sizeof(*exptab)); yading@10: yading@10: for (i = 0; i < (n/2); i++) { yading@10: alpha = 2 * M_PI * (float)i / (float)n; yading@10: c1 = cos(alpha); yading@10: s1 = sin(alpha); yading@10: if (!inverse) yading@10: s1 = -s1; yading@10: exptab[i].re = c1; yading@10: exptab[i].im = s1; yading@10: } yading@10: } yading@10: yading@10: static void fft_ref(FFTComplex *tabr, FFTComplex *tab, int nbits) yading@10: { yading@10: int n, i, j, k, n2; yading@10: double tmp_re, tmp_im, s, c; yading@10: FFTComplex *q; yading@10: yading@10: n = 1 << nbits; yading@10: n2 = n >> 1; yading@10: for (i = 0; i < n; i++) { yading@10: tmp_re = 0; yading@10: tmp_im = 0; yading@10: q = tab; yading@10: for (j = 0; j < n; j++) { yading@10: k = (i * j) & (n - 1); yading@10: if (k >= n2) { yading@10: c = -exptab[k - n2].re; yading@10: s = -exptab[k - n2].im; yading@10: } else { yading@10: c = exptab[k].re; yading@10: s = exptab[k].im; yading@10: } yading@10: CMAC(tmp_re, tmp_im, c, s, q->re, q->im); yading@10: q++; yading@10: } yading@10: tabr[i].re = REF_SCALE(tmp_re, nbits); yading@10: tabr[i].im = REF_SCALE(tmp_im, nbits); yading@10: } yading@10: } yading@10: yading@10: static void imdct_ref(FFTSample *out, FFTSample *in, int nbits) yading@10: { yading@10: int n = 1<= 1e-3) { yading@10: av_log(NULL, AV_LOG_ERROR, "ERROR %5d: "FMT" "FMT"\n", yading@10: i, tab1[i], tab2[i]); yading@10: err = 1; yading@10: } yading@10: error+= e*e; yading@10: if(e>max) max= e; yading@10: } yading@10: av_log(NULL, AV_LOG_INFO, "max:%f e:%g\n", max, sqrt(error/n)); yading@10: return err; yading@10: } yading@10: yading@10: yading@10: static void help(void) yading@10: { yading@10: av_log(NULL, AV_LOG_INFO,"usage: fft-test [-h] [-s] [-i] [-n b]\n" yading@10: "-h print this help\n" yading@10: "-s speed test\n" yading@10: "-m (I)MDCT test\n" yading@10: "-d (I)DCT test\n" yading@10: "-r (I)RDFT test\n" yading@10: "-i inverse transform test\n" yading@10: "-n b set the transform size to 2^b\n" yading@10: "-f x set scale factor for output data of (I)MDCT to x\n" yading@10: ); yading@10: } yading@10: yading@10: enum tf_transform { yading@10: TRANSFORM_FFT, yading@10: TRANSFORM_MDCT, yading@10: TRANSFORM_RDFT, yading@10: TRANSFORM_DCT, yading@10: }; yading@10: yading@10: #if !HAVE_GETOPT yading@10: #include "compat/getopt.c" yading@10: #endif yading@10: yading@10: int main(int argc, char **argv) yading@10: { yading@10: FFTComplex *tab, *tab1, *tab_ref; yading@10: FFTSample *tab2; yading@10: int it, i, c; yading@10: int cpuflags; yading@10: int do_speed = 0; yading@10: int err = 1; yading@10: enum tf_transform transform = TRANSFORM_FFT; yading@10: int do_inverse = 0; yading@10: FFTContext s1, *s = &s1; yading@10: FFTContext m1, *m = &m1; yading@10: #if CONFIG_FFT_FLOAT yading@10: RDFTContext r1, *r = &r1; yading@10: DCTContext d1, *d = &d1; yading@10: int fft_size_2; yading@10: #endif yading@10: int fft_nbits, fft_size; yading@10: double scale = 1.0; yading@10: AVLFG prng; yading@10: av_lfg_init(&prng, 1); yading@10: yading@10: fft_nbits = 9; yading@10: for(;;) { yading@10: c = getopt(argc, argv, "hsimrdn:f:c:"); yading@10: if (c == -1) yading@10: break; yading@10: switch(c) { yading@10: case 'h': yading@10: help(); yading@10: return 1; yading@10: case 's': yading@10: do_speed = 1; yading@10: break; yading@10: case 'i': yading@10: do_inverse = 1; yading@10: break; yading@10: case 'm': yading@10: transform = TRANSFORM_MDCT; yading@10: break; yading@10: case 'r': yading@10: transform = TRANSFORM_RDFT; yading@10: break; yading@10: case 'd': yading@10: transform = TRANSFORM_DCT; yading@10: break; yading@10: case 'n': yading@10: fft_nbits = atoi(optarg); yading@10: break; yading@10: case 'f': yading@10: scale = atof(optarg); yading@10: break; yading@10: case 'c': yading@10: cpuflags = av_get_cpu_flags(); yading@10: yading@10: if (av_parse_cpu_caps(&cpuflags, optarg) < 0) yading@10: return 1; yading@10: yading@10: av_force_cpu_flags(cpuflags); yading@10: break; yading@10: } yading@10: } yading@10: yading@10: fft_size = 1 << fft_nbits; yading@10: tab = av_malloc(fft_size * sizeof(FFTComplex)); yading@10: tab1 = av_malloc(fft_size * sizeof(FFTComplex)); yading@10: tab_ref = av_malloc(fft_size * sizeof(FFTComplex)); yading@10: tab2 = av_malloc(fft_size * sizeof(FFTSample)); yading@10: yading@10: switch (transform) { yading@10: case TRANSFORM_MDCT: yading@10: av_log(NULL, AV_LOG_INFO,"Scale factor is set to %f\n", scale); yading@10: if (do_inverse) yading@10: av_log(NULL, AV_LOG_INFO,"IMDCT"); yading@10: else yading@10: av_log(NULL, AV_LOG_INFO,"MDCT"); yading@10: ff_mdct_init(m, fft_nbits, do_inverse, scale); yading@10: break; yading@10: case TRANSFORM_FFT: yading@10: if (do_inverse) yading@10: av_log(NULL, AV_LOG_INFO,"IFFT"); yading@10: else yading@10: av_log(NULL, AV_LOG_INFO,"FFT"); yading@10: ff_fft_init(s, fft_nbits, do_inverse); yading@10: fft_ref_init(fft_nbits, do_inverse); yading@10: break; yading@10: #if CONFIG_FFT_FLOAT yading@10: case TRANSFORM_RDFT: yading@10: if (do_inverse) yading@10: av_log(NULL, AV_LOG_INFO,"IDFT_C2R"); yading@10: else yading@10: av_log(NULL, AV_LOG_INFO,"DFT_R2C"); yading@10: ff_rdft_init(r, fft_nbits, do_inverse ? IDFT_C2R : DFT_R2C); yading@10: fft_ref_init(fft_nbits, do_inverse); yading@10: break; yading@10: case TRANSFORM_DCT: yading@10: if (do_inverse) yading@10: av_log(NULL, AV_LOG_INFO,"DCT_III"); yading@10: else yading@10: av_log(NULL, AV_LOG_INFO,"DCT_II"); yading@10: ff_dct_init(d, fft_nbits, do_inverse ? DCT_III : DCT_II); yading@10: break; yading@10: #endif yading@10: default: yading@10: av_log(NULL, AV_LOG_ERROR, "Requested transform not supported\n"); yading@10: return 1; yading@10: } yading@10: av_log(NULL, AV_LOG_INFO," %d test\n", fft_size); yading@10: yading@10: /* generate random data */ yading@10: yading@10: for (i = 0; i < fft_size; i++) { yading@10: tab1[i].re = frandom(&prng); yading@10: tab1[i].im = frandom(&prng); yading@10: } yading@10: yading@10: /* checking result */ yading@10: av_log(NULL, AV_LOG_INFO,"Checking...\n"); yading@10: yading@10: switch (transform) { yading@10: case TRANSFORM_MDCT: yading@10: if (do_inverse) { yading@10: imdct_ref((FFTSample *)tab_ref, (FFTSample *)tab1, fft_nbits); yading@10: m->imdct_calc(m, tab2, (FFTSample *)tab1); yading@10: err = check_diff((FFTSample *)tab_ref, tab2, fft_size, scale); yading@10: } else { yading@10: mdct_ref((FFTSample *)tab_ref, (FFTSample *)tab1, fft_nbits); yading@10: yading@10: m->mdct_calc(m, tab2, (FFTSample *)tab1); yading@10: yading@10: err = check_diff((FFTSample *)tab_ref, tab2, fft_size / 2, scale); yading@10: } yading@10: break; yading@10: case TRANSFORM_FFT: yading@10: memcpy(tab, tab1, fft_size * sizeof(FFTComplex)); yading@10: s->fft_permute(s, tab); yading@10: s->fft_calc(s, tab); yading@10: yading@10: fft_ref(tab_ref, tab1, fft_nbits); yading@10: err = check_diff((FFTSample *)tab_ref, (FFTSample *)tab, fft_size * 2, 1.0); yading@10: break; yading@10: #if CONFIG_FFT_FLOAT yading@10: case TRANSFORM_RDFT: yading@10: fft_size_2 = fft_size >> 1; yading@10: if (do_inverse) { yading@10: tab1[ 0].im = 0; yading@10: tab1[fft_size_2].im = 0; yading@10: for (i = 1; i < fft_size_2; i++) { yading@10: tab1[fft_size_2+i].re = tab1[fft_size_2-i].re; yading@10: tab1[fft_size_2+i].im = -tab1[fft_size_2-i].im; yading@10: } yading@10: yading@10: memcpy(tab2, tab1, fft_size * sizeof(FFTSample)); yading@10: tab2[1] = tab1[fft_size_2].re; yading@10: yading@10: r->rdft_calc(r, tab2); yading@10: fft_ref(tab_ref, tab1, fft_nbits); yading@10: for (i = 0; i < fft_size; i++) { yading@10: tab[i].re = tab2[i]; yading@10: tab[i].im = 0; yading@10: } yading@10: err = check_diff((float *)tab_ref, (float *)tab, fft_size * 2, 0.5); yading@10: } else { yading@10: for (i = 0; i < fft_size; i++) { yading@10: tab2[i] = tab1[i].re; yading@10: tab1[i].im = 0; yading@10: } yading@10: r->rdft_calc(r, tab2); yading@10: fft_ref(tab_ref, tab1, fft_nbits); yading@10: tab_ref[0].im = tab_ref[fft_size_2].re; yading@10: err = check_diff((float *)tab_ref, (float *)tab2, fft_size, 1.0); yading@10: } yading@10: break; yading@10: case TRANSFORM_DCT: yading@10: memcpy(tab, tab1, fft_size * sizeof(FFTComplex)); yading@10: d->dct_calc(d, (FFTSample *)tab); yading@10: if (do_inverse) { yading@10: idct_ref((FFTSample*)tab_ref, (FFTSample *)tab1, fft_nbits); yading@10: } else { yading@10: dct_ref((FFTSample*)tab_ref, (FFTSample *)tab1, fft_nbits); yading@10: } yading@10: err = check_diff((float *)tab_ref, (float *)tab, fft_size, 1.0); yading@10: break; yading@10: #endif yading@10: } yading@10: yading@10: /* do a speed test */ yading@10: yading@10: if (do_speed) { yading@10: int64_t time_start, duration; yading@10: int nb_its; yading@10: yading@10: av_log(NULL, AV_LOG_INFO,"Speed test...\n"); yading@10: /* we measure during about 1 seconds */ yading@10: nb_its = 1; yading@10: for(;;) { yading@10: time_start = av_gettime(); yading@10: for (it = 0; it < nb_its; it++) { yading@10: switch (transform) { yading@10: case TRANSFORM_MDCT: yading@10: if (do_inverse) { yading@10: m->imdct_calc(m, (FFTSample *)tab, (FFTSample *)tab1); yading@10: } else { yading@10: m->mdct_calc(m, (FFTSample *)tab, (FFTSample *)tab1); yading@10: } yading@10: break; yading@10: case TRANSFORM_FFT: yading@10: memcpy(tab, tab1, fft_size * sizeof(FFTComplex)); yading@10: s->fft_calc(s, tab); yading@10: break; yading@10: #if CONFIG_FFT_FLOAT yading@10: case TRANSFORM_RDFT: yading@10: memcpy(tab2, tab1, fft_size * sizeof(FFTSample)); yading@10: r->rdft_calc(r, tab2); yading@10: break; yading@10: case TRANSFORM_DCT: yading@10: memcpy(tab2, tab1, fft_size * sizeof(FFTSample)); yading@10: d->dct_calc(d, tab2); yading@10: break; yading@10: #endif yading@10: } yading@10: } yading@10: duration = av_gettime() - time_start; yading@10: if (duration >= 1000000) yading@10: break; yading@10: nb_its *= 2; yading@10: } yading@10: av_log(NULL, AV_LOG_INFO,"time: %0.1f us/transform [total time=%0.2f s its=%d]\n", yading@10: (double)duration / nb_its, yading@10: (double)duration / 1000000.0, yading@10: nb_its); yading@10: } yading@10: yading@10: switch (transform) { yading@10: case TRANSFORM_MDCT: yading@10: ff_mdct_end(m); yading@10: break; yading@10: case TRANSFORM_FFT: yading@10: ff_fft_end(s); yading@10: break; yading@10: #if CONFIG_FFT_FLOAT yading@10: case TRANSFORM_RDFT: yading@10: ff_rdft_end(r); yading@10: break; yading@10: case TRANSFORM_DCT: yading@10: ff_dct_end(d); yading@10: break; yading@10: #endif yading@10: } yading@10: yading@10: av_free(tab); yading@10: av_free(tab1); yading@10: av_free(tab2); yading@10: av_free(tab_ref); yading@10: av_free(exptab); yading@10: yading@10: return err; yading@10: }