Index: pjproject/trunk/pjsip-apps/src/swig/pjsua2.i
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/pjsua2.i	(revision 4844)
+++ pjproject/trunk/pjsip-apps/src/swig/pjsua2.i	(revision 4845)
@@ -95,4 +95,7 @@
 %template(BuddyVector)			std::vector<pj::Buddy*>;
 %template(AudioMediaVector)		std::vector<pj::AudioMedia*>;
+%template(ToneDescVector)		std::vector<pj::ToneDesc>;
+%template(ToneDigitVector)		std::vector<pj::ToneDigit>;
+%template(ToneDigitMapVector)	        std::vector<pj::ToneDigitMapDigit>;
 %template(MediaFormatVector)		std::vector<pj::MediaFormat*>;
 %template(AudioDevInfoVector)		std::vector<pj::AudioDevInfo*>;
Index: pjproject/trunk/pjsip-apps/src/swig/python/test.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/python/test.py	(revision 4844)
+++ pjproject/trunk/pjsip-apps/src/swig/python/test.py	(revision 4845)
@@ -1,4 +1,5 @@
 import pjsua2 as pj
 import sys
+import time
 
 #
@@ -101,4 +102,59 @@
 
 #
+# Tone generator
+#
+def ua_tonegen_test():
+        print "UA tonegen test.."
+        ep_cfg = pj.EpConfig()
+
+        ep = pj.Endpoint()
+        ep.libCreate()
+        ep.libInit(ep_cfg)
+        ep.libStart()
+        
+        tonegen = pj.ToneGenerator()
+        tonegen.createToneGenerator()
+
+        tone = pj.ToneDesc()
+        tone.freq1 = 400
+        tone.freq2 = 600
+        tone.on_msec = 1000
+        tone.off_msec = 1000
+        tones = pj.ToneDescVector()
+        tones.append(tone)
+
+        digit = pj.ToneDigit()
+        digit.digit = '0'
+        digit.on_msec = 1000
+        digit.off_msec = 1000
+        digits = pj.ToneDigitVector()
+        digits.append(digit)
+
+        adm = ep.audDevManager()
+        spk = adm.getPlaybackDevMedia()
+
+        tonegen.play(tones, True)
+        tonegen.startTransmit(spk)
+        time.sleep(5)
+
+	tonegen.stop()
+        tonegen.playDigits(digits, True)
+        time.sleep(5)
+
+	dm = tonegen.getDigitMap()
+	print dm[0].digit
+	dm[0].freq1 = 400
+	dm[0].freq2 = 600
+	tonegen.setDigitMap(dm)
+	
+	tonegen.stop()
+        tonegen.playDigits(digits, True)
+        time.sleep(5)
+	
+        tonegen = None
+
+        ep.libDestroy()
+
+#
 # main()
 #
@@ -108,4 +164,5 @@
 	ua_run_log_test()
 	ua_run_ua_test()
+	ua_tonegen_test()
 	sys.exit(0)
 
Index: pjproject/trunk/pjsip-apps/src/swig/symbols.i
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 4844)
+++ pjproject/trunk/pjsip-apps/src/swig/symbols.i	(revision 4845)
@@ -1,3 +1,4 @@
 // This file is autogenerated by importsym script, do not modify!
+
 typedef int pj_status_t;
 
@@ -30,5 +31,5 @@
 } pj_qos_params;
 
-typedef enum pj_ssl_cipher {PJ_TLS_NULL_WITH_NULL_NULL = 0x00000000, PJ_TLS_RSA_WITH_NULL_MD5 = 0x00000001, PJ_TLS_RSA_WITH_NULL_SHA = 0x00000002, PJ_TLS_RSA_WITH_NULL_SHA256 = 0x0000003B, PJ_TLS_RSA_WITH_RC4_128_MD5 = 0x00000004, PJ_TLS_RSA_WITH_RC4_128_SHA = 0x00000005, PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A, PJ_TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F, PJ_TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035, PJ_TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C, PJ_TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D, PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D, PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010, PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013, PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B, PJ_TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018, PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, PJ_TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008, PJ_TLS_RSA_WITH_DES_CBC_SHA = 0x00000009, PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B, PJ_TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C, PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E, PJ_TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F, PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011, PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012, PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014, PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015, PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017, PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019, PJ_TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A, PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C, PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x0000001D, PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E, PJ_SSL_CK_RC4_128_WITH_MD5 = 0x00010080, PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080, PJ_SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080, PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080, PJ_SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080, PJ_SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040, PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0} pj_ssl_cipher;
+typedef enum pj_ssl_cipher {PJ_TLS_UNKNOWN_CIPHER = -1, PJ_TLS_NULL_WITH_NULL_NULL = 0x00000000, PJ_TLS_RSA_WITH_NULL_MD5 = 0x00000001, PJ_TLS_RSA_WITH_NULL_SHA = 0x00000002, PJ_TLS_RSA_WITH_NULL_SHA256 = 0x0000003B, PJ_TLS_RSA_WITH_RC4_128_MD5 = 0x00000004, PJ_TLS_RSA_WITH_RC4_128_SHA = 0x00000005, PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A, PJ_TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F, PJ_TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035, PJ_TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C, PJ_TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D, PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D, PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010, PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013, PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039, PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E, PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F, PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040, PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067, PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068, PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069, PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A, PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B, PJ_TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018, PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, PJ_TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008, PJ_TLS_RSA_WITH_DES_CBC_SHA = 0x00000009, PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B, PJ_TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C, PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E, PJ_TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F, PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011, PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012, PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014, PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015, PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017, PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019, PJ_TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A, PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C, PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x0000001D, PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E, PJ_SSL_CK_RC4_128_WITH_MD5 = 0x00010080, PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080, PJ_SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080, PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080, PJ_SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080, PJ_SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040, PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0} pj_ssl_cipher;
 
 typedef enum pj_stun_nat_type {PJ_STUN_NAT_TYPE_UNKNOWN, PJ_STUN_NAT_TYPE_ERR_UNKNOWN, PJ_STUN_NAT_TYPE_OPEN, PJ_STUN_NAT_TYPE_BLOCKED, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP, PJ_STUN_NAT_TYPE_FULL_CONE, PJ_STUN_NAT_TYPE_SYMMETRIC, PJ_STUN_NAT_TYPE_RESTRICTED, PJ_STUN_NAT_TYPE_PORT_RESTRICTED} pj_stun_nat_type;
@@ -54,4 +55,33 @@
 enum pjmedia_file_player_option {PJMEDIA_FILE_NO_LOOP = 1};
 
+typedef struct pjmedia_tone_digit
+{
+  char digit;
+  short on_msec;
+  short off_msec;
+  short volume;
+} pjmedia_tone_digit;
+
+typedef struct pjmedia_tone_digit_map
+{
+  unsigned count;
+  struct 
+  {
+    char digit;
+    short freq1;
+    short freq2;
+  } digits[16];
+} pjmedia_tone_digit_map;
+
+typedef struct pjmedia_tone_desc
+{
+  short freq1;
+  short freq2;
+  short on_msec;
+  short off_msec;
+  short volume;
+  short flags;
+} pjmedia_tone_desc;
+
 typedef enum pjmedia_type {PJMEDIA_TYPE_NONE, PJMEDIA_TYPE_AUDIO, PJMEDIA_TYPE_VIDEO, PJMEDIA_TYPE_APPLICATION, PJMEDIA_TYPE_UNKNOWN} pjmedia_type;
 
@@ -76,5 +106,5 @@
 enum pjsip_transport_flags_e {PJSIP_TRANSPORT_RELIABLE = 1, PJSIP_TRANSPORT_SECURE = 2, PJSIP_TRANSPORT_DATAGRAM = 4};
 
-typedef enum pjsip_transport_state {PJSIP_TP_STATE_CONNECTED, PJSIP_TP_STATE_DISCONNECTED} pjsip_transport_state;
+typedef enum pjsip_transport_state {PJSIP_TP_STATE_CONNECTED, PJSIP_TP_STATE_DISCONNECTED, PJSIP_TP_STATE_SHUTDOWN, PJSIP_TP_STATE_DESTROY} pjsip_transport_state;
 
 typedef enum pjsip_ssl_method {PJSIP_SSL_UNSPECIFIED_METHOD = 0, PJSIP_TLSV1_METHOD = 31, PJSIP_SSLV2_METHOD = 20, PJSIP_SSLV3_METHOD = 30, PJSIP_SSLV23_METHOD = 23} pjsip_ssl_method;
Index: pjproject/trunk/pjsip-apps/src/swig/symbols.lst
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/symbols.lst	(revision 4844)
+++ pjproject/trunk/pjsip-apps/src/swig/symbols.lst	(revision 4845)
@@ -14,4 +14,5 @@
 pjmedia-audiodev/audiodev.h 	pjmedia_aud_dev_route pjmedia_aud_dev_cap
 pjmedia/wav_port.h              pjmedia_file_writer_option pjmedia_file_player_option
+pjmedia/tonegen.h		pjmedia_tone_digit pjmedia_tone_digit_map pjmedia_tone_desc
 pjmedia/types.h                 pjmedia_type pjmedia_dir pjmedia_tp_proto
 pjmedia/format.h		pjmedia_format_id
Index: pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 4844)
+++ pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 4845)
@@ -512,4 +512,143 @@
 };
 
+/**
+ * Tone descriptor (abstraction for pjmedia_tone_desc)
+ */
+class ToneDesc : public pjmedia_tone_desc
+{
+public:
+    ToneDesc()
+    {
+	pj_bzero(this, sizeof(*this));
+    }
+    ~ToneDesc() {}
+};
+
+/**
+ * Array of tone descriptor.
+ */
+typedef std::vector<ToneDesc> ToneDescVector;
+
+/**
+ * Tone digit (abstraction for pjmedia_tone_digit)
+ */
+class ToneDigit : public pjmedia_tone_digit
+{
+public:
+    ToneDigit()
+    {
+	pj_bzero(this, sizeof(*this));
+    }
+    ~ToneDigit() {}
+};
+
+/**
+ * Array of tone digits.
+ */
+typedef std::vector<ToneDigit> ToneDigitVector;
+
+/**
+ * A digit in tone digit map
+ */
+struct ToneDigitMapDigit
+{
+public:
+    string	digit;
+    int		freq1;
+    int		freq2;
+};
+
+/**
+ * Tone digit map
+ */
+typedef std::vector<ToneDigitMapDigit> ToneDigitMapVector;
+
+/**
+ * Tone generator.
+ */
+class ToneGenerator : public AudioMedia
+{
+public:
+    /**
+     * Constructor.
+     */
+    ToneGenerator();
+
+    /**
+     * Destructor.
+     */
+    ~ToneGenerator();
+
+    /**
+     * Create tone generator.
+     */
+    void createToneGenerator(unsigned clock_rate = 16000,
+			     unsigned channel_count = 1) throw(Error);
+
+    /**
+     * Check if the tone generator is still busy producing some tones.
+     * @return		    Non-zero if busy.
+     */
+    bool isBusy() const;
+
+    /**
+     * Instruct the tone generator to stop current processing.
+     */
+    void stop() throw(Error);
+
+    /**
+     * Rewind the playback. This will start the playback to the first
+     * tone in the playback list.
+     */
+    void rewind() throw(Error);
+
+    /**
+     * Instruct the tone generator to play single or dual frequency tones
+     * with the specified duration. The new tones will be appended to
+     * currently playing tones, unless stop() is called before calling this
+     * function. The playback will begin as soon as the tone generator is
+     * connected to other media.
+     *
+     * @param tones	    Array of tones to be played.
+     * @param loop	    Play the tone in a loop.
+     */
+    void play(const ToneDescVector &tones,
+              bool loop=false) throw(Error);
+
+    /**
+     * Instruct the tone generator to play multiple MF digits with each of
+     * the digits having individual ON/OFF duration. Each of the digit in the
+     * digit array must have the corresponding descriptor in the digit map.
+     * The new tones will be appended to currently playing tones, unless
+     * stop() is called before calling this function. The playback will begin
+     * as soon as the tone generator is connected to a sink media.
+     *
+     * @param digits	    Array of MF digits.
+     * @param loop	    Play the tone in a loop.
+     */
+    void playDigits(const ToneDigitVector &digits,
+                    bool loop=false) throw(Error);
+
+    /**
+     * Get the digit-map currently used by this tone generator.
+     *
+     * @return		    The digitmap currently used by the tone generator
+     */
+    ToneDigitMapVector getDigitMap() const throw(Error);
+
+    /**
+     * Set digit map to be used by the tone generator.
+     *
+     * @param digit_map	    Digitmap to be used by the tone generator.
+     */
+    void setDigitMap(const ToneDigitMapVector &digit_map) throw(Error);
+
+private:
+    pj_pool_t *pool;
+    pjmedia_port *tonegen;
+    pjmedia_tone_digit_map digitMap;
+};
+
+
 /*************************************************************************
 * Sound device management
Index: pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 4844)
+++ pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 4845)
@@ -424,4 +424,167 @@
 
 ///////////////////////////////////////////////////////////////////////////////
+
+ToneGenerator::ToneGenerator()
+: pool(NULL), tonegen(NULL)
+{
+}
+
+ToneGenerator::~ToneGenerator()
+{
+    if (tonegen) {
+	unregisterMediaPort();
+	pjmedia_port_destroy(tonegen);
+	tonegen = NULL;
+    }
+    if (pool) {
+	pj_pool_release(pool);
+	pool = NULL;
+    }
+}
+
+void ToneGenerator::createToneGenerator(unsigned clock_rate,
+					unsigned channel_count) throw(Error)
+{
+    pj_status_t status;
+
+    if (pool) {
+	PJSUA2_RAISE_ERROR(PJ_EEXISTS);
+    }
+
+    pool = pjsua_pool_create( "tonegen%p", 512, 512);
+    if (!pool) {
+	PJSUA2_RAISE_ERROR(PJ_ENOMEM);
+    }
+
+    status = pjmedia_tonegen_create( pool, clock_rate, channel_count,
+				     clock_rate * 20 / 1000, 16,
+				     0, &tonegen);
+    if (status != PJ_SUCCESS) {
+	PJSUA2_RAISE_ERROR(status);
+    }
+
+    registerMediaPort(tonegen);
+}
+
+bool ToneGenerator::isBusy() const
+{
+    return tonegen && pjmedia_tonegen_is_busy(tonegen) != 0;
+}
+
+void ToneGenerator::stop() throw(Error)
+{
+    pj_status_t status;
+
+    if (!tonegen) {
+	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+    }
+
+    status = pjmedia_tonegen_stop(tonegen);
+    PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::stop()");
+}
+
+void ToneGenerator::rewind() throw(Error)
+{
+    pj_status_t status;
+
+    if (!tonegen) {
+	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+    }
+
+    status = pjmedia_tonegen_rewind(tonegen);
+    PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::rewind()");
+}
+
+void ToneGenerator::play(const ToneDescVector &tones,
+                         bool loop) throw(Error)
+{
+    pj_status_t status;
+
+    if (!tonegen) {
+	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+    }
+    if (tones.size() == 0) {
+	PJSUA2_RAISE_ERROR(PJ_EINVAL);
+    }
+
+    status = pjmedia_tonegen_play(tonegen, tones.size(), &tones[0],
+				  loop? PJMEDIA_TONEGEN_LOOP : 0);
+    PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::play()");
+}
+
+void ToneGenerator::playDigits(const ToneDigitVector &digits,
+                               bool loop) throw(Error)
+{
+    pj_status_t status;
+
+    if (!tonegen) {
+	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+    }
+    if (digits.size() == 0) {
+	PJSUA2_RAISE_ERROR(PJ_EINVAL);
+    }
+
+    status = pjmedia_tonegen_play_digits(tonegen, digits.size(), &digits[0],
+					 loop? PJMEDIA_TONEGEN_LOOP : 0);
+    PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::playDigits()");
+}
+
+ToneDigitMapVector ToneGenerator::getDigitMap() const throw(Error)
+{
+    const pjmedia_tone_digit_map *pdm;
+    ToneDigitMapVector tdm;
+    unsigned i;
+    pj_status_t status;
+
+    if (!tonegen) {
+	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+    }
+
+    status = pjmedia_tonegen_get_digit_map(tonegen, &pdm);
+    PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::getDigitMap()");
+
+    for (i=0; i<pdm->count; ++i) {
+	ToneDigitMapDigit d;
+	char str_digit[2];
+
+	str_digit[0] = pdm->digits[i].digit;
+	str_digit[1] = '\0';
+
+	d.digit = str_digit;
+	d.freq1 = pdm->digits[i].freq1;
+	d.freq2 = pdm->digits[i].freq2;
+
+	tdm.push_back(d);
+    }
+
+    return tdm;
+}
+
+void ToneGenerator::setDigitMap(const ToneDigitMapVector &digit_map)
+				throw(Error)
+{
+    unsigned i;
+    pj_status_t status;
+
+    if (!tonegen) {
+	PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+    }
+
+    digitMap.count = digit_map.size();
+    if (digitMap.count > PJ_ARRAY_SIZE(digitMap.digits))
+	digitMap.count = PJ_ARRAY_SIZE(digitMap.digits);
+
+    for (i=0; i<digitMap.count; ++i) {
+	digitMap.digits[i].digit = digit_map[i].digit.c_str()[0];
+	digitMap.digits[i].freq1 = digit_map[i].freq1;
+	digitMap.digits[i].freq2 = digit_map[i].freq2;
+    }
+
+    status = pjmedia_tonegen_set_digit_map(tonegen, &digitMap);
+    PJSUA2_CHECK_RAISE_ERROR2(status, "ToneGenerator::setDigitMap()");
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
 void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info)
 {
