Changeset 2292


Ignore:
Timestamp:
Sep 18, 2008 11:14:21 AM (11 years ago)
Author:
bennylp
Message:

Large reorganization of the tonegen for ticket #619:

  • Deprecate the automatic selection of algorithm
  • Introduced various constants for tonegen backends
  • Allow user to specify/override the algorithm by setting
  • Fix the floating-point approximation backend
Location:
pjproject/trunk/pjmedia
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/include/pjmedia/config.h

    r2290 r2292  
    542542 
    543543 
     544/*  
     545 * Below specifies the various tone generator backend algorithm. 
     546 */ 
     547 
     548/**  
     549 * The math's sine(), floating point. This has very good precision  
     550 * but it's the slowest and requires floating point support and 
     551 * linking with the math library. 
     552 */ 
     553#define PJMEDIA_TONEGEN_SINE                        1 
     554 
     555/** 
     556 * Floating point approximation of sine(). This has relatively good 
     557 * precision and much faster than plain sine(), but it requires floating- 
     558 * point support and linking with the math library. 
     559 */ 
     560#define PJMEDIA_TONEGEN_FLOATING_POINT              2 
     561 
     562/** 
     563 * Fixed point using sine signal generated by Cordic algorithm. This 
     564 * algorithm can be tuned to provide balance between precision and 
     565 * performance by tuning the PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP  
     566 * setting, and may be suitable for platforms that lack floating-point 
     567 * support. 
     568 */ 
     569#define PJMEDIA_TONEGEN_FIXED_POINT_CORDIC          3 
     570 
     571/** 
     572 * Fast fixed point using some approximation to generate sine waves. 
     573 * The tone generated by this algorithm is not very precise, however 
     574 * the algorithm is very fast. 
     575 */ 
     576#define PJMEDIA_TONEGEN_FAST_FIXED_POINT            4 
     577 
     578 
     579/** 
     580 * Specify the tone generator algorithm to be used. 
     581 * 
     582 * Default value: 
     583 *  - PJMEDIA_TONEGEN_FLOATING_POINT when PJ_HAS_FLOATING_POINT is set 
     584 *  - PJMEDIA_TONEGEN_FIXED_POINT_CORDIC when PJ_HAS_FLOATING_POINT is not set 
     585 */ 
     586#ifndef PJMEDIA_TONEGEN_ALG 
     587#   if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT 
     588#       define PJMEDIA_TONEGEN_ALG      PJMEDIA_TONEGEN_FLOATING_POINT 
     589#   else 
     590#       define PJMEDIA_TONEGEN_ALG      PJMEDIA_TONEGEN_FIXED_POINT_CORDIC 
     591#   endif 
     592#endif 
     593 
     594 
     595/** 
     596 * Specify the number of calculation loops to generate the tone, when 
     597 * PJMEDIA_TONEGEN_FIXED_POINT_CORDIC algorithm is used. With more calculation 
     598 * loops, the tone signal gets more precise, but this will add more  
     599 * processing. 
     600 * 
     601 * Valid values are 1 to 28. 
     602 * 
     603 * Default value: 7 
     604 */ 
     605#ifndef PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP 
     606#   define PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP  7 
     607#endif 
     608 
     609 
    544610/** 
    545611 * Enable high quality of tone generation, the better quality will cost 
     
    548614 * By default it is enabled when PJ_HAS_FLOATING_POINT is set. 
    549615 * 
    550  * @see PJMEDIA_TONEGEN_FORCE_FLOAT 
    551  */ 
    552 #ifndef PJMEDIA_USE_HIGH_QUALITY_TONEGEN 
    553 #   define PJMEDIA_USE_HIGH_QUALITY_TONEGEN         PJ_HAS_FLOATING_POINT 
    554 #endif 
    555  
    556  
    557 /** 
    558  * Force the tone generation to use floating point computation, even when 
    559  * PJ_HAS_FLOATING_POINT is disabled. This may be necessary if the tone 
    560  * generator is used to produce DTMF to be sent inband, since the fixed 
    561  * point algorithm may not have the correct frequency accuracy. 
    562  * 
    563  * This option, combined with PJ_HAS_FLOATING_POINT will produce the  
    564  * following selection of tone generator algorithm: 
    565  *  - if both PJ_HAS_FLOATING_POINT and PJMEDIA_USE_HIGH_QUALITY_TONEGEN  
    566  *    are set, the standard sin() function will be used. This will produce 
    567  *    the highest quality tones, at the expense of more processing power. 
    568  *  - if PJ_HAS_FLOATING_POINT is not set: 
    569  *      - if both PJMEDIA_USE_HIGH_QUALITY_TONEGEN and  
    570  *        PJMEDIA_TONEGEN_FORCE_FLOAT are set, sin() based algorithm will 
    571  *        be used (similar as above). 
    572  *      - if PJMEDIA_USE_HIGH_QUALITY_TONEGEN is not set but the 
    573  *        PJMEDIA_TONEGEN_FORCE_FLOAT is set, a floating point approximation 
    574  *        algorithm will be used. This should produce good enough tone 
    575  *        for most uses, and the performance is faster than using pure 
    576  *        sin() based algorithm. Note that linking to math library may 
    577  *        still be needed. 
    578  *      - if both are not set, the fixed point approximation algorithm 
    579  *        will be used. 
    580  * 
    581  * Default: 1 
    582  */ 
    583 #ifndef PJMEDIA_TONEGEN_FORCE_FLOAT 
    584 #   define PJMEDIA_TONEGEN_FORCE_FLOAT              1 
     616 * This macro has been deprecated in version 1.0-rc3. 
     617 */ 
     618#ifdef PJMEDIA_USE_HIGH_QUALITY_TONEGEN 
     619#   error   "The PJMEDIA_USE_HIGH_QUALITY_TONEGEN macro is obsolete" 
    585620#endif 
    586621 
  • pjproject/trunk/pjmedia/src/pjmedia/tonegen.c

    r2284 r2292  
    2626#include <pj/pool.h> 
    2727 
    28  
    29 /* float can be twice slower on i686! */ 
    30 #define DATA    double 
    31  
    3228/* amplitude */ 
    3329#define AMP     PJMEDIA_TONEGEN_VOLUME 
    34  
    3530 
    3631#ifndef M_PI 
     
    3833#endif 
    3934 
    40  
    41 #if (defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0) || \ 
    42     (defined(PJMEDIA_TONEGEN_FORCE_FLOAT) && PJMEDIA_TONEGEN_FORCE_FLOAT != 0) 
    43 #   include <math.h> 
    44  
    45 #   if defined(PJMEDIA_USE_HIGH_QUALITY_TONEGEN) && \ 
    46        PJMEDIA_USE_HIGH_QUALITY_TONEGEN!=0 
    47  
    48         /* 
    49          * This is the good old tone generator using sin(). 
    50          * Speed = 222.5 cycles per sample. 
     35#if PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_SINE 
     36    #include <math.h> 
     37    #define DATA        double 
     38 
     39    /* 
     40     * This is the good old tone generator using sin(). 
     41     * Speed = 1347 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). 
     42     *         approx. 10.91 MIPS 
     43     */ 
     44    struct gen 
     45    { 
     46        DATA add; 
     47        DATA c; 
     48        DATA vol; 
     49    }; 
     50 
     51    #define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A 
     52    #define GEN_SAMP(val,var)   val = (short)(sin(var.c * 2 * M_PI) * \ 
     53                                              var.vol); \ 
     54                                var.c += var.add 
     55 
     56#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FLOATING_POINT 
     57    #include <math.h> 
     58    #define DATA        float 
     59 
     60    /* 
     61     * Default floating-point based tone generation using sine wave  
     62     * generation from: 
     63     *   http://www.musicdsp.org/showone.php?id=10. 
     64     * This produces good quality tone in relatively faster time than 
     65     * the normal sin() generator. 
     66     * Speed = 350 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). 
     67     *         approx. 2.84 MIPS 
     68     */ 
     69    struct gen 
     70    { 
     71        DATA a, s0, s1; 
     72    }; 
     73 
     74    #define GEN_INIT(var,R,F,A) var.a = (DATA) (2.0 * sin(M_PI * F / R)); \ 
     75                                var.s0 = 0; \ 
     76                                var.s1 = (DATA)(0 - (int)A) 
     77    #define GEN_SAMP(val,var)   var.s0 = var.s0 - var.a * var.s1; \ 
     78                                var.s1 = var.s1 + var.a * var.s0; \ 
     79                                val = (short) var.s0 
     80 
     81#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FIXED_POINT_CORDIC 
     82    /* Cordic algorithm with 28 bit size, from: 
     83     * http://www.dcs.gla.ac.uk/~jhw/cordic/ 
     84     * Speed = 742 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). 
     85     *         (PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP=7) 
     86     *         approx. 6.01 MIPS 
     87     */ 
     88    #define CORDIC_1K           0x026DD3B6 
     89    #define CORDIC_HALF_PI      0x06487ED5 
     90    #define CORDIC_PI           (CORDIC_HALF_PI * 2) 
     91    #define CORDIC_MUL_BITS     26 
     92    #define CORDIC_MUL          (1 << CORDIC_MUL_BITS) 
     93    #define CORDIC_NTAB         28 
     94    #define CORDIC_LOOP         PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP 
     95 
     96    static int cordic_ctab [] =  
     97    { 
     98        0x03243F6A, 0x01DAC670, 0x00FADBAF, 0x007F56EA, 0x003FEAB7,  
     99        0x001FFD55, 0x000FFFAA, 0x0007FFF5, 0x0003FFFE, 0x0001FFFF,  
     100        0x0000FFFF, 0x00007FFF, 0x00003FFF, 0x00001FFF, 0x00000FFF,  
     101        0x000007FF, 0x000003FF, 0x000001FF, 0x000000FF, 0x0000007F,  
     102        0x0000003F, 0x0000001F, 0x0000000F, 0x00000007, 0x00000003,  
     103        0x00000001, 0x00000000, 0x00000000  
     104    }; 
     105 
     106    static pj_int32_t cordic(pj_int32_t theta, unsigned n) 
     107    { 
     108        unsigned k; 
     109        int d; 
     110        pj_int32_t tx; 
     111        pj_int32_t x = CORDIC_1K, y = 0, z = theta; 
     112 
     113        for (k=0; k<n; ++k) { 
     114            #if 0 
     115            d = (z>=0) ? 0 : -1; 
     116            #else 
     117            /* Only slightly (~2.5%) faster, but not portable? */ 
     118             d = z>>27; 
     119            #endif 
     120            tx = x - (((y>>k) ^ d) - d); 
     121            y = y + (((x>>k) ^ d) - d); 
     122            z = z - ((cordic_ctab[k] ^ d) - d); 
     123            x = tx; 
     124        }   
     125        return y; 
     126    } 
     127 
     128    /* Note: theta must be uint32 here */ 
     129    static pj_int32_t cordic_sin(pj_uint32_t theta, unsigned n) 
     130    { 
     131        if (theta < CORDIC_HALF_PI) 
     132            return cordic(theta, n); 
     133        else if (theta < CORDIC_PI) 
     134            return cordic(CORDIC_HALF_PI-(theta-CORDIC_HALF_PI), n); 
     135        else if (theta < CORDIC_PI + CORDIC_HALF_PI) 
     136            return -cordic(theta - CORDIC_PI, n); 
     137        else if (theta < 2 * CORDIC_PI) 
     138            return -cordic(CORDIC_HALF_PI-(theta-3*CORDIC_HALF_PI), n); 
     139        else { 
     140            pj_assert(!"Invalid cordic_sin() value"); 
     141            return 0; 
     142        } 
     143    } 
     144 
     145    struct gen 
     146    { 
     147        unsigned    add; 
     148        pj_uint32_t c; 
     149        unsigned    vol; 
     150    }; 
     151 
     152    #define VOL(var,v)          (((v) * var.vol) >> 15) 
     153    #define GEN_INIT(var,R,F,A) gen_init(&var, R, F, A) 
     154    #define GEN_SAMP(val,var)   val = gen_samp(&var) 
     155 
     156    static void gen_init(struct gen *var, unsigned R, unsigned F, unsigned A) 
     157    { 
     158        var->add = 2*CORDIC_PI/R * F; 
     159        var->c = 0; 
     160        var->vol = A; 
     161    } 
     162 
     163    PJ_INLINE(short) gen_samp(struct gen *var) 
     164    { 
     165        pj_int32_t val; 
     166        val = cordic_sin(var->c, CORDIC_LOOP); 
     167        /*val = (val * 32767) / CORDIC_MUL; 
     168         *val = VOL((*var), val); 
    51169         */ 
    52         struct gen 
    53         { 
    54             DATA add; 
    55             DATA c; 
    56             DATA vol; 
    57         }; 
    58  
    59 #       define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A 
    60 #       define GEN_SAMP(val,var)   val = (short)(sin(var.c * 2 * M_PI) * \ 
    61                                                  var.vol); \ 
    62                                    var.c += var.add 
    63  
    64 #   else 
    65  
    66         /* 
    67          * Default floating-point based tone generation using sine wave  
    68          * generation from: 
    69          *   http://www.musicdsp.org/showone.php?id=10. 
    70          * This produces good quality tone in relatively faster time than 
    71          * the normal sin() generator. 
    72          * Speed = 40.6 cycles per sample. 
    73          */ 
    74         struct gen 
    75         { 
    76             DATA a, s0, s1; 
    77         }; 
    78  
    79 #       define GEN_INIT(var,R,F,A) var.a = (DATA) (2.0 * sin(M_PI * F / R)); \ 
    80                                    var.s0 = A; \ 
    81                                    var.s1 = 0 
    82 #       define GEN_SAMP(val,var)   var.s0 = var.s0 - var.a * var.s1; \ 
    83                                    var.s1 = var.s1 + var.a * var.s0; \ 
    84                                    val = (short) var.s0 
    85 #   endif 
    86  
    87 #else 
     170        val = ((val >> 10) * var->vol) >> 16; 
     171        var->c += var->add; 
     172        if (var->c > 2*CORDIC_PI) 
     173            var->c -= (2 * CORDIC_PI); 
     174        return (short) val; 
     175    } 
     176 
     177#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FAST_FIXED_POINT 
    88178 
    89179    /*  
     
    93183     *    http://www.audiomulch.com/~rossb/code/sinusoids/  
    94184     * Quality wise not so good, but it's blazing fast! 
    95      * Speed:  
    96      *  - with volume adjustment: 14 cycles per sample  
    97      *  - without volume adjustment: 12.22 cycles per sample 
     185     * Speed = 117 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4). 
     186     *         approx. 0.95 MIPS 
    98187     */ 
    99188    PJ_INLINE(int) approximate_sin3(unsigned x) 
     
    113202    }; 
    114203 
    115 #   define MAXI                 ((unsigned)0xFFFFFFFF) 
    116 #   define SIN                  approximate_sin3 
    117 #   if 1    /* set this to 0 to disable volume adjustment */ 
    118 #       define VOL(var,v)       (((v) * var.vol) >> 15) 
    119 #   else 
    120 #       define VOL(var,v)       (v) 
    121 #   endif 
    122 #   define GEN_INIT(var,R,F,A)  var.add = MAXI/R * F, var.c=0, var.vol=A 
    123 #   define GEN_SAMP(val,var)    val = (short) VOL(var,SIN(var.c)>>16);\ 
     204    #define MAXI                ((unsigned)0xFFFFFFFF) 
     205    #define SIN                 approximate_sin3 
     206    #define VOL(var,v)          (((v) * var.vol) >> 15) 
     207    #define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0, var.vol=A 
     208    #define GEN_SAMP(val,var)   val = (short) VOL(var,SIN(var.c)>>16); \ 
    124209                                var.c += var.add 
    125210 
     211#else 
     212    #error "PJMEDIA_TONEGEN_ALG is not set correctly" 
    126213#endif 
    127214 
Note: See TracChangeset for help on using the changeset viewer.