📄 tonegen.c
字号:
/* $Id: tonegen.c 1266 2007-05-11 15:14:34Z bennylp $ */
/*
* Copyright (C) 2003-2007 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 8192
#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 = 40.6 cycles per sample.
*/
# 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) 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:
* - with volume adjustment: 14 cycles per sample
* - without volume adjustment: 12.22 cycles per sample
*/
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;
unsigned vol;
};
# define MAXI ((unsigned)0xFFFFFFFF)
# define SIN approximate_sin3
# if 1 /* set this to 0 to disable volume adjustment */
# define VOL(var,v) (((v) * var.vol) >> 16)
# else
# define VOL(var,v) (v)
# endif
# define GEN_INIT(var,R,F,A) var.add = MAXI/R * F, var.c=0, var.vol=A
# define GEN_SAMP(val,var) val = (short) VOL(var,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 = 222.5 cycles per sample.
*/
struct gen
{
DATA add;
DATA c;
DATA vol;
};
# define GEN_INIT(var,R,F,A) var.add = ((DATA)F)/R, var.c=0, var.vol=A
# define GEN_SAMP(val,var) val = (short)(sin(var.c * 2 * M_PI) * var.vol);\
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,
unsigned vol)
{
GEN_INIT(state->tone1,clock_rate,freq,vol);
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);
}
} else if (channel_count == 2) {
while (buf < end) {
GEN_SAMP(*buf, state->tone1);
*(buf+1) = *buf;
buf += 2;
}
}
}
static void init_generate_dual_tone(struct gen_state *state,
unsigned clock_rate,
unsigned freq1,
unsigned freq2,
unsigned vol)
{
GEN_INIT(state->tone1,clock_rate,freq1,vol);
GEN_INIT(state->tone2,clock_rate,freq2,vol);
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);
GEN_SAMP(val2, state->tone2);
*buf++ = (short)((val+val2) >> 1);
}
} else if (channel_count == 2) {
int val, val2;
while (buf < end) {
GEN_SAMP(val, state->tone1);
GEN_SAMP(val2, state->tone2);
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,
unsigned vol)
{
if (freq2)
init_generate_dual_tone(state, clock_rate, freq1, freq2 ,vol);
else
init_generate_single_tone(state, clock_rate, freq1,vol);
}
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;
/* options */
unsigned options;
unsigned playback_options;
/* 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_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_T(pool, struct tonegen);
if (name == NULL || name->slen == 0) name = &STR_TONE_GEN;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -