summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-01-24 02:02:09 +0000
committerBenny Prijono <bennylp@teluu.com>2007-01-24 02:02:09 +0000
commitb7680744381c03eeaf56d600c30ba95c062cec1b (patch)
tree3ee9d55c6ddd44483c5b69c399da7d9e6c0f293f
parentf362291d6e707ba5400937d89350f98827ff6e85 (diff)
Implement ticket #62: option to play tones continuously, and added --play-tone option in pjsua
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@904 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/tonegen.h51
-rw-r--r--pjmedia/src/pjmedia/tonegen.c90
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c72
3 files changed, 192 insertions, 21 deletions
diff --git a/pjmedia/include/pjmedia/tonegen.h b/pjmedia/include/pjmedia/tonegen.h
index 21a4f063..c167e11d 100644
--- a/pjmedia/include/pjmedia/tonegen.h
+++ b/pjmedia/include/pjmedia/tonegen.h
@@ -98,6 +98,18 @@ typedef struct pjmedia_tone_digit_map
} pjmedia_tone_digit_map;
+/**
+ * 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
+};
+
/**
* Create an instance of tone generator with the specified parameters.
@@ -111,7 +123,8 @@ typedef struct pjmedia_tone_digit_map
* @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, 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.
*
* @return PJ_SUCCESS on success, or the appropriate
@@ -127,6 +140,36 @@ PJ_DECL(pj_status_t) pjmedia_tonegen_create(pj_pool_t *pool,
/**
+ * 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.
*
* @param tonegen The tone generator instance.
@@ -156,7 +199,8 @@ PJ_DECL(pj_status_t) pjmedia_tonegen_stop(pjmedia_port *tonegen);
* @param tonegen The tone generator instance.
* @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
* there are too many digits in the queue.
@@ -178,7 +222,8 @@ PJ_DECL(pj_status_t) pjmedia_tonegen_play(pjmedia_port *tonegen,
* @param tonegen The tone generator instance.
* @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
* there are too many digits in the queue, or
diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c
index 2e62ce88..e913e6aa 100644
--- a/pjmedia/src/pjmedia/tonegen.c
+++ b/pjmedia/src/pjmedia/tonegen.c
@@ -27,7 +27,7 @@
#define DATA double
/* amplitude */
-#define AMP 16383
+#define AMP 8192
#ifndef M_PI
@@ -233,6 +233,10 @@ struct tonegen
{
pjmedia_port base;
+ /* options */
+ unsigned options;
+ unsigned playback_options;
+
/* Digit map */
pjmedia_tone_digit_map *digit_map;
@@ -280,7 +284,8 @@ static pj_status_t tonegen_get_frame(pjmedia_port *this_port,
* When the tone generator is first created, it will be loaded with the
* default digit map.
*/
-PJ_DEF(pj_status_t) pjmedia_tonegen_create( pj_pool_t *pool,
+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,
@@ -294,19 +299,21 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create( pj_pool_t *pool,
PJ_ASSERT_RETURN(pool && clock_rate && channel_count &&
samples_per_frame && bits_per_sample == 16 &&
- options == 0 && p_port != NULL, PJ_EINVAL);
+ 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,
+ 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;
@@ -316,6 +323,20 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create( pj_pool_t *pool,
}
+PJ_DEF(pj_status_t) pjmedia_tonegen_create( pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned bits_per_sample,
+ unsigned options,
+ pjmedia_port **p_port)
+{
+ return pjmedia_tonegen_create2(pool, NULL, clock_rate, channel_count,
+ samples_per_frame, bits_per_sample,
+ options, p_port);
+}
+
+
/*
* Check if the tone generator is still busy producing some tones.
*/
@@ -358,9 +379,17 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
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;
+ }
}
if (tonegen->dig_samples>=(tonegen->digits[tonegen->cur_digit].on_msec+
@@ -376,15 +405,23 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
/* After we're finished with the last digit, 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;
+ }
}
dst = frame->buf;
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;
@@ -425,6 +462,17 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
if (tonegen->dig_samples == on_samp + off_samp) {
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;
+ }
+ }
}
}
@@ -434,8 +482,17 @@ static pj_status_t tonegen_get_frame(pjmedia_port *port,
frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
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;
}
@@ -453,12 +510,15 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_play( pjmedia_port *port,
unsigned i;
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 */
PJ_ASSERT_RETURN(count+tonegen->count <= PJMEDIA_TONEGEN_MAX_DIGITS,
PJ_ETOOMANY);
+ /* Set playback options */
+ tonegen->playback_options = options;
+
/* Copy digits */
pj_memcpy(tonegen->digits + tonegen->count,
tones, count * sizeof(pjmedia_tone_desc));
@@ -492,7 +552,7 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_play_digits( pjmedia_port *port,
unsigned i;
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);
for (i=0; i<count; ++i) {
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 4ce1a59e..641c32cc 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -60,6 +60,9 @@ static struct app_config
pj_bool_t null_audio;
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;
pj_bool_t auto_play;
@@ -158,7 +161,11 @@ static void usage(void)
puts (" --add-codec=name Manually add codec (default is to enable all)");
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");
puts (" --auto-conf Automatically put calls in conference with others");
@@ -309,8 +316,8 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
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,
OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
@@ -357,6 +364,7 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "auto-loop", 0, 0, OPT_AUTO_LOOP},
{ "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},
{ "add-codec", 1, 0, OPT_ADD_CODEC},
@@ -681,6 +689,25 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
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);
break;
@@ -1136,6 +1163,12 @@ static int write_settings(const struct app_config *config,
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);
+ }
if (config->rec_file.slen) {
pj_ansi_sprintf(line, "--rec-file %s\n",
config->rec_file.ptr);
@@ -2925,6 +2958,33 @@ pj_status_t app_init(int argc, char *argv[])
}
}
+ /* 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) {
status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
@@ -3056,6 +3116,7 @@ pj_status_t app_main(void)
pj_status_t app_destroy(void)
{
pj_status_t status;
+ unsigned i;
#ifdef STEREO_DEMO
if (app_config.snd) {
@@ -3064,6 +3125,11 @@ pj_status_t app_destroy(void)
}
#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) {
pj_pool_release(app_config.pool);
app_config.pool = NULL;