Index: pjproject/trunk/pjmedia/include/pjmedia/tonegen.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/tonegen.h	(revision 903)
+++ pjproject/trunk/pjmedia/include/pjmedia/tonegen.h	(revision 904)
@@ -99,4 +99,16 @@
 
 
+/**
+ * Tone generator options.
+ */
+enum
+{
+    /**
+     * Play the tones in loop, restarting playing the first tone after
+     * the last tone has been played.
+     */
+    PJMEDIA_TONEGEN_LOOP    = 1
+};
+
 
 /**
@@ -112,5 +124,6 @@
  * @param bits_per_sample   Number of bits per sample. This version of PJMEDIA
  *			    only supports 16bit per sample.
- * @param options	    Option flags, must be zero for now.
+ * @param options	    Option flags. Application may specify 
+ *			    PJMEDIA_TONEGEN_LOOP to play the tone in a loop.
  * @param p_port	    Pointer to receive the port instance.
  *
@@ -128,4 +141,34 @@
 
 /**
+ * Create an instance of tone generator with the specified parameters.
+ * When the tone generator is first created, it will be loaded with the
+ * default digit map.
+ *
+ * @param pool		    Pool to allocate memory for the port structure.
+ * @param name		    Optional name for the tone generator.
+ * @param clock_rate	    Sampling rate.
+ * @param channel_count	    Number of channels. Currently only mono and stereo
+ *			    are supported.
+ * @param samples_per_frame Number of samples per frame.
+ * @param bits_per_sample   Number of bits per sample. This version of PJMEDIA
+ *			    only supports 16bit per sample.
+ * @param options	    Option flags. Application may specify 
+ *			    PJMEDIA_TONEGEN_LOOP to play the tone in a loop.
+ * @param p_port	    Pointer to receive the port instance.
+ *
+ * @return		    PJ_SUCCESS on success, or the appropriate
+ *			    error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool,
+					     const pj_str_t *name,
+					     unsigned clock_rate,
+					     unsigned channel_count,
+					     unsigned samples_per_frame,
+					     unsigned bits_per_sample,
+					     unsigned options,
+					     pjmedia_port **p_port);
+
+
+/**
  * Check if the tone generator is still busy producing some tones.
  *
@@ -157,5 +200,6 @@
  * @param count		    The number of tones in the array.
  * @param tones		    Array of tones to be played.
- * @param options	    Playback options, must be zero for now.
+ * @param options	    Option flags. Application may specify 
+ *			    PJMEDIA_TONEGEN_LOOP to play the tone in a loop.
  *
  * @return		    PJ_SUCCESS on success, or PJ_ETOOMANY if
@@ -179,5 +223,6 @@
  * @param count		    Number of digits in the array.
  * @param digits	    Array of MF digits.
- * @param options	    Playback options, must be zero for now.
+ * @param options	    Option flags. Application may specify 
+ *			    PJMEDIA_TONEGEN_LOOP to play the tone in a loop.
  *
  * @return		    PJ_SUCCESS on success, or PJ_ETOOMANY if
Index: pjproject/trunk/pjmedia/src/pjmedia/tonegen.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/tonegen.c	(revision 903)
+++ pjproject/trunk/pjmedia/src/pjmedia/tonegen.c	(revision 904)
@@ -28,5 +28,5 @@
 
 /* amplitude */
-#define AMP	16383
+#define AMP	8192
 
 
@@ -234,4 +234,8 @@
     pjmedia_port	base;
 
+    /* options */
+    unsigned		options;
+    unsigned		playback_options;
+
     /* Digit map */
     pjmedia_tone_digit_map  *digit_map;
@@ -281,4 +285,43 @@
  * default digit map.
  */
+PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool,
+					    const pj_str_t *name,
+					    unsigned clock_rate,
+					    unsigned channel_count,
+					    unsigned samples_per_frame,
+					    unsigned bits_per_sample,
+					    unsigned options,
+					    pjmedia_port **p_port)
+{
+    const pj_str_t STR_TONE_GEN = pj_str("tone-gen");
+    struct tonegen  *tonegen;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && clock_rate && channel_count && 
+		     samples_per_frame && bits_per_sample == 16 && 
+		     p_port != NULL, PJ_EINVAL);
+
+    /* Only support mono and stereo */
+    PJ_ASSERT_RETURN(channel_count==1 || channel_count==2, PJ_EINVAL);
+
+    /* Create and initialize port */
+    tonegen = pj_pool_zalloc(pool, sizeof(struct tonegen));
+    if (name == NULL || name->slen == 0) name = &STR_TONE_GEN;
+    status = pjmedia_port_info_init(&tonegen->base.info, name, 
+				    SIGNATURE, clock_rate, channel_count, 
+				    bits_per_sample, samples_per_frame);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    tonegen->options = options;
+    tonegen->base.get_frame = &tonegen_get_frame;
+    tonegen->digit_map = &digit_map;
+
+    /* Done */
+    *p_port = &tonegen->base;
+    return PJ_SUCCESS;
+}
+
+
 PJ_DEF(pj_status_t) pjmedia_tonegen_create( pj_pool_t *pool,
 					    unsigned clock_rate,
@@ -289,29 +332,7 @@
 					    pjmedia_port **p_port)
 {
-    const pj_str_t STR_TONE_GEN = pj_str("tone-gen");
-    struct tonegen  *tonegen;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(pool && clock_rate && channel_count && 
-		     samples_per_frame && bits_per_sample == 16 && 
-		     options == 0 && p_port != NULL, PJ_EINVAL);
-
-    /* Only support mono and stereo */
-    PJ_ASSERT_RETURN(channel_count==1 || channel_count==2, PJ_EINVAL);
-
-    /* Create and initialize port */
-    tonegen = pj_pool_zalloc(pool, sizeof(struct tonegen));
-    status = pjmedia_port_info_init(&tonegen->base.info, &STR_TONE_GEN, 
-				    SIGNATURE, clock_rate, channel_count, 
-				    bits_per_sample, samples_per_frame);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    tonegen->base.get_frame = &tonegen_get_frame;
-    tonegen->digit_map = &digit_map;
-
-    /* Done */
-    *p_port = &tonegen->base;
-    return PJ_SUCCESS;
+    return pjmedia_tonegen_create2(pool, NULL, clock_rate, channel_count,
+				   samples_per_frame, bits_per_sample, 
+				   options, p_port);
 }
 
@@ -359,7 +380,15 @@
     if (tonegen->cur_digit > tonegen->count) {
 	/* We have played all the digits */
-	tonegen->count = 0;
-	frame->type = PJMEDIA_FRAME_TYPE_NONE;
-	return PJ_SUCCESS;
+	if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP)
+	{
+	    /* Reset back to the first tone */
+	    tonegen->cur_digit = 0;
+	    tonegen->dig_samples = 0;
+
+	} else {
+	    tonegen->count = 0;
+	    frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	    return PJ_SUCCESS;
+	}
     }
 
@@ -377,7 +406,15 @@
 	 * the digits 
 	 */
-	tonegen->count = 0;
-	frame->type = PJMEDIA_FRAME_TYPE_NONE;
-	return PJ_SUCCESS;
+	if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP)
+	{
+	    /* Reset back to the first tone */
+	    tonegen->cur_digit = 0;
+	    tonegen->dig_samples = 0;
+
+	} else {
+	    tonegen->count = 0;
+	    frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	    return PJ_SUCCESS;
+	}
     }
     
@@ -385,5 +422,5 @@
     end = dst + port->info.samples_per_frame;
 
-    while (dst < end && tonegen->cur_digit < tonegen->count) {
+    while (dst < end) {
 	const pjmedia_tone_desc *dig = &tonegen->digits[tonegen->cur_digit];
 	unsigned required, cnt, on_samp, off_samp;
@@ -426,4 +463,15 @@
 	    tonegen->cur_digit++;
 	    tonegen->dig_samples = 0;
+
+	    if (tonegen->cur_digit >= tonegen->count) {
+		/* All digits have been played */
+		if ((tonegen->options & PJMEDIA_TONEGEN_LOOP) ||
+		    (tonegen->playback_options & PJMEDIA_TONEGEN_LOOP))
+		{
+		    tonegen->cur_digit = 0;
+		} else {
+		    break;
+		}
+	    }
 	}
     }
@@ -435,6 +483,15 @@
     frame->size = port->info.bytes_per_frame;
 
-    if (tonegen->cur_digit >= tonegen->count)
-	tonegen->count = 0;
+    if (tonegen->cur_digit >= tonegen->count) {
+	if ((tonegen->options|tonegen->playback_options)&PJMEDIA_TONEGEN_LOOP)
+	{
+	    /* Reset back to the first tone */
+	    tonegen->cur_digit = 0;
+	    tonegen->dig_samples = 0;
+
+	} else {
+	    tonegen->count = 0;
+	}
+    }
 
     return PJ_SUCCESS;
@@ -454,5 +511,5 @@
 
     PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE &&
-		     count && tones && options==0, PJ_EINVAL);
+		     count && tones, PJ_EINVAL);
 
     /* Don't put more than available buffer */
@@ -460,4 +517,7 @@
 		     PJ_ETOOMANY);
 
+    /* Set playback options */
+    tonegen->playback_options = options;
+    
     /* Copy digits */
     pj_memcpy(tonegen->digits + tonegen->count,
@@ -493,5 +553,5 @@
 
     PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE &&
-		     count && digits && options==0, PJ_EINVAL);
+		     count && digits, PJ_EINVAL);
     PJ_ASSERT_RETURN(count < PJMEDIA_TONEGEN_MAX_DIGITS, PJ_ETOOMANY);
 
Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 903)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 904)
@@ -61,4 +61,7 @@
     unsigned		    wav_count;
     pj_str_t		    wav_files[32];
+    unsigned		    tone_count;
+    pjmedia_tone_desc	    tones[32];
+    pjsua_conf_port_id	    tone_slots[32];
     pjsua_player_id	    wav_id;
     pjsua_conf_port_id	    wav_port;
@@ -159,5 +162,9 @@
     puts  ("  --clock-rate=N      Override sound device clock rate");
     puts  ("  --null-audio        Use NULL audio device");
-    puts  ("  --play-file=file    Register WAV file in conference bridge");
+    puts  ("  --play-file=file    Register WAV file in conference bridge.");
+    puts  ("                      This can be specified multiple times.");
+    puts  ("  --play-tone=F1,F2,ON,OFF    Register tone to the conference bridge.");
+    puts  ("                      f1,f2=frequency, on,off=on/off duration in msec.");
+    puts  ("                      This can be specified multiple times.");
     puts  ("  --auto-play         Automatically play the file (to incoming calls only)");
     puts  ("  --auto-loop         Automatically loop incoming RTP to outgoing RTP");
@@ -310,6 +317,6 @@
 	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
 	   OPT_AUTO_CONF, OPT_CLOCK_RATE,
-	   OPT_PLAY_FILE, OPT_RTP_PORT, OPT_ADD_CODEC, OPT_ILBC_MODE,
-	   OPT_REC_FILE, OPT_AUTO_REC,
+	   OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 
+	   OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
 	   OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
 	   OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
@@ -358,4 +365,5 @@
 	{ "auto-conf",  0, 0, OPT_AUTO_CONF},
 	{ "play-file",  1, 0, OPT_PLAY_FILE},
+	{ "play-tone",  1, 0, OPT_PLAY_TONE},
 	{ "rec-file",   1, 0, OPT_REC_FILE},
 	{ "rtp-port",	1, 0, OPT_RTP_PORT},
@@ -682,4 +690,23 @@
 	    break;
 
+	case OPT_PLAY_TONE:
+	    {
+		int f1, f2, on, off;
+		int n;
+
+		n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
+		if (n != 4) {
+		    puts("Expecting f1,f2,on,off in --play-tone");
+		    return -1;
+		}
+
+		cfg->tones[cfg->tone_count].freq1 = (short)f1;
+		cfg->tones[cfg->tone_count].freq2 = (short)f2;
+		cfg->tones[cfg->tone_count].on_msec = (short)on;
+		cfg->tones[cfg->tone_count].off_msec = (short)off;
+		++cfg->tone_count;
+	    }
+	    break;
+
 	case OPT_REC_FILE:
 	    cfg->rec_file = pj_str(pj_optarg);
@@ -1135,4 +1162,10 @@
 	pj_ansi_sprintf(line, "--play-file %s\n",
 			config->wav_files[i].ptr);
+	pj_strcat2(&cfg, line);
+    }
+    for (i=0; i<config->tone_count; ++i) {
+	pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
+			config->tones[i].freq1, config->tones[i].freq2, 
+			config->tones[i].on_msec, config->tones[i].off_msec);
 	pj_strcat2(&cfg, line);
     }
@@ -2926,4 +2959,31 @@
     }
 
+    /* Optionally registers tone players */
+    for (i=0; i<app_config.tone_count; ++i) {
+	pjmedia_port *tport;
+	char name[80];
+	pj_str_t label;
+	pj_status_t status;
+
+	pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
+			 app_config.tones[i].freq1, 
+			 app_config.tones[i].freq2);
+	label = pj_str(name);
+	status = pjmedia_tonegen_create2(app_config.pool, &label,
+					 8000, 1, 160, 16, 
+					 PJMEDIA_TONEGEN_LOOP,  &tport);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
+	    goto on_error;
+	}
+
+	status = pjsua_conf_add_port(app_config.pool, tport, 
+				     &app_config.tone_slots[i]);
+	pj_assert(status == PJ_SUCCESS);
+
+	status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
+	pj_assert(status == PJ_SUCCESS);
+    }
+
     /* Optionally create recorder file, if any. */
     if (app_config.rec_file.slen) {
@@ -3057,4 +3117,5 @@
 {
     pj_status_t status;
+    unsigned i;
 
 #ifdef STEREO_DEMO
@@ -3064,4 +3125,9 @@
     }
 #endif
+
+    /* Close tone generators */
+    for (i=0; i<app_config.tone_count; ++i) {
+	pjsua_conf_remove_port(app_config.tone_slots[i]);
+    }
 
     if (app_config.pool) {
