diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-09-02 23:45:18 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-09-02 23:45:18 +0000 |
commit | 0b10957f8616c1e3c9ed66442f0f880fe5ead450 (patch) | |
tree | 2ad6e04e067263e8c874be0b4ae4e68ca62f09b3 /pjmedia | |
parent | 39b4b3d3bb933da80b4f80fb4561f031456fe8d1 (diff) |
Added multi-purpose tone generator in PJMEDIA (tonegen.[hc])
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@693 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r-- | pjmedia/build/Makefile | 3 | ||||
-rw-r--r-- | pjmedia/build/pjmedia.dsp | 8 | ||||
-rw-r--r-- | pjmedia/include/pjmedia.h | 1 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/config.h | 8 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/tonegen.h | 225 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/stream.c | 15 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/tonegen.c | 529 |
7 files changed, 780 insertions, 9 deletions
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile index 14dfc5a2..8b5e9441 100644 --- a/pjmedia/build/Makefile +++ b/pjmedia/build/Makefile @@ -74,7 +74,8 @@ export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ port.o splitcomb.o resample.o \ resample_port.o rtcp.o rtp.o sdp.o sdp_cmp.o sdp_neg.o \ session.o silencedet.o sound_port.o stream.o \ - transport_udp.o wav_player.o wav_writer.o wave.o \ + tonegen.o transport_udp.o \ + wav_player.o wav_writer.o wave.o \ $(SOUND_OBJS) $(NULLSOUND_OBJS) export PJMEDIA_CFLAGS += $(_CFLAGS) diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp index 1f19648f..75f71205 100644 --- a/pjmedia/build/pjmedia.dsp +++ b/pjmedia/build/pjmedia.dsp @@ -227,6 +227,10 @@ SOURCE=..\src\pjmedia\stream.c # End Source File
# Begin Source File
+SOURCE=..\src\pjmedia\tonegen.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\transport_udp.c
# End Source File
# Begin Source File
@@ -363,6 +367,10 @@ SOURCE=..\include\pjmedia\stream.h # End Source File
# Begin Source File
+SOURCE=..\include\pjmedia\tonegen.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\pjmedia\transport.h
# End Source File
# Begin Source File
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h index 233dc889..83fded59 100644 --- a/pjmedia/include/pjmedia.h +++ b/pjmedia/include/pjmedia.h @@ -50,6 +50,7 @@ #include <pjmedia/sound.h> #include <pjmedia/sound_port.h> #include <pjmedia/splitcomb.h> +#include <pjmedia/tonegen.h> #include <pjmedia/transport.h> #include <pjmedia/transport_udp.h> #include <pjmedia/wav_port.h> diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 344ef2c2..7bd9f946 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -241,6 +241,14 @@ #endif +/** + * Maximum tones/digits that can be enqueued in the tone generator. + */ +#ifndef PJMEDIA_TONEGEN_MAX_DIGITS +# define PJMEDIA_TONEGEN_MAX_DIGITS 32 +#endif + + /** * @} diff --git a/pjmedia/include/pjmedia/tonegen.h b/pjmedia/include/pjmedia/tonegen.h new file mode 100644 index 00000000..c1d47200 --- /dev/null +++ b/pjmedia/include/pjmedia/tonegen.h @@ -0,0 +1,225 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJMEDIA_TONEGEN_PORT_H__ +#define __PJMEDIA_TONEGEN_PORT_H__ + +/** + * @file tonegen.h + * @brief Tone (sine, MF, DTMF) generator media port. + */ +#include <pjmedia/port.h> + + +/** + * @defgroup PJMEDIA_MF_DTMF_TONE_GENERATOR Tone (sine, MF, DTMF) Generator + * @ingroup PJMEDIA_PORT + * @brief Tone (sine, MF, DTMF) Generator + * @{ + * This page describes tone generator media port. A tone generator can be + * used to generate a single frequency sine wave or dual frequency tones + * such as DTMF. + * + * The tone generator media port provides two functions to generate tones. + * The function #pjmedia_tonegen_play() can be used to generate arbitrary + * single or dual frequency tone, and #pjmedia_tonegen_play_digits() is + * used to play digits such as DTMF. Each tone specified in the playback + * function has individual on and off signal duration that must be + * specified by application. + * + * In order to play digits such as DTMF, the tone generator is equipped + * with digit map, which contain information about the frequencies of + * the digits. The default digit map is DTMF (0-9,a-d,*,#), but application + * may specifiy different digit map to the tone generator by calling + * #pjmedia_tonegen_set_digit_map() function. + */ + +PJ_BEGIN_DECL + + +/** + * This structure describes individual MF digits to be played + * with #pjmedia_tonegen_play(). + */ +typedef struct pjmedia_tone_desc +{ + short freq1; /**< First frequency. */ + short freq2; /**< Optional second frequency. */ + short on_msec; /**< Playback ON duration, in miliseconds. */ + short off_msec; /**< Playback OFF duration, ini miliseconds. */ +} pjmedia_tone_desc; + + + +/** + * This structure describes individual MF digits to be played + * with #pjmedia_tonegen_play_digits(). + */ +typedef struct pjmedia_tone_digit +{ + char digit; /**< The ASCI identification for the digit. */ + short on_msec; /**< Playback ON duration, in miliseconds. */ + short off_msec; /**< Playback OFF duration, ini miliseconds. */ +} pjmedia_tone_digit; + + +/** + * This structure describes the digit map which is used by the tone generator + * to produce tones from an ASCII digits. + * Digit map used by a particular tone generator can be retrieved/set with + * #pjmedia_tonegen_get_digit_map() and #pjmedia_tonegen_load_digit_map(). + */ +typedef struct pjmedia_tone_digit_map +{ + unsigned count; /**< Number of digits in the map. */ + + struct { + char digit; /**< The ASCI identification for the digit. */ + short freq1; /**< First frequency. */ + short freq2; /**< Optional second frequency. */ + } digits[16]; +} pjmedia_tone_digit_map; + + + +/** + * 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 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, must be zero for now. + * @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_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); + + +/** + * Check if the tone generator is still busy producing some tones. + * + * @param tonegen The tone generator instance. + * + * @return Non-zero if busy. + */ +PJ_DECL(pj_bool_t) pjmedia_tonegen_is_busy(pjmedia_port *tonegen); + + +/** + * Instruct the tone generator to stop current processing. + * + * @param tonegen The tone generator instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_tonegen_stop(pjmedia_port *tonegen); + + +/** + * 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 #pjmedia_tonegen_stop() is called before calling + * this function. The playback will begin as soon as the first get_frame() + * is called to the generator. + * + * @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. + * + * @return PJ_SUCCESS on success, or PJ_ETOOMANY if + * there are too many digits in the queue. + */ +PJ_DECL(pj_status_t) pjmedia_tonegen_play(pjmedia_port *tonegen, + unsigned count, + const pjmedia_tone_desc tones[], + unsigned options); + +/** + * 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 + * #pjmedia_tonegen_stop() is called before calling this function. + * The playback will begin as soon as the first get_frame() is called + * to the generator. + * + * @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. + * + * @return PJ_SUCCESS on success, or PJ_ETOOMANY if + * there are too many digits in the queue, or + * PJMEDIA_RTP_EINDTMF if invalid digit is + * specified. + */ +PJ_DECL(pj_status_t) pjmedia_tonegen_play_digits(pjmedia_port *tonegen, + unsigned count, + const pjmedia_tone_digit digits[], + unsigned options); + + +/** + * Get the digit-map currently used by this tone generator. + * + * @param tonegen The tone generator instance. + * @param m On output, it will be filled with the pointer to + * the digitmap currently used by the tone generator. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_tonegen_get_digit_map(pjmedia_port *tonegen, + const pjmedia_tone_digit_map **m); + + +/** + * Set digit map to be used by the tone generator. + * + * @param tonegen The tone generator instance. + * @param m Digitmap to be used by the tone generator. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *tonegen, + pjmedia_tone_digit_map *m); + + +PJ_END_DECL + +/** + * @} + */ + + +#endif /* __PJMEDIA_TONEGEN_PORT_H__ */ + diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 064fd1f9..523042cf 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1273,22 +1273,21 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream, */ for (i=0; i<digit_char->slen; ++i) { unsigned pt; + int dig = pj_tolower(digit_char->ptr[i]); - if (digit_char->ptr[i] >= '0' && - digit_char->ptr[i] <= '9') + if (dig >= '0' && dig <= '9') { - pt = digit_char->ptr[i] - '0'; + pt = dig - '0'; } - else if (pj_tolower(digit_char->ptr[i]) >= 'a' && - pj_tolower(digit_char->ptr[i]) <= 'd') + else if (dig >= 'a' && dig <= 'd') { - pt = pj_tolower(digit_char->ptr[i]) - 'a' + 12; + pt = dig - 'a' + 12; } - else if (digit_char->ptr[i] == '*') + else if (dig == '*') { pt = 10; } - else if (digit_char->ptr[i] == '#') + else if (dig == '#') { pt = 11; } diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c new file mode 100644 index 00000000..aa4ce69c --- /dev/null +++ b/pjmedia/src/pjmedia/tonegen.c @@ -0,0 +1,529 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/tonegen.h> +#include <pjmedia/errno.h> +#include <pj/assert.h> +#include <pj/ctype.h> +#include <pj/pool.h> + + +/* float can be twice slower on i686! */ +#define DATA double + +/* amplitude */ +#define AMP 16383 + + +#ifndef M_PI +# define M_PI ((DATA)3.141592653589793238462643383279) +#endif + + +#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 + /* + * 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 = 32,500 cycles (to generate 100 msec single-tone). + */ +# include <math.h> + 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 = A; \ + var.s1 = 0 +# define GEN_SAMP(val,var,A) var.s0 = var.s0 - var.a * var.s1; \ + var.s1 = var.s1 + var.a * var.s0; \ + val = (short) var.s0 + + +#elif defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 + /* + * Fallback algorithm when floating point is disabled. + * This is a very fast fixed point tone generation using sine wave + * approximation from + * http://www.audiomulch.com/~rossb/code/sinusoids/ + * Quality wise not so good, but it's blazing fast! + * Speed = 9,776 cycles (to generate 100 msec single-tone). + */ + PJ_INLINE(int) approximate_sin3(unsigned x) + { + unsigned s=-(int)(x>>31); + x+=x; + x=x>>16; + x*=x^0xffff; // x=x*(2-x) + x+=x; // optional + return x^s; + } + struct gen + { + unsigned add; + unsigned c; + }; + +# define MAXI ((unsigned)0xFFFFFFFF) +# define SIN approximate_sin3 +# define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0 +# define GEN_SAMP(val,var,A) val = (short) (SIN(var.c) >> 16); \ + var.c += var.add + + +#else +# error "Should never get to this part" +# include <math.h> + /* + * Should never really reach here, but anyway it's provided for reference. + * This is the good old tone generator using sin(). + * Speed = 178,000 cycles (to generate 100 msec single-tone). + */ + struct gen + { + DATA add; + DATA c; + }; + +# define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0 +# define GEN_SAMP(val,var,A) val = (short) (sin(var.c * 2 * M_PI) * A); \ + var.c += var.add + +#endif + +struct gen_state +{ + struct gen tone1; + struct gen tone2; + pj_bool_t has_tone2; +}; + + +static void init_generate_single_tone(struct gen_state *state, + unsigned clock_rate, + unsigned freq) +{ + GEN_INIT(state->tone1,clock_rate,freq,AMP); + state->has_tone2 = PJ_FALSE; +} + +static void generate_single_tone(struct gen_state *state, + unsigned channel_count, + unsigned samples, + short buf[]) +{ + short *end = buf + samples; + + if (channel_count==1) { + + while (buf < end) { + GEN_SAMP(*buf++, state->tone1, AMP); + } + + } else if (channel_count == 2) { + + while (buf < end) { + GEN_SAMP(*buf, state->tone1, AMP); + *(buf+1) = *buf; + buf += 2; + } + } +} + + +static void init_generate_dual_tone(struct gen_state *state, + unsigned clock_rate, + unsigned freq1, + unsigned freq2) +{ + GEN_INIT(state->tone1,clock_rate,freq1,AMP); + GEN_INIT(state->tone2,clock_rate,freq2,AMP); + state->has_tone2 = PJ_TRUE; +} + + +static void generate_dual_tone(struct gen_state *state, + unsigned channel_count, + unsigned samples, + short buf[]) +{ + short *end = buf + samples; + + if (channel_count==1) { + int val, val2; + while (buf < end) { + GEN_SAMP(val, state->tone1, AMP); + GEN_SAMP(val2, state->tone2, AMP); + *buf++ = (short)((val+val2) >> 1); + } + } else if (channel_count == 2) { + int val, val2; + while (buf < end) { + + GEN_SAMP(val, state->tone1, AMP); + GEN_SAMP(val2, state->tone2, AMP); + val = (val + val2) >> 1; + + *buf++ = (short)val; + *buf++ = (short)val; + } + } +} + + +static void init_generate_tone(struct gen_state *state, + unsigned clock_rate, + unsigned freq1, + unsigned freq2) +{ + if (freq2) + init_generate_dual_tone(state, clock_rate, freq1, freq2); + else + init_generate_single_tone(state, clock_rate, freq1); +} + + +static void generate_tone(struct gen_state *state, + unsigned channel_count, + unsigned samples, + short buf[]) +{ + if (!state->has_tone2) + generate_single_tone(state, channel_count, samples, buf); + else + generate_dual_tone(state, channel_count, samples, buf); +} + + +/****************************************************************************/ + +#define SIGNATURE PJMEDIA_PORT_SIGNATURE('t', 'n', 'g', 'n') + +struct tonegen +{ + pjmedia_port base; + + /* Digit map */ + pjmedia_tone_digit_map *digit_map; + + /* Tone generation state */ + struct gen_state state; + + /* Currently played digits: */ + unsigned count; /* # of digits */ + unsigned cur_digit; /* currently played */ + unsigned dig_samples; /* sample pos in cur digit */ + pjmedia_tone_desc digits[PJMEDIA_TONEGEN_MAX_DIGITS];/* array of digits*/ +}; + + +/* Default digit map is DTMF */ +static pjmedia_tone_digit_map digit_map = +{ + 16, + { + { '0', 941, 1336 }, + { '1', 697, 1209 }, + { '2', 697, 1336 }, + { '3', 697, 1447 }, + { '4', 770, 1209 }, + { '5', 770, 1336 }, + { '6', 770, 1447 }, + { '7', 852, 1209 }, + { '8', 852, 1336 }, + { '9', 852, 1447 }, + { 'a', 697, 1633 }, + { 'b', 770, 1633 }, + { 'c', 852, 1633 }, + { 'd', 941, 1633 }, + { '*', 941, 1209 }, + { '#', 941, 1477 }, + } +}; + + +static pj_status_t tonegen_get_frame(pjmedia_port *this_port, + pjmedia_frame *frame); + +/* + * 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. + */ +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) +{ + 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; +} + + +/* + * Check if the tone generator is still busy producing some tones. + */ +PJ_DEF(pj_bool_t) pjmedia_tonegen_is_busy(pjmedia_port *port) +{ + struct tonegen *tonegen = (struct tonegen*) port; + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_TRUE); + return tonegen->count != 0; +} + + +/* + * Instruct the tone generator to stop current processing. + */ +PJ_DEF(pj_status_t) pjmedia_tonegen_stop(pjmedia_port *port) +{ + struct tonegen *tonegen = (struct tonegen*) port; + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); + return tonegen->count = 0; +} + + +/* + * Fill a frame with tones. + */ +static pj_status_t tonegen_get_frame(pjmedia_port *port, + pjmedia_frame *frame) +{ + struct tonegen *tonegen = (struct tonegen*) port; + short *dst, *end; + unsigned clock_rate = tonegen->base.info.clock_rate; + + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); + + if (tonegen->count == 0) { + /* We don't have digits to play */ + frame->type = PJMEDIA_FRAME_TYPE_NONE; + return PJ_SUCCESS; + } + + 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->dig_samples>=(tonegen->digits[tonegen->cur_digit].on_msec+ + tonegen->digits[tonegen->cur_digit].off_msec)* + clock_rate / 1000) + { + /* We have finished with current digit */ + tonegen->cur_digit++; + tonegen->dig_samples = 0; + } + + if (tonegen->cur_digit > tonegen->count) { + /* 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; + } + + dst = frame->buf; + end = dst + port->info.samples_per_frame; + + while (dst < end && tonegen->cur_digit < tonegen->count) { + const pjmedia_tone_desc *dig = &tonegen->digits[tonegen->cur_digit]; + unsigned required, cnt, on_samp, off_samp; + + required = end - dst; + on_samp = dig->on_msec * clock_rate / 1000; + off_samp = dig->off_msec * clock_rate / 1000; + + /* Init tonegen */ + if (tonegen->dig_samples == 0) { + init_generate_tone(&tonegen->state, port->info.clock_rate, + dig->freq1, dig->freq2); + } + + /* Add tone signal */ + if (tonegen->dig_samples < on_samp) { + cnt = on_samp - tonegen->dig_samples; + if (cnt > required) + cnt = required; + generate_tone(&tonegen->state, port->info.channel_count, + cnt, dst); + dst += cnt; + tonegen->dig_samples += cnt; + required -= cnt; + + if (dst == end) + break; + } + + /* Add silence signal */ + cnt = off_samp + on_samp - tonegen->dig_samples; + if (cnt > required) + cnt = required; + pjmedia_zero_samples(dst, cnt); + dst += cnt; + tonegen->dig_samples += cnt; + + /* Move to next digit if we're finished with this tone */ + if (tonegen->dig_samples == on_samp + off_samp) { + tonegen->cur_digit++; + tonegen->dig_samples = 0; + } + } + + if (dst < end) + pjmedia_zero_samples(dst, end-dst); + + frame->type = PJMEDIA_FRAME_TYPE_AUDIO; + frame->size = port->info.bytes_per_frame; + + if (tonegen->cur_digit >= tonegen->count) + tonegen->count = 0; + + return PJ_SUCCESS; +} + + +/* + * Play tones. + */ +PJ_DEF(pj_status_t) pjmedia_tonegen_play( pjmedia_port *port, + unsigned count, + const pjmedia_tone_desc tones[], + unsigned options) +{ + struct tonegen *tonegen = (struct tonegen*) port; + + PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE && + count && tones && options==0, PJ_EINVAL); + + /* Don't put more than available buffer */ + PJ_ASSERT_RETURN(count+tonegen->count <= PJMEDIA_TONEGEN_MAX_DIGITS, + PJ_ETOOMANY); + + pj_memcpy(tonegen->digits + tonegen->count, + tones, count * sizeof(pjmedia_tone_desc)); + tonegen->count += count; + + return PJ_SUCCESS; +} + + +/* + * Play digits. + */ +PJ_DEF(pj_status_t) pjmedia_tonegen_play_digits( pjmedia_port *port, + unsigned count, + const pjmedia_tone_digit digits[], + unsigned options) +{ + struct tonegen *tonegen = (struct tonegen*) port; + pjmedia_tone_desc tones[PJMEDIA_TONEGEN_MAX_DIGITS]; + const pjmedia_tone_digit_map *map = tonegen->digit_map; + unsigned i; + + PJ_ASSERT_RETURN(port && port->info.signature == SIGNATURE && + count && digits && options==0, PJ_EINVAL); + PJ_ASSERT_RETURN(count < PJMEDIA_TONEGEN_MAX_DIGITS, PJ_ETOOMANY); + + for (i=0; i<count; ++i) { + int d = pj_tolower(digits[i].digit); + unsigned j; + + /* Translate ASCII digits with digitmap */ + for (j=0; j<map->count; ++j) { + if (d == map->digits[j].digit) + break; + } + if (j == map->count) + return PJMEDIA_RTP_EINDTMF; + + tones[i].freq1 = map->digits[j].freq1; + tones[i].freq2 = map->digits[j].freq2; + tones[i].on_msec = digits[i].on_msec; + tones[i].off_msec = digits[i].off_msec; + } + + return pjmedia_tonegen_play(port, count, tones, options); +} + + +/* + * Get the digit-map currently used by this tone generator. + */ +PJ_DEF(pj_status_t) pjmedia_tonegen_get_digit_map(pjmedia_port *port, + const pjmedia_tone_digit_map **m) +{ + struct tonegen *tonegen = (struct tonegen*) port; + + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); + PJ_ASSERT_RETURN(m != NULL, PJ_EINVAL); + + *m = tonegen->digit_map; + + return PJ_SUCCESS; +} + + +/* + * Set digit map to be used by the tone generator. + */ +PJ_DEF(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *port, + pjmedia_tone_digit_map *m) +{ + struct tonegen *tonegen = (struct tonegen*) port; + + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL); + PJ_ASSERT_RETURN(m != NULL, PJ_EINVAL); + + tonegen->digit_map = m; + + return PJ_SUCCESS; +} + + |