'
' IcClient
'
' V01.00  05.06.12 AD    new started up from iclient 00.42.
'                        + added base ABCL fw upgrade to work with IPAM102.
'                        + added in/out levels.
'                        c chged web UI to new web UI look and feel.
' V01.01  25.06.12 AD    ! fixed web ui syntax errors.
'                        ! fixed iclient call next master not working when using ID's.
'                        c chged msg 3 (PA) timeout to  10000 (> 2x4secs intervals).
'                        ! fixed hdx unattended receiving from state 4(intercom).
' V01.02  04.07.12 AD    ! fixed web UI home page help syntax.
'                        ! fixed delayed audio in FD after call established.
' V01.03  10.07.12 AD    ! fixed high latency on master side in IC mode.
'                        ! fixed regression IC call not working properly after BGM.
'                        c chged to greater file chunks to be read for ring tone.
' V01.04  28.08.12 AD    ! fixed various web ui syntax errors.
'                        ! fixed bug #110.06 glitches on audio issue.
'                        ! fixed audio format not available for decoder only.
'                        + added mechanism to exit for timeout on established call.
' V01.05  10.09.12 AD    + added echo cancellation feature (AEC).
' V01.06  25.10.12 AD    c chged #110.09 paging/ic method using link.
'                        c chged #110.12 paging volume to be separated from bgm volume.
'                        + added routines with link timeout mechanism.
'                        ! fixed #109.01 mechanism to exit HD TX if master crash.
'                        ! fixed non working channel 0 local line input.
' V01.07  31.01.13 AD    + added new debug trace preprocessor macroes.
'                        + added tuned rtp delay on vlsi1053 to avoid stream timeout in pa.
'                        c chged no link for paging.
'                        ! fixed ICG not able to start spontaneous intercom.
'                        ! fixed client timeout to idle when in HD TX (ICG is in intercom RX).
'                        ! fixed #110.16, added BARP flags for extended timout checks.
'                        ! fixed #110,17, application receive and play multiple streams.
'                        ! fixed BGM audible when switching back to low priority page,
'                                reported from SG report 1.09 point 4.5.
'                        ! fixed BGM not starting on local channel at first boot,
'                                reported from SG report 1.09 point 6.2.
'                        ! fixed relay not closed while receiving paging,
'                                reported from SG report 1.09 point 8.4.
'                        ! fixed #110.02 false call starts on Annuncicom 200.
'                        + added payload/codec/delay detection for notification messages.
'                        + added BGM state on web UI.
'                        + added NOTIFICATION state on web UI.

	dim ver$(18)
	ver$="1.07 31.01.2013"

	dim nul$

#define TEST_VERSION 1

#define sysi #1 syslog "(i) "+#1
#define sysw #1 syslog "(w) "+#1
#define syse #1 syslog "(e) "+#1
' DEBUG
'#define sysd #1 syslog "(-) "+#1
' else
#define sysd nul$=#1

	syslog "Barix IcClient "+ver$+" starting..."
	syslog "syslog marks: (-) debug, (i) info, (w) warning, (e) errors"


#define CLEAR_AUDIO_LEVELS  inl=-127:oul=-127

#define H_UDP_STREAM_PR  0
#define H_UDP_STREAM     4
#define H_UDP_BARPMSG    5
#define H_AUDIO          7

#define STA_IDLE         1
#define STA_CALL_BUZZ    2
#define STA_CALL         3
#define STA_INTERCOM     4
#define STA_PAGING       5

' substates
#define SUBST_IDLE_BGM   1
#define SUBST_NOTIF      2

' BARP flags (16-bit bitmask)
#define FLAG_EXTO        1

	' link timeout support
#define TO_LS_IDLE       0
#define TO_LS_BUFFERING  1
#define TO_LS_PLAYING    2

#define STACK_TOP        32

	dim _Mr$(1501)          ' one UDP block
	dim chan$(20)
	dim set1$               ' read EEPROM settings from offset 100
	dim set5$               ' read EEPROM settings from offset 500
	dim set8$               ' read EEPROM settings from offset 800
	dim state               ' application mode state machine:
                            ' 1 - Idle state
                            ' 2 - Initialize call to master
                            ' 3 - Incoming call from the master
                            ' 4 - Active call state
                            ' 5 - Paging state
	dim stmsg$(20)          ' Changing state debug message
	stmsg$="Changing to state "

	dim codec               ' variable holding the current codec
                            ' 0 - not configured
                            ' 1 - preconfigured (open_aud_cfg) 
                            ' 2 - aLaw 8KHz (open_aud_al8)
                            ' 3 - MP3
                            ' 4 - notification RTP
	dim id                  ' my station ID
	dim mr_ip(3,1)          ' master stations IP address and ID list
	dim mrtst               ' MasteR To STarT dialing first
	dim mrtcl               ' next MasteR To CaLl 
	dim ip$(20),mip$(20)    ' the current IP address, master ip
	dim bcast$(20)          ' broadcast address
	dim mcast$(20)          ' multicast address extracted from paging request (cmd-3) command
	dim barip$(20)          ' BARP IP address. Used to set another IP (other than broadcast) 
                            ' for status and control messages if needed.
	dim xt                  ' timeout for "ringing" playback
	dim rngst               ' ring start time 
	dim rngtm               ' ring timeout (auto hang-up if no reply)
	
	' main ports
	dim audp,ctlp,stsp      ' BARP audio, control and status udp ports
	dim audpx               ' configurable audio port 
	dim audpd               ' destination audio port
	
    ' priority audio port variables
    ' This port is used as a backup paging port if for any reason the 
    ' imaster(s) fail. The iclient should always play audio received on
    ' the priority port (if enabled from the web UI)
	dim paudp               ' priority audio port
	dim prpen               ' priority port enabled
	dim prmes               ' priority message active
	dim srate               ' sample rate
	dim todr                ' door timeout in 100ms units
	dim autm                ' time of the last audio message
	dim aurxtm              ' time of the last received audio packet
	dim cmdtm               ' time of the last command received
	dim bps                 ' bytes per sample
	dim fmt                 ' audio format
	dim kplvt               ' keepalive time in milliseconds
	dim prio                ' priority of the currently played message (valid only in playing state)
	' dim calpr             ' intercom call priority
	dim aud$(100)           ' open string for audio open
	dim aud_not$(100)       ' open string for priority audio (RTP)
	dim isrtp               ' currently used only for priority message
	dim incom$(2)           ' Used to notify that the client has been in intercom when the priority message arrived
	incom$="n"
	
	dim name$               ' device name
	dim tmp$(64)            ' general
	
	' boolean strings (because of lack of variable names)
	dim idadr$(2)           ' boolean: use id addressing = "y", use IP addressing ="n"
	dim clnxt$(2)           ' call next: used to configure if we have to always call the next master
	                        ' if the one we're calling does not respond
	dim id130$(2)           ' ID of the server that is supposed to get cmd-130 message
	dim disp1               ' display available?

        ' half duplex mode variables
	dim halfd               ' halfduplex/AI_Phone available
	dim aiphn               ' aiphone available? Read from EEPROM
	dim aiptm               ' AI phone phantom button timer
	dim aists$(2)           ' AI phone state
	aists$="9"              ' just set it to something different than 0,1,2 (means not set)
	
	' BGM (BackGround Music) variables
	dim bgm_chan            ' current background music channel. 0 if nothing playing
	dim bgm_local_chan      ' local BGM channel. Corresponds to channel 0, and is mapped to
	                        ' BGM start port-1
	dim bgm_start_chan      ' BGM start channel
	dim bgm_avail_chans     ' BGM number of channels available  
	dim bgm_start_port      ' BGM start port (usualy 11001). This is the port of the first channel, the
	                        ' others follow consequently
	dim bgmip$(20)          ' BGM broadcast address
	dim bgmln(0,1)          ' BGM LINK array
	dim tmr_ssb
	dim tmr_aud
	dim tmr_rng

	' new volume management
	dim vol_pa
	dim vol_pa_min
	dim vol_pa_max
	dim vol_bgm
	dim vol_bgm_min
	dim vol_bgm_max
	dim vol_bgm_ramp
	dim vol_not
	
	' relay behaviour
	dim rly                 ' relay on while. Currently only state 3 is handled for the client. Handling 
	                        ' other states to be added on demand.
                            ' In any case, the relay state may be changed also by sending cmd=4 message
                            ' 0 - always off
                            ' 1 - on while ringing (not currently used)
                            ' 2 - on while talking
                            ' 3 - always on, off while talking
                            ' 4 - on while paging
                            ' 5 - on while receiving audiostream
	dim rly5_tm             ' timer to switch off the relay after no samples are detected (for rly-5 mode)
	dim rly5_to             ' relay off timeout (for rly=5)
	dim oldvol, volpct
	dim tmr_rtx             ' timer for msg retrasmission
	dim echo
	dim rtp_delay
	dim to_logic_state
	dim to_logic_state_old
	dim to_logic_check
	dim audio_zc_old
	dim recv
	dim pa_in_ip$(16)
	
	dim _Ms8$(161)          ' need to read from file the correct 20msec chunk
	dim _Ms24$(481)         ' need to read from file the correct 20msec chunk

	' keyboard variables
	' in this application a mix of keyevents and IO polling is used for keyboard/button handling
	' keys that are active while pressed (e.g. PA button) are polled by iostate
	' other keys are handled by the subroutine 6000
	' keys are stored in the kbhit array which is accessed by the application and also by the handler
	' the keyboard handler sets the appropriate item of kbhit to 1 if a key is pressed
	' the application monitors kbhit() and triggers on <>0 value
	' after the key is processed, the application clears kbhit
	' key assignment:
	'	0 - call master station I
	'	1 - call master station II

	dim key$(3)             ' string for reading key events
	dim nkeys               ' number of keys supported by the system (other keys will be ignored)
	nkeys=2                 ' keys numbered 0 to 1
	dim kbhit(20)           ' keyboard status; if set to 1 key was pressed and not yet handled

	' X8 VSC variables
	dim x8avl               ' X8 available?
	dim x8key$(2)           ' string for reading x8 key events
	dim xnkeys 
	xnkeys=2                ' keys numbered 0,1,2
	dim x8hit(8)            ' x8 key status; if set to 1 key was pressed and not yet handled. Key numbers
	                        ' 3,4,5 are assigned for the IR remote control.
	dim disp2               ' X8 display. NB! The display is not accessible if any other display is available
	                        ' (i.e. Extreamer 110 or PS16), so do not set it in this case!
	dim x8tmr               ' X8 timer. Used to restore the X8 idle state message 3 seconds after setting 
	                        ' volume
	dim x8idl               ' X8 display idle mode
	dim x8mod               ' X8 display mode (0-control/show volume, 1-control/show BGM channel)
	dim mutev               ' variable to store vol when muted
	dim x8ctm               ' X8 custom command timer
	dim cm130               ' selected command with message 130
	dim blink$(1)
	dim htype
	dim itype               ' ipam type

	' IR remote control variables
	dim ir$                 ' IR string containing complete command
	dim irtmp$              ' IR command line number string
	dim ircmd               ' IR command number
	                        ' NB! if you change the lines in the remote.ini, the commands will change as well!
	dim brate
	dim cmd                 ' command message from the command parser
	' command overview:
	' 0 - idle, not a command, do nothing
	' 1 - stop streamback
	' 2 - start streamback
	' 3 - PA request
	' 4 - activate relay  
	' 5 - change volume  
	' 6 - set BGM channel          
	' 7 - call rejected   (reason: 0-busy
	'                              1-rejected by user
	'                              2-not available,call next master in list)
	' 8 - end group request

	dim par1,par2,par3,par4 ' command parameters
	dim par5                ' added for flags
	dim cm7rs$(1)           ' command 7 ReaSon parameter
	dim blk1                ' flag for the first block
	dim sts$(297)           ' status message - big enough for station name
	dim stslen
	dim _Mzro$(961)         ' silence for the codec 
	dim l,i$,x,p,y

	dim ad_gain,mic_gain,inp,stereo,enc

	dim offvl               ' store volume level here when usint IR off/on command
	dim inl,oul

	' group membership variables
	dim mbyte               ' my ID byte from the address bitmap
	dim mybit               ' my address bit in this byte
	dim barp_flags
	dim linkdst(1,1)
	' page override stack
	dim stackpos
	dim stack(32,1)
	dim subst

	' custom functions
	dim am_i_member      <gosub 4000>
	dim ip_select        <gosub 2030>
	dim aimod            <gosub 4200>   ' adjust HDX/AI_Phone  mode
	dim setvol           <gosub 2220>
	dim open_aud_cfg     <gosub 2200>   ' reopen audio in precofngired mode
	dim open_aud_al8     <gosub 2210>   ' reopen audio in aLaw 8Khz
	dim open_aud_mp3     <gosub 2230>   ' reopen audio as MP3
	dim open_aud_not     <gosub 2240>   ' prepare notification audio
	dim open_udp         <gosub 1400>
	dim open_udp_cfg     <gosub 1500>
	dim do_pa_start      <gosub 1550>
	dim do_ic_start      <gosub 1600>
	dim do_ic_send       <gosub 1700>
	dim do_ic_recv       <gosub 1800>
	dim check_timeout    <gosub 2000>
	dim bgm_local        <gosub 7500>
	dim set_aud_delay    <gosub 8000>
	dim prio_check       <gosub 8010>
	dim stack_push       <gosub 9000>
	dim stack_pop        <gosub 9100>
	
	' timers here
	on timer1 gosub 6000 : timer 1,30   ' keyboard handler
	on timer2 gosub 5000                ' relay handler
	on timer3 gosub 3300 : timer 3,0    ' BGM volume ramp-up. Start this timer only when BGM active
	on timer4 gosub 3400 : timer 4,50   ' X8 VSC/IR events routine, run timer4 always
	
	' prepare page stack
	stackpos=STACK_TOP

	' init levels
	CLEAR_AUDIO_LEVELS

    ' read VSC setting from EEPROM
	open "STP:100" as 2 : read 2,set1$ : close 2

	x8avl=midget (set1$,100,1)
	' prepare the timer routine for LCD idle state
	x8idl=0
	x8tmr=_TMR_(0)
	if x8avl then disp2=1             ' set X8 display
	
	' 960x 0xFF
	for l=1 to 240 
		midset _Mzro$,1+((l-1)*4),4,&Hffffffff
	next l

	bcast$=sprintf$("%A",5)			' broadcast address

	' read setups
	open "STP:98"  as 2 : read 2,name$ : close 2
	open "STP:500" as 2 : read 2,set5$ : close 2
	open "STP:800" as 2 : read 2,set8$ : close 2
	
	' get the RTP flag for the priority message
	if shr(and(2,midget(set5$,30,1)),1) = 0 then 
		sysi "using raw UDP for notifications" 
		isrtp=0
	else 
		sysi "using RTP mode for notifications"
		isrtp=1
	endif
	
	rly=midget(set8$,49,1)			' relay on while...
	rly5_to=midget(set5$,46,2)		' relay off delay

	' get HDX/FDX aiphone params
	halfd=midget(set8$,31,1)
	aiphn=midget(set8$,32,1)
	
	' Addressing masters by IP or ID ?
	if midget(set8$,75,1)=1 then idadr$="y" else idadr$="n"
	' Call next master if no reply ?
	if midget(set8$,76,1)=1 then clnxt$="y" else clnxt$="n"
	
	' get BGM params
	bgmip$=sprintf$("%lA",midget(set8$,32+1,4))
	bgm_start_port=midget(set8$,36+1,2)
	bgm_avail_chans=midget(set8$,38+1,1)
	bgm_start_chan=midget(set8$,39+1,1)

	' if the start port is "0", or no channels, we have no BGM
	if or(bgm_start_port=0,bgm_avail_chans=0) then
		sysi "BGM not enabled!"
		bgm_chan=0
		bgm_avail_chans=0
		bgm_local_chan=0
	else
		bgm_chan=bgm_start_chan
		if bgm_chan>bgm_avail_chans then bgm_chan=bgm_avail_chans
		if bgm_chan<1 then bgm_chan=0
		' bgm local channel, always there in icpaging (no option)
		bgm_local_chan=1
		sysi "BGM enabled from address " + bgmip$ + ":" + str$(bgm_start_port)
		sysi "start channel      : " + str$(bgm_start_chan)
		sysi "current channel    : " + str$(bgm_chan)
		sysi "number of channels : " + str$(bgm_avail_chans)
	endif

	echo=midget(set8$,27,1)
	
	rngtm=1000*midget(set5$,45,1)
	
	' get volumes
	vol_pa      = midget(set5$, 1,1)
	vol_pa_min  = midget(set5$, 4,1)
	vol_pa_max  = midget(set5$, 5,1)

	vol_bgm     = midget(set5$,18,1)
	vol_bgm_min = midget(set5$,19,1)
	vol_bgm_max = midget(set5$,20,1)

	vol_not     = midget(set8$,41,1)

	' volume checks
	if vol_pa > vol_pa_max then
		sysw "floor paging volume to maximal"
		vol_pa=vol_pa_max
	endif
	if vol_pa < vol_pa_min then
		sysw "ceil paging volume to minimal"
		vol_pa=vol_pa_min
	endif
	if vol_bgm > vol_bgm_max then
		sysw "floor BGM volume to maximal"
		vol_bgm=vol_bgm_max
	endif
	if vol_bgm < vol_bgm_min then
		sysw "ceil BGM volume to minimal"
		vol_bgm=vol_bgm_min
	endif

	ad_gain=midget(set5$,2,1)
	if ad_gain>15 then ad_gain=15
	
	mic_gain=midget(set5$,3,1)
	if mic_gain>15 then mic_gain=15

	if midget(set5$,11,1)=129 then      ' input (129=line, 130=mic)
		inp=1
		stereo=0
	else
		inp=2
		stereo=0
	endif

	' get ipam type
	itype=iostate(60008)

	sysd "ipam type : "+str$(itype)
	
	' running on extreamer 110? Then set to stereo.
	htype=iostate(60000)
	if htype=20 then 
	   stereo=1		    ' better BGM quality
	   inp=1		    ' No mic, use line input instead
	   disp1=1
	   ' When we have another display we can't drive X8 display
	   disp2=0
	endif
	
	id=midget(set5$,41,2) ' station ID

	if id=0 then id=1:mbyte=1:mybit=128
	if id>=1 then
	   mbyte=(id-1)/8 
	   mybit=shr(128,(id-1)%8)
	endif
	
	midset id130$,1,1, midget (set5$,16,2)
	
	mr_ip(0,0)=midget(set5$,31,4)      ' master 1 IP
	mr_ip(1,0)=midget(set5$,35,4)      ' master 2 IP
	mr_ip(2,0)=midget(set8$,83,4)      ' master 3 IP
	mr_ip(3,0)=midget(set8$,87,4)      ' master 4 IP
	
	mr_ip(0,1)=midget(set8$,67,2)      ' master 1 ID
	mr_ip(1,1)=midget(set8$,69,2)      ' master 2 ID
	mr_ip(2,1)=midget(set8$,71,2)      ' master 3 ID
	mr_ip(3,1)=midget(set8$,73,2)      ' master 4 ID

	' get BARP IP
	barip$=sprintf$("%lA",midget(set8$,79,4))
	
	' if it is multicast, then set also the bcast$
	if and(midget(set8$,79,1),240)=224 then bcast$=barip$

	enc=midget(set5$,12,1)      ' encoding

	' chainsaw, was happening also for bad codec selection, sometime enc was < 6
	' but web application page was showing right uLaw24 selected ...
	' so added a protection from unknow format, expecially when device just flashed
	if enc < 6 then enc = 6

	' uLaw/24kHz
	if enc=6  then
		srate = 24
		fmt   = 0
		bps   = 1
	endif
	' uLaw/8kHz
	if enc=7  then
		srate = 8
		fmt   = 0
		bps   = 1
	endif
	' aLaw/24kHz
	if enc=8  then
		srate = 24
		fmt   = &H100
		bps   = 1
	endif
	' aLaw/8kHz
	if enc=9  then
		srate = 8
		fmt   = &H100
		bps   = 1
	endif
	' set minimal appropriate delay
	rtp_delay=set_aud_delay()

	' prepare audio for opening ...
	' preconfugured audio
	' set audio parameters
	x=&Hc00 ' only enc+dec
	if echo=1 then x=&H2c00 ' echo cancellation

	sysd "delay: "+str$(rtp_delay)
	aud$     = "AUD:11,129,"+str$(x+fmt+srate)+","+str$(rtp_delay)
	
	brate=bps*20*srate

	sysd "enc/aud$ at startup: " + str$(enc) + "/" + aud$

	' start timer 2 now
	timer 2,100

	' open audio
	open_aud_cfg()

	if aiphn=1 then aimod(0)
	
	' handle 0: priority audio port
	' handle 1: USB file handle (not always open)
	' handle 2: EEPROM setup read
	' handle 4: udp audio port
	' handle 5: udp control port/status send
	' handle 6: used for BGM
	' handle 7: used for audio codec
	' handle 8: used as "invalid" handle (handle 0 now used for priority audio)

	paudp=midget(set8$,51,2)                ' default 5554
	audp =midget(set8$,53,2)                ' default 5555
	ctlp =midget(set8$,55,2)                ' default 5556
	stsp =midget(set8$,57,2)                ' default 5557
	prpen=midget(set8$,50,1)
	
	if prpen then 
		open "UDP:0.0.0.0:"+str$(paudp) as H_UDP_STREAM_PR   ' Priority audio port
	endif

	open "UDP:"+barip$+":"+str$(ctlp) as H_UDP_BARPMSG  ' BARP control port
	' std audio open
	open_udp()

	if halfd=1 then sysi "operating mode: HDX" else sysi "operating mode: FDX"

	' starting state
	state=STA_IDLE : if bgm_avail_chans>0 then subst=SUBST_IDLE_BGM
	'reset priority
	prio=255

	kplvt=_TMR_(0)-30000

	' prepare status message
	' BAR \0   | vers | msg | rsvd | src ID | dst ID | seq | rsvd | params
	' 1        | 5    | 6   | 7    | 23     | 25     | 27  | 29   | 35
	' 4B       | 1B   | 1B  | 16B  | 2B     | 2B     | 2B  | 6B   | 
	
	midset sts$,1,4,&H00524142		' BAR\0 magic
	midset sts$,5,1,1			' protocol version 1
	midset sts$,6,1,128			' client status report
	midset sts$,23,2,id			' client ID
	
	' Start the timer to ignore the phantom button ring of the AI_Phone
	if aiphn=1 then aiptm=_TMR_(0) : aimod(0)
	
	' if BGM is active, prepare its playback now
	if and(bgm_avail_chans,bgm_chan>0) then
		sysi "network bgm on, opening udp socket"
		open "UDP:" + bgmip$ +":"+str$(bgm_start_port-1+bgm_chan) as 6
		gosub 7100
	else
		if bgm_local_chan then bgm_local()
	endif
	
	' relay control as preconfigured
	if rly=3 then ioctl 1,1
	
	sysd "initialization finished"
	x=_TMR_(0)
	if htype=19 then
		sysd "fix false call start"
		' wait for false call triggered from Aiphone interface
		' note: issue not happening if certain Aiphone interface are connected,
		' in that case, timeout exit after 1 sec
99
		if kbhit(0)<>0 then kbhit(0)=0 : sysd "false call discarded" : goto 1000
		if _TMR_(0)-x>1000 then sysd "false call timeout" : goto 1000
		goto 99
	endif

' **********************************************
' *             *** MAIN LOOP ***              *
' **********************************************
1000
	gosub 3200	' send keepalive status message every 30s
	on state goto 1010,1020,1030,1040,1050
	goto 1000

' pre-idle state
1009
	sysd stmsg$+"1: Idle"
	
	CLEAR_AUDIO_LEVELS
	kbhit(0)=0
	kbhit(1)=0

	'reset vars
	subst=0 : prio=255 : barp_flags=0

	' stop sending
	linkdst(0,1)=0
	if aiphn=1 then aimod(0)
	
	' back from receving, rleay off-delay
	if rly=5 then
		if _TMR_(0)>rly5_tm+rly5_to then ioctl 1,0
	else
		' relay control as preconfigured
		if rly=3 then ioctl 1,1 else ioctl 1,0
	endif
	
	if aiphn=1 then aiptm=_TMR_(0)
	if bgm_avail_chans=0 then
		if disp1 then write -2,"\x1b[2J",0 : write -2,"Idle mode",0
		if disp2 then write -2,"\x1b[2J",0
	else
		subst=SUBST_IDLE_BGM
		if disp1 then write -2,"\x1b[2J",0 : write -2,"Playing BGM\nchannel "+str$(bgm_chan),0
		if disp2 then write -2,"\x1b[2J",0 : write -2,sprintf$("%2u",bgm_chan),0
		' ramp up to BGM
		open_aud_mp3()
		if bgm_chan>0 then
			link 6,7,bgmln
		else
			if bgm_local_chan then bgm_local()
		endif
		timer 3,300
	endif
	' do immediate status report
	gosub 3201

' idle state
1010
	' receive and parse control messages
	gosub 2100
	' priority message interrupted the intercom? Try to restore it
	if incom$="y" then incom$="n" : state=STA_INTERCOM : goto 1049
	' cmd=3-->PA, priority check
	if and(cmd=3,par4=1) then
		if prio_check()=1 then state=STA_PAGING : cmd=0 : goto 1059
	endif
	' priority port enabled and audio message came in  -> start paging
	if prpen<>0 then
		l=lastlen(H_UDP_STREAM_PR)
		if l<0 then
			read H_UDP_STREAM_PR, _Mr$, -l
			open_aud_not()
			' notification audio, RTP door priority messages
			state=STA_PAGING : subst=SUBST_NOTIF : prmes=1 : goto 1059
		endif
	endif
	' button 0 pressed - call destination 1
	if kbhit(0)<>0 then kbhit(0)=0 : state=2 : rngst=_TMR_(0) : ip_select(0) : mrtst=0 : mrtcl=0 : goto 1029
	' button 1 pressed - call destination 2
	if kbhit(1)<>0 then kbhit(1)=0 : state=2 : rngst=_TMR_(0) : ip_select(1) : mrtst=1 : mrtcl=1 : goto 1029
	' play BGM ?
	' we have active BGM channel and the codec is not mp3? -> open mp3 codec and start playback
	if and(bgm_chan<>0,codec<>3,mediatype(6)) then
		sysd "reopening audio for BGM"
		open_aud_mp3()
		link 6,7,bgmln
		' ramp up to BGM
		timer 3,300
	endif

	goto 1000

' PRE-STATE 2 : INITIATE CALL
1029
	sysd stmsg$+"2: Initializing call to master"

	' no BGM ramps
	timer 3,0
	
	gosub 3250	' call request
	gosub 3201	' send status

	' back to predefined format from BGM/PLAYLIST (mp3)
	if codec=3 then open_aud_cfg()
	' ring volume, as pa
	setvol(vol_pa)
	' loopback disabled, always
	write H_AUDIO,str$(&H4000),-13
	
	sysd "opening ring file as enc: "+str$(enc)
	
	' fix to avoid audio delay after chime (no reopen audio)
	if enc=6 then open "F_R:baring_u24.wav" as 1 ' uLaw/24kHz
	if enc=7 then open "F_R:baring_u8.wav"  as 1 ' uLaw/8kHz
	if enc=8 then open "F_R:baring_a24.wav" as 1 ' aLaw/24kHz
	if enc=9 then open "F_R:baring_a8.wav"  as 1 ' aLaw/8kHz

	if mediatype(1)=0 then syse "no baring found " : state=3 : goto 1039	' file doesn't exist

	' skip headeer
	seek 1, 58
	kbhit(0)=0
	kbhit(1)=0
    ' relay control as preconfigured
	if rly=3 then ioctl 1,0
	if rly=2 then ioctl 1,1
	
	tmr_rng=_TMR_(0)

	sysd "going into state 2 (calling)"

' STATE 2: CALL IN PROGRESS, with BUZZER
' incomming audio -> connection established
1020
	' receive and parse control messages
	gosub 2100
	' page request in reply to call request -> connection established
	if cmd=3 then 
		close 1 : write H_AUDIO,"",0
		barp_flags=par5
		state=STA_INTERCOM : cmd=0 : goto 1049
	endif
	' call rejected -> play busy tone
	if cmd=7 then gosub 7200
	' little faster than correct bitrate, to keep buffer low
	if _TMR_(0)-tmr_rng<20 then goto 1000
	tmr_rng=tmr_rng+20
	if or(enc=6,enc=8) then read 1,_Ms24$ else read 1,_Ms8$
	l=lastlen(1)
	if l>0 then 
		if or(enc=6,enc=8) then write H_AUDIO,_Ms24$,l else write H_AUDIO,_Ms8$,l
	else
		close 1 : state=3 : goto 1039
	endif
	
	goto 1000

' PRE-STATE 3: ringing, waiting between rings (audio file closed)
1039
	xt=_TMR_(0)

' STATE 3: CALL IN PROGRESS, without BUZZER
1030
	' incomming audio -> connection established
	' receive and parse control messages
	gosub 2100
	' page request in reply to call request -> connection established
	if cmd=3 then barp_flags=par5 : write H_AUDIO,"",0 : state=STA_INTERCOM : cmd=0 : goto 1049
	' call rejected -> play busy tone
	if cmd=7 then gosub 7200
	' call ring timeout -> goto idle
	if _TMR_(0)-rngst>rngtm then
		if clnxt$="y" then 
			' restart the call timer, we call next
			rngst=_TMR_(0)
			' simulate call reject, reason 2, and call the fn to call next
			sysi "call ring timeout, call next now"
			cmd=7 : midset cm7rs$,1,1,2 : gosub 7200
		else
			state=STA_IDLE : goto 1009
		endif
	endif
	
	' wait finished -> ring again
	if _TMR_(0)-xt>4000 then state=2 : goto 1029
	goto 1000

1049 ' connection established (talking)

	sysi stmsg$ + "4: intercom"
	
	' send status update
	gosub 3201
	' reset status-resend timer
	tmr_rtx=_TMR_(0)

	' relay control as preconfigured
	if rly=2 then ioctl 1,1
	if or (rly=0,rly=3) then ioctl 1,0
	
	blk1=0
	autm=_TMR_(0) : aurxtm=_TMR_(0)
	
	' fast IC start, get remote audio port here also
	audpd=par1

	' no BGM ramps
	timer 3,0
	
	open_udp()
	' ready to receive for HD, even without "stop streamback"
	do_ic_start()
	if aimod=1 then aimod(2)
	
	' setup volume
	setvol(vol_pa)
	' show flags
	sysd "barp_flags :" + str$(barp_flags)

	' init timer for sending timeout
	tmr_ssb=_TMR_(0)
	
	sysd "talking to remote port "+str$(audpd)


' STATE :4 INTERCOM (CALL ACTIVE)
1040
	' receive and parse control messages
	gosub 2100
	' priority port enabled and audio message came in  -> start paging
	if and(prpen,lastlen(0)<0) then state=STA_PAGING : prmes=1 : incom$="y" : goto 1059
	' get audio info, always for all modes
	if mediatype(H_AUDIO) then
		read H_AUDIO,_Mr$,-15 : oul=lastlen(H_AUDIO)
		read H_AUDIO,_Mr$,-13 : inl=lastlen(H_AUDIO)
	endif

	if cmd=1 then
		' stop streamback command
		if halfd=1 then
			' clean audio
			if recv=0 then
				sysd "HD intercom, switch to RX"
				if aiphn=1 then aimod(2)
				do_ic_recv()
				if rly=5 then ioctl 1,1 : rly5_tm=_TMR_(0)
			endif
		endif
		cmd=0
		' give almost 5 secs of time (timeout 2500 below)
		autm=_TMR_(0)-2000
		goto 1041
	endif
	' start streamback command, message repeated every 1 sec !
	' it means the client must send the stream now.
	' NOTE: it is sent in FD also !
	if cmd=2 then
		' added to timeout if master crashes
		tmr_ssb=_TMR_(0)
		if halfd=1 then
			' was already sending ?
			if recv=1 then
				sysd "HD intercom, switch to TX"
				if aiphn=1 then aimod(1)
				do_ic_send()
				' timeout reset
				to_logic_state=TO_LS_IDLE : to_logic_state_old=to_logic_state
				if rly=5 then ioctl 1,0
			endif
		endif
		cmd=0
		if par1<>0 then audpd=par1
		goto 1041
	endif
	if cmd=4 then
		cmd=0
		'' FIXME: don't ignore relay number (par1)
		'' FIXME: check source IP address
		' BARP relay control, may overwrite the default settings !
		if par2=0 then todr=0 : ioctl 1,0 : goto 1041	    ' relay off
		if par2=255 then todr=-1 : ioctl 1,1 : goto 1041    ' relay permanently on
		todr=par2 : ioctl 1,1  ' relay with timeout in 100ms units
	endif
	if cmd=8 then
		cmd=0
		' reset balance, in case of IC end
		if mediatype(H_AUDIO) then write H_AUDIO,"0",-8
		close H_UDP_STREAM
		state=STA_IDLE : to_logic_state=TO_LS_IDLE : to_logic_state_old=to_logic_state
		goto 1009
	endif
1041
	' connection status retransmission, for master to know client is up.
	if _TMR_(0)>tmr_rtx+4000 then
		tmr_rtx=_TMR_(0)
		gosub 3201
	endif
	' HD/FD receive timeout ? end of IC
	if recv=1 then
		'read H_AUDIO,_Mr$,-3 : sysd "out peak left:" +str$(lastlen(H_AUDIO))
		'write H_AUDIO,"0",-8
		'write H_AUDIO,"20",-12
		' HD RECEIVE HERE
		write H_AUDIO,"0",-8
		check_timeout()
		if check_timeout=1 then
			if halfd=1 then
				' if was broadcast, back to use broadcast for the call
				ip$=mip$
			endif
			' don't close audio here
			state=STA_IDLE : goto 1009
		endif
		' rleay timeout ?
		if rly=5 then rly5_tm=_TMR_(0)
	else
		' HD SEND HERE
		' check send timeout, only if start streamback is redundant (flags used)
		if and(barp_flags,FLAG_EXTO) then
			if _TMR_(0)-tmr_ssb > 10000 then
				' unlock device
				sysi "start streamback timeout, unlocking device"
				' reset balance, in case of IC end
				if mediatype(H_AUDIO) then write H_AUDIO,"0",-8
				state=STA_IDLE : goto 1009
			endif
		endif
	endif
	goto 1000

' PRE PA STATE
1059
	sysd stmsg$+"5: Paging mode"

	' relay control as preconfigured
	if or(rly=0,rly=2) then ioctl 1,0      ' switch off the relay when not in a call
	if or(rly=3,rly=4) then ioctl 1,1      ' switch on the relay when in a call
	
	if prmes then 
		sysi "getting emergency PA msg from IP "+rmthost$(H_UDP_STREAM_PR)
		to_logic_state=TO_LS_BUFFERING
	endif
	if disp1 then write -2,"\x1b[2J",0 : write -2,"Paging... ",0
	if disp2 then write -2,"\x1b[2J",0 : write -2,"PA",0

	autm =_TMR_(0)
	cmdtm=_TMR_(0)
	
	' no BGM ramps
	timer 3,0

	' priority channel
	if prmes=0 then
		sysd "opening udp, par1="+str$(par1)
		x=open_udp_cfg(par1)		' reopen udp, audio to par1 (audio port in PA command)
		write H_AUDIO, "",0			' flush audio buffer
		gosub 3210					' send status=2 to the master that we got the page message
		blk1=0
		' pa start only if no pmres !
		do_pa_start()
	endif

	if aimod=1 then aimod(2)

	' set minimum priority volume
	if prmes then
		if vol_not>vol_pa then 
			setvol(vol_not)
			sysw "current volume too low: changed to "+str$(vol_not*5)+"%"
		else
			setvol(vol_pa)
		endif
	else
		sysd "setting pa volume to "+str$(vol_pa*5)+"%"
		setvol(vol_pa)
	endif
	' reset timeout timer
	tmr_aud=_tmr_(0)+1000
	' switch on for on while receiving stream
	if rly=5 then rly5_tm=_TMR_(0) : ioctl 1,1

	sysd "going into state 5 (paging RX)"
	
1050
	' reading output level
	if mediatype(H_AUDIO) then read H_AUDIO,_Mr$,-15 : oul=lastlen(H_AUDIO)

	' audio stream handling here
	if prmes=1 then l=lastlen(H_UDP_STREAM_PR) else l=lastlen(H_UDP_STREAM)
	if l<0 then
		if rly=5 then rly5_tm=_TMR_(0)
		if prmes=1 then 
			read H_UDP_STREAM_PR,_Mr$,-l 
			write H_AUDIO,_Mr$,-l
		else
			i$=rmthost$(H_UDP_STREAM)
			read H_UDP_STREAM,_Mr$,-l 
			if i$=pa_in_ip$ then write H_AUDIO,_Mr$,-l
		endif
		tmr_aud=_tmr_(0)
	endif
	
	' timeout check
	if _tmr_(0)-tmr_aud>1000 then
		sysi "paging timeout, exiting to idle"
		' simple paging timeout
		if prmes then
			close H_UDP_STREAM_PR
			open "UDP:0.0.0.0:"+str$(paudp) as H_UDP_STREAM_PR
			prmes=0
		else
			close H_UDP_STREAM
		endif
		state=STA_IDLE : goto 1009
	endif

	' ignore anything else if priority paging is active
	if prmes then goto 1055
	
	' if priority message is not active, check for incoming audio on the priority port first
	if and(prpen,lastlen(H_UDP_STREAM_PR)<0) then 
	   sysi "priority message arrived while in paging mode"
	   close H_AUDIO
	   ' stay in the same state, but switch to priority port
	   state=STA_PAGING : prmes=1 : goto 1059 
	endif

	' receive and parse control messages
	gosub 2100

	' direct intercom request from ICG (no call), move to IC.
	if or(cmd=1,cmd=2) then audpd=par1 : state=STA_INTERCOM : goto 1049

	if cmd=3 then 
		cmd=0
		' group member (par4) and higher priority message (lower number)-> start
		sysd "cmd3, prio check"
		tmp$=pa_in_ip$ : p=prio
		if and(par4=1,prio_check()=1) then
			sysd "saving current paging source"
			stack_push(resolve(tmp$),p)
			goto 1059
		endif
		' group member (par4), same priority (par2), different port (par1)
		if and(par2=prio,par4=1,par1<>audpx) then goto 1059
		' same priority, not a member -> stop
		if and(par2=prio,par4=0) then state=1 : goto 1009
	endif
	if cmd=8 then
		sysd "end of paging"
		cmd=0
		if stackpos<>STACK_TOP then
			' there was an lower priority paging
			sysd "back to previous source"
			stack_pop() : if tmp$<>"" then pa_in_ip$=tmp$ : sysd "previous ip="+pa_in_ip$
		else
			' break the link
			close H_UDP_STREAM
			state=STA_IDLE : to_logic_state=TO_LS_IDLE : to_logic_state_old=to_logic_state
			goto 1009
		endif
	endif
1055
	goto 1000

' open_udp
1400
	if mediatype(H_UDP_STREAM) then close H_UDP_STREAM
	open "UDP:0.0.0.0:"+str$(audp) as H_UDP_STREAM
	return

' open_udp_cfg
1500
	LOCAL lport
	' always reopen this handle
	if mediatype(H_UDP_STREAM) then close H_UDP_STREAM
	if par3=0 then
		open "UDP:0.0.0.0:"+str$(lport) as H_UDP_STREAM
	else
	    open "UDP:"+mcast$+":"+str$(lport) as H_UDP_STREAM
	endif
	audpx=lport
	sysd "changing audio to port "+str$(audpx)
	return

' do_pa_start()
1550
	open_aud_cfg()
	' start rx timeout check if in HD
	recv=1
	' no link for paging, otherwise we cannot have priority on multicast
	return

' do_ic_start()
1600
	open_aud_cfg()
	if halfd=1 then
		do_ic_recv()
	else
		do_ic_send()
		recv=1
	endif
	link H_AUDIO,H_UDP_STREAM,linkdst
	return

' do_ic_send()
1700
	' stop rx timeout check if in HD
	if halfd=1 then 
		recv=0
		' flush encoding buffer
		seek H_AUDIO,0
		' note: ICG HD sends the stream anyway ...
		' and we was not expecting it. 
		' Breaking link, closing handles, reopening handles and recreating link 
		' requires now to much time/resources.
		' So muting the speaker through balance (all right) is a quite fast solution.
		sysd "do_ic_send(): setting balance to right (speaker muted)"
		if or(itype=IPAM101,itype=IPAM102,itype=IPAM301,itype=IPAM302) then
			write H_AUDIO,"0",-12
		else
			write H_AUDIO,"10",-8
		endif
	else
		' clear any unwanted setting
		write H_AUDIO,str$(vol_pa),-12
		write H_AUDIO,"0",-8
	endif
	sysd "do_ic_send(): sending stream to "+ip$+":"+str$(audpd)
	linkdst(0,0)=resolve(ip$)
	linkdst(0,1)=audpd
	
	return

' do_ic_recv()
1800
	' start rx timeout check if in HD
	recv=1
	' unmute speaker, balance back to default
	' NOTE: on fast master TX/RX switch this can fail 
	sysd "do_ic_recv(): setting balance to center"
	if or(itype=IPAM101,itype=IPAM102,itype=IPAM301,itype=IPAM302) then
		write H_AUDIO,str$(vol_pa),-12
	else
		write H_AUDIO,"0",-8
	endif
	linkdst(0,0)=0
	linkdst(0,1)=0
	return

' check_timeout()
' this function must be called at every IC/Paging loop cycle
#define TO_BUFFERING 2000
#define TO_PLAYING   1000

2000
	LOCAL audio_zc,audio_pb

	check_timeout=0
	on to_logic_state+1 goto 2001,2002,2003

' TO_LS_IDLE
2001
	' do nothing
	goto 2004

' TO_LS_BUFFERING
2002
	if to_logic_state<>to_logic_state_old then to_logic_check=_TMR_(0) : audio_pb=0
	' read pb status, always
	read H_AUDIO,_Mr$,-17
	x=lastlen(H_AUDIO) : if x<>0 then audio_pb=1
	if _TMR_(0)-to_logic_check>TO_BUFFERING then
		if audio_pb=0 then
			sysd "trying to recover a false pbactive=0"
			read H_AUDIO,_Mr$,-6 : if lastlen(H_AUDIO)>50 then audio_pb=1
		endif
		if audio_pb then
			sysd "check_timeout() : switch to playing state"
			' get first zc and go
			read H_AUDIO,_Mr$,-9 : audio_zc_old=lastlen(H_AUDIO)
			to_logic_check=_TMR_(0) : to_logic_state=TO_LS_PLAYING
		else
			
			sysi "check_timeout() : buffering time-out"
			' no playback, exit
			check_timeout=1 : to_logic_state=TO_LS_IDLE
		endif
	endif
	goto 2004

' TO_LS_PLAYING
2003
	' read zc status, always
	if _TMR_(0)-to_logic_check>TO_PLAYING then
		read H_AUDIO,_Mr$,-9
		audio_zc=lastlen(H_AUDIO)
		if audio_zc-audio_zc_old>=10 then
			sysd "check_timeout() : zero count >= 10, zc,zc_old:"+str$(audio_zc)+","+str$(audio_zc_old)
			read H_AUDIO,_Mr$,-17
			if lastlen(H_AUDIO)=0 then
				check_timeout=1 : to_logic_state=TO_LS_IDLE : goto 2004
			endif
			sysd "check_timeout() : playback active, false timeout"
		endif
		audio_zc_old=audio_zc : to_logic_check=_TMR_(0)
	endif

2004
	to_logic_state_old=to_logic_state
	return

	
' --------------- helper functions

2010	' discard incomming control messages
	if lastlen(H_UDP_BARPMSG)>=0 then return 
	read H_UDP_BARPMSG, _Mr$
	sysd "command received: "+_Mr$
	goto 2010

2030	' set destination ip address
	local _ip
	if idadr$="y" then 
	   ip$=bcast$
	   sysi "calling master by ID ..."
	else
	   if mr_ip(_ip,0)=0 then ip$=bcast$ else ip$=sprintf$("%lA",mr_ip(_ip,0))
	   sysi "calling master by IP ..."
	endif
	return

' incoming control messages, read and parse
2100
	cmd=0
	l=-lastlen(H_UDP_BARPMSG)
	if l<=0 then return		' no control message
	i$=rmthost$(H_UDP_BARPMSG)
	p =rmtport (H_UDP_BARPMSG)
	read 5, _Mr$
	if l<34 then return		' message too short - garbage

	' BAR \0   | vers | msg | rsvd | src ID | dst ID | seq | rsvd | params
	' 1        | 5    | 6   | 7    | 23     | 25     | 27  | 29   | 35
	' 4B       | 1B   | 1B  | 16B  | 2B     | 2B     | 2B  | 6B   | 

	if midget(_Mr$,1,4)<>&H00524142 then return	' magic mismatch
	if midget(_Mr$,5,1)<>1 then return		' unsupported protocol version
	
	if am_i_member(43)=0 then return		' do not care, this command is not for me
	
	cmdtm=_TMR_(0)					' set the command timer only if command to me
	x=midget(_Mr$,6,1)				' message type

	sysd "received command 0x"+sprintf$("%x",x)

	on (x+1) goto 2101,2110,2120,2130,2140,2150,2160,2170,2180
	return

	' INCOMING BARP MSG 0 (not used at time being)
	' --------------------------------------------
2101
	return

	' INCOMING BARP MSG 1 - STOP STREAMBACK
	' --------------------------------------------
2110
	' no params
	cmd=1
	if to_logic_state=TO_LS_IDLE then to_logic_state=TO_LS_BUFFERING
	sysd "received stop streamback"
	return
	
	' INCOMING BARP MSG 2 - START STREAMBACK
	' --------------------------------------------
2120
	' par1=port number
	if l<34+2 then return		' message too short -> ignore
	par1=midget(_Mr$,34+1,2)
	cmd=2
	sysd "received start streamback to port "+str$(par1)
	' store master ip$, could be broadcast, and streamback to master only
	ip$=i$
	return

	' INCOMING BARP MSG 3 - PA GROUP REQUEST
	' --------------------------------------------
2130
	' par1=audio port
	' par2=priority
	' par3=Multicast/broadcast address
	' par4=member yes (1) or no (0)

	if l<34+8+128 then return	' message too short -> ignore
	par1=midget(_Mr$,34+1,2)
	par2=midget(_Mr$,34+1+2,1)
	par3=midget(_Mr$,34+1+2+1+1,4)
	mcast$=sprintf$("%lA",par3)
	par4=am_i_member(34+1+2+1+1+4)
	' get flags now
	par5=midget(_Mr$,7,2)

	sysd "pa port="+str$(par1)+", prio="+str$(par2)+", bcast/mcast address="+mcast$+", group member="+sprintf$("%d",par4)
	if to_logic_state=TO_LS_IDLE then to_logic_state=TO_LS_BUFFERING
	ip$=i$
	cmd=3
	return

	' INCOMING BARP MSG 4 - ACTIVATE RELAY
	' --------------------------------------------
2140
	' par1=relay number
	' par2=time in 100ms
	if l<34+1+1 then return		' message too short -> ignore
	if rly > 0 then 			' ignore this command if we want relay in preconfigured mode 
		sysw "relay in preconfigured mode "+str$(rly)+", cmd-4 ignored!"
		return
	endif
	par1=midget(_Mr$,34+1,1)
	par2=midget(_Mr$,34+1+1,1)
	sysd "activate relay number="+str$(par1)+", time="+str$(par2)
	cmd=4
	return

	' INCOMING BARP MSG 5 - SET VOLUME
	' --------------------------------------------
2150
	if l<34+1 then return		' message too short -> ignore
	cmd=5 
	x=midget(_Mr$,34+1,1)/5
	if am_i_member(34+1+2+1+1+4) then     'set volume to vol
		' Playing BGM in idle state?
		if and(state=STA_IDLE,or (and(bgm_local_chan,bgm_chan=0),bgm_chan>0)) then
			vol_bgm=x
			' do not allow to go over  the maximal BGM volume
			if vol_bgm>vol_bgm_max then vol_bgm=vol_bgm_max
			' do not allow to go below the minimal BGM volume
			if vol_bgm<vol_bgm_min then vol_bgm=vol_bgm_min
			' apply
			sysd "set BGM volume to "+str$(5*vol_bgm)+"%"
			setvol(vol_bgm)
		endif
		if or(state=STA_INTERCOM,state=STA_PAGING) then
			vol_pa=x
			' do not allow to go over  the maximal BGM volume
			if vol_bgm>vol_bgm_max then vol_bgm=vol_bgm_max
			' do not allow to go below the minimal BGM volume
			if vol_bgm<vol_bgm_min then vol_bgm=vol_bgm_min
			' apply
			sysd "set PA volume to "+str$(5*vol_pa)+"%"
			setvol(vol_pa)
		endif
		gosub 7000
		x8tmr=_TMR_(0)
		x8idl=0
	endif
	cmd=0                           ' command processed, no further processing needed
	return

	' INCOMING BARP MSG 6 - SET BGM CHANNEL
	' --------------------------------------------
2160
	if l<34+1+1+6+128 then return   ' message too short -> ignore FIXME(message length)?
	cmd=6
	par1=midget(_Mr$,34+1,1)
	par2=midget(_Mr$,34+1+1,1)
	par4=am_i_member(34+1+2+1+1+4)
	if par4 then bgm_chan=par1: gosub 3500
	x8tmr=_TMR_(0)
	x8idl=0
	cmd=0      ' command processed, no further processing needed
	return

	' INCOMING BARP MSG 7 - CALL REJECTED
	' --------------------------------------------
2170
	if l<34+1 then return		' message too short -> ignore
	
	' reject message do not use address map, no need to call am_i_member function
	' just check the message header, and ignore if not for me
	if midget(_Mr$,25,2)<>id then return
	midset cm7rs$,1,1,midget(_Mr$,35,1)
	
	sysd "call rejected, reason: "+str$(midget(_Mr$,35,1))

	cmd=7
	return

	' INCOMING BARP MSG 8 - END PAGE GROUP
	' --------------------------------------------
2180
	if am_i_member(34+1+2+1+1+4) then cmd=8
	return

' open_aud_cfg(), reopen audio as preconfigured
2200
	if codec=1 then return
	sysd "codec switch, closing audio if open ..."

	if mediatype(H_AUDIO) then
		setvol(0)
		' clean garbages
		write H_AUDIO, "", 0 : delay 50
		close H_AUDIO
	endif
	' set audio parameters
	open aud$ as H_AUDIO

	' loopback disabled
	write H_AUDIO,str$(&H4000),-13

	sysd "opening audio as preconfigured, " + aud$
	sysd "setting ... "
	sysd "ad_gain  : " + str$(ad_gain)
	sysd "mic_gain : " + str$(mic_gain)
	sysd "inp      : " + str$(inp)
	sysd "stereo   : " + str$(stereo)

	write H_AUDIO,str$(ad_gain),-2      'set A/D Gain
	write H_AUDIO,str$(mic_gain),-1     'set Mic Gain
	write H_AUDIO,str$(inp),-3          'set input source to mic/line
	write H_AUDIO,str$(stereo),-4       'set mono/stereo

	open_aud_cfg=1
	codec=1
	return

 ' reopen audio as fixed aLaw 8Khz
2210
	if codec=2 then return
	sysd "codec change, closing audio if open..."
	if mediatype(H_AUDIO) then
		' low blub
		y = vol_pa
		for x = 1 to 10
			if y>0 then setvol(y)
			delay 50 : y = y - 1
		next x
		' clean garbages
		write H_AUDIO, "", 0 : delay 50
		close H_AUDIO
	endif
	tmp$ = "AUD:11,0,3336,0"
	sysd "opening audio as aLaw 8khz, " + tmp$
	open tmp$ as 7
	' loopback disabled
	write H_AUDIO,str$(&H4000),-13
	delay 50
	' using current paging volume for rings and sounds
	setvol(vol_pa)
	open_aud_al8=1
	codec=2
	return

' set volume vol
2220
	LOCAL vset

	if mediatype(H_AUDIO) then
		write H_AUDIO,str$(vset),-12        'set volume to vol
		setvol=1
	else
		' if the codec is closed for some reason (most likely when
		' try to switch to BGM channel 0 and the local BGM streamer 
		' is not set), open it temporary to store the volume, and 
		' close it again
		sysi "audio codec closed, reopen to store volume ..."
		codec=0
		open_aud_mp3()	' does not matter really which codec we will use
		if mediatype(H_AUDIO) then
			write H_AUDIO,str$(vset),-12     ' set volume to vol
			setvol=1
			close H_AUDIO : codec=0
		else
			' codec open failed ?
			sysw "codec reopen failed, volume change not stored!"
			setvol=0
		endif
	endif
	return
	
 ' open_aud_mp3() : reopen audio as MP3 for BGM
2230
	if codec=3 then return
	sysd "codec change to MP3, closing audio if open..."
	if mediatype(H_AUDIO) then
		' coming here from pa, low blub before closing
		y = vol_pa
		for x = 1 to 10
			if y>0 then setvol(y)
			delay 50 : y = y - 1
		next x
		' clean garbages
		write H_AUDIO, "", 0 : delay 50
		close H_AUDIO
	endif

	tmp$ = "AUD:1,1,0,1000"
	open tmp$ as H_AUDIO
	' set mono/stereo
	write H_AUDIO,str$(stereo),-4
	' loopback disabled
	write H_AUDIO,str$(&H4000),-13
	vol_bgm_ramp=0
	' mute at the beginning, we'll ramp it up
	setvol(0)
	codec=3
	
	open_aud_mp3=1
	return

' open_aud_not() : open priority audio as RTP
2240
	dim msdel,qlty,bit5,mpf
	
	' break link if any
	if mediatype(H_AUDIO) then
		setvol(0)
		' clean garbages
		read  H_AUDIO, _Mr$, -6
		write H_AUDIO, "", 0 : delay 50
		close H_AUDIO
	else
		syse "error opening audio for notification" : return
	endif
	if mediatype(H_UDP_STREAM) then close H_UDP_STREAM

	if isrtp=1 then
		p=and(midget(_Mr$,2,1),&H7f)
		sysd "notification payload type:"+str$(p)
		if p = 0   then qlty = &H0008 : bit5 = 0 : msdel = 444 : goto 2241 'u-law, 8bit, mono, 8kHz
		if p = 8   then qlty = &H0108 : bit5 = 0 : msdel = 444 : goto 2241 'A-law, 8bit, mono, 8kHz
		if p = 10  then qlty = &H122C : bit5 = 0 : msdel =  79 : goto 2241 'PCM,  16bit, MSB first, signed, 44.1kHz stereo, left channel first
		if p = 11  then qlty = &H022C : bit5 = 0 : msdel = 110 : goto 2241 'PCM,  16bit, MSB first, signed, 44.1kHz mono
		if p = 14  then mpf = 1                  : msdel = 600 : goto 2241 'MPEG audio
		if p = 96  then qlty = &H0208 : bit5 = 0 : msdel = 444 : goto 2241 'PCM,  16bit, MSB first, signed,  8kHz mono
		if p = 97  then qlty = &H0018 : bit5 = 0 : msdel = 188 : goto 2241 'u-law, 8bit, mono, 24kHz
		if p = 98  then qlty = &H0118 : bit5 = 0 : msdel = 188 : goto 2241 'A-law, 8bit, mono, 24kHz
		if p = 99  then qlty = &H0218 : bit5 = 0 : msdel = 188 : goto 2241 'PCM,  16bit, MSB first, signed, 24kHz mono
		if p = 100 then qlty = &H0020 : bit5 = 0 : msdel = 156 : goto 2241 'u-law, 8bit, mono, 32kHz
		if p = 101 then qlty = &H0120 : bit5 = 0 : msdel = 156 : goto 2241 'A-law, 8bit, mono, 32kHz
		if p = 102 then qlty = &H0220 : bit5 = 0 : msdel = 152 : goto 2241 'PCM,  16bit, MSB first, signed, 32kHz mono
		if p = 103 then qlty = &H1230 : bit5 = 0 : msdel =  72 : goto 2241 'PCM,  16bit, MSB first, signed, 48kHz stereo, left channel first
		if p = 104 then qlty = &H0208 : bit5 = 1 : msdel = 444 : goto 2241 'PCM,  16bit, LSB first, signed,  8kHz mono
		if p = 105 then qlty = &H0218 : bit5 = 1 : msdel = 188 : goto 2241 'PCM,  16bit, LSB first, signed, 24kHz mono
		if p = 106 then qlty = &H0220 : bit5 = 1 : msdel = 152 : goto 2241 'PCM,  16bit, LSB first, signed, 32kHz mono
		if p = 107 then qlty = &H122C : bit5 = 1 : msdel =  79 : goto 2241 'PCM,  16bit, LSB first, signed, 44.1kHz stereo, left channel first
		if p = 108 then qlty = &H1230 : bit5 = 1 : msdel =  72 : goto 2241 'PCM,  16bit, LSB first, signed, 48kHz stereo, left channel first
		if p = 109 then qlty = &H000C : bit5 = 0 : msdel = 316 : goto 2241 'u-law, 8bit, mono, 12kHz
		if p = 110 then qlty = &H010C : bit5 = 0 : msdel = 316 : goto 2241 'A-law, 8bit, mono, 12kHz
		if p = 111 then qlty = &H020C : bit5 = 0 : msdel = 316 : goto 2241 'PCM,  16bit, MSB first, signed, 12kHz mono
		if p = 112 then qlty = &H020C : bit5 = 1 : msdel = 316 : goto 2241 'PCM,  16bit, LSB first, signed, 12kHz mono
		' unkown payload, ap receive raw udp ? try it ... 
		goto 2242
2241	
		sysd "setting RTP codec for priority message ..."

		if mpf=1 then
			aud_not$="AUD:1,1,0,"+str$(msdel)
		else
			aud_not$="AUD:11,"+str$(or(1,shl(bit5,5)))+","+str$(or(qlty,shl(1,11)))+","+str$(msdel)
		endif
		sysd "opening notification as "+aud_not$
		open aud_not$ as H_AUDIO
		codec=4
	else
2242	open_aud_cfg()
	endif
	
	' loopback disabled
	write H_AUDIO,str$(&H4000),-13
	write H_AUDIO,str$(ad_gain),-2      ' set A/D Gain
	write H_AUDIO,str$(mic_gain),-1     ' set Mic Gain
	write H_AUDIO,str$(inp),-3          ' set input source to mic/line
	write H_AUDIO,str$(stereo),-4       ' set mono/stereo
	return

3200
	if _TMR_(0)-kplvt<30000 then return			' send keepalive status message every 30s
3201	' send status message, reset keepalive timer
	kplvt=_TMR_(0)

3210	' jump here if immediate status is needed (no timer dependent)
	midset sts$,6,1,128			' client status report
	stslen=40
	if state=1 then 			' idle state
		if bgm_chan<>0 then
			midset sts$,35,1,1                ' 1 - we play BGM
			midset sts$,35+1,1,255            ' BGM is always with priority=255
			midset sts$,35+1+1,2,-bgm_chan    ' and channel=bgm_chan
			midset sts$,39,1,vol_bgm          ' current volume is
			midset sts$,40,1,bgm_start_chan 
			' Now put here the "extra" info needed for the ICgraph dynamic labels
			chan$=" "+str$(bgm_chan)
			midcpy sts$,41,len(chan$),chan$
			midset sts$,41+len(chan$),1,0     ' terminate the message with "0"
			stslen=41+len(chan$)
		else 
			midset sts$,35,2,0                ' 0 - idle: status=0, prio=0
			midset sts$,35+1+1,2,0            ' channel=0
			midset sts$,39,1,vol_pa           ' current volume is
			midset sts$,40,1,0 
		endif
	endif
	if or(state=2,state=3) then midset sts$,35,2,3	' call request
	if state=STA_INTERCOM then midset sts$,35,2,2	' direct connection: status=2, prio=0
	if state=STA_PAGING   then midset sts$,35,1,2: midset sts$,36,1,prio

	sysd "status 0x"+sprintf$("%x",midget(sts$,35,1))+" sent to "+bcast$
	
	' always send status as broadcast
	write 5, sts$, stslen, bcast$, stsp
	
	return
	
' send call request (cmd=129)
3250
	midset sts$,6,1,129			' client call request
	midset sts$,35,1,127		' for the moment just hardwire it calpr
	midset sts$,36,1,len(name$) ' length of the client description
	midcpy sts$,37,len(name$),name$  ' client description
	stslen=36+len(name$)
	if idadr$="y" then
	   ' broadcast with destination ID of the master in the header
	   midset sts$,25,2,mr_ip(mrtcl,1)
	   write 5, sts$, stslen, bcast$, stsp 
	   sysi "sending call request to: Master ID="+str$(mr_ip(mrtcl,1))
	else
	   ' we have master ip addressing, so send unicast
	   ' it will be also broadcasted if ip=0.0.0.0
	   midset sts$,25,2,0
	   write 5, sts$, stslen, ip$, stsp
	   sysi "sending call request to: "+ip$ 
	endif

	return
	
' send custom command message
3260
	midset sts$,6,1,130		                 ' custom command message
	midset sts$,25,2, midget (id130$,1,2)            ' destination ID of the server
	midset sts$,35,1,cm130                           ' custom command number to be executed 
	write 5, sts$, 35, bcast$, stsp 
	sysi "sent custom command="+str$(cm130)+" to ID="+str$(midget (id130$,1,2))
	
	return 

 ' ramp up BGM volume. Can be used for any slow process changes 
3300
	if mediatype(H_AUDIO)=0 then goto 3301
	if or(bgm_chan<>0,bgm_local_chan=1) then
		if cmd=0 then 'only for BGM audio
			if vol_bgm_ramp < vol_bgm then 
				vol_bgm_ramp = vol_bgm_ramp+1
				write H_AUDIO,str$(vol_bgm_ramp),-12
				'ramp volume up to actual volume
				sysd "bgm volume:" + str$(vol_bgm_ramp)
			else
				' disengage, auto
				timer 3,0
			endif
		endif
	endif
3301
	return


3400    ' X8 Volume Source Control
        ' called only if X8 available
	' Behaviour:
	' in idle shows only ch No (one digit) - ex. _1 for ch 1
	' knob press changes to vol/chan up/down mode
	' knob rotate changes vol/chan up/down in 5% steps
	' when changing chan, "c" is displayed in front of ch No -ex. c1
	' when ch No=0, display Lo (Local) if available
	' setting vol=0 turns off display after 5 sec
	' new press/rotate restores the previous vol/ch values
	' Mute display "--"
	
	' do not process IR commands if X8 not present
	if x8avl=0 then goto 3450
	
	' ignore if not in idle
	if state<>1 then return
	
	' check for IR commands
	read -3,ir$
	if ir$="" then goto 3410
	
	' decode IR commands
	midcpy irtmp$,1,3,ir$
	ircmd=val(irtmp$)
	
	if ircmd=11 then x8hit(1)=1                      ' IR V+, reuse the same key event as the knob
	if ircmd=10 then x8hit(2)=1                      ' IR V+, reuse the same key event as the knob
	if ircmd=7 then x8hit(3)=1                       ' IR Mute
	if or(ircmd=6,ircmd=9) then x8hit(4)=1           ' IR Song+, Plist+
	if or(ircmd=5,ircmd=8) then x8hit(6)=1           ' IR Song-, Plist-
	if and(ircmd>13,ircmd<24) then x8hit(5)=1        ' IR digits 0-9
	if ircmd=26 then x8hit(7)=1

	
3410	
	' knob press change between BGM channel/Volume control
	if x8hit(0)then 
		' knob held pressed?
		if x8hit(8) then
			' We enter in custom command mode
			if x8ctm+2000<_TMR_(0) then  x8mod=2: cm130=1 : x8hit(0)=0 : gosub 7400
		else
			' if in command mode, keypress should send the selected command
			if x8mod=2 then gosub 3260 : x8mod=0 : goto 3415
			' process volume/channel change
			if x8mod then
				x8mod=0          ' set to volume control
				gosub 7000
			else 
				x8mod=1          ' set to channel control
				gosub 7100
			endif
3415		x8hit(0)=0
			x8tmr=_TMR_(0)
			x8idl=0
		endif
	endif
	
    ' knob rotate counter clockwise (volume/channel/command down)	   
	if x8hit(1) then 
		if x8mod=1 then 
			' change channel
			bgm_chan=bgm_chan-1
			gosub 3500
		endif
		if x8mod=0 then  
			vol_bgm=vol_bgm-1
			if vol_bgm<vol_bgm_min then vol_bgm=vol_bgm_min
			setvol(vol_bgm)
			gosub 7000
		endif
		if x8mod=2 then
			cm130=cm130-1
			if cm130 < 1 then cm130=1
			sysi "selected command: "+str$(cm130)
			gosub 7400
		endif
		x8tmr=_TMR_(0)
		x8idl=0
		x8hit(1)=0
	endif

	' knob rotate clockwise (volume/channel/command up)
	if x8hit(2) then 
		if x8mod=1 then
			' change channel
			bgm_chan=bgm_chan+1
			gosub 3500
		endif
		if x8mod=0 then
			vol_bgm=vol_bgm+1
			if vol_bgm>vol_bgm_max then vol_bgm=vol_bgm_max
			setvol(vol_bgm)
			gosub 7000
		endif
		if x8mod=2 then
			cm130=cm130+1
			if cm130 > 99 then cm130=99
			gosub 7400
		endif
		x8tmr=_TMR_(0)
		x8idl=0
		x8hit(2)=0
	endif
	
	
	' phantom (generated by IR) X8 buttons
	' IR mute
	if x8hit(3) then 
		if vol_bgm=0 then
			vol_bgm=mutev
			setvol(vol_bgm)
			mutev=0
		else
			mutev=vol_bgm
			vol_bgm=0
			setvol(0)
		endif
		gosub 7000
		x8hit(3)=0
		x8tmr=_TMR_(0)
		x8idl=0
	endif
	
	' IR Channel down
	if x8hit(4)then 
		bgm_chan=bgm_chan-1
		gosub 3500
		x8hit(4)=0
		x8tmr=_TMR_(0)
		x8idl=0
	endif
	
	' IR Channel up
	if x8hit(6)then 
		bgm_chan=bgm_chan+1
		gosub 3500
		x8hit(6)=0
		x8tmr=_TMR_(0)
		x8idl=0
	endif

	
	' IR set channel
	if x8hit(5)then 
		bgm_chan=ircmd-14
		gosub 3500
		x8hit(5)=0
		x8tmr=_TMR_(0)
		x8idl=0
	endif
	
	' IR off/on
	if x8hit(7) then
		if offvl=0 then
			offvl=vol_bgm
			vol_bgm=0
			setvol(vol_bgm)
			if disp2 then write -2,"\x1b[2J",0
		else 
			vol_bgm=offvl
			offvl=0
			setvol(vol_bgm)
			' show volume
			gosub 7000
		endif
		x8hit(7)=0
		x8tmr=_TMR_(0)
		x8idl=0
	endif
	
' jump here when X8 is not present, we need the display idle anyway
3450
	' blink each 200ms when entering custom command > 10 not to confuse it with volume
	if and (x8mod=2, cm130>9) then
		if midget (blink$,1,1)=0 then
			' rewrite it
			write -2,sprintf$("%02d",cm130),0
		endif
		' count in 50 ms steps
		midset blink$,1,1, (midget (blink$,1,1) + 1)
		if midget (blink$,1,1)=4 then
			' clear diplay
			write -2,"\x1b[2J",0 
		endif
		if midget (blink$,1,1)=8 then midset blink$,1,1,0
	endif
	      
	' make sure that the volume is replaced by channel No or Idle message
	' on the display
	if and (x8idl=0,_TMR_(0)>x8tmr+5000) then
		' If mute active, display mute info
		if mutev>0 then 
			if disp1 then write -2,"\x1b[2J",0 : write -2,"Mute ...",0
			if disp2 then write -2,"\x1b[2J",0 : write -2,"--",0
		else
			' Blank X8 screen when vol =0
			if and(vol_bgm=0,disp2=1) then 
				write -2,"\x1b[2J",0
			else
				if bgm_chan>0 then 
					if disp1 then write -2,"\x1b[2J",0 : write -2,"Playing BGM\nchannel "+str$(bgm_chan),0
					if disp2 then write -2,"\x1b[2J",0 : write -2,sprintf$("%2u",bgm_chan),0
				else
					if bgm_local_chan then 
					   if disp1 then write -2,"\x1b[2J",0 : write -2,"Playing local\nBGM channel",0
					   if disp2 then write -2,"\x1b[2J",0 : write -2,"Lo",0
					endif 
				endif
			endif  
		endif
		x8idl=1
		x8mod=0
	endif
	return

' change BGM channel
3500
	timer 3,0
	vol_bgm_ramp=0
	
	' channel number out of range?
	if bgm_chan>bgm_avail_chans then bgm_chan=bgm_avail_chans
	if bgm_chan<1 then bgm_chan=0
	
	sysd "requested switch to BGM channel: "+str$(bgm_chan)+", set flag: "+str$(par2)
	
	if mediatype(6) then 
		' decrease volume before closing the RTP stream
		y = vol_bgm
		for x = 1 to 10
			if y>0 then setvol(y)
			delay 50 : y = y - 1
		next x
		' close the audio first, and do not forget to reset
		' the codec, otherwise the audio will not be reopened!
		if mediatype(7) then close 7 : codec=0
		' A bit redundant check, but better to be done
		if mediatype(6) then close 6
	endif

	' Store the changes in EEPROM before opening the stream
	' to avoid audio clicks

	if and (cmd=6,par2) then
		open "STP:800" as 2
		read 2,set8$
		midset set8$,40,1,bgm_chan
		sysd "storing BGM start channel "+str$(bgm_chan)+" in the EEPROM ..."
		write 2,set8$,256
		close 2
		sysd "BGM channel stored!"
	endif

	if bgm_chan>0 then
		open_aud_mp3()
		delay 100
		open "UDP:" + bgmip$ +":"+str$(bgm_start_port-1+bgm_chan) as 6    'open BGM stream audio port
		' remove loopback, no line in
		write H_AUDIO,str$(&H4000),-13
		gosub 7100
		link 6,7,bgmln
		delay 100
		timer 3,300
	else
		' bgm from local line, enable loopback
		if bgm_local_chan then bgm_local()
	endif
	'update the status now!
	gosub 3210
	
	return

' --------------------- functions

4000	' am_i_member function
	' checks if we are member of the group (1024bits) starting at position f1 in _Mr$
        
	local f1
	am_i_member=0
	if id=0 then return 				     ' no ID configured, not a member of any group
	if midget(_Mr$,25,2)=id then 
	   am_i_member=1 
	   return                                            ' destination ID is me, ignore address map
	endif

	if and(midget(_Mr$,f1+mbyte,1),mybit)<>0 then am_i_member=1
	return

4200 
	' change aiphone mode
	' 0 - audio off
	' 1 - input (listen)
	' 2 - output (talk)

	local moden
	if aiphn=1 then goto 4209
	if mediatype(7) then on moden+1 goto 4202,4204,4206
	goto 4240

' OFF
4202
	goto 4240

' Spk OFF, Mic ON
4204
	sysd "AIPHONE 1: Spk OFF, Mic ON"
	goto 4240

' Spk ON, Mic OFF
4206
	sysd "AIPHONE 2: Spk ON, Mic OFF"
	goto 4240
	
4209
	' If the mode is the same, then do not change again
	' However, make an exception for state 1, when we
	' change the ai_phone state independently from its
	' current state (see the comment at label 4010)
	if and(str$(moden)=aists$,state<>1) then return	
							
	if mediatype(7) then on moden+1 goto 4210,4220,4230
	goto 4240
	
' Audio OFF
	' do not use the OFF state in idle, set it to DECODING
	' instead to avoid the accoustic feedback.
	' FIXME: This may not be needed for AI_Phone stations
	' using the speaker as microphone! Add a web UI configuration
	' item about the AI_Phone type
4210
	if state=1 then goto 4230
	write 7,"0",-17
	aists$="0"
	goto 4240
	
' encoding (mic ON, spk OFF)
4220
	write 7,"0",-17
	' flush buffers
	seek 7,0
	delay 10
	' activate mic
	write 7,"1",-17
	aists$="1"
	goto 4240

 ' decoding (mic OFF, spk ON)
4230
	write 7,"2",-17
	aists$="2"
4240	
	return

' relay handler
5000
	if rly > 0 then return		' do not swich off the relay after 5 seconds
	' if it is in a preconfigured mode
	if todr<=0 then return
	todr=todr-1
	if todr=0 then ioctl 1,0
	return

'----------------- keyboard handling, read key events
' timer safe
dim q, r
' this routine reads key events and sets the appropriate kbhit() variable to 1 if pressed
6000
	if lastlen(-2)=0 then goto 6100			' no key input - process selected key events
	read -2,key$,0
	q=midget(key$,1,2)
	' keyboard key pressed
	r=and(q,127)
	if or(r>nkeys,r>xnkeys) then goto 6000					' skip this key
	if and(q,256) then
		' x8 key pressed?	
		if and(q,128) then 
			' Set the command timer only the first time it detects a knobpress
			if r=0 then x8ctm=_TMR_(0) : x8hit(8)=1
			x8hit(r)=1
		else
			' indicate the knob is down
			x8hit(8)=0
		endif
	else
		' keyboard key pressed?
		if and(q,128) then kbhit(r)=1
	endif
	if and(aiphn=1,kbhit(0)=1) then
		if or(aiptm+2000>_TMR_(0),state<>1) then kbhit(0)=0 : sysd "key ignored in state="+str$(state)
	endif
	goto 6000

' process selected key events
6100
	if and(aiphn=1,kbhit(0)=1) then
		if or(aiptm+2000>_TMR_(0),state<>1) then kbhit(0)=0 : sysd "key ignored in state="+str$(state)
	endif
	return

' display volume
7000 
	local vlpct   ' volume in percent
	volpct=vol_bgm*5
	if volpct=100 then volpct=99
	if disp1 then write -2,"\x1b[2J",0 : write -2,"BGM Volume: "+sprintf$("%2u",vol_bgm*5)+"%",0
	if disp2 then write -2,"\x1b[2J",0 : write -2,sprintf$("%02d",volpct),0
	return

7100    ' display channel
        
	if disp1 then write -2,"\x1b[2J",0 : write -2,"Changing to BGM\nchannel "+str$(bgm_chan),0
	if disp2 then write -2,"\x1b[2J",0 : write -2,"c"+str$(bgm_chan),0
	return
	
7200    ' process call reject in states 2 and 3

	if and(midget(cm7rs$,1,1)=2,clnxt$="y") then
		' this one is not available, go to next master in list
		sysi "call rejected, calling next master!"
7210	mrtcl=mrtcl+1
		mrtcl=and(mrtcl,3)
		if mrtcl=mrtst then
			' we looped all masters, no one answered, so play busy tone and exit
			gosub 7300 : state=1 : cmd=0 : goto 1009
		else
			' call by IP ?
			if idadr$="n" then
				' ignore ip address 0.0.0.0
				if mr_ip(mrtcl,0)=0 then goto 7210 else ip_select(mrtcl)
			endif
			' reset the ring timer
			rngst=_TMR_(0)
			' send call request
			gosub 3250
			cmd=0
		endif
	else
		' no loop needed, play busy tone and exit
		sysi "call rejected, closing call ..."
		gosub 7300 : state=1 : cmd=0 : goto 1009
	endif

	return	

	
7300    ' play busy tone
	if mediatype(1) then close 1
	open_aud_al8()
	open "F_R:busy.wav" as 1
	if mediatype(1) then
		' skip headeer
		sysd "seeking busy header"
		seek 1, 58
7310
		read  1, _Mr$
		l = lastlen(1)
		if l>0 then write H_AUDIO, _Mr$, l : goto 7310
		close 1
		' discard queue
		gosub 2010
	endif
	' back to audio cfg, only after busy is played
7320
	read H_AUDIO,_Mr$,-6 : if lastlen(H_AUDIO)>10 then delay 100 :goto 7320
	open_aud_cfg()
	return
	
7400    ' display custom command
	if disp1 then write -2,"\x1b[2J",0 : write -2,"Custom command: "+sprintf$("%2u",cm130)+"%",0
	if disp2 then 
	   if cm130<10 then 
	      write -2,"\x1b[2J",0 : write -2,sprintf$("d%02d",cm130),0
	   else
	      write -2,"\x1b[2J",0 : write -2,sprintf$("%02d",cm130),0
	   endif
	endif
	return
	
'bgm_local()	
7500
	sysd "bgm_local() : setting LO channel"
	
	open_aud_mp3() 
	delay 100
	write H_AUDIO,str$(1),-3
	write H_AUDIO,str$(&H4040),-13
	gosub 7100
	timer 3,300
	
	return

' fix auto setup of RTP delay. Needed because default (0) don't work fine 
' with some IPAM's like 300, vlsi1053
#define IPAM101 1
#define IPAM102 2
#define IPAM301 3
#define IPAM302 4
' set_aud_delay()
8000
	set_aud_delay=0
	' u-aLaw/24kHz
	if or(enc=6,enc=8) then
		' unknow IPAM, we can't assume micronas, can be 100,200,or 300
		if itype=0 then
			' using hw type here
			if or(htype=25,htype=7,htype=19) then set_aud_delay=60 else set_aud_delay=188
		endif
		if or(itype=IPAM101,itype=IPAM301) then set_aud_delay=188
		' IPAM 102/302, set a uniform 100
		if or(itype=IPAM102,itype=IPAM302) then set_aud_delay=100
	endif
	' u-aLaw/8kHz
	if or(enc=7,enc=9) then
		if itype=0 then
			' using hw type here
			if or(htype=25,htype=7,htype=19) then set_aud_delay=80 else set_aud_delay=444
		endif
		' unknow IPAM, we can't assume micronas, can be 100,200,or 300
		if or(itype=IPAM101,itype=IPAM301) then set_aud_delay=444
		' IPAM 102/302, set a uniform 100
		if or(itype=IPAM102,itype=IPAM302) then set_aud_delay=100
	endif

	return

' prio_check()
8010
	prio_check=0

	sysd "priority check: par2="+str$(par2)+" prio="+str$(prio)
	' priority check here
	if par2<prio then
		prio=par2
		' selective input
		pa_in_ip$=i$
		sysd "accepting PA from "+pa_in_ip$
		prio_check=1
	endif
	return

' stack_push()
9000
	LOCAL save_ip, save_prio
	
	if stackpos=0 then 
		syse "page stack full, page source discarded"
		return
	endif
	stack(stackpos,0)=save_ip
	stack(stackpos,1)=save_prio
	stackpos=stackpos-1
	
	return
	
' stack_pop(), result ip in tmp$
9100
	tmp$=""
	if stackpos=STACK_TOP then
		syse "page stack empty, returning null"
		return
	endif
	stackpos=stackpos+1
	prio=stack(stackpos,1)
	tmp$=sprintf$("%lA",stack(stackpos,0))
	
	return

