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
This commit is contained in:
marian 2011-01-18 10:17:17 +00:00
parent 4801fa3f7c
commit cb37e92229
2 changed files with 995 additions and 108 deletions

578
conf.d/tonegen.conf.sample Normal file
View File

@ -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

View File

@ -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<ToneDesc*>(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<ToneSource*>(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<DataConsumer> 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<DataConsumer> 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<CallEndpoint*>(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