2004-05-22 00:05:20 +00:00
|
|
|
/**
|
|
|
|
* tonegen.cpp
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* Tones generator
|
2004-11-29 03:56:41 +00:00
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2005-04-29 22:05:07 +00:00
|
|
|
* Copyright (C) 2004, 2005 Null Team
|
2004-11-29 03:56:41 +00:00
|
|
|
*
|
|
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
2004-05-22 00:05:20 +00:00
|
|
|
*/
|
|
|
|
|
2005-03-18 18:16:59 +00:00
|
|
|
#include <yatephone.h>
|
2004-05-22 00:05:20 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
|
|
|
static ObjList tones;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int nsamples;
|
|
|
|
const short *data;
|
|
|
|
} Tone;
|
|
|
|
|
|
|
|
class ToneSource : public ThreadedSource
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
~ToneSource();
|
|
|
|
virtual void run();
|
|
|
|
inline const String &name()
|
|
|
|
{ return m_name; }
|
|
|
|
static ToneSource *getTone(const String &tone);
|
|
|
|
private:
|
|
|
|
ToneSource(const String &tone);
|
2004-06-26 23:10:50 +00:00
|
|
|
static const Tone *getBlock(const String &tone);
|
2004-05-22 00:05:20 +00:00
|
|
|
String m_name;
|
2004-06-26 23:10:50 +00:00
|
|
|
const Tone *m_tone;
|
2004-10-21 23:11:46 +00:00
|
|
|
DataBlock m_data;
|
2004-05-22 00:05:20 +00:00
|
|
|
unsigned m_brate;
|
|
|
|
unsigned m_total;
|
2005-04-02 00:49:38 +00:00
|
|
|
u_int64_t m_time;
|
2004-05-22 00:05:20 +00:00
|
|
|
};
|
|
|
|
|
2005-03-29 01:50:20 +00:00
|
|
|
class ToneChan : public Channel
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
ToneChan(const String &tone);
|
|
|
|
~ToneChan();
|
|
|
|
};
|
|
|
|
|
2004-09-07 16:24:40 +00:00
|
|
|
class AttachHandler : public MessageHandler
|
|
|
|
{
|
|
|
|
public:
|
2004-12-21 04:16:09 +00:00
|
|
|
AttachHandler() : MessageHandler("chan.attach") { }
|
2004-09-07 16:24:40 +00:00
|
|
|
virtual bool received(Message &msg);
|
|
|
|
};
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
class ToneGenDriver : public Driver
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
|
|
|
public:
|
2005-04-24 01:08:51 +00:00
|
|
|
ToneGenDriver();
|
|
|
|
~ToneGenDriver();
|
2004-05-22 00:05:20 +00:00
|
|
|
virtual void initialize();
|
2005-03-29 01:50:20 +00:00
|
|
|
virtual bool msgExecute(Message& msg, String& dest);
|
|
|
|
protected:
|
|
|
|
void statusModule(String& str);
|
|
|
|
void statusParams(String& str);
|
2004-05-22 00:05:20 +00:00
|
|
|
private:
|
2005-03-29 01:50:20 +00:00
|
|
|
AttachHandler* m_handler;
|
2004-05-22 00:05:20 +00:00
|
|
|
};
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
INIT_PLUGIN(ToneGenDriver);
|
2005-03-29 01:50:20 +00:00
|
|
|
|
2004-05-22 00:05:20 +00:00
|
|
|
// 421.052Hz (19 samples @ 8kHz) sine wave, pretty close to standard 425Hz
|
2004-06-26 23:10:50 +00:00
|
|
|
static const short tone421hz[] = {
|
2004-05-22 00:05:20 +00:00
|
|
|
19,
|
|
|
|
3246,6142,8371,9694,9965,9157,7357,4759,1645,
|
|
|
|
-1645,-4759,-7357,-9157,-9965,-9694,-8371,-6142,-3246,
|
|
|
|
0 };
|
|
|
|
|
2004-11-04 00:32:25 +00:00
|
|
|
// 1000Hz (8 samples @ 8kHz) standard digital milliwatt
|
|
|
|
static const short tone1000hz[] = {
|
|
|
|
8,
|
|
|
|
8828, 20860, 20860, 8828,
|
|
|
|
-8828, -20860, -20860, -8828
|
|
|
|
};
|
|
|
|
|
2004-06-26 23:10:50 +00:00
|
|
|
static const Tone t_dial[] = { { 8000, tone421hz }, { 0, 0 } };
|
2004-05-22 00:05:20 +00:00
|
|
|
|
2004-06-26 23:10:50 +00:00
|
|
|
static const Tone t_busy[] = { { 4000, tone421hz }, { 4000, 0 }, { 0, 0 } };
|
2004-05-22 00:05:20 +00:00
|
|
|
|
2004-06-26 23:10:50 +00:00
|
|
|
static const Tone t_specdial[] = { { 7600, tone421hz }, { 400, 0 }, { 0, 0 } };
|
2004-05-22 00:05:20 +00:00
|
|
|
|
2004-06-26 23:10:50 +00:00
|
|
|
static const Tone t_ring[] = { { 8000, tone421hz }, { 32000, 0 }, { 0, 0 } };
|
2004-05-22 00:05:20 +00:00
|
|
|
|
2004-11-04 00:32:25 +00:00
|
|
|
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 } };
|
|
|
|
|
2004-05-22 00:05:20 +00:00
|
|
|
ToneSource::ToneSource(const String &tone)
|
2004-10-21 23:11:46 +00:00
|
|
|
: m_name(tone), m_tone(0), m_data(0,480), m_brate(16000), m_total(0), m_time(0)
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(&__plugin,DebugAll,"ToneSource::ToneSource(\"%s\") [%p]",tone.c_str(),this);
|
2004-05-22 00:05:20 +00:00
|
|
|
m_tone = getBlock(tone);
|
|
|
|
tones.append(this);
|
|
|
|
if (m_tone)
|
|
|
|
start("ToneSource");
|
|
|
|
}
|
|
|
|
|
|
|
|
ToneSource::~ToneSource()
|
|
|
|
{
|
2005-03-29 01:50:20 +00:00
|
|
|
Lock lock(__plugin);
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(&__plugin,DebugAll,"ToneSource::~ToneSource() [%p] total=%u stamp=%lu",this,m_total,timeStamp());
|
2004-12-20 04:11:29 +00:00
|
|
|
tones.remove(this,false);
|
2004-05-22 00:05:20 +00:00
|
|
|
if (m_time) {
|
|
|
|
m_time = Time::now() - m_time;
|
2004-08-08 23:56:18 +00:00
|
|
|
if (m_time) {
|
2005-04-02 00:49:38 +00:00
|
|
|
m_time = (m_total*(u_int64_t)1000000 + m_time/2) / m_time;
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(&__plugin,DebugInfo,"ToneSource rate=" FMT64U " b/s",m_time);
|
2004-08-08 23:56:18 +00:00
|
|
|
}
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-26 23:10:50 +00:00
|
|
|
const Tone *ToneSource::getBlock(const String &tone)
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
|
|
|
if (tone == "dial" || tone == "dt")
|
|
|
|
return t_dial;
|
|
|
|
else if (tone == "busy" || tone == "bs")
|
|
|
|
return t_busy;
|
|
|
|
else if (tone == "ring" || tone == "rt")
|
|
|
|
return t_ring;
|
|
|
|
else if (tone == "specdial" || tone == "sd")
|
|
|
|
return t_specdial;
|
2004-11-04 00:32:25 +00:00
|
|
|
else if (tone == "congestion" || tone == "cg")
|
|
|
|
return t_congestion;
|
|
|
|
else if (tone == "outoforder" || tone == "oo")
|
|
|
|
return t_outoforder;
|
|
|
|
else if (tone == "milliwatt" || tone == "mw")
|
|
|
|
return t_mwatt;
|
2004-05-22 00:05:20 +00:00
|
|
|
Debug(DebugWarn,"No waveform is defined for tone '%s'",tone.c_str());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ToneSource *ToneSource::getTone(const 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new ToneSource(tone);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ToneSource::run()
|
|
|
|
{
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(&__plugin,DebugAll,"ToneSource::run() [%p]",this);
|
2005-04-02 00:49:38 +00:00
|
|
|
u_int64_t tpos = Time::now();
|
2004-05-22 00:05:20 +00:00
|
|
|
m_time = tpos;
|
2004-06-26 23:10:50 +00:00
|
|
|
int samp = 0; // sample number
|
|
|
|
int dpos = 1; // position in data
|
|
|
|
const Tone *tone = m_tone;
|
|
|
|
int nsam = tone->nsamples;
|
2004-05-22 00:05:20 +00:00
|
|
|
while (m_tone) {
|
2005-04-11 21:20:12 +00:00
|
|
|
Thread::check();
|
2004-10-21 23:11:46 +00:00
|
|
|
short *d = (short *) m_data.data();
|
|
|
|
for (unsigned int i = m_data.length()/2; i--; samp++,dpos++) {
|
2004-06-26 23:10:50 +00:00
|
|
|
if (samp >= nsam) {
|
|
|
|
samp = 0;
|
|
|
|
const Tone *otone = tone;
|
|
|
|
tone++;
|
|
|
|
if (!tone->nsamples)
|
|
|
|
tone = m_tone;
|
|
|
|
nsam = tone->nsamples;
|
|
|
|
if (tone != otone)
|
|
|
|
dpos = 1;
|
|
|
|
}
|
2004-08-20 12:22:30 +00:00
|
|
|
if (tone->data) {
|
|
|
|
if (dpos > tone->data[0])
|
|
|
|
dpos = 1;
|
|
|
|
*d++ = tone->data[dpos];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*d++ = 0;
|
2004-06-26 23:10:50 +00:00
|
|
|
}
|
2005-04-02 00:49:38 +00:00
|
|
|
int64_t dly = tpos - Time::now();
|
2004-05-22 00:05:20 +00:00
|
|
|
if (dly > 0) {
|
2005-05-06 18:13:33 +00:00
|
|
|
XDebug(&__plugin,DebugAll,"ToneSource sleeping for " FMT64 " usec",dly);
|
2005-04-02 00:49:38 +00:00
|
|
|
Thread::usleep((unsigned long)dly);
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
2004-10-21 23:11:46 +00:00
|
|
|
Forward(m_data,m_data.length()/2);
|
|
|
|
m_total += m_data.length();
|
2005-04-02 00:49:38 +00:00
|
|
|
tpos += (m_data.length()*(u_int64_t)1000000/m_brate);
|
2004-05-22 00:05:20 +00:00
|
|
|
};
|
|
|
|
m_time = Time::now() - m_time;
|
2005-04-02 00:49:38 +00:00
|
|
|
m_time = (m_total*(u_int64_t)1000000 + m_time/2) / m_time;
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(&__plugin,DebugAll,"ToneSource [%p] end, total=%u (" FMT64U " b/s)",this,m_total,m_time);
|
2004-05-22 00:05:20 +00:00
|
|
|
m_time = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ToneChan::ToneChan(const String &tone)
|
2005-03-29 01:50:20 +00:00
|
|
|
: Channel(__plugin)
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(this,DebugAll,"ToneChan::ToneChan(\"%s\") [%p]",tone.c_str(),this);
|
2004-05-22 00:05:20 +00:00
|
|
|
ToneSource *t = ToneSource::getTone(tone);
|
|
|
|
if (t) {
|
|
|
|
setSource(t);
|
|
|
|
t->deref();
|
|
|
|
}
|
2004-09-07 16:24:40 +00:00
|
|
|
else
|
|
|
|
Debug(DebugWarn,"No source tone '%s' in ToneChan [%p]",tone.c_str(),this);
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ToneChan::~ToneChan()
|
|
|
|
{
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(this,DebugAll,"ToneChan::~ToneChan() %s [%p]",id().c_str(),this);
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
bool ToneGenDriver::msgExecute(Message& msg, String& dest)
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
2005-05-05 20:37:30 +00:00
|
|
|
CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userData());
|
|
|
|
if (ch) {
|
2005-04-09 22:10:00 +00:00
|
|
|
ToneChan *tc = new ToneChan(dest);
|
2005-05-05 20:37:30 +00:00
|
|
|
if (ch->connect(tc))
|
2004-10-24 21:35:54 +00:00
|
|
|
tc->deref();
|
|
|
|
else {
|
|
|
|
tc->destruct();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2004-05-22 00:05:20 +00:00
|
|
|
else {
|
2005-01-16 04:39:36 +00:00
|
|
|
Message m("call.route");
|
2005-05-05 20:37:30 +00:00
|
|
|
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;
|
2004-10-24 21:35:54 +00:00
|
|
|
}
|
2005-05-05 20:37:30 +00:00
|
|
|
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("targetid",tc->id());
|
|
|
|
m.userData(tc);
|
|
|
|
if (Engine::dispatch(m)) {
|
|
|
|
tc->deref();
|
|
|
|
return true;
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
2005-05-05 20:37:30 +00:00
|
|
|
Debug(DebugWarn,"Tone outgoing call not accepted!");
|
|
|
|
tc->destruct();
|
2004-05-22 00:05:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2004-09-07 16:24:40 +00:00
|
|
|
bool AttachHandler::received(Message &msg)
|
|
|
|
{
|
|
|
|
String src(msg.getValue("source"));
|
|
|
|
if (src.null())
|
|
|
|
return false;
|
|
|
|
Regexp r("^tone/\\(.*\\)$");
|
|
|
|
if (!src.matches(r))
|
|
|
|
return false;
|
|
|
|
src = src.matchString(1);
|
2005-05-14 01:38:58 +00:00
|
|
|
CallEndpoint *ch = static_cast<CallEndpoint *>(msg.userData());
|
|
|
|
if (ch) {
|
2005-03-29 01:50:20 +00:00
|
|
|
Lock lock(__plugin);
|
2004-09-07 16:24:40 +00:00
|
|
|
ToneSource *t = ToneSource::getTone(src);
|
|
|
|
if (t) {
|
2005-05-14 01:38:58 +00:00
|
|
|
ch->setSource(t);
|
2004-09-07 16:24:40 +00:00
|
|
|
t->deref();
|
|
|
|
// Let the message flow if it wants to attach a consumer too
|
|
|
|
return !msg.getValue("consumer");
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
Debug(DebugWarn,"No source tone '%s' could be attached to [%p]",src.c_str(),ch);
|
2004-09-07 16:24:40 +00:00
|
|
|
}
|
|
|
|
else
|
2004-10-22 02:45:19 +00:00
|
|
|
Debug(DebugWarn,"Tone '%s' attach request with no data channel!",src.c_str());
|
2004-09-07 16:24:40 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
void ToneGenDriver::statusModule(String& str)
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
2005-03-29 01:50:20 +00:00
|
|
|
Module::statusModule(str);
|
|
|
|
}
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
void ToneGenDriver::statusParams(String& str)
|
2005-03-29 01:50:20 +00:00
|
|
|
{
|
|
|
|
str << "tones=" << tones.count() << ",chans=" << channels().count();
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
ToneGenDriver::ToneGenDriver()
|
2005-03-29 01:50:20 +00:00
|
|
|
: Driver("tone","misc"), m_handler(0)
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
|
|
|
Output("Loaded module ToneGen");
|
|
|
|
}
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
ToneGenDriver::~ToneGenDriver()
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
|
|
|
Output("Unloading module ToneGen");
|
2005-03-29 01:50:20 +00:00
|
|
|
ObjList *l = &channels();
|
2004-05-22 00:05:20 +00:00
|
|
|
while (l) {
|
|
|
|
ToneChan *t = static_cast<ToneChan *>(l->get());
|
|
|
|
if (t)
|
2004-11-01 12:41:38 +00:00
|
|
|
t->disconnect("shutdown");
|
2004-05-22 00:05:20 +00:00
|
|
|
if (l->get() == t)
|
|
|
|
l = l->next();
|
|
|
|
}
|
2005-04-28 22:46:59 +00:00
|
|
|
lock();
|
|
|
|
channels().clear();
|
2004-05-22 00:05:20 +00:00
|
|
|
tones.clear();
|
2005-04-28 22:46:59 +00:00
|
|
|
unlock();
|
2004-05-22 00:05:20 +00:00
|
|
|
}
|
|
|
|
|
2005-04-24 01:08:51 +00:00
|
|
|
void ToneGenDriver::initialize()
|
2004-05-22 00:05:20 +00:00
|
|
|
{
|
|
|
|
Output("Initializing module ToneGen");
|
2005-04-09 22:10:00 +00:00
|
|
|
setup(0,true); // no need to install notifications
|
2005-03-29 01:50:20 +00:00
|
|
|
Driver::initialize();
|
2004-05-22 00:05:20 +00:00
|
|
|
if (!m_handler) {
|
2005-03-29 01:50:20 +00:00
|
|
|
m_handler = new AttachHandler;
|
2004-05-22 00:05:20 +00:00
|
|
|
Engine::install(m_handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|