From cb37e922295c85f9eba4cb1b4948b58ce91bcf74 Mon Sep 17 00:00:00 2001 From: marian Date: Tue, 18 Jan 2011 10:17:17 +0000 Subject: [PATCH] Added suport to build custom tones from config and specify a tone language. Imported tone descriptions from Asterisk indications.conf. git-svn-id: http://yate.null.ro/svn/yate/trunk@4031 acf43c95-373e-0410-b603-e72c3f656dc1 --- conf.d/tonegen.conf.sample | 578 +++++++++++++++++++++++++++++++++++++ modules/tonegen.cpp | 525 ++++++++++++++++++++++++++------- 2 files changed, 995 insertions(+), 108 deletions(-) create mode 100644 conf.d/tonegen.conf.sample diff --git a/conf.d/tonegen.conf.sample b/conf.d/tonegen.conf.sample new file mode 100644 index 00000000..94a09659 --- /dev/null +++ b/conf.d/tonegen.conf.sample @@ -0,0 +1,578 @@ +; This file configures the tone generator +; If not explicitly specified, all parameters are processed at startup only (no reload) +; Each section, except for 'general' configures a lang list +; +; Here is an example of lang list +; +;[example] +; +; alias: string: Comma separated list of tone languages sharing the same tones +; This parameter is ignored in 'itu' section +; Subsequent sections may override tones specified for aliases +;alias= +; +; All other parameters must have the format +; name=tone[,tone] +; Each tone must be specified in the format [!]freq[/duration] +; !: Don't repeat this tone (play it on first pass only) +; freq: Tone frequency +; f: This is the frequency used to build the tone +; f1+f2: The frequency is a mixture of f1 and f2 +; f1*f2: The frequency f1 is modulated by f2 +; Frequency can be 0 for silence +; duration: Tone duration in milliseconds. Defaults to 1000 if missing or invalid +; Note: if all tones start with '!' (not repeated) playing will stop after the +; last tone in the list +; +; +; The algorithm used to play a requested tone is the following: +; 1. If a language is specified in the call.execute or chan.attach message, +; find the tone using the specified language +; 2. Find the tone using the configured language if any +; 3. Find the tone using the default ('itu') +; If 'itu' language is specified in the handled message the tone is searched in +; the default list only: any configured language is ignored + + +[general] + +; lang: string: Default tones language +; This is the name of the section containing the tones to play if no language +; is specified in processed messages or the tone is not found for the specified +; language section +;lang= + + +[itu] +; This section configures the default tones to play +; The following tones are pre-generated: dial,busy,ring,specdial,congestion, +; outoforder,info,milliwatt,silence,noise,probe/0,probe/1,probe/2,cotv,cots + + +[at] +; Austria +dial=420 +busy=420/400,0/400 +ring=420/1000,0/5000 +congestion=420/200,0/200 +callwaiting=420/40,0/1960 +dialrecall=420 +record=1400/80,0/14920 +info=950/330,1450/330,1850/330,0/1000 +stutter=380+420 + + +[au] +; Australia +dial=413+438 +busy=425/375,0/375 +ring=413+438/400,0/200,413+438/400,0/2000 +congestion=425/375,0/375,420/375,0/375 +callwaiting=425/200,0/200,425/200,0/4400 +dialrecall=413+438 +record=!425/1000,!0/15000,425/360,0/15000 +info=425/2500,0/500 +std=!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100 +facility=425 +stutter=413+438/100,0/40 +ringmobile=400+450/400,0/200,400+450/400,0/2000 + + +[bg] +; Bulgaria +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/250,0/250 +callwaiting=425/150,0/150,425/150,0/4000 +dialrecall=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 +record=1400/425,0/15000 +info=950/330,1400/330,1800/330,0/1000 +stutter=425/1500,0/100 + + +[br] +; Brazil +dial=425 +busy=425/250,0/250 +ring=425/1000,0/4000 +congestion=425/250,0/250,425/750,0/250 +callwaiting=425/50,0/1000 +dialrecall=350+440 +record=425/250,0/250 +info=950/330,1400/330,1800/330 +stutter=350+440 + + +[be] +; Belgium +dial=425 +busy=425/500,0/500 +ring=425/1000,0/3000 +congestion=425/167,0/167 +callwaiting=1400/175,0/175,1400/175,0/3500 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" +record=1400/500,0/15000 +info=900/330,1400/330,1800/330,0/1000 +stutter=425/1000,0/250 + + +[ch] +; Switzerland +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=425/200,0/200,425/200,0/4000 +dialrecall=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 +record=1400/80,0/15000 +info=950/330,1400/330,1800/330,0/1000 +stutter=425+340/1100,0/1100 + + +[cl] +; Chile +dial=400 +busy=400/500,0/500 +ring=400/1000,0/3000 +congestion=400/200,0/200 +callwaiting=400/250,0/8750 +dialrecall=!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 +record=1400/500,0/15000 +info=950/333,1400/333,1800/333,0/1000 +stutter=!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 + + +[cn] +; China +dial=450 +busy=450/350,0/350 +ring=450/1000,0/4000 +congestion=450/700,0/700 +callwaiting=450/400,0/4000 +dialrecall=450 +record=950/400,0/10000 +info=450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400 +stutter=450+425 + + +[cz] +; Czech Republic +dial=425/330,0/330,425/660,0/660 +busy=425/330,0/330 +ring=425/1000,0/4000 +congestion=425/165,0/165 +callwaiting=425/330,0/9000 +dialrecall=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660 +record=1400/500,0/14000 +info=950/330,0/30,1400/330,0/30,1800/330,0/1000 +stutter=425/450,0/50 + + +[de] +; Germany +dial=425 +busy=425/480,0/480 +ring=425/1000,0/4000 +congestion=425/240,0/240 +callwaiting=!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0 +dialrecall=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 +record=1400/80,0/15000 +info=950/330,1400/330,1800/330,0/1000 +stutter=425+400 + + +[dk] +; Denmark +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=!425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0 +dialrecall=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 +record=1400/80,0/15000 +info=950/330,1400/330,1800/330,0/1000 +stutter=425/450,0/50 + + +[ee] +; Estonia +dial=425 +busy=425/300,0/300 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=950/650,0/325,950/325,0/30,1400/1300,0/2600 +dialrecall=425/650,0/25 +record=1400/500,0/15000 +info=950/650,0/325,950/325,0/30,1400/1300,0/2600 +stutter=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 + + +[es] +; Spain +dial=425 +busy=425/200,0/200 +ring=425/1500,0/3000 +congestion=425/200,0/200,425/200,0/200,425/200,0/600 +callwaiting=425/175,0/175,425/175,0/3500 +dialrecall=!425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425 +record=1400/500,0/15000 +info=950/330,0/1000 +dialout=500 + + +[fi] +; Finland +dial=425 +busy=425/300,0/300 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=425/150,0/150,425/150,0/8000 +dialrecall=425/650,0/25 +record=1400/500,0/15000 +info=950/650,0/325,950/325,0/30,1400/1300,0/2600 +stutter=425/650,0/25 + + +[fr] +; France +dial=440 +busy=440/500,0/500 +ring=440/1500,0/3500 +congestion=440/250,0/250 +callwait=440/300,0/10000 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330 +stutter=!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440 + + +[gr] +; Greece +dial=425/200,0/300,425/700,0/800 +busy=425/300,0/300 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=425/150,0/150,425/150,0/8000 +dialrecall=425/650,0/25 +record=1400/400,0/15000 +info=!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 +stutter=425/650,0/25 + + +[hu] +; Hungary +dial=425 +busy=425/300,0/300 +ring=425/1250,0/3750 +congestion=425/300,0/300 +callwaiting=425/40,0/1960 +dialrecall=425+450 +record=1400/400,0/15000 +info=!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 +stutter=350+375+400 + + +[il] +; Israel +dial=414 +busy=414/500,0/500 +ring=414/1000,0/3000 +congestion=414/250,0/250 +callwaiting=414/100,0/100,414/100,0/100,414/600,0/3000 +dialrecall=!414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414 +record=1400/500,0/15000 +info=1000/330,1400/330,1800/330,0/1000 +stutter=!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414 + + +[in] +; India +dial=400*25 +busy=400/750,0/750 +ring=400*25/400,0/200,400*25/400,0/2000 +congestion=400/250,0/250 +callwaiting=400/200,0/100,400/200,0/7500 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0/1000 +stutter=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 + + +[it] +; Italy +dial=425/200,0/200,425/600,0/1000 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=425/400,0/100,425/250,0/100,425/150,0/14000 +dialrecall=470/400,425/400 +record=1400/400,0/15000 +info=!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 +stutter=470/400,425/400 + + +[lt] +; Lithuania +dial=425 +busy=425/350,0/350 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=425/150,0/150,425/150,0/4000 +dialrecall=425/500,0/50 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 +stutter=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 + + +[jp] +; Japan +dial=400 +busy=400/500,0/500 +ring=400+15/1000,0/2000 +congestion=400/500,0/500 +callwaiting=400+16/500,0/8000 +dialrecall=!400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0 +stutter=!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 + + +[mx] +; Mexico +dial=425 +busy=425/250,0/250 +ring=425/1000,0/4000 +congestion=425/250,0/250 +callwaiting=425/200,0/600,425/200,0/10000 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 +record=1400/500,0/15000 +info=950/330,0/30,1400/330,0/30,1800/330,0/1000 +stutter=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 + + +[my] +; Malaysia +dial=425 +busy=425/500,0/500 +ring=425/400,0/200 +congestion=425/500,0/500 + + +[nl] +; Netherlands +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/250,0/250 +callwaiting=425/500,0/9500 +dialrecall=425/500,0/50 +record=1400/500,0/15000 +info=950/330,1400/330,1800/330,0/1000 +stutter=425/500,0/50 + + +[no] +; Norway +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/200,0/200 +callwaiting=425/200,0/600,425/200,0/10000 +dialrecall=470/400,425/400 +record=1400/400,0/15000 +info=!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 +stutter=470/400,425/400 + + +[nz] +; New Zealand +dial=400 +busy=400/250,0/250 +ring=400+450/400,0/200,400+450/400,0/2000 +congestion=400/375,0/375 +callwaiting=!400/200,!0/3000,!400/200,!0/3000,!400/200,!0/3000,!400/200 +dialrecall=!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400 +record=1400/425,0/15000 +info=400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400 +stutter=!400/100!0/100,!400/100,!0/100,!400/100,!0/100,!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400 +unobtainable=400/75,0/100,400/75,0/100,400/75,0/100,400/75,0/400 + + +[ph] +; Philippines +dial=425 +busy=480+620/500,0/500 +ring=425+480/1000,0/4000 +congestion=480+620/250,0/250 +callwaiting=440/300,0/10000 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0 +stutter=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 + + +[pl] +; Poland +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/500,0/500 +callwaiting=425/150,0/150,425/150,0/4000 +dialrecall=425/500,0/50 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000 +stutter=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 + + +[pt] +; Portugal +dial=425 +busy=425/500,0/500 +ring=425/1000,0/5000 +congestion=425/200,0/200 +callwaiting=440/300,0/10000 +dialrecall=425/1000,0/200 +record=1400/500,0/15000 +info=950/330,1400/330,1800/330,0/1000 +stutter=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 + + +[ru] +; Russia / ex Soviet Union +dial=425 +busy=425/350,0/350 +ring=425/800,0/3200 +congestion=425/350,0/350 +callwaiting=425/200,0/5000 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0 + + +[se] +; Sweden +dial=425 +busy=425/250,0/250 +ring=425/1000,0/5000 +congestion=425/250,0/750 +callwaiting=425/200,0/500,425/200,0/9100 +dialrecall=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 +record=1400/500,0/15000 +info=!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,0 +stutter=!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 + + +[sg] +; Singapore +dial=425 +ring=425*24/400,0/200,425*24/400,0/2000 +busy=425/750,0/750 +congestion=425/250,0/250 +callwaiting=425*24/300,0/200,425*24/300,0/3200 +stutter=!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425 +info=950/330,1400/330,1800/330,0/1000 +dialrecall=425*24/500,0/500,425/500,0/2500 +record=1400/500,0/15000 +nutone=425/2500,0/500 +intrusion=425/250,0/2000 +warning=425/624,0/4376 +acceptance=425/125,0/125 +holdinga=!425*24/500,!0/500 +holdingb=!425/500,!0/2500 + + +[th] +; Thailand +dial=400*50 +busy=400/500,0/500 +ring=420/1000,0/5000 +congestion=400/300,0/300 +callwaiting=1000/400,10000/400,1000/400 +dialrecall=400*50/400,0/100,400*50/400,0/100 +record=1400/500,0/15000 +info=950/330,1400/330,1800/330 +stutter=!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,400 + + +[uk] +; United Kingdom +dial=350+440 +specdial=350+440/750,440/750 +busy=400/375,0/375 +congestion=400/400,0/350,400/225,0/525 +speccongestion=400/200,1004/300 +unobtainable=400 +ring=400+450/400,0/200,400+450/400,0/2000 +callwaiting=400/100,0/4000 +speccallwaiting=400/250,0/250,400/250,0/250,400/250,0/5000 +creditexpired=400/125,0/125 +confirm=1400 +switching=400/200,0/400,400/2000,0/400 +info=950/330,0/15,1400/330,0/15,1800/330,0/1000 +record=1400/500,0/60000 +stutter=350+440/750,440/750 + + +[us] +; United States / North America +dial=350+440 +busy=480+620/500,0/500 +ring=440+480/2000,0/4000 +congestion=480+620/250,0/250 +callwaiting=440/300,0/10000 +dialrecall=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0 +stutter=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 + + +[us-old] +; United States Circa 1950 / North America +dial=600*120 +busy=500*100/500,0/500 +ring=420*40/2000,0/4000 +congestion=500*100/250,0/250 +callwaiting=440/300,0/10000 +dialrecall=!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0 +stutter=!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120 + + +[tw] +; Taiwan +dial=350+440 +busy=480+620/500,0/500 +ring=440+480/1000,0/2000 +congestion=480+620/250,0/250 +callwaiting=350+440/250,0/250,350+440/250,0/3250 +dialrecall=300/1500,0/500 +record=1400/500,0/15000 +info=!950/330,!1400/330,!1800/330,0 +stutter=!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 + + +[ve] +; Venezuela / South America +dial=425 +busy=425/500,0/500 +ring=425/1000,0/4000 +congestion=425/250,0/250 +callwaiting=400+450/300,0/6000 +dialrecall=425 +record=1400/500,0/15000 +info=!950/330,!1440/330,!1800/330,0/1000 + + +[za] +; South Africa +dial=400*33 +ring=400*33/400,0/200,400*33/400,0/2000 +callwaiting=400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250 +congestion=400/250,0/250 +busy=400/500,0/500 +dialrecall=350+440 +record=1400/500,0/10000 +info=950/330,1400/330,1800/330,0/330 +stutter=!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33 diff --git a/modules/tonegen.cpp b/modules/tonegen.cpp index bbc93419..58e288ff 100644 --- a/modules/tonegen.cpp +++ b/modules/tonegen.cpp @@ -45,13 +45,41 @@ static ObjList datas; typedef struct { int nsamples; const short* data; + bool repeat; } Tone; -typedef struct { - const Tone* tone; - const char* name; - const char* alias; -} ToneDesc; +class ToneDesc : public String +{ +public: + ToneDesc(const Tone* tone, const String& name, + const String& prefix = String::empty()); + ~ToneDesc(); + inline const Tone* tones() const + { return m_tones; } + inline bool repeatAll() const + { return m_repeatAll; } + // Init this tone description from comma separated list of tone data + bool setTones(const String& desc); + // Tone name/alias match. + // Set name to this object's name if true is returned and alias matches + bool isName(String& name) const; + // Build tones from a list + static void buildTones(const String& name, const NamedList& list); +private: + inline void clearTones() { + if (m_tones && m_ownTones) + delete[] m_tones; + m_tones = 0; + m_ownTones = true; + toneListChanged(); + } + // Called when tones list changed to update data + void toneListChanged(); + String m_alias; // Tone name alias + Tone* m_tones; // Tones array. Ends with an invalid one (zero) + bool m_ownTones; // Clear tones when reset/destroyed + bool m_repeatAll; // True if all tones repeated +}; class ToneData : public GenObject { @@ -80,6 +108,11 @@ public: { return (m_f1 == other.f1()) && (m_f2 == other.f2()); } const short* data(); static ToneData* getData(const char* desc); + // Decode a tone description from [!]desc[/duration] + // Build a tone data if needded + // Return true on success + static bool decode(const String& desc, int& samples, const short*& data, + bool& repeat); private: bool parse(const char* desc); int m_f1; @@ -96,8 +129,8 @@ public: inline const String& name() { return m_name; } bool startup(); - static ToneSource* getTone(String& tone); - static const ToneDesc* getBlock(String& tone, bool oneShot = false); + static ToneSource* getTone(String& tone, const String& prefix); + static const ToneDesc* getBlock(String& tone, const String& prefix, bool oneShot = false); static Tone* buildCadence(const String& desc); static Tone* buildDtmf(const String& dtmf, int len = DTMF_LEN, int gap = DTMF_GAP); protected: @@ -105,10 +138,13 @@ protected: virtual bool noChan() const { return false; } virtual void cleanup(); + void advanceTone(const Tone*& tone); static const ToneDesc* getBlock(String& tone, const ToneDesc* table); + static const ToneDesc* findToneDesc(String& tone, const String& prefix); String m_name; const Tone* m_tone; int m_repeat; + bool m_firstPass; private: DataBlock m_data; unsigned m_brate; @@ -119,7 +155,7 @@ private: class TempSource : public ToneSource { public: - TempSource(String& desc, DataBlock* rawdata); + TempSource(String& desc, const String& prefix, DataBlock* rawdata); virtual ~TempSource(); protected: virtual bool noChan() const @@ -132,7 +168,7 @@ private: class ToneChan : public Channel { public: - ToneChan(String& tone); + ToneChan(String& tone, const String& prefix); ~ToneChan(); bool attachConsumer(const char* consumer); }; @@ -159,6 +195,10 @@ private: }; INIT_PLUGIN(ToneGenDriver); +static ObjList s_toneDesc; // List of configured tones +static ObjList s_defToneDesc; // List of default tones +static String s_defLang; // Default tone language +static const String s_default = "itu"; // 421.052Hz (19 samples @ 8kHz) sine wave, pretty close to standard 425Hz static const short tone421hz[] = { @@ -195,45 +235,45 @@ static const short tone1777hz[] = { 6429, 8659, -3420, -9848, 0 }; -static const Tone t_dial[] = { { 8000, tone421hz }, { 0, 0 } }; +static const Tone t_dial[] = { { 8000, tone421hz, true }, { 0, 0 } }; -static const Tone t_busy[] = { { 4000, tone421hz }, { 4000, 0 }, { 0, 0 } }; +static const Tone t_busy[] = { { 4000, tone421hz, true }, { 4000, 0, true }, { 0, 0 } }; -static const Tone t_specdial[] = { { 7600, tone421hz }, { 400, 0 }, { 0, 0 } }; +static const Tone t_specdial[] = { { 7600, tone421hz, true }, { 400, 0, true }, { 0, 0 } }; -static const Tone t_ring[] = { { 8000, tone421hz }, { 32000, 0 }, { 0, 0 } }; +static const Tone t_ring[] = { { 8000, tone421hz, true }, { 32000, 0, true }, { 0, 0 } }; -static const Tone t_congestion[] = { { 2000, tone421hz }, { 2000, 0 }, { 0, 0 } }; +static const Tone t_congestion[] = { { 2000, tone421hz, true }, { 2000, 0, true }, { 0, 0 } }; static const Tone t_outoforder[] = { - { 800, tone421hz }, { 800, 0 }, - { 800, tone421hz }, { 800, 0 }, - { 800, tone421hz }, { 800, 0 }, - { 1600, tone421hz }, { 1600, 0 }, + { 800, tone421hz, true }, { 800, 0, true }, + { 800, tone421hz, true }, { 800, 0, true }, + { 800, tone421hz, true }, { 800, 0, true }, + { 1600, tone421hz, true }, { 1600, 0, true }, { 0, 0 } }; static const Tone t_callwait[] = { - { 160, 0 }, - { 800, tone421hz }, { 800, 0 }, { 800, tone421hz }, - { 160, 0 }, + { 160, 0, true }, + { 800, tone421hz, true }, { 800, 0, true }, { 800, tone421hz, true }, + { 160, 0, true }, { 0, 0 } }; static const Tone t_info[] = { - { 2640, tone941hz }, { 240, 0 }, - { 2640, tone1454hz }, { 240, 0 }, - { 2640, tone1777hz }, { 8000, 0 }, + { 2640, tone941hz, true }, { 240, 0, true }, + { 2640, tone1454hz, true }, { 240, 0, true }, + { 2640, tone1777hz, true }, { 8000, 0, true }, { 0, 0 } }; -static const Tone t_mwatt[] = { { 8000, tone1000hz }, { 0, 0 } }; +static const Tone t_mwatt[] = { { 8000, tone1000hz, true }, { 0, 0 } }; -static const Tone t_silence[] = { { 8000, 0 }, { 0, 0 } }; +static const Tone t_silence[] = { { 8000, 0, true }, { 0, 0 } }; -static const Tone t_noise[] = { { 2000, ToneData::getData("noise")->data() }, { 0, 0 } }; +static const Tone t_noise[] = { { 2000, ToneData::getData("noise")->data(), true }, { 0, 0 } }; #define MAKE_DTMF(s) { \ - { DTMF_GAP, 0 }, \ - { DTMF_LEN, ToneData::getData(s)->data() }, \ - { DTMF_GAP, 0 }, \ + { DTMF_GAP, 0, true }, \ + { DTMF_LEN, ToneData::getData(s)->data(), true }, \ + { DTMF_GAP, 0, true }, \ { 0, 0 } \ } static const Tone t_dtmf[][4] = { @@ -257,7 +297,7 @@ static const Tone t_dtmf[][4] = { #undef MAKE_DTMF #define MAKE_PROBE(s) { \ - { 8000, ToneData::getData(s)->data() }, \ + { 8000, ToneData::getData(s)->data(), true }, \ { 0, 0 } \ } static const Tone t_probes[][2] = { @@ -269,44 +309,25 @@ static const Tone t_probes[][2] = { }; #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_info, "info", "in" }, - { t_mwatt, "milliwatt", "mw" }, - { t_silence, "silence", 0 }, - { t_noise, "noise", "cn" }, - { t_probes[0], "probe/0", "probe" }, - { t_probes[1], "probe/1", 0 }, - { t_probes[2], "probe/2", 0 }, - { t_probes[3], "cotv", "co1" }, - { t_probes[4], "cots", "co2" }, - { 0, 0, 0 } -}; - static const ToneDesc s_descOne[] = { - { t_callwait, "callwaiting", "cw" }, - { 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" }, - { 0, 0, 0 } + ToneDesc(t_callwait,"callwaiting"), + ToneDesc(t_dtmf[0],"dtmf/0"), + ToneDesc(t_dtmf[1],"dtmf/1"), + ToneDesc(t_dtmf[2],"dtmf/2"), + ToneDesc(t_dtmf[3],"dtmf/3"), + ToneDesc(t_dtmf[4],"dtmf/4"), + ToneDesc(t_dtmf[5],"dtmf/5"), + ToneDesc(t_dtmf[6],"dtmf/6"), + ToneDesc(t_dtmf[7],"dtmf/7"), + ToneDesc(t_dtmf[8],"dtmf/8"), + ToneDesc(t_dtmf[9],"dtmf/9"), + ToneDesc(t_dtmf[10],"dtmf/*"), + ToneDesc(t_dtmf[11],"dtmf/#"), + ToneDesc(t_dtmf[12],"dtmf/a"), + ToneDesc(t_dtmf[13],"dtmf/b"), + ToneDesc(t_dtmf[14],"dtmf/c"), + ToneDesc(t_dtmf[15],"dtmf/d"), + ToneDesc((Tone*)0,"") }; // This function is here mainly to avoid 64bit gcc b0rking optimizations @@ -320,6 +341,156 @@ static unsigned int byteRate(u_int64_t time, unsigned int bytes) return (unsigned int)((bytes*(u_int64_t)1000000 + time/2) / time); } +// Retrieve the alias associated with a given name +static const char* getAlias(const String& name) +{ +#define TONE_GETALIAS(n,a) { if (name == n) return a; } + if (!name) + return 0; + TONE_GETALIAS("dial","dt"); + TONE_GETALIAS("busy","bs"); + TONE_GETALIAS("ring","rt"); + TONE_GETALIAS("specdial","sd"); + TONE_GETALIAS("congestion","cg"); + TONE_GETALIAS("outoforder","oo"); + TONE_GETALIAS("info","in"); + TONE_GETALIAS("milliwatt","mw"); + TONE_GETALIAS("silence",0); + TONE_GETALIAS("noise","cn"); + TONE_GETALIAS("probe/0","probe"); + TONE_GETALIAS("probe/1",0); + TONE_GETALIAS("probe/2",0); + TONE_GETALIAS("cotv","co1"); + TONE_GETALIAS("cots","co2"); + TONE_GETALIAS("callwaiting","cw"); + TONE_GETALIAS("dtmf/0","0"); + TONE_GETALIAS("dtmf/1","1"); + TONE_GETALIAS("dtmf/2","2"); + TONE_GETALIAS("dtmf/3","3"); + TONE_GETALIAS("dtmf/4","4"); + TONE_GETALIAS("dtmf/5","5"); + TONE_GETALIAS("dtmf/6","6"); + TONE_GETALIAS("dtmf/7","7"); + TONE_GETALIAS("dtmf/8","8"); + TONE_GETALIAS("dtmf/9","9"); + TONE_GETALIAS("dtmf/*","*"); + TONE_GETALIAS("dtmf/#","#"); + TONE_GETALIAS("dtmf/a","a"); + TONE_GETALIAS("dtmf/b","b"); + TONE_GETALIAS("dtmf/c","c"); + TONE_GETALIAS("dtmf/d","d"); + return 0; +#undef TONE_GETALIAS +} + +ToneDesc::ToneDesc(const Tone* tone, const String& name, const String& prefix) + : String(prefix + name), + m_tones((Tone*)tone), + m_ownTones(false), + m_repeatAll(true) +{ + const char* alias = getAlias(name); + if (alias) + m_alias = prefix + alias; + toneListChanged(); + XDebug(&__plugin,DebugAll,"ToneDesc(%s) [%p]",c_str(),this); +} + +ToneDesc::~ToneDesc() +{ + clearTones(); +} + +// Init this tone description from comma separated list if tone data +bool ToneDesc::setTones(const String& desc) +{ + Debug(&__plugin,DebugAll,"ToneDesc(%s) initializing from '%s' [%p]", + c_str(),desc.c_str(),this); + clearTones(); + ObjList* list = desc.split(',',false); + m_tones = new Tone[list->count() + 1]; + m_ownTones = true; + int n = 0; + for (ObjList* o = list->skipNull(); o; o = o->skipNext(), n++) { + const String& s = o->get()->toString(); + if (ToneData::decode(s,m_tones[n].nsamples,m_tones[n].data,m_tones[n].repeat)) + DDebug(&__plugin,DebugAll, + "ToneDesc(%s) added tone '%s' samples=%d data=%p repeat=%d [%p]", + c_str(),s.c_str(),m_tones[n].nsamples,m_tones[n].data, + m_tones[n].repeat,this); + else { + Debug(&__plugin,DebugNote,"ToneDesc(%s) invalid tone description '%s' [%p]", + c_str(),s.c_str(),this); + n = -1; + break; + } + } + TelEngine::destruct(list); + if (n > 0) + // Invalidate the last tone in the list + ::memset(m_tones + n,0,sizeof(Tone)); + else + clearTones(); + toneListChanged(); + return n != 0; +} + +// Tone name/alias equality operator. Set name if true is returned +bool ToneDesc::isName(String& name) const +{ + if (name == *this) + return true; + if (!m_alias || m_alias != name) + return false; + name = *this; + return true; +} + +// Build tone descriptions from a list +void ToneDesc::buildTones(const String& name, const NamedList& list) +{ + DDebug(&__plugin,DebugAll,"Building tones lang=%s from list=%s", + name.c_str(),list.c_str()); + String prefix; + ObjList* target = &s_defToneDesc; + if (name && name != s_default) { + prefix << name << "/"; + target = &s_toneDesc; + } + unsigned int n = list.length(); + for (unsigned int i = 0; i < n; i++) { + NamedString* ns = list.getParam(i); + if (TelEngine::null(ns)) + continue; + ToneDesc* d = new ToneDesc(0,ns->name(),prefix); + if (d->setTones(*ns)) { + ObjList* o = target->find(d->toString()); + if (!o) + target->append(d); + else { + Debug(&__plugin,DebugInfo,"Replacing tone '%s' (from list '%s')", + d->toString().c_str(),list.c_str()); + o->set(d); + } + } + else + TelEngine::destruct(d); + } +} + +// Called when tones list changed to update data +void ToneDesc::toneListChanged() +{ + m_repeatAll = true; + if (!m_tones) + return; + for (Tone* tone = m_tones; tone->nsamples; tone++) + if (!tone->repeat) { + m_repeatAll = false; + break; + } +} + ToneData::ToneData(const char* desc) : m_f1(0), m_f2(0), m_mod(false), m_data(0) @@ -446,14 +617,47 @@ ToneData* ToneData::getData(const char* desc) return d; } +// Decode a tone description from [!]desc[/duration] +// Build a tone data if needded +// Return true on success +bool ToneData::decode(const String& desc, int& samples, const short*& data, bool& repeat) +{ + if (!desc) + return false; + samples = 8000; + data = 0; + repeat = (desc[0] != '!'); + int start = repeat ? 0 : 1; + int pos = desc.find('/',start); + String freq; + if (pos > 0) { + String dur = desc.substr(pos + 1); + int duration = dur.toInteger(); + if (duration > 0) { + // Round up to a multiple of 20 + duration += 19; + samples = duration / 20 * 160; + } + freq = desc.substr(start,pos - start); + } + else + freq = desc.substr(start); + // Silence ? + if (freq.toInteger(-1) == 0) + return true; + ToneData* td = ToneData::getData(freq); + if (td) + data = td->data(); + return td != 0; +} ToneSource::ToneSource(const ToneDesc* tone) - : m_tone(0), m_repeat(tone == 0), + : m_tone(0), m_repeat(tone == 0), m_firstPass(true), m_data(0,320), m_brate(16000), m_total(0), m_time(0) { if (tone) { - m_tone = tone->tone; - m_name = tone->name; + m_tone = tone->tones(); + m_name = *tone; } Debug(&__plugin,DebugAll,"ToneSource::ToneSource(%p) '%s' [%p]", tone,m_name.c_str(),this); @@ -483,24 +687,80 @@ void ToneSource::cleanup() ThreadedSource::cleanup(); } +void ToneSource::advanceTone(const Tone*& tone) +{ + if (!tone) + return; + const Tone* start = tone; + tone++; + while (tone && tone != start) { + if (!tone->nsamples) { + if ((m_repeat > 0) && !(--m_repeat)) + m_tone = 0; + tone = m_tone; + m_firstPass = false; + continue; + } + if (m_firstPass || tone->repeat) + break; + tone++; + } + if (tone == start && !m_firstPass && !tone->repeat) { + m_tone = 0; + tone = 0; + } +} + const ToneDesc* ToneSource::getBlock(String& tone, const ToneDesc* table) { - for (; table->tone; table++) { - if (tone == table->name) + for (; table->tones(); table++) { + if (table->isName(tone)) return table; - if (table->alias && (tone == table->alias)) { - tone = table->name; - return table; - } } return 0; } -const ToneDesc* ToneSource::getBlock(String& tone, bool oneShot) +const ToneDesc* ToneSource::findToneDesc(String& tone, const String& prefix) +{ + XDebug(&__plugin,DebugAll,"ToneSource::findToneDesc(%s,%s)", + tone.c_str(),prefix.c_str()); + ObjList* target = &s_defToneDesc; + if (prefix) { + tone = prefix + "/" + tone; + target = &s_toneDesc; + } + for (ObjList* o = target->skipNull(); o; o = o->skipNext()) { + const ToneDesc* d = static_cast(o->get()); + if (d->isName(tone)) + return d; + } + if (prefix) + tone.startSkip(prefix + "/",false); + return 0; +} + +const ToneDesc* ToneSource::getBlock(String& tone, const String& prefix, bool oneShot) { if (tone.trimBlanks().toLower().null()) return 0; - const ToneDesc* d = getBlock(tone,s_desc); + XDebug(&__plugin,DebugAll,"ToneSource::getBlock(%s,%s,%u)", + tone.c_str(),prefix.c_str(),oneShot); + const ToneDesc* d = 0; + if (prefix) { + if (prefix != s_default) + d = findToneDesc(tone,prefix); + else { + // Default tone explicitly required + d = findToneDesc(tone,String::empty()); + if (!d && oneShot) + d = getBlock(tone,s_descOne); + return d; + } + } + if (!d && s_defLang && s_defLang != prefix) + d = findToneDesc(tone,s_defLang); + if (!d) + d = findToneDesc(tone,String::empty()); if (d) return d; if (oneShot) @@ -528,6 +788,7 @@ Tone* ToneSource::buildDtmf(const String& dtmf, int len, int gap) for (unsigned int i = 0; i < dtmf.length(); i++) { t->nsamples = gap; t->data = 0; + t->repeat = true; t++; int c = dtmf.at(i); @@ -543,11 +804,13 @@ Tone* ToneSource::buildDtmf(const String& dtmf, int len, int gap) t->nsamples = len; t->data = ((c >= 0) && (c < 16)) ? t_dtmf[c][1].data : 0; + t->repeat = true; t++; } t->nsamples = gap; t->data = 0; + t->repeat = true; t++; t->nsamples = 0; t->data = 0; @@ -555,11 +818,15 @@ Tone* ToneSource::buildDtmf(const String& dtmf, int len, int gap) return tmp; } -ToneSource* ToneSource::getTone(String& tone) +ToneSource* ToneSource::getTone(String& tone, const String& prefix) { - const ToneDesc* td = ToneSource::getBlock(tone); + const ToneDesc* td = ToneSource::getBlock(tone,prefix); + bool repeat = !td || td->repeatAll(); + XDebug(&__plugin,DebugAll,"ToneSource::getTone(%s,%s) found %p '%s' repeatall=%s", + tone.c_str(),prefix.c_str(),td,td ? td->c_str() : "",String::boolText(repeat)); // tone name is now canonical - ObjList* l = &tones; + // Build a fresh source if the list contains tones not repeated + ObjList* l = repeat ? &tones : 0; for (; l; l = l->next()) { ToneSource* t = static_cast(l->get()); if (t && (t->name() == tone) && t->running() && (t->refcount() > 1)) { @@ -594,12 +861,7 @@ void ToneSource::run() // 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; - } + advanceTone(tone); nsam = tone ? tone->nsamples : 32000; if (nsam < 0) { nsam = -nsam; @@ -634,10 +896,11 @@ void ToneSource::run() } -TempSource::TempSource(String& desc, DataBlock* rawdata) +TempSource::TempSource(String& desc, const String& prefix, DataBlock* rawdata) : m_single(0), m_rawdata(rawdata) { - Debug(&__plugin,DebugAll,"TempSource::TempSource(\"%s\") [%p]",desc.c_str(),this); + Debug(&__plugin,DebugAll,"TempSource::TempSource(\"%s\",\"%s\") [%p]", + desc.c_str(),prefix.safe(),this); if (desc.null()) return; m_name = desc; @@ -654,14 +917,15 @@ TempSource::TempSource(String& desc, DataBlock* rawdata) m_tone = m_single = (Tone*)::malloc(2*sizeof(Tone)); m_single[0].nsamples = m_rawdata->length() / sizeof(short); m_single[0].data = (short*)m_rawdata->data(); + m_single[0].repeat = true; m_single[1].nsamples = 0; m_single[1].data = 0; return; } // try first the named tones - const ToneDesc* tde = getBlock(desc,true); + const ToneDesc* tde = getBlock(desc,prefix,true); if (tde) { - m_tone = tde->tone; + m_tone = tde->tones(); return; } // for performance reason accept an entire string of DTMFs @@ -675,12 +939,15 @@ TempSource::TempSource(String& desc, DataBlock* rawdata) return; } // now try to build a single tone - ToneData* td = ToneData::getData(desc); - if (!td) + int samples = 8000; + const short* data = 0; + bool repeat = true; + if (!ToneData::decode(desc,samples,data,repeat)) return; m_single = (Tone*)::malloc(2*sizeof(Tone)); - m_single[0].nsamples = 8000; - m_single[0].data = td->data(); + m_single[0].nsamples = samples; + m_single[0].data = data; + m_single[0].repeat = repeat; m_single[1].nsamples = 0; m_single[1].data = 0; m_tone = m_single; @@ -697,13 +964,13 @@ TempSource::~TempSource() } -ToneChan::ToneChan(String& tone) +ToneChan::ToneChan(String& tone, const String& prefix) : Channel(__plugin) { - Debug(this,DebugAll,"ToneChan::ToneChan(\"%s\") [%p]",tone.c_str(),this); + Debug(this,DebugAll,"ToneChan::ToneChan(\"%s\",\"%s\") [%p]",tone.c_str(),prefix.safe(),this); // protect the list while the new tone source is added to it __plugin.lock(); - ToneSource* t = ToneSource::getTone(tone); + ToneSource* t = ToneSource::getTone(tone,prefix); __plugin.unlock(); if (t) { setSource(t); @@ -781,7 +1048,7 @@ bool AttachHandler::received(Message& msg) Lock lock(__plugin); if (src) { - ToneSource* t = ToneSource::getTone(src); + ToneSource* t = ToneSource::getTone(src,msg["lang"]); if (t) { de->setSource(t); t->deref(); @@ -797,7 +1064,7 @@ bool AttachHandler::received(Message& msg) RefPointer c = de->getConsumer(); DataEndpoint::commonMutex().unlock(); if (c) { - TempSource* t = new TempSource(ovr,getRawData(msg)); + TempSource* t = new TempSource(ovr,msg["lang"],getRawData(msg)); if (DataTranslator::attachChain(t,c,true) && t->startup()) msg.clearParam("override"); else { @@ -816,7 +1083,7 @@ bool AttachHandler::received(Message& msg) RefPointer c = de->getConsumer(); DataEndpoint::commonMutex().unlock(); if (c) { - TempSource* t = new TempSource(repl,getRawData(msg)); + TempSource* t = new TempSource(repl,msg["lang"],getRawData(msg)); if (DataTranslator::attachChain(t,c,false) && t->startup()) msg.clearParam("replace"); else { @@ -838,7 +1105,7 @@ bool ToneGenDriver::msgExecute(Message& msg, String& dest) { CallEndpoint* ch = static_cast(msg.userData()); if (ch) { - ToneChan *tc = new ToneChan(dest); + ToneChan *tc = new ToneChan(dest,msg["lang"]); tc->initChan(); tc->attachConsumer(msg.getValue("consumer")); if (ch->connect(tc,msg.getValue("reason"))) { @@ -881,7 +1148,7 @@ bool ToneGenDriver::msgExecute(Message& msg, String& dest) } m = "call.execute"; m.setParam("callto",callto); - ToneChan *tc = new ToneChan(dest); + ToneChan *tc = new ToneChan(dest,msg["lang"]); tc->initChan(); tc->attachConsumer(msg.getValue("consumer")); m.setParam("id",tc->id()); @@ -936,11 +1203,53 @@ 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); - installRelay(Halt); + if (m_handler) + return; + // Init default tones + s_defToneDesc.append(new ToneDesc(t_dial,"dial")); + s_defToneDesc.append(new ToneDesc(t_busy,"busy")); + s_defToneDesc.append(new ToneDesc(t_ring,"ring")); + s_defToneDesc.append(new ToneDesc(t_specdial,"specdial")); + s_defToneDesc.append(new ToneDesc(t_congestion,"congestion")); + s_defToneDesc.append(new ToneDesc(t_outoforder,"outoforder")); + s_defToneDesc.append(new ToneDesc(t_info,"info")); + s_defToneDesc.append(new ToneDesc(t_mwatt,"milliwatt")); + s_defToneDesc.append(new ToneDesc(t_silence,"silence")); + s_defToneDesc.append(new ToneDesc(t_noise,"noise")); + s_defToneDesc.append(new ToneDesc(t_probes[0],"probe/0")); + s_defToneDesc.append(new ToneDesc(t_probes[1],"probe/1")); + s_defToneDesc.append(new ToneDesc(t_probes[2],"probe/2")); + s_defToneDesc.append(new ToneDesc(t_probes[3],"cotv")); + s_defToneDesc.append(new ToneDesc(t_probes[4],"cots")); + // Init tones from config + Configuration cfg(Engine::configFile("tonegen")); + s_defLang = cfg.getValue("general","lang"); + if (s_defLang == s_default) + s_defLang.clear(); + unsigned int n = cfg.sections(); + for (unsigned int i = 0; i < n; i++) { + NamedList* l = cfg.getSection(i); + if (!l || *l == "general") + continue; + String aliases; + if (*l != s_default) + aliases = l->getValue("alias"); + l->clearParam("alias"); + ToneDesc::buildTones(*l,*l); + if (aliases) { + ObjList* list = aliases.split(',',false); + for (ObjList* o = list->skipNull(); o; o = o->skipNext()) { + const String& name = o->get()->toString(); + if (name != s_default) + ToneDesc::buildTones(name,*l); + } + TelEngine::destruct(list); + } } + // Init module + m_handler = new AttachHandler; + Engine::install(m_handler); + installRelay(Halt); } }; // anonymous namespace