Probably the best way to show the basic library functions and the library calling conventions is to provide a simple example. The example below simply waits for an incoming call on the first channel of the first E1 port, plays a message and then hangs up. It is assumed that the reader is familiar with the Telecom Engine standard library set and the Aculab Speech module library (CXACUDSP.DLL).
$include "aculab.inc"
int port, chan, vox_chan, x, event;
var filename:64;
main
port=0
chan=1;
vox_chan=1;
filename="hello.vox";
// Make full duplex H.100 bus routing between voice channel and network port/channel
CClisten(port,chan,SMgetslot(vox_chan));
SMlisten(vox_chan, CCgetslot(port, chan));
// Enable inbound calls on this port/channel
CCenablein(port,chan);
// loop waiting for incoming call
while(1)
x=CCwait(port,chan,CC_WAIT_FOREVER,&event);
if(x > 0 and event eq CS_INCOMING_CALL_DETECTED)
break;
endif
endwhile
// Answer the call
CCaccept(port,chan);
// Play a vox file to caller
SMplay(vox_chan,filename);
// Hangup the call
CCdisconnect(port,chan,CAUSE_NORMAL);
// Wait for state to return to IDLE the release call
while(1)
x=CCwait(port,chan,CC_WAIT_FOREVER,&event);
if(x eq CS_IDLE)
break;
endif
endwhile
// release the call
CCrelease(port,chan);
// restart the application to wait for another call..
restart;
endmain
The program should be fairly self explanatory but I will describe the key parts of the program below.
The “aculab.inc” file is provided with the library and defines all the constants that are used with the library such as CC_WAIT_FOREVER, CAUSE_NORMAL, CS_INCOMING_CALL_DETECTED etc. These are be described in more detail in the call control library function library reference (CXACULAB).
The first call: CClisten() simply makes the receiving stream/channel of the call control channel ‘Listen’ to the transmit stream/channel of the Voice channel, so that anything that is output by the voice channel will be heard by the caller.
The second call: SMlisten() makes the receiving stream/channel voice channel ‘listen’ to the transmit stream/channel of the Call control channel, so that any DTMF digits or other audio transmitted by the caller will be heard by the voice channel.
As mentioned above all this is done by switching from and to the extern H.100 or SCBUS.
The CCenablein(port,channel) allows inbound calls to be received on the channel, and then the application goes into a loop waiting for calls.
The CCwait(port,channel,timeout_100ms,&event) function call will wait for the specified timeout (in 10ths of a second) for an event. If the timeout is defined as -1 (CC_WAIT_FOREVER) then the call will not return until an event is found or it is aborted by a CCabort() call. Really the only event that should be received here is CS_INCOMING_CALL_DETECTED but we do a specific check for it anyway in case the channel was in a unknown state when the program started (probably some error handling should be carried out if we found an unexpected event).
Once a CS_INCOMING_CALL_DETECTED event has been received then the call is answered immediately with CCaccept(port,channel) and the voice prompt is played to the caller using the SMplay(vox_chan,filename) function from the CXACUDSP.DLL library.
The call is then disconnected using the CCdisconnect(port,channel) call and the application goes into a loop waiting for the channel to return to the CS_IDLE state before releasing the call with CCrelease(port,channel) and restarting the program to wait for the next call.
There are obviously a number of improvements that can be made to this application to make it more useful. Currently it only waits for and accepts a call on one channel, whereas in a real life situation there would be 30 or more channels on an E1.
Usually, one would have a ‘master’ program which would ‘spawn’ a task to take control of a single channel on a port. In the application below there are 4 E1 ports and so we ‘spawn’ a channel control task for each channel on each E1, something like this:
int port, channel;
const MAX_PORTS=4
const MAX_CHANNELS=32;
main
for(port=1;port <= MAX_PORTS)
for(channel=1;channel <= MAX_CHANNELS;channel++)
// Skip the signalling channel
if(channel <> 16)
// spawn the task called chantask.tex
// and pass the port and channel as arguments
task_spawn("chantask",port,channel);
endif
endfor
endfor
endmain
The chantask.tex application would then be similar to the first example but instead of the port and chan variables being hard-coded, we would instead take these from the arguments passed to the task through the task_spawn() function call:
$include "aculab.inc"
int port, chan, vox_chan, x, event;
var filename:64;
main
port=arg(1);
chan=arg(2);
// Use voice channels sequentially 1..x
vox_chan=1+(port*32+chan);
filename="hello.vox";
// Make full duplex H.100 bus routing between
// voice channel and network port/channel
CClisten(port,chan,SMgetslot(vox_chan));
SMlisten(vox_chan, CCgetslot(port, chan));
// Enable inbound calls on this port/channel
CCenablein(port,chan);
// loop waiting for incoming call
while(1)
x=CCwait(port,chan,CC_WAIT_FOREVER,&event);
if(x > 0 and event eq CS_INCOMING_CALL_DETECTED)
break;
endif
endwhile
... etc
endmain
Also it is likely that rather than playing a fixed prompt, as in the program above, a more usual way of handling incoming calls is to inspect the DID or ANI and make a decision based on these about how to handle the call (usually a table lookup). Typically this would result in the chantask.tex program ‘chaining’ on to another application which then takes control of playing messages and receiving DTMF etc. When the caller hangs up or the application disconnects the call then the application would then ‘chain’ back to the chantask.tex program to wait for another call. The CCgetparm(port,channel,ParmID) function is used to extract call specific parameters such as the DID and ANI.
$include "aculab.inc"
int port, chan, vox_chan, x, event;
var filename:64, did:64,ani:64, service_name:64;
main
port=arg(1);
chan=arg(2);
// Use voice channels sequentially based on the port/channel
vox_chan=1+(port*32+chan);
filename="hello.vox";
// Make full duplex H.100 bus routing between
// voice channel and network port/channel
CClisten(port,chan,SMgetslot(vox_chan));
SMlisten(vox_chan, CCgetslot(port, chan));
// Enable inbound calls on this port/channel
CCenablein(port,chan);
// loop waiting for incoming call
while(1)
x=CCwait(port,chan,CC_WAIT_FOREVER,&event);
if(x > 0 and event eq CS_INCOMING_CALL_DETECTED) break; endif
endwhile
// Get the CLID and DID
did=CCgetparm(port,chan,CP_DESTINATION_ADDR);
ani=CCgetparm(port,chan,CP_ORIGINATING_ADDR);
// Use the DID to decide which application to chain
// This usually will be a database lookup based on the DID
service_name=DID_LOOKUP(did);
// A blank service_name indicates unknown DID number
if(service_name strneq "")
// Answer the call
CCaccept(port,chan);
// Chain on to the IVR service task
task_chain(service_name,port,channel);
// If we get here then the chain call failed (ie. Invalid TEX file)
endif
// Hangup the call
CCdisconnect(port,chan,CAUSE_NORMAL);
// Wait for state to return to IDLE the release call
while(1)
x=CCwait(port,chan,CC_WAIT_FOREVER,&event);
if(x eq CS_IDLE) break; endif
endwhile
// release the call
CCrelease(port,chan);
// restart the application to wait for another call..
restart;
endmain