yate/modules/tonegen.cpp

789 lines
18 KiB
C++

/**
* tonegen.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Tones generator
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2006 Null Team
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <yatephone.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// 40ms silence, 120ms tone, 40ms silence, total 200ms - slow but safe
#define DTMF_LEN 960
#define DTMF_GAP 320
using namespace TelEngine;
namespace { // anonymous
static ObjList tones;
static ObjList datas;
typedef struct {
int nsamples;
const short* data;
} Tone;
typedef struct {
const Tone* tone;
const char* name;
const char* alias;
} ToneDesc;
class ToneData : public GenObject
{
public:
ToneData(const char* desc);
inline ToneData(int f1, int f2 = 0, bool modulated = false)
: m_f1(f1), m_f2(f2), m_mod(modulated), m_data(0)
{ }
inline ToneData(const ToneData& original)
: m_f1(original.f1()), m_f2(original.f2()),
m_mod(original.modulated()), m_data(0)
{ }
virtual ~ToneData();
inline int f1() const
{ return m_f1; }
inline int f2() const
{ return m_f2; }
inline bool modulated() const
{ return m_mod; }
inline bool valid() const
{ return m_f1 != 0; }
inline bool equals(int f1, int f2) const
{ return (m_f1 == f1) && (m_f2 == f2); }
inline bool equals(const ToneData& other) const
{ return (m_f1 == other.f1()) && (m_f2 == other.f2()); }
const short* data();
static ToneData* getData(const char* desc);
private:
bool parse(const char* desc);
int m_f1;
int m_f2;
bool m_mod;
const short* m_data;
};
class ToneSource : public ThreadedSource
{
public:
virtual ~ToneSource();
virtual void run();
inline const String& name()
{ return m_name; }
bool startup();
static ToneSource* getTone(String& tone);
static const Tone* getBlock(String& tone);
static Tone* buildCadence(const String& desc);
static Tone* buildDtmf(const String& dtmf, int len = DTMF_LEN, int gap = DTMF_GAP);
protected:
ToneSource();
ToneSource(String& tone);
String m_name;
const Tone* m_tone;
int m_repeat;
private:
DataBlock m_data;
unsigned m_brate;
unsigned m_total;
u_int64_t m_time;
};
class TempSource : public ToneSource
{
public:
TempSource(String& desc);
virtual ~TempSource();
protected:
virtual void cleanup();
private:
Tone* m_single;
};
class ToneChan : public Channel
{
public:
ToneChan(String& tone);
~ToneChan();
};
class AttachHandler : public MessageHandler
{
public:
AttachHandler() : MessageHandler("chan.attach") { }
virtual bool received(Message& msg);
};
class ToneGenDriver : public Driver
{
public:
ToneGenDriver();
~ToneGenDriver();
virtual void initialize();
virtual bool msgExecute(Message& msg, String& dest);
protected:
void statusModule(String& str);
void statusParams(String& str);
private:
AttachHandler* m_handler;
};
INIT_PLUGIN(ToneGenDriver);
// 421.052Hz (19 samples @ 8kHz) sine wave, pretty close to standard 425Hz
static const short tone421hz[] = {
19,
3246,6142,8371,9694,9965,9157,7357,4759,1645,
-1645,-4759,-7357,-9157,-9965,-9694,-8371,-6142,-3246,
0 };
// 1000Hz (8 samples @ 8kHz) standard digital milliwatt
static const short tone1000hz[] = {
8,
8828, 20860, 20860, 8828,
-8828, -20860, -20860, -8828
};
static const Tone t_dial[] = { { 8000, tone421hz }, { 0, 0 } };
static const Tone t_busy[] = { { 4000, tone421hz }, { 4000, 0 }, { 0, 0 } };
static const Tone t_specdial[] = { { 7600, tone421hz }, { 400, 0 }, { 0, 0 } };
static const Tone t_ring[] = { { 8000, tone421hz }, { 32000, 0 }, { 0, 0 } };
static const Tone t_congestion[] = { { 2000, tone421hz }, { 2000, 0 }, { 0, 0 } };
static const Tone t_outoforder[] = {
{ 800, tone421hz }, { 800, 0 },
{ 800, tone421hz }, { 800, 0 },
{ 800, tone421hz }, { 800, 0 },
{ 1600, tone421hz }, { 1600, 0 },
{ 0, 0 } };
static const Tone t_mwatt[] = { { 8000, tone1000hz }, { 0, 0 } };
static const Tone t_silence[] = { { 8000, 0 }, { 0, 0 } };
static const Tone t_noise[] = { { 2000, ToneData::getData("noise")->data() }, { 0, 0 } };
#define MAKE_DTMF(s) { \
{ DTMF_GAP, 0 }, \
{ DTMF_LEN, ToneData::getData(s)->data() }, \
{ DTMF_GAP, 0 }, \
{ 0, 0 } \
}
static const Tone t_dtmf[][4] = {
MAKE_DTMF("1336+941"),
MAKE_DTMF("1209+697"),
MAKE_DTMF("1336+697"),
MAKE_DTMF("1477+697"),
MAKE_DTMF("1209+770"),
MAKE_DTMF("1336+770"),
MAKE_DTMF("1477+770"),
MAKE_DTMF("1209+852"),
MAKE_DTMF("1336+852"),
MAKE_DTMF("1477+852"),
MAKE_DTMF("1209+941"),
MAKE_DTMF("1477+941"),
MAKE_DTMF("1633+697"),
MAKE_DTMF("1633+770"),
MAKE_DTMF("1633+852"),
MAKE_DTMF("1633+941")
};
#undef MAKE_DTMF
#define MAKE_PROBE(s) { \
{ 8000, ToneData::getData(s)->data() }, \
{ 0, 0 } \
}
static const Tone t_probes[][2] = {
MAKE_PROBE("2000+125"),
MAKE_PROBE("2000*125"),
MAKE_PROBE("2000*1000"),
};
#undef MAKE_PROBE
static const ToneDesc s_desc[] = {
{ t_dial, "dial", "dt" },
{ t_busy, "busy", "bs" },
{ t_ring, "ring", "rt" },
{ t_specdial, "specdial", "sd" },
{ t_congestion, "congestion", "cg" },
{ t_outoforder, "outoforder", "oo" },
{ t_mwatt, "milliwatt", "mw" },
{ t_silence, "silence", 0 },
{ t_noise, "noise", "cn" },
{ t_dtmf[0], "dtmf/0", "0" },
{ t_dtmf[1], "dtmf/1", "1" },
{ t_dtmf[2], "dtmf/2", "2" },
{ t_dtmf[3], "dtmf/3", "3" },
{ t_dtmf[4], "dtmf/4", "4" },
{ t_dtmf[5], "dtmf/5", "5" },
{ t_dtmf[6], "dtmf/6", "6" },
{ t_dtmf[7], "dtmf/7", "7" },
{ t_dtmf[8], "dtmf/8", "8" },
{ t_dtmf[9], "dtmf/9", "9" },
{ t_dtmf[10], "dtmf/*", "*" },
{ t_dtmf[11], "dtmf/#", "#" },
{ t_dtmf[12], "dtmf/a", "a" },
{ t_dtmf[13], "dtmf/b", "b" },
{ t_dtmf[14], "dtmf/c", "c" },
{ t_dtmf[15], "dtmf/d", "d" },
{ t_probes[0], "probe/0", "probe" },
{ t_probes[1], "probe/1", 0 },
{ t_probes[2], "probe/2", 0 },
{ 0, 0, 0 }
};
ToneData::ToneData(const char* desc)
: m_f1(0), m_f2(0), m_mod(false), m_data(0)
{
if (!parse(desc)) {
Debug(&__plugin,DebugWarn,"Invalid tone description '%s'",desc);
m_f1 = m_f2 = 0;
m_mod = false;
}
}
ToneData::~ToneData()
{
if (m_data) {
::free((void*)m_data);
m_data = 0;
}
}
// a tone data description is something like "425" or "350+440" or "15*2100"
bool ToneData::parse(const char* desc)
{
if (!desc)
return false;
String tmp(desc);
if (tmp == "noise") {
m_f1 = -10;
return true;
}
tmp >> m_f1;
if (!m_f1)
return false;
if (m_f1 < -15)
m_f1 = -15;
if (tmp) {
char sep;
tmp >> sep;
switch (sep) {
case '+':
break;
case '*':
m_mod = true;
break;
default:
return false;
}
tmp >> m_f2;
if (!m_f2)
return false;
// order components so we can compare correctly
if (m_f1 < m_f2) {
int t = m_f1;
m_f1 = m_f2;
m_f2 = t;
}
}
return true;
}
const short* ToneData::data()
{
if (m_f1 && !m_data) {
// generate the data on first call
short len = 8000;
if (m_f1 < 0) {
Debug(&__plugin,DebugAll,"Building comfort noise at level %d",m_f1);
// we don't need much memory for noise...
len /= 8;
}
else if (m_f2)
Debug(&__plugin,DebugAll,"Building tone of %d %s %d Hz",
m_f1,(m_mod ? "modulated by" : "+"),m_f2);
else {
Debug(&__plugin,DebugAll,"Building tone of %d Hz",m_f1);
// half the buffer for even frequencies
if ((m_f1 & 1) == 0)
len /= 2;
}
short* dat = (short*)::malloc((len+1)*sizeof(short));
if (!dat) {
Debug(&__plugin,DebugGoOn,"ToneData::data() cold not allocate memory for %d elements",len);
return 0;
}
short* tmp = dat;
*tmp++ = len;
if (m_f1 < 0) {
int ofs = 65535 >> (-m_f1);
int max = 2 * ofs + 1;
for (int x = 0; x < len; x++)
*tmp++ = (short)((::random() % max) - ofs);
}
else {
double samp = 2*M_PI/8000;
for (int x = 0; x < len; x++) {
double y = ::sin(x*samp*m_f1);
if (m_f2) {
double z = ::sin(x*samp*m_f2);
if (m_mod)
y *= (1+0.5*z);
else
y += z;
}
*tmp++ = (short)(y*5000);
}
}
m_data = dat;
}
return m_data;
}
ToneData* ToneData::getData(const char* desc)
{
ToneData td(desc);
if (!td.valid())
return 0;
ObjList* l = &datas;
for (; l; l = l->next()) {
ToneData* d = static_cast<ToneData*>(l->get());
if (d && d->equals(td))
return d;
}
ToneData* d = new ToneData(td);
datas.append(d);
return d;
}
ToneSource::ToneSource()
: m_tone(0), m_repeat(1),
m_data(0,320), m_brate(16000), m_total(0), m_time(0)
{
Debug(&__plugin,DebugAll,"ToneSource::ToneSource() [%p]",this);
}
ToneSource::ToneSource(String& tone)
: m_name(tone), m_tone(0), m_repeat(0),
m_data(0,320), m_brate(16000), m_total(0), m_time(0)
{
Debug(&__plugin,DebugAll,"ToneSource::ToneSource(\"%s\") [%p]",tone.c_str(),this);
m_tone = getBlock(tone);
if (!m_tone)
Debug(DebugWarn,"No waveform is defined for tone '%s'",tone.c_str());
}
ToneSource::~ToneSource()
{
Lock lock(__plugin);
Debug(&__plugin,DebugAll,"ToneSource::~ToneSource() [%p] total=%u stamp=%lu",this,m_total,timeStamp());
tones.remove(this,false);
stop();
if (m_time) {
m_time = Time::now() - m_time;
if (m_time) {
m_time = (m_total*(u_int64_t)1000000 + m_time/2) / m_time;
Debug(&__plugin,DebugInfo,"ToneSource rate=" FMT64U " b/s",m_time);
}
}
}
bool ToneSource::startup()
{
return m_tone && start("ToneSource");
}
const Tone* ToneSource::getBlock(String& tone)
{
if (tone.trimBlanks().toLower().null())
return 0;
const ToneDesc* d = s_desc;
for (; d->tone; d++) {
if (tone == d->name)
return d->tone;
if (d->alias && (tone == d->alias)) {
tone = d->name;
return d->tone;
}
}
return 0;
}
// Build an user defined cadence
Tone* ToneSource::buildCadence(const String& desc)
{
// TBD
return 0;
}
// Build a cadence out of DTMFs
Tone* ToneSource::buildDtmf(const String& dtmf, int len, int gap)
{
if (dtmf.null())
return 0;
Tone* tmp = (Tone*)::malloc(2*sizeof(Tone)*(dtmf.length()+1));
if (!tmp)
return 0;
Tone* t = tmp;
for (unsigned int i = 0; i < dtmf.length(); i++) {
t->nsamples = gap;
t->data = 0;
t++;
int c = dtmf.at(i);
if ((c >= '0') && (c <= '9'))
c -= '0';
else if (c == '*')
c = 10;
else if (c == '#')
c = 11;
else if ((c >= 'a') && (c <= 'd'))
c -= ('a' - 12);
else c = -1;
t->nsamples = len;
t->data = ((c >= 0) && (c < 16)) ? t_dtmf[c][1].data : 0;
t++;
}
t->nsamples = gap;
t->data = 0;
t++;
t->nsamples = 0;
t->data = 0;
return tmp;
}
ToneSource* ToneSource::getTone(String& tone)
{
ObjList* l = &tones;
for (; l; l = l->next()) {
ToneSource* t = static_cast<ToneSource*>(l->get());
if (t && (t->name() == tone)) {
t->ref();
return t;
}
}
ToneSource* t = new ToneSource(tone);
tones.append(t);
t->startup();
return t;
}
void ToneSource::run()
{
Debug(&__plugin,DebugAll,"ToneSource::run() [%p]",this);
u_int64_t tpos = Time::now();
m_time = tpos;
int samp = 0; // sample number
int dpos = 1; // position in data
const Tone* tone = m_tone;
int nsam = tone->nsamples;
if (nsam < 0)
nsam = -nsam;
while (alive() && m_tone) {
Thread::check();
short *d = (short *) m_data.data();
for (unsigned int i = m_data.length()/2; i--; samp++,dpos++) {
if (samp >= nsam) {
// go to the start of the next tone
samp = 0;
const Tone *otone = tone;
tone++;
if (!tone->nsamples) {
if ((m_repeat > 0) && !(--m_repeat))
m_tone = 0;
tone = m_tone;
}
nsam = tone ? tone->nsamples : 32000;
if (nsam < 0) {
nsam = -nsam;
// reset repeat point here
m_tone = tone;
}
if (tone != otone)
dpos = 1;
}
if (tone && tone->data) {
if (dpos > tone->data[0])
dpos = 1;
*d++ = tone->data[dpos];
}
else
*d++ = 0;
}
int64_t dly = tpos - Time::now();
if (dly > 0) {
XDebug(&__plugin,DebugAll,"ToneSource sleeping for " FMT64 " usec",dly);
Thread::usleep((unsigned long)dly);
}
Forward(m_data,m_total/2);
m_total += m_data.length();
tpos += (m_data.length()*(u_int64_t)1000000/m_brate);
}
m_time = Time::now() - m_time;
m_time = (m_total*(u_int64_t)1000000 + m_time/2) / m_time;
Debug(&__plugin,DebugAll,"ToneSource [%p] end, total=%u (" FMT64U " b/s)",this,m_total,m_time);
m_time = 0;
}
TempSource::TempSource(String& desc)
: m_single(0)
{
Debug(&__plugin,DebugAll,"TempSource::TempSource(\"%s\") [%p]",desc.c_str(),this);
if (desc.null())
return;
if (desc.startSkip("*",false))
m_repeat = 0;
// try first the named tones
m_tone = getBlock(desc);
if (m_tone)
return;
// for performance reason accept an entire string of DTMFs
if (desc.startSkip("dtmfstr/",false)) {
m_tone = m_single = buildDtmf(desc);
return;
}
// or an entire user defined cadence of tones
if (desc.startSkip("cadence/",false)) {
m_tone = m_single = buildCadence(desc);
return;
}
// now try to build a single tone
ToneData* td = ToneData::getData(desc);
if (!td)
return;
m_single = (Tone*)::malloc(2*sizeof(Tone));
m_single[0].nsamples = 8000;
m_single[0].data = td->data();
m_single[1].nsamples = 0;
m_single[1].data = 0;
m_tone = m_single;
}
TempSource::~TempSource()
{
Debug(&__plugin,DebugAll,"TempSource::~TempSource() [%p]",this);
if (m_single) {
::free(m_single);
m_single = 0;
}
}
void TempSource::cleanup()
{
ToneSource::cleanup();
deref();
}
ToneChan::ToneChan(String& tone)
: Channel(__plugin)
{
Debug(this,DebugAll,"ToneChan::ToneChan(\"%s\") [%p]",tone.c_str(),this);
// protect the list while the new tone source is added to it
__plugin.lock();
ToneSource* t = ToneSource::getTone(tone);
__plugin.unlock();
if (t) {
setSource(t);
m_address = t->name();
t->deref();
}
else
Debug(DebugWarn,"No source tone '%s' in ToneChan [%p]",tone.c_str(),this);
}
ToneChan::~ToneChan()
{
Debug(this,DebugAll,"ToneChan::~ToneChan() %s [%p]",id().c_str(),this);
}
bool ToneGenDriver::msgExecute(Message& msg, String& dest)
{
CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userData());
if (ch) {
ToneChan *tc = new ToneChan(dest);
if (ch->connect(tc,msg.getValue("reason"))) {
msg.setParam("peerid",tc->id());
tc->deref();
}
else {
tc->destruct();
return false;
}
}
else {
Message m("call.route");
m.addParam("module",name());
String callto(msg.getValue("direct"));
if (callto.null()) {
const char *targ = msg.getValue("target");
if (!targ) {
Debug(DebugWarn,"Tone outgoing call with no target!");
return false;
}
callto = msg.getValue("caller");
if (callto.null())
callto << prefix() << dest;
m.addParam("called",targ);
m.addParam("caller",callto);
if (!Engine::dispatch(m)) {
Debug(DebugWarn,"Tone outgoing call but no route!");
return false;
}
callto = m.retValue();
m.retValue().clear();
}
m = "call.execute";
m.addParam("callto",callto);
ToneChan *tc = new ToneChan(dest);
m.setParam("id",tc->id());
m.userData(tc);
if (Engine::dispatch(m)) {
msg.setParam("id",tc->id());
tc->deref();
return true;
}
Debug(DebugWarn,"Tone outgoing call not accepted!");
tc->destruct();
return false;
}
return true;
}
bool AttachHandler::received(Message& msg)
{
String src(msg.getValue("source"));
if (!src.startSkip("tone/",false))
src.clear();
String ovr(msg.getValue("override"));
if (!ovr.startSkip("tone/",false))
ovr.clear();
if (src.null() && ovr.null())
return false;
DataEndpoint* de = static_cast<DataEndpoint*>(msg.userObject("DataEndpoint"));
if (!de) {
CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userObject("CallEndpoint"));
if (ch)
de = ch->setEndpoint();
}
if (!de) {
Debug(DebugWarn,"Tone attach request with no control or data channel!");
return false;
}
// if single attach was requested we can return true if everything is ok
bool ret = msg.getBoolValue("single");
Lock lock(__plugin);
if (src) {
ToneSource* t = ToneSource::getTone(src);
if (t) {
de->setSource(t);
t->deref();
msg.clearParam("source");
}
else {
Debug(DebugWarn,"No source tone '%s' could be attached to %p",src.c_str(),de);
ret = false;
}
}
if (ovr) {
DataConsumer* c = de->getConsumer();
if (c) {
TempSource* t = new TempSource(ovr);
if (DataTranslator::attachChain(t,c,true) && t->startup())
msg.clearParam("override");
else {
Debug(DebugWarn,"Override source tone '%s' failed to start [%p]",ovr.c_str(),t);
ret = false;
}
}
else {
Debug(DebugWarn,"Requested override '%s' to missing consumer of %p",ovr.c_str(),de);
ret = false;
}
}
return ret;
}
void ToneGenDriver::statusModule(String& str)
{
Module::statusModule(str);
}
void ToneGenDriver::statusParams(String& str)
{
str << "tones=" << tones.count() << ",chans=" << channels().count();
}
ToneGenDriver::ToneGenDriver()
: Driver("tone","misc"), m_handler(0)
{
Output("Loaded module ToneGen");
}
ToneGenDriver::~ToneGenDriver()
{
Output("Unloading module ToneGen");
ObjList* l = &channels();
while (l) {
ToneChan* t = static_cast<ToneChan *>(l->get());
if (t)
t->disconnect("shutdown");
if (l->get() == t)
l = l->next();
}
lock();
channels().clear();
tones.clear();
unlock();
}
void ToneGenDriver::initialize()
{
Output("Initializing module ToneGen");
setup(0,true); // no need to install notifications
Driver::initialize();
if (!m_handler) {
m_handler = new AttachHandler;
Engine::install(m_handler);
}
}
}; // anonymous namespace
/* vi: set ts=8 sw=4 sts=4 noet: */