summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-09-18 11:14:21 +0000
committerBenny Prijono <bennylp@teluu.com>2008-09-18 11:14:21 +0000
commitfd64f65109f5cc5ce611cd5ec842c08796566a05 (patch)
treee9a0d6b6c8841b38513d3e74f1d01b1fc7c064c1 /pjmedia
parent5059cf00692fdb889344d9839fad80773e58deb8 (diff)
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 git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2292 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/include/pjmedia/config.h95
-rw-r--r--pjmedia/src/pjmedia/tonegen.c207
2 files changed, 212 insertions, 90 deletions
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 70354ee7..86a408e9 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -541,47 +541,82 @@
#endif
+/*
+ * Below specifies the various tone generator backend algorithm.
+ */
+
+/**
+ * The math's sine(), floating point. This has very good precision
+ * but it's the slowest and requires floating point support and
+ * linking with the math library.
+ */
+#define PJMEDIA_TONEGEN_SINE 1
+
/**
- * Enable high quality of tone generation, the better quality will cost
- * more CPU load. This is only applied to floating point enabled machines.
+ * Floating point approximation of sine(). This has relatively good
+ * precision and much faster than plain sine(), but it requires floating-
+ * point support and linking with the math library.
+ */
+#define PJMEDIA_TONEGEN_FLOATING_POINT 2
+
+/**
+ * Fixed point using sine signal generated by Cordic algorithm. This
+ * algorithm can be tuned to provide balance between precision and
+ * performance by tuning the PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP
+ * setting, and may be suitable for platforms that lack floating-point
+ * support.
+ */
+#define PJMEDIA_TONEGEN_FIXED_POINT_CORDIC 3
+
+/**
+ * Fast fixed point using some approximation to generate sine waves.
+ * The tone generated by this algorithm is not very precise, however
+ * the algorithm is very fast.
+ */
+#define PJMEDIA_TONEGEN_FAST_FIXED_POINT 4
+
+
+/**
+ * Specify the tone generator algorithm to be used.
*
- * By default it is enabled when PJ_HAS_FLOATING_POINT is set.
+ * Default value:
+ * - PJMEDIA_TONEGEN_FLOATING_POINT when PJ_HAS_FLOATING_POINT is set
+ * - PJMEDIA_TONEGEN_FIXED_POINT_CORDIC when PJ_HAS_FLOATING_POINT is not set
+ */
+#ifndef PJMEDIA_TONEGEN_ALG
+# if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT
+# define PJMEDIA_TONEGEN_ALG PJMEDIA_TONEGEN_FLOATING_POINT
+# else
+# define PJMEDIA_TONEGEN_ALG PJMEDIA_TONEGEN_FIXED_POINT_CORDIC
+# endif
+#endif
+
+
+/**
+ * Specify the number of calculation loops to generate the tone, when
+ * PJMEDIA_TONEGEN_FIXED_POINT_CORDIC algorithm is used. With more calculation
+ * loops, the tone signal gets more precise, but this will add more
+ * processing.
+ *
+ * Valid values are 1 to 28.
*
- * @see PJMEDIA_TONEGEN_FORCE_FLOAT
+ * Default value: 7
*/
-#ifndef PJMEDIA_USE_HIGH_QUALITY_TONEGEN
-# define PJMEDIA_USE_HIGH_QUALITY_TONEGEN PJ_HAS_FLOATING_POINT
+#ifndef PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP
+# define PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP 7
#endif
/**
- * Force the tone generation to use floating point computation, even when
- * PJ_HAS_FLOATING_POINT is disabled. This may be necessary if the tone
- * generator is used to produce DTMF to be sent inband, since the fixed
- * point algorithm may not have the correct frequency accuracy.
+ * Enable high quality of tone generation, the better quality will cost
+ * more CPU load. This is only applied to floating point enabled machines.
*
- * This option, combined with PJ_HAS_FLOATING_POINT will produce the
- * following selection of tone generator algorithm:
- * - if both PJ_HAS_FLOATING_POINT and PJMEDIA_USE_HIGH_QUALITY_TONEGEN
- * are set, the standard sin() function will be used. This will produce
- * the highest quality tones, at the expense of more processing power.
- * - if PJ_HAS_FLOATING_POINT is not set:
- * - if both PJMEDIA_USE_HIGH_QUALITY_TONEGEN and
- * PJMEDIA_TONEGEN_FORCE_FLOAT are set, sin() based algorithm will
- * be used (similar as above).
- * - if PJMEDIA_USE_HIGH_QUALITY_TONEGEN is not set but the
- * PJMEDIA_TONEGEN_FORCE_FLOAT is set, a floating point approximation
- * algorithm will be used. This should produce good enough tone
- * for most uses, and the performance is faster than using pure
- * sin() based algorithm. Note that linking to math library may
- * still be needed.
- * - if both are not set, the fixed point approximation algorithm
- * will be used.
+ * By default it is enabled when PJ_HAS_FLOATING_POINT is set.
*
- * Default: 1
+ * This macro has been deprecated in version 1.0-rc3.
*/
-#ifndef PJMEDIA_TONEGEN_FORCE_FLOAT
-# define PJMEDIA_TONEGEN_FORCE_FLOAT 1
+#ifdef PJMEDIA_USE_HIGH_QUALITY_TONEGEN
+# error "The PJMEDIA_USE_HIGH_QUALITY_TONEGEN macro is obsolete"
#endif
diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c
index 60732500..3f2cbe37 100644
--- a/pjmedia/src/pjmedia/tonegen.c
+++ b/pjmedia/src/pjmedia/tonegen.c
@@ -25,66 +25,156 @@
#include <pj/log.h>
#include <pj/pool.h>
-
-/* float can be twice slower on i686! */
-#define DATA double
-
/* amplitude */
#define AMP PJMEDIA_TONEGEN_VOLUME
-
#ifndef M_PI
# define M_PI ((DATA)3.141592653589793238462643383279)
#endif
+#if PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_SINE
+ #include <math.h>
+ #define DATA double
-#if (defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0) || \
- (defined(PJMEDIA_TONEGEN_FORCE_FLOAT) && PJMEDIA_TONEGEN_FORCE_FLOAT != 0)
-# include <math.h>
+ /*
+ * This is the good old tone generator using sin().
+ * Speed = 1347 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
+ * approx. 10.91 MIPS
+ */
+ struct gen
+ {
+ DATA add;
+ DATA c;
+ DATA vol;
+ };
-# if defined(PJMEDIA_USE_HIGH_QUALITY_TONEGEN) && \
- PJMEDIA_USE_HIGH_QUALITY_TONEGEN!=0
+ #define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A
+ #define GEN_SAMP(val,var) val = (short)(sin(var.c * 2 * M_PI) * \
+ var.vol); \
+ var.c += var.add
+
+#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FLOATING_POINT
+ #include <math.h>
+ #define DATA float
+
+ /*
+ * Default floating-point based tone generation using sine wave
+ * generation from:
+ * http://www.musicdsp.org/showone.php?id=10.
+ * This produces good quality tone in relatively faster time than
+ * the normal sin() generator.
+ * Speed = 350 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
+ * approx. 2.84 MIPS
+ */
+ struct gen
+ {
+ DATA a, s0, s1;
+ };
- /*
- * This is the good old tone generator using sin().
- * Speed = 222.5 cycles per sample.
- */
- struct gen
- {
- DATA add;
- DATA c;
- DATA vol;
- };
-
-# define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A
-# define GEN_SAMP(val,var) val = (short)(sin(var.c * 2 * M_PI) * \
- var.vol); \
- var.c += var.add
-
-# else
-
- /*
- * Default floating-point based tone generation using sine wave
- * generation from:
- * http://www.musicdsp.org/showone.php?id=10.
- * This produces good quality tone in relatively faster time than
- * the normal sin() generator.
- * Speed = 40.6 cycles per sample.
- */
- struct gen
- {
- DATA a, s0, s1;
- };
+ #define GEN_INIT(var,R,F,A) var.a = (DATA) (2.0 * sin(M_PI * F / R)); \
+ var.s0 = 0; \
+ var.s1 = (DATA)(0 - (int)A)
+ #define GEN_SAMP(val,var) var.s0 = var.s0 - var.a * var.s1; \
+ var.s1 = var.s1 + var.a * var.s0; \
+ val = (short) var.s0
+
+#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FIXED_POINT_CORDIC
+ /* Cordic algorithm with 28 bit size, from:
+ * http://www.dcs.gla.ac.uk/~jhw/cordic/
+ * Speed = 742 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
+ * (PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP=7)
+ * approx. 6.01 MIPS
+ */
+ #define CORDIC_1K 0x026DD3B6
+ #define CORDIC_HALF_PI 0x06487ED5
+ #define CORDIC_PI (CORDIC_HALF_PI * 2)
+ #define CORDIC_MUL_BITS 26
+ #define CORDIC_MUL (1 << CORDIC_MUL_BITS)
+ #define CORDIC_NTAB 28
+ #define CORDIC_LOOP PJMEDIA_TONEGEN_FIXED_POINT_CORDIC_LOOP
+
+ static int cordic_ctab [] =
+ {
+ 0x03243F6A, 0x01DAC670, 0x00FADBAF, 0x007F56EA, 0x003FEAB7,
+ 0x001FFD55, 0x000FFFAA, 0x0007FFF5, 0x0003FFFE, 0x0001FFFF,
+ 0x0000FFFF, 0x00007FFF, 0x00003FFF, 0x00001FFF, 0x00000FFF,
+ 0x000007FF, 0x000003FF, 0x000001FF, 0x000000FF, 0x0000007F,
+ 0x0000003F, 0x0000001F, 0x0000000F, 0x00000007, 0x00000003,
+ 0x00000001, 0x00000000, 0x00000000
+ };
+
+ static pj_int32_t cordic(pj_int32_t theta, unsigned n)
+ {
+ unsigned k;
+ int d;
+ pj_int32_t tx;
+ pj_int32_t x = CORDIC_1K, y = 0, z = theta;
+
+ for (k=0; k<n; ++k) {
+ #if 0
+ d = (z>=0) ? 0 : -1;
+ #else
+ /* Only slightly (~2.5%) faster, but not portable? */
+ d = z>>27;
+ #endif
+ tx = x - (((y>>k) ^ d) - d);
+ y = y + (((x>>k) ^ d) - d);
+ z = z - ((cordic_ctab[k] ^ d) - d);
+ x = tx;
+ }
+ return y;
+ }
+
+ /* Note: theta must be uint32 here */
+ static pj_int32_t cordic_sin(pj_uint32_t theta, unsigned n)
+ {
+ if (theta < CORDIC_HALF_PI)
+ return cordic(theta, n);
+ else if (theta < CORDIC_PI)
+ return cordic(CORDIC_HALF_PI-(theta-CORDIC_HALF_PI), n);
+ else if (theta < CORDIC_PI + CORDIC_HALF_PI)
+ return -cordic(theta - CORDIC_PI, n);
+ else if (theta < 2 * CORDIC_PI)
+ return -cordic(CORDIC_HALF_PI-(theta-3*CORDIC_HALF_PI), n);
+ else {
+ pj_assert(!"Invalid cordic_sin() value");
+ return 0;
+ }
+ }
-# define GEN_INIT(var,R,F,A) var.a = (DATA) (2.0 * sin(M_PI * F / R)); \
- var.s0 = A; \
- var.s1 = 0
-# define GEN_SAMP(val,var) var.s0 = var.s0 - var.a * var.s1; \
- var.s1 = var.s1 + var.a * var.s0; \
- val = (short) var.s0
-# endif
+ struct gen
+ {
+ unsigned add;
+ pj_uint32_t c;
+ unsigned vol;
+ };
-#else
+ #define VOL(var,v) (((v) * var.vol) >> 15)
+ #define GEN_INIT(var,R,F,A) gen_init(&var, R, F, A)
+ #define GEN_SAMP(val,var) val = gen_samp(&var)
+
+ static void gen_init(struct gen *var, unsigned R, unsigned F, unsigned A)
+ {
+ var->add = 2*CORDIC_PI/R * F;
+ var->c = 0;
+ var->vol = A;
+ }
+
+ PJ_INLINE(short) gen_samp(struct gen *var)
+ {
+ pj_int32_t val;
+ val = cordic_sin(var->c, CORDIC_LOOP);
+ /*val = (val * 32767) / CORDIC_MUL;
+ *val = VOL((*var), val);
+ */
+ val = ((val >> 10) * var->vol) >> 16;
+ var->c += var->add;
+ if (var->c > 2*CORDIC_PI)
+ var->c -= (2 * CORDIC_PI);
+ return (short) val;
+ }
+
+#elif PJMEDIA_TONEGEN_ALG==PJMEDIA_TONEGEN_FAST_FIXED_POINT
/*
* Fallback algorithm when floating point is disabled.
@@ -92,9 +182,8 @@
* approximation from
* http://www.audiomulch.com/~rossb/code/sinusoids/
* Quality wise not so good, but it's blazing fast!
- * Speed:
- * - with volume adjustment: 14 cycles per sample
- * - without volume adjustment: 12.22 cycles per sample
+ * Speed = 117 usec to generate 1 second, 8KHz dual-tones (2.66GHz P4).
+ * approx. 0.95 MIPS
*/
PJ_INLINE(int) approximate_sin3(unsigned x)
{
@@ -112,17 +201,15 @@
unsigned vol;
};
-# define MAXI ((unsigned)0xFFFFFFFF)
-# define SIN approximate_sin3
-# if 1 /* set this to 0 to disable volume adjustment */
-# define VOL(var,v) (((v) * var.vol) >> 15)
-# else
-# define VOL(var,v) (v)
-# endif
-# define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0, var.vol=A
-# define GEN_SAMP(val,var) val = (short) VOL(var,SIN(var.c)>>16);\
+ #define MAXI ((unsigned)0xFFFFFFFF)
+ #define SIN approximate_sin3
+ #define VOL(var,v) (((v) * var.vol) >> 15)
+ #define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0, var.vol=A
+ #define GEN_SAMP(val,var) val = (short) VOL(var,SIN(var.c)>>16); \
var.c += var.add
+#else
+ #error "PJMEDIA_TONEGEN_ALG is not set correctly"
#endif
struct gen_state