/** * 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 #include #include #include #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(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(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(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(msg.userObject("DataEndpoint")); if (!de) { CallEndpoint* ch = static_cast(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(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: */