Index: /pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 2280)
+++ /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 2281)
@@ -542,8 +542,63 @@
  * more CPU load. This is only applied to floating point enabled machines.
  *
- * By default it is enabled.
+ * By default it is enabled when PJ_HAS_FLOATING_POINT is set.
+ *
+ * @see PJMEDIA_TONEGEN_FORCE_FLOAT
  */
 #ifndef PJMEDIA_USE_HIGH_QUALITY_TONEGEN
-#   define PJMEDIA_USE_HIGH_QUALITY_TONEGEN	    1
+#   define PJMEDIA_USE_HIGH_QUALITY_TONEGEN	    PJ_HAS_FLOATING_POINT
+#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.
+ *
+ * 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.
+ *
+ * Default: 1
+ */
+#ifndef PJMEDIA_TONEGEN_FORCE_FLOAT
+#   define PJMEDIA_TONEGEN_FORCE_FLOAT		    1
+#endif
+
+
+/**
+ * Fade-in duration for the tone, in milliseconds. Set to zero to disable
+ * this feature.
+ *
+ * Default: 1 (msec)
+ */
+#ifndef PJMEDIA_TONEGEN_FADE_IN_TIME
+#   define PJMEDIA_TONEGEN_FADE_IN_TIME		    1
+#endif
+
+
+/**
+ * Fade-out duration for the tone, in milliseconds. Set to zero to disable
+ * this feature.
+ *
+ * Default: 2 (msec)
+ */
+#ifndef PJMEDIA_TONEGEN_FADE_OUT_TIME
+#   define PJMEDIA_TONEGEN_FADE_OUT_TIME	    2
 #endif
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/tonegen.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/tonegen.c	(revision 2280)
+++ /pjproject/trunk/pjmedia/src/pjmedia/tonegen.c	(revision 2281)
@@ -39,5 +39,6 @@
 
 
-#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0
+#if (defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0) || \
+    (defined(PJMEDIA_TONEGEN_FORCE_FLOAT) && PJMEDIA_TONEGEN_FORCE_FLOAT != 0)
 #   include <math.h>
 
@@ -250,4 +251,6 @@
     unsigned		options;
     unsigned		playback_options;
+    unsigned		fade_in_len;	/* fade in for this # of samples */
+    unsigned		fade_out_len;	/* fade out for this # of samples*/
 
     /* lock */
@@ -336,4 +339,7 @@
     tonegen->digit_map = &digit_map;
 
+    tonegen->fade_in_len = PJMEDIA_TONEGEN_FADE_IN_TIME * clock_rate / 1000;
+    tonegen->fade_out_len = PJMEDIA_TONEGEN_FADE_OUT_TIME * clock_rate / 1000;
+
     /* Lock */
     if (options & PJMEDIA_TONEGEN_NO_LOCK) {
@@ -534,7 +540,41 @@
 	    generate_tone(&tonegen->state, port->info.channel_count,
 			  cnt, dst);
+
 	    dst += cnt;
 	    tonegen->dig_samples += cnt;
 	    required -= cnt;
+
+	    if (tonegen->dig_samples == cnt) {
+		/* Fade in */
+		short *samp = (dst - cnt);
+		short *end;
+
+		if (cnt > tonegen->fade_in_len)
+		    cnt = tonegen->fade_in_len;
+		end = samp + cnt;
+		if (cnt) {
+		    const unsigned step = 0xFFFF / cnt;
+		    unsigned scale = 0;
+
+		    for (; samp < end; ++samp) {
+			(*samp) = (short)(((*samp) * scale) >> 16);
+			scale += step;
+		    }
+		}
+	    } else if (tonegen->dig_samples == on_samp) {
+		/* Fade out */
+		if (cnt > tonegen->fade_out_len)
+		    cnt = tonegen->fade_out_len;
+		if (cnt) {
+		    short *samp = (dst - cnt);
+		    const unsigned step = 0xFFFF / cnt;
+		    unsigned scale = 0xFFFF - step;
+
+		    for (; samp < dst; ++samp) {
+			(*samp) = (short)(((*samp) * scale) >> 16);
+			scale -= step;
+		    }
+		}
+	    }
 
 	    if (dst == end)
