An Example of a Simple Application Written in C/C++

To illustrate the huge learning curve and code maintenance required to write applications in low level languages such as C or C++, below is the source code from a simple application written in C that has similar functionality the the 50 line example given for the Telecom Engine:


////////////////////////////////////////////////////////////////////////////////////////
//
//    example.c  - Simple voting app.   Play menu, get DTMF, display result..
//
//    Main entry point for this application.  Links in the required modules for the
//    application, i.e. call-control, switching and Prosody.
//
////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////
//                   
//    Standard C Includes       
//                   
////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>

//////////////////////////////////////
//                       
//    V6 API Header File Include       
//    Statements               
//                       
//////////////////////////////////////

#include "acu_type.h"
#include "res_lib.h"
#include "cl_lib.h"
#include "sw_lib.h"
#include "smdrvr.h"
#include "smrtp.h"
#include "iptel_lib.h"
#include "mg_lib.h"
#include "eventset.h"

//////////////////////////////////////   
//    SDK Header File Include       
//    Statements           
//////////////////////////////////////

#include "acu_bin.h"
#include "acu_os_port.h"

#include "switching.h"
#include "call_control.h"
#include "prosody_common.h"
#include "prosody.h"
#include "card.h"


#include "smbesp.h"        // Basic Speech Processing functionallity
#include "smhlib.h"        // High-level Speech functionallity
#include "smwavlib.h"        // Wav file replay/record funtions
#include "eventset.h"

#include "settings.h"
#include "vmp_wrapper.h"


ACU_INT setup_display( void );
void ShutdownSystem( void );
void setup_prosody( void );
void free_prosody_cs( void );

//global head of a linked list of card_data structs
ACU_QQ_HDR    CARD_LIST;

ACU_INT main( void )
{
    ACU_ERR        result        = 0;
    ACU_UINT    this_card    = 0;

    CARD_DATA* card_data = NULL;

    ACU_SNAPSHOT_PARMS        snapshot_parms;

    INIT_ACU_STRUCT( &snapshot_parms );

    //create the conn_calls_cs critical section
    result = init_call_cs();
    if( result != 0 )
    {
        printf("failed to create conn_call_cs critical section\n");

        return result;
    }

    //setup all the H100 resources for the system
    result = init_h100_resources();
    if( result != 0 )
    {
        printf("h100 resource setup failed\n");

        return result;
    }

    result = setup_display();
    if( result != 0 )
    {
        printf("Error: Could not set-up display\n");

        return result;
    }

    //allocate the global critical section
    setup_prosody();

    //first determine the number of cards in the system and their serial numbers
    result = acu_get_system_snapshot( &snapshot_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to get system snapshot" );

        return result;

    }

    ////////////////////////////////////////////////////
    //
    //    For each card we have detected carry out the following:
    //    1. allocate memory for the card_data struct
    //    2. Start the card - includes starting call control and prosody activity
    //    3. Add the card data struct to the CARDS linked list
    //
    /////////////////////////////////////////////////////

    for( this_card = 0; this_card < snapshot_parms.count; this_card++ )
    {
       
        card_data = acu_os_alloc( sizeof(CARD_DATA) );

        if( card_data == NULL )
        {
            acu_os_printf_at(ERROR_COLUMN, ERROR_ROW, "Error : Unable to alloc memory for card_Data");

            return ERR_ACU_OS_NO_MEMORY;

        }

        StartCard(card_data, snapshot_parms.serial_no[this_card]);

        //add the card to the global linked list of cards
        acu_qq_add_tail( &CARD_LIST, &card_data->node ,card_data );
    }

    acu_os_printf_at( 1, 1, "System initialised and Ready" );

   

    //Wait here for a key to be pressed to indicate the application should be shutdown
    acu_os_get_next_keypress();

    //Function which shutdowns the system in an orderly fashion
    ShutdownSystem();

    //free the critical section
    free_call_cs();

    //free the prosody resource cs
    free_prosody_cs();

    free_h100_resources();

    acu_os_uninitialise_screen();

    return result;
}



//////////////////////////////////////////////////////////////////////
//
//    setup_display
//
//    Set-up the on screen display.
//
//    Returns 0 for success, or a negative value indicating the error.
//
//////////////////////////////////////////////////////////////////////
ACU_INT setup_display( void )
{
    ACU_ERR result = 0;

    result = acu_os_initialise_screen();
    if ( result != 0 )
    {
        printf("Error: Could not initialise screen\n");
    }
    else
    {
        // Initialised screen, now clear it:
        acu_os_clear_scr();

        // Set-up the data for our display
        acu_os_printf_at( CONTESTANT_COLUMN, VOTE_ROW, "Gareth Gates");
        acu_os_printf_at( CONTESTANT_COLUMN, VOTE_ROW + 1, "Darius");
        acu_os_printf_at( CONTESTANT_COLUMN, VOTE_ROW + 2, "So Solid Crew");
        acu_os_printf_at( CONTESTANT_COLUMN, VOTE_ROW + 3, "Will Young");
        acu_os_printf_at( CONTESTANT_COLUMN, VOTE_ROW + 4, "Celiene Dion");

        acu_os_printf_at( VOTE_COLUMN, VOTE_ROW, "0");
        acu_os_printf_at( VOTE_COLUMN, VOTE_ROW + 1, "0");
        acu_os_printf_at( VOTE_COLUMN, VOTE_ROW + 2, "0");
        acu_os_printf_at( VOTE_COLUMN, VOTE_ROW + 3, "0");
        acu_os_printf_at( VOTE_COLUMN, VOTE_ROW + 4, "0");

        acu_os_printf_at( ACTIVE_CALL_COLUMN, ACTIVE_CALL_ROW, "Active calls:");
        acu_os_printf_at( ACTIVE_CALL_COUNT_COLUMN, ACTIVE_CALL_ROW, "0");
    }

    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    ShutdownSystem
//
//    For each card in the system calls the ShutdownCard function. Once the
//    card has been shutdown frees memory associated with the card.
//
//////////////////////////////////////////////////////////////////////

void ShutdownSystem( void )
{
    CARD_DATA* card_data = NULL;

    //obtain the head of the card linked list
    card_data = acu_qq_get_head( &CARD_LIST );

    /*Logic for the loop below:

    while the card data pointer is not equal to NULL:
    (if the pointer is equal to NULL we have removed all cards from the system and should exit the loop)

    Shutdown the card

    Get the next card from the linked list*/

    while( card_data != NULL )
    {
        ShutdownCard( card_data );

        acu_os_free( card_data );

        card_data = acu_qq_get_head( &CARD_LIST );

    }

    //destroy the critical section protecting the vote array
    destroy_vote_cs();
}



//////////////////////////////////////////////////////////////////////
//
//    setup_prosody
//
//    This function allocates a critical section which is used to access
//    the linked list of prosody resources
//
//////////////////////////////////////////////////////////////////////

void setup_prosody( void )
{
    free_resource_lock = acu_os_create_critical_section();
    if( free_resource_lock == NULL )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to create free_resource_lock critical section");

        exit( 1 );
    }

    vote_lock = acu_os_create_critical_section();
    if( vote_lock == NULL )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to create free_resource_lock critical section");

        exit( 1 );
    }

}

//free the gloabl critical section
void free_prosody_cs( void )
{
    acu_os_destroy_critical_section( free_resource_lock );
}

////////////////////////////////////////////////////////////////////////////////
//
//   
//   
//    The following contains all of the functions for handlgin call control on a card
//    i.e.
//    - opening an incoming call
//    - handling the events generated by these calls
//
////////////////////////////////////////////////////////////////////////////////


#define EVENT_HANDLER_WAIT        500        // Length of time we should wait for a call-control event

#define MAX_TIMESLOTS            32        //maximum number of timeslots

#define MAX_PORTS                1        //the number of ports to be used for this application


//////////////////////////////////////////////////////////////////////
//
//    init_call_control
//
//    This function determines the number of ports on a card. Then
//    opens the port for calls and spawns a port_call_control_thread
//
//    Return 0 for success - a negative value indicating an error
//
//////////////////////////////////////////////////////////////////////

ACU_INT init_call_control( CARD_DATA* card_data )
{
    ACU_ERR            result            = 0;
    ACU_UINT        this_port        = 0;
    ACU_INT            first_ip_port    = 0;
    ACU_INT            max_ip_channels_port_port = 0;

    CARD_INFO_PARMS        card_info_parms;

    INIT_ACU_STRUCT( &card_info_parms );

    card_info_parms.card_id = card_data->card_id;

    //obtain information about the card
    result = call_get_card_info( &card_info_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to get card information %d", result );

        return result;
    }

    //record the number of ports on the card
    card_data->num_of_ports = card_info_parms.ports;

    /*

    For each port on this card carry out the following:

    1. Open the port
    2. Spawn a thread which controls the call control for the port
    3. Then add the port_data struct to the tail PORTS linked list

    */

    for( this_port = 0; this_port < card_data->num_of_ports; this_port++ )
    {
        ACU_ERR        result = 0;

        ACU_OS_THREAD* thread_id = NULL;

        OPEN_PORT_PARMS        open_port;
        PORT_INFO_PARMS        port_info;

        PORT_DATA* port_data = NULL;

        INIT_ACU_STRUCT( &open_port );
        INIT_ACU_STRUCT( &port_info );

        //allocate memory for the port_data struct
        port_data = acu_os_alloc( sizeof(PORT_DATA) );

        open_port.port_ix = this_port;
        open_port.card_id = card_data->card_id;

        //open the port so we get a port_id which can be used with the API
        result = call_open_port( &open_port );
        if( result != 0 )
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to open port %d", result );

            return result;
        }

        //record the port_id, port_ix and terminate flag values
        port_data->port_id                = open_port.port_id;
        port_data->port_ix                = this_port;
        port_data->terminate            = ACU_OS_FALSE;
        port_data->call_direction        = IN_CALL;    //specify the direction of calls on this port
        port_data->num_calls_to_open    = 0;//specify the  number of calls to open on this port - 0 meaning open all possible calls
       
        port_info.port_id = port_data->port_id;

        result = call_port_info ( &port_info );
        if( result != 0 )
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Unable to obtain port info for port <%x> on card <%x>", port_data->port_id, card_data->card_id );

            return result;
        }

        port_data->port_type = port_info.port_type;

        //check if this is an IP Port
        if( port_data->port_type == ACU_PORT_CAP_IP )
        {
            if( first_ip_port == 0 )
            {
                //check if there is another port on the card must be an IP Port so we will have to half the number of channels we are using
                if( card_data->num_of_ports - (this_port+1) )
                {
                    max_ip_channels_port_port = port_info.channel_count / 2;
                }
                else
                {
                    //there is only one IP Port it will use all the channels on this card
                    max_ip_channels_port_port = port_info.channel_count;
                }
            }

            //work out if the port is H323 or SIP
            port_data->ip_port_type = call_type( port_data->port_id );

            //increment this counter as we have dealt with the first ip port now
            first_ip_port++;
        }

        if( port_info.port_type == ACU_PORT_CAP_IP )
        {
            port_data->num_calls_to_open = max_ip_channels_port_port;
        }
       
       
        /*
        Spawn a thread which handles the call control for this port
        */
        thread_id = acu_os_create_thread(port_call_control_thread, port_data );
        if( thread_id == NULL)
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to start port call control thread" );

        }
        else
        {
            //record the thread_id of the port_call_control_thread we have jus spawned
            port_data->thread_id = thread_id;

            //add the port_data struct to the tail of the PORTS linked list
            acu_qq_add_tail( &card_data->PORTS, &port_data->node, port_data );

        }

    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    port_call_control_thread
//
//
//    This is the call-control thread that is responsible for handling
//    all of the call-control events that occur on a port.
//    The thread determines which call that an event is for and performs
//    the appropriate action, e.g. accepting an incoming call.
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

ACU_INT port_call_control_thread( void* param )
{
    ACU_ERR        result            = 0;
    ACU_INT        calls_active    = 0;    // Number of calls currently active in the system
    ACU_INT        timeslot        = 0;
    ACU_INT        connected_calls = 0;    // The number of connected calls in the system

    ACU_SDK_WAIT_OBJECT* queue_wait_object = NULL;

    STATE_XPARMS                    event_parms;
    ACU_QUEUE_WAIT_OBJECT_PARMS        queue_wo_parms;

    CALL_DATA        *call_data = NULL;    // Call data of current call

    //conver the argument into something meaningful
    PORT_DATA* port_data = (PORT_DATA*)param;

    INIT_ACU_STRUCT( &event_parms );
    INIT_ACU_STRUCT( &queue_wo_parms );

    /*
    Allocate an event queue for the port
    */
    result = allocate_port_event_queue( port_data );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to allocate port event queue" );

        return result;
    }

    /*
    Set the previously allocate port event queue to be the default event queue for this port
    */
    result = set_default_port_queue( port_data );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to set default port queue" );

        return result;
    }

    /*
    Obtain an operating system specific wait object for the event queue we have just allocated
    */

    queue_wo_parms.queue_id = port_data->queue_id;

    result = acu_get_event_queue_wait_object( &queue_wo_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN , ERROR_ROW, "Error: Unable to obtain a port queue wait object" );

        return result;
    }

    /*
    Convert the OS specific wait object to an ACU_SDK_WAIT_OBJECT
    */

    queue_wait_object = acu_os_convert_wait_object( queue_wo_parms.wait_object );
    if( queue_wait_object == NULL )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW , "Error: Unable to convert wait objects" );

    }

    /*
    Open all calls on the port
    */

    //pass in the direction of the calls and the number of calls
    calls_active = open_calls_on_port( port_data );

    /*
    Now wait for events on the port until there are no more active calls
    */

    while( calls_active != 0 )
    {
        //wait here until an event occurs on the port
        result = acu_os_wait_for_wait_object( queue_wait_object, EVENT_HANDLER_WAIT );

        if( result == ERR_ACU_OS_NO_ERROR )
        {
            //wait object has been signalled - there is an event on the queue that needs to be collected
            call_data = get_call_data_from_queue( port_data );

            //if call_data is NULL we have not been able to obtain the call_data token
            if(call_data == NULL )
            {
                acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to get call_data struct from event queue " );

                //fatal error - cannot recover from this
                exit( 1 );

            }

            event_parms.handle = call_data->handle;

            //obtain the actual event that has occured
            result = call_event( &event_parms );
            if( result != 0 )
            {
                acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Call event failed <%d>", result );

                exit( 1 );
            }
            else
            {
                //if the handle is zero (which isn't valid) break and return to the top of the loop
                if( event_parms.handle == 0 )
                {
                    continue;
                }

                switch( event_parms.state )
                {

                case EV_WAIT_FOR_OUTGOING:

                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW -3, "wait out" );

                    break;

                case EV_OUTGOING_PROCEEDING:
                    // The out going call is proceeding
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "proceed");
                    break;
               
                case EV_OUTGOING_RINGING:
                    // Outgoing call got ring tone
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "ringing");
                    break;

                case EV_WAIT_FOR_INCOMING:

                    //wait for an incoming call
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "wait in");

                    break;

                case EV_INCOMING_CALL_DET:

                    //an incoming call has been detected
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "in det");

                    result = get_call_details( call_data );

                    if ( result != 0 )
                    {
                        // Failed getting details, clear call
                        disconnect_call( call_data );
                    }
                    else
                    {
                        // Got the call's details now we'll call "alert_call" to
                        // determine what to do next:
                        result = alert_call( call_data, port_data );
                        if ( result != 0 )
                        {
                            // Failed alerting call, clear call
                            disconnect_call( call_data );
                        }
                    }

                    break;

                case EV_WAIT_FOR_ACCEPT:

                    //wait for accept
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "accept");

                    //accept the call
                    result = accept_call( call_data );
                    if ( result != 0 )
                    {
                        // Couldn't accept call, clear call:
                        disconnect_call( call_data );
                    }

                    break;

                case EV_DETAILS:

                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "details");

                    // More call details have arrived (usually CLI/DDI digits)
                    // Get the call's details:
                    result = get_call_details( call_data );
                    if ( result != 0 )
                    {
                        // Failed getting details, clear call
                        disconnect_call( call_data );
                    }
                    else
                    {
                        if( port_data->call_direction == IN_CALL )
                        {
                            // Got the call's details now we'll call "alert_call" to
                            // determine what to do next:
                            result = alert_call( call_data, port_data );
                            if ( result != 0 )
                            {
                                // Failed alerting call, clear call
                                disconnect_call( call_data );
                            }
                        }
                    }

                    break;

                case EV_CALL_CONNECTED:

                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "connect");
               
                    //////////////////////////////////////////////////////////
                    //
                    // This call has moved to the connected state, so get the
                    // call's details and connect to a free Prosody resource
                    // thread.
                    //
                    //////////////////////////////////////////////////////////

                    result = get_call_details( call_data );
                    if ( result != 0 )
                    {
                        // If we cannot get the details then we'll be unable to
                        // connect to the Prosody resources, so clear the call
                        disconnect_call( call_data );
                    }
                    else
                    {
                        // Incremented the connected calls' count:
                        ++connected_calls;
                       
                        call_data->connected = ACU_OS_TRUE;

                        // Connect to a Prosody resource:
                        result = start_prosody( call_data, port_data );
                        if ( result != 0 )
                        {
                            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to associate call with Prosody Resource" );
                           
                            disconnect_call( call_data );
                        }
                        else
                        {
                            // Display the new count
                            acu_os_printf_at( ACTIVE_CALL_COUNT_COLUMN, ACTIVE_CALL_ROW, "%d", connected_calls);
                        }
                    }

                    break;

                case EV_REMOTE_DISCONNECT:

                    // Far-end user has hung-up.
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "rem disc");

                    // Now we can initiate call clearing:
                    disconnect_call( call_data );

                       
                    break;

                case EV_IDLE:

                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW - 3, "idle     ");

                    //disconnect our call from the prosody resource
                    stop_prosody( call_data );

                    //one less active call
                    --calls_active;

                    //release the call
                    release_call( call_data );
           

                    if( call_data->connected == ACU_OS_TRUE )
                    {
                        //this call was connected so one less connected call now
                        --connected_calls;

                        //display the new connected call count
                        acu_os_printf_at( ACTIVE_CALL_COUNT_COLUMN, ACTIVE_CALL_ROW, "%d", connected_calls );
                    }

                    //check the state of the terminate flag to see if the call should be reopened or not
                    if ( port_data->terminate == ACU_OS_FALSE )
                    {
                       
                        // Record the last call's timeslot:
                        timeslot = call_data->ts;

                        //need to remove the call from the linked list
                        acu_qq_extract( &port_data->CALLS, &call_data->node );

                        //clear the call data struct here
                        memset( call_data, 0, sizeof( CALL_DATA ));

                        //assign the timeslot that the previous call was on
                        call_data->ts = timeslot;

                        if( port_data->call_direction == IN_CALL )
                        {
                            result = open_call_incoming( call_data, port_data );
                        }
                        if( port_data->call_direction == OUT_CALL )
                        {
                            result = open_call_outgoing( call_data, port_data );
                        }
                        if( result != 0 )
                        {
                            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Couldn't open call %d %d %d ", port_data->port_id, call_data->handle, result );

                            exit( 1 );
                        }
                       
                        ++calls_active;

                        acu_qq_add_tail( &port_data->CALLS, &call_data->node, call_data );
                    }
                           
                    else
                    {
                        //need to remove the call from the linked list
                        acu_qq_extract( &port_data->CALLS, &call_data->node );

                        // Application is terminating, so we want to free the memory we allocated
                        // for this call:

                        acu_os_free(call_data);
                    }
                   
                    break;

                default:

                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW,
                            "Unknown event occurred: ignored <%X> H:%X>", event_parms.state,
                            event_parms.handle);

                    break;

                }//end switch
            }//end else
        }//end if
    }//end while

   
    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    open_calls_on_port
//
//    This function opens all calls avaliable by checking valid_vector
//    on a port.
//
//    Return 'active_calls' - the number of calls succesfully opened
//
//////////////////////////////////////////////////////////////////////

ACU_INT    open_calls_on_port( PORT_DATA* port_data )
{

    ACU_ERR        result            = 0;
    ACU_INT        this_ts            = 0;
    ACU_INT        active_calls    = 0;
    ACU_INT        max_ts            = MAX_TIMESLOTS;

    ACU_LONG    mask            = 1;

    CALL_DATA* call_data = NULL;

    PORT_INFO_PARMS    port_info_parms;

    INIT_ACU_STRUCT(&port_info_parms);

    port_info_parms.port_id = port_data->port_id;
   
    result = call_port_info(&port_info_parms);

    if(result != 0)
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to obtain valid vector info <%d>",result );

    }
    else
    {
        if( port_info_parms.port_type == ACU_PORT_CAP_IP )
        {
            max_ts = port_data->num_calls_to_open;
        }
       
       
        for(this_ts = 0; this_ts < max_ts; this_ts++)
        {
            //check the valid_vector to see if this is a valid timeslot on which to make a call
            //only if this is a CCS/CAS port not valid for VoIP
            if( port_data->port_type != ACU_PORT_CAP_IP )
            {
               
                if( !( port_info_parms.valid_vector & mask ) )
                {
                    //invalid timeslot adjust the mask and go back to the top of the loop
                    mask <<= 1L;
                    continue;
                   
                }
            }

            result = 0;
           
            call_data = acu_os_alloc( sizeof(CALL_DATA) );
           
            if( port_data->port_type == ACU_PORT_CAP_IP )
            {
                //populate the IP Props here
                call_data->ts = -1;
                call_data->ip = ACU_OS_TRUE;
               
            }
            else
            {
                call_data->ts = this_ts;
            }
           
            if( port_data->call_direction == IN_CALL )
            {
                result = open_call_incoming( call_data, port_data );
            }
           
            if( port_data->call_direction == OUT_CALL )
            {
                result = open_call_outgoing( call_data, port_data );
            }
           
            if( result != 0 )
            {
                acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not open call on port %d ts %d result %d", port_data->port_ix, call_data->ts, result );
               
                return result;
               
            }

            if ( port_data->port_type != ACU_PORT_CAP_IP )
            {
                // Need to adjust timeslot mask:
                mask <<= 1L;
            }
           
            //add the call to the CALLS linked list for this port
            acu_qq_add_tail( &port_data->CALLS, &call_data->node, call_data );
           
            //one more active call
            ++active_calls;       
           
            if( active_calls == port_data->num_calls_to_open )
            {
                return active_calls;
            }
           
        }   
    }

    if( result == 0)
    {
        return active_calls;
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    open_call_outgoing
//
//    This function is a wrapper around the 'call_openin' function.
//    If this function succeds the call will enter the wait for incoming
//    state
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////
ACU_INT open_call_outgoing( CALL_DATA* call_data, PORT_DATA* port_data )
{
    ACU_ERR        result = 0;

    OUT_XPARMS    open_parms;

    INIT_ACU_STRUCT( &open_parms );

    open_parms.net                    = port_data->port_id;    //port_id of the port this call will take place on
    open_parms.ts                    = call_data->ts;        //timeslot the call will take place on
    open_parms.queue_id                = port_data->queue_id;    //queue that any call events will occur on for this call handle
    open_parms.app_context_token    = (ACU_ACT)call_data;    //application context token that will be returned when an event occurs on this call handle
    open_parms.cnf                    = CNF_REM_DISC;            //flag which allows the remote disconnect event to be generated
    strcpy( open_parms.destination_addr, "1234" );            //Number we are calling

    result = call_openout( &open_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error; Unable to open incoming call <%d>", result );

        return result;
    }

    //record the handle this call has been allocated
    call_data->handle = open_parms.handle;

    return result;

}


//////////////////////////////////////////////////////////////////////
//
//    open_call_incoming
//
//    This function is a wrapper around the 'call_openin' function.
//    If this function succeds the call will enter the wait for incoming
//    state
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

ACU_INT open_call_incoming( CALL_DATA* call_data, PORT_DATA* port_data )
{
    ACU_ERR        result = 0;

    IN_XPARMS    open_parms;

    INIT_ACU_STRUCT( &open_parms );

    open_parms.net                    = port_data->port_id;
    open_parms.ts                    = call_data->ts;
    open_parms.queue_id                = port_data->queue_id;    //queue that any call events will occur on for this call handle
    open_parms.app_context_token    = (ACU_ACT)call_data;    //application context token that will be returned when an event occurs on this call handle
    open_parms.cnf                    = CNF_REM_DISC;            //set the flag so the EV_REMOTE_DISCONNECT event can be generated

    result = call_openin( &open_parms );
    if( result != 0 )
    {
       
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error; Unable to open incoming call <%d>", result );

        return result;
    }

    //record the handle this call has been allocated
    call_data->handle = open_parms.handle;

    return result;

}

//////////////////////////////////////////////////////////////////////
//
//    allocate_port_event_queue
//
//    This function allocates an event queue for a port
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

ACU_INT allocate_port_event_queue( PORT_DATA* port_data )
{
    ACU_ERR        result = 0;

    ACU_ALLOC_EVENT_QUEUE_PARMS        alloc_q_parms;

    INIT_ACU_STRUCT(&alloc_q_parms);

    result = acu_allocate_event_queue(&alloc_q_parms);

    if(result != 0)
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to create port event queue" );

        return result;
    }
    else
    {
        //record the queue_id that has been allocated by the API
        port_data->queue_id = alloc_q_parms.queue_id;

        return result;
    }

}

//////////////////////////////////////////////////////////////////////
//
//    free_port_event_queue
//
//    This function frees an event queue previously allocated for a port
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

ACU_INT free_port_event_queue( PORT_DATA* port_data )
{
    ACU_ERR        result = 0;

    ACU_FREE_EVENT_QUEUE_PARMS    free_q_parms;

    INIT_ACU_STRUCT( &free_q_parms );

    free_q_parms.queue_id = port_data->queue_id;

    result = acu_free_event_queue( &free_q_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to free port event queue <%d>", result );

        return result;
    }

    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    set_default_port_queue
//
//    This function sets the default event queue for a port
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

ACU_INT set_default_port_queue( PORT_DATA* port_data )
{
    ACU_ERR    result = 0;

    ACU_QUEUE_PARMS                queue_parms;

    INIT_ACU_STRUCT( &queue_parms );

    queue_parms.queue_id        = port_data->queue_id;
    queue_parms.resource_id        = port_data->port_id;

    //set the default event queue for call control events for this port
    result = call_set_port_default_handle_event_queue(&queue_parms);

    if(result != 0)
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to set default port queue" );

        return result;
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    get_call_data_from_queue
//
//    This function gets the call_data token from associated with the
//    event which has just occured on the port event queue
//
//    Return valid CALL_DATA if successfull, otherwise return NULL
//
//////////////////////////////////////////////////////////////////////

CALL_DATA* get_call_data_from_queue( PORT_DATA* port_data )
{
    ACU_ERR        result = 0;

    ACU_EVENT_QUEUE_PARMS    event_queue_parms;

    CALL_DATA* call_data = NULL;

    INIT_ACU_STRUCT( &event_queue_parms );

    event_queue_parms.queue_id = port_data->queue_id;

    //get the event from the event queue
    result = acu_get_event_from_queue( &event_queue_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to get event from queue <%d> ", result );

        //return NULL if there has been an error
        return call_data;
    }

    //dereference the context token to a call_data struct
    call_data = (CALL_DATA*)event_queue_parms.context;

    //return the call_data struct we have got from the event queue
    return call_data;
}

//////////////////////////////////////////////////////////////////////
//
//    get_call_details
//
//    Get the call details of the specified call.  If the 'valid' flag
//    is set, then the call's "call_io_data" structure is set-up.
//    If there 'destination_addr' field is set-up for CCS/CAS calls,
//    then "num_digits" field is set-up with its length.
//
//////////////////////////////////////////////////////////////////////
ACU_INT get_call_details( CALL_DATA *call_data )
{
    DETAIL_XPARMS    call_info;
    ACU_INT result = 0;

    INIT_ACU_STRUCT( &call_info );

    call_info.handle  = call_data->handle;
    call_info.timeout = 0;    // Return immediatley from API call

    result = call_details( &call_info );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not get call's details H:%X <%d>", call_data->handle, result);
    }
    else
    {
        // Got some details, see if we can do anything with them:
        if ( call_info.valid == 1 )
        {
            // This means that the timeslot value can be used, so set-up
            // the call's stream & timeslot:
            call_data->ts      = call_info.ts;
            call_data->stream = call_info.stream;
        }

        // This is a CCS or CAS call, so check the destination address:
        if ( call_info.destination_addr[0] != '\0' )
        {
            // Number isn't null, so get it's length
            call_data->num_digits = strlen( call_info.destination_addr );       
        }

        // Finally, check if "sending_complete" is set.  This means that the call has
        // sent all the digits that it's going to, so we may as well accept the call:
        if ( call_info.sending_complete != 0 )
        {
            // We've received all the digits, so adjust num_digits to DDI_DIGITS,
            // this will cause the call to be accepted immediatley.
            call_data->num_digits = DDI_DIGITS;
        }
    }
    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    alert_call
//
//    This function is a wrapper around the call_incoming_ringing.  As
//    "call_incoming_ringing" stops anymore digits being recieved we
//    need to determine if we've received enough information for us to
//    accept the call.  This is mainly used by CAS protocol as they
//    do not support en bloc sending.
//
//    Return if the function succeeds, otherwise return a negative error
//
//////////////////////////////////////////////////////////////////////
ACU_INT alert_call( CALL_DATA *call_data, PORT_DATA* port_data )
{
    ACU_ERR result = 0;

    INCOMING_RINGING_XPARMS    ringing_parms;
   
    INIT_ACU_STRUCT( &ringing_parms );

    ///////////////////////////////////////////////////////////////////////
    //
    // This is a CCS/CAS call, check the number of digits we've recieved
    // NOTE: The value of DDI_DIGITS is an arbitrary value, in "real-life"
    // circumstances this limit would be set by the switch provider.
    //
    ///////////////////////////////////////////////////////////////////////
    if( !call_data->ip )
    {   
        if ( call_data->num_digits >= DDI_DIGITS )
        {
            // We have enough digits, so stop anymore being sent:
            result = call_incoming_ringing( call_data->handle );
            if ( result != 0 )
            {
                acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Failed at call_incoming_ringing <%d>", result);
            }
        }
    }
    else
    {
        ringing_parms.handle = call_data->handle;

        if( COMPANDING == COMPANDING_ALAW )
        {
            ringing_parms.unique_xparms.sig_iptel.media_settings.tdm_encoding = TDM_ALAW;

            //if this is a SIP port enable early media support
            if( port_data->ip_port_type == S_SIP )
            {
                ringing_parms.unique_xparms.sig_iptel.protocol_specific.sig_sip.send_early_media = 1;
            }
        }
        else if (COMPANDING == COMPANDING_MULAW )
        {
            ringing_parms.unique_xparms.sig_iptel.media_settings.tdm_encoding = TDM_ULAW;
        }

        result = xcall_incoming_ringing( &ringing_parms );
        if( result != 0 )
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Failed to call xcall_incoming_ringing <%d>", result );

        }
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    disconnect_call
//
//    This function is a wrapper around the call_disconnect API call.
//    When this function is called the specified call begins to clear
//    down.
//
//////////////////////////////////////////////////////////////////////
ACU_INT    disconnect_call( CALL_DATA *call_data )
{
    ACU_ERR result = 0;
   
    CAUSE_XPARMS    disconnect_parms;
   
    INIT_ACU_STRUCT( &disconnect_parms );

    disconnect_parms.handle = call_data->handle;

    result = call_disconnect( &disconnect_parms );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not disconnect call H:%X <%d>", call_data->handle, result);
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    accept_call
//
//    This function is a wrapper around the 'call_accept' function.
//  If this function succeeds the call will move to the connected
//    state.
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////
ACU_INT accept_call( CALL_DATA *call_data )
{
    ACU_ERR result = 0;

    ACCEPT_XPARMS    accept_parms;
   
    INIT_ACU_STRUCT( &accept_parms );

    accept_parms.handle = call_data->handle;

    result = xcall_accept( &accept_parms );
    if ( result != 0 )
    {
        // Couldn't accept the call, so clear it down:
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not accept call <%d>", result);
    }
   
    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    stop_call_control_on_port
//
//    This function sets the terminate flag for the port
//    Then clears all the calls on the port - then waits for all the
//    calls to clear down and then destroys the thread
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

void stop_call_control_on_port( PORT_DATA* port_data )
{
    //first set the terminate flag
    port_data->terminate = ACU_OS_TRUE;

    //the above will catch calls that are in the connected state and make sure they are not reopened but we need to call call_disconnect()
    //on all calls currently open on the port
    close_all_calls_on_port( port_data );

    //wait for the thread to terminate
    acu_os_wait_for_thread( port_data->thread_id );

    //destroy the thread data
    acu_os_destroy_thread( port_data->thread_id );

}

//////////////////////////////////////////////////////////////////////
//
//    close_all_calls_on_port
//
//    This function disconnects all calls currently active on the port
//    It recursivly gets the head of the call linked list until it is
//    empty indicated by card_data == NULL
//    Then call disconnect_call for each call_data item returned from
//    the linked list
//
//    Return 0 if successfull, otherwise return a negative error value
//
//////////////////////////////////////////////////////////////////////

void close_all_calls_on_port( PORT_DATA* port_data )
{
    //need to get each thing from the head of the linked list and call disconnect_call()
    CALL_DATA* call_data = NULL;

    //get the head of the linked list
    call_data = acu_qq_get_head( &port_data->CALLS );

    //disconnect each call in the linked list until all calls have been removed indicated by call_data == NULL
    while( call_data != NULL )
    {
        disconnect_call( call_data );
       
        call_data = acu_qq_get_head( &port_data->CALLS );
    }
}

//////////////////////////////////////////////////////////////////////
//
//    release_call
//
//    This function is a wrapper around the call_release API call, and
//    should only be called when a call has fully cleared down, i.e.
//    has progress to the IDLE state.
//
//    If call_release is successful, we move the specified call from
//    the port's active list to the port's free list.
//
//////////////////////////////////////////////////////////////////////
ACU_INT release_call( CALL_DATA *call_data )
{
    ACU_ERR            result = 0;
   
    CAUSE_XPARMS    release_parms;

    INIT_ACU_STRUCT( &release_parms );    // Clear the structure

    release_parms.handle = call_data->handle;

    result = call_release( &release_parms );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not release call H:%X <%d>", call_data->handle, result);
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    close_port
//
//    This function is a wrapper around the call_close_port API function.
//    This function is called once the port thread is terminating and
//    all calls on the port have terminated
//
//    0 returned for success otherwise a negative value returned for an error
//
//////////////////////////////////////////////////////////////////////

ACU_INT close_port( PORT_DATA* port_data )
{
    ACU_INT result = 0;

    CLOSE_PORT_PARMS    close_port;

    INIT_ACU_STRUCT( &close_port );

    close_port.port_id = port_data->port_id;

    result = call_close_port( &close_port );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to close port <%d>", result );

        return result;
    }

    return result;
}
////////////////////////////////////////////////////////////////////////////////
//
//   
//   
//    The following contains all of the functions for controlling the speech processing
//    resources on a card
//    i.e.
//    - allocating a channel
//    - controlling playback/record/dtmf detection and more
//
////////////////////////////////////////////////////////////////////////////////



/*
 * Prosody resource thread state machine defines:
 */
#define STATE_PLAY_VOTE_PROMPT        0        // Play a prompt and wait for caller vote:
#define STATE_WAIT_FOR_VOTE            1        // Wait for caller to vote:
#define STATE_PLAY_HANGUP_PROMPT    2        // Play a prompt to infrom caller to hangup
#define STATE_WAIT_HANG_UP            3        // Wait for the caller to hang-up
#define STATE_COMPLETE                4        // Caller has hung-up
#define STATE_APP_EXIT                5        // Application is terminating


#define VALID_DIGIT                    0        // A valid digit was recognised
#define INVALID_DIGIT                1        // An invalid digit was recognised
#define NO_DIGIT                    2        // No digit was recognised

#define VOTE_PROMPT_FILE            "../../wav_files/onetofive.wav"    // Name (and path) of the file to play
                                                        // as vote prompt
#define HANGUP_PROMPT_FILE            "../../wav_files/hangup.wav"    // Name (and path) of the file to play

#define REPROMPT_DELAY                10000    // Number of milliseconds to wait before replaying a prompt
                                            // (10,000 == 10 second delay).


/*
 * pros_res_man
 * The structure contains the data required for managing the Prosody resource
 * once the resource has begun it's main purpose (e.g. playing prompts & detecting digits).
 */
typedef struct _pros_res_man
{
    tSMChannelId        channel;    // The resource's channel
    ACU_SDK_WAIT_OBJECT    *kill_me;    // Signalled if our resource thread is terminating
    ACU_SDK_WAIT_OBJECT    *prompt;    // Signalled if the prompt thread has terminated
    ACU_SDK_WAIT_OBJECT    *digits;    // Signalled if we've detected DTMF digits
    ACU_SDK_WAIT_OBJECT    *hungup;    // Signalled if our call has hung-up
    tSMEventId            *event;        // The Prosody event converted to 'digits' wait object
    ACU_OS_THREAD        *prompt_thread_id;    // Thread ID of the pompt
    SM_FILE_REPLAY_PARMS *prompt_parms;        // Parameters used by thread to progress prompt

} PROS_RES_MAN;


ACU_INT setup_detection( PROS_RES_MAN* pros_res_man );
ACU_INT play_prompt_wait_for_vote( PROS_RES_MAN* pros_res_man );
ACU_INT wait_for_vote( PROS_RES_MAN* pros_res_man );
ACU_INT play_prompt_wait_for_hangup( PROS_RES_MAN *pros_res_man );
ACU_INT wait_for_hangup( PROS_RES_MAN *pros_res_man );
ACU_INT start_prompt( PROS_RES_MAN *pros_res_man, char *filename );
ACU_INT prompt_playing( void *parameters );
ACU_INT stop_prompt( PROS_RES_MAN *pros_res_man );
ACU_INT stars_in_their_eyes( PROS_DATA* pros_data );

ACU_INT get_digit( PROS_RES_MAN *pros_res_man );
ACU_OS_BOOL validate_digit( tSM_INT digit );

void update_votes( ACU_INT contestant );
void remove_detection( PROS_RES_MAN *pros_res_man );


/////////////////////////////////////////////////
//
//    destroy_vote_cs
//
//    This function destroys the critical section
//    used to protect the votes array
//
/////////////////////////////////////////////////

void destroy_vote_cs( void )
{
    acu_os_destroy_critical_section( vote_lock );
}

////////////////////////////////////////////////////////////////////
//
//    prosody_resource_thread
//
//    This is a Prosody thread that is responsible for handling the
//    DTMF detection, and wav playback for a single channel.
//
//    NOTE: It is also the responsibility of the thread to indicate to
//          it's caller that the thread is fully initialised.
//
//    Returns 0 for success, or a negative value indicating the error.
//
/////////////////////////////////////////////////////////////////////

ACU_INT prosody_resource_thread( void* param )
{
    ACU_ERR        result = 0;

    PROS_DATA* pros_data = (PROS_DATA*)param;
    MODULE_DATA* module_data = NULL;

    ACU_SDK_WAIT_OBJECT    *resource_events[2];
    ACU_OS_BOOL            sig_res_events[2];
    ACU_OS_BOOL            terminate = ACU_OS_FALSE;

    CALL_2_PROS_PARMS    connect_parms;

    //setup a local copy of the module data which relates to this Prosody thread
    module_data = pros_data->module_data;

    result = init_pros_data( pros_data );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not setup Prosody resource data");
    }
    else
    {
        //successfully initialised everything, so inform our caller by adding ourselves to the free prosody resources
        //set_prosody_resource_free( pros_data );
        set_prosody_resource_free( pros_data );


        //set-up the wait objects
        resource_events[0] = pros_data->kill_me;            //Event 0 is application terminate
        resource_events[1] = pros_data->call_connected;        //Event 1 is caller connected

        //Now perform the loop until our thread is told to terminate:
        while( terminate == ACU_OS_FALSE )
        {
            //Wait until we've had a call associated with out thread or the application is terminated
            result = acu_os_wait_for_any_wait_object( 2,
                                                        resource_events,
                                                        sig_res_events,
                                                        ACU_OS_INFINITE );
           
           
            if( result != ERR_ACU_OS_NO_ERROR )
            {
                //an error occured in the wait object function, kill our thread
                acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not wait on connected wait object <%d>", result );

                break;
            }
            else if( sig_res_events[0] == ACU_OS_TRUE )
            {
                //Application is terminating so set loop exit condition
                terminate = ACU_OS_TRUE;
            }
            else
            {
                //We have been associated with a call ( sig_res_events[1] == ACU_OS_TRUE )
                //NOTE: This means that this Prosody resource will be removed from the free resource list
                //to stop any other call from attempting to use this channel

                INIT_ACU_SM_STRUCT( &connect_parms );

                //setup the information we need to connect the call to our Prosody channel
                connect_parms.call_card_id        = pros_data->call_card;
                connect_parms.call_stream        = pros_data->call_stream;
                connect_parms.call_ts            = pros_data->call_ts;
                connect_parms.pros_channel        = pros_data->my_channel;
                connect_parms.module_card_id    = pros_data->module_data->card_id; //card id the module is based on
                connect_parms.handle            = pros_data->call_handle;

                if( pros_data->using_vmp )
                {
                    result = connect_vmp_call_2_prosody( &connect_parms );
                }
                else
                {
                    result = connect_call_2_prosody( &connect_parms );
                    if( result != 0 )
                    {
                        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not connect call and Prosody <%d>", result );
                       
                        terminate = ACU_OS_TRUE;
                    }
                }
               
                //start the main purpose for this application the stars in their eyes voting system
                result = stars_in_their_eyes( pros_data );
                if(result != 0 )
                {
                    // Either the application is terminating or something critical failed,
                    // so we need to terminate our thread:
                    terminate = ACU_OS_TRUE;
                }

                // We'' only get to here if the application is terminating or the caller
                // has hung-up.  In either case we want to break the switch connection
                // between the caller and our Prosody channel:
                // Break the switch connections that we set-up:
                if( pros_data->using_vmp )
                {
                    disconnect_vmp_call_from_prosody( &connect_parms, pros_data );

                    destroy_vmp_context( pros_data->vmprx, pros_data->vmptx );
                }
                else
                {
                    disconnect_call_from_prosody( &connect_parms );
                }

                pros_data->using_vmp    = 0;
                pros_data->vmprx        = NULL;
                pros_data->vmptx        = NULL;

                // Now that we've broken the switch connections we can add ourselves to
                // the free resource list:
                set_prosody_resource_free( pros_data );

                   
            }


        }
   
    }

    // Free the resources that we allocated
    // NOTE: The application guarantees that in "normal" termination we'll be
    //         able to free our resources before the thread will be killed.
   
    release_prosody_channel( pros_data );    // Release the Prosody channel

    free_pros_data( pros_data );            // Release the events we used

    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    stars_in_their_eyes
//
//    Main function which carries out the voting system
//
//    Returns 0 for success, or a negative value indicating the error.
//
//////////////////////////////////////////////////////////////////////

ACU_INT stars_in_their_eyes( PROS_DATA* pros_data )
{
    ACU_ERR            result    = 0;
    ACU_INT            next_state = STATE_PLAY_VOTE_PROMPT;

    ACU_OS_BOOL        terminate = ACU_OS_FALSE;

    PROS_RES_MAN    pros_res_man;

    //Set-up the data required for managing this resource's behaviour:
    //NOTE: 'digits' is set-up by setup_detection()

    pros_res_man.channel = pros_data->my_channel;
    pros_res_man.kill_me = pros_data->kill_me;
    pros_res_man.hungup     = pros_data->call_cleared;

    pros_res_man.prompt = acu_os_create_wait_object();
    if( pros_res_man.prompt == NULL )
    {
        //Fatal Error
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not create prompt event, terminating" );

        return result;
    }

    //Set-up DTMF detection parameters (and digits event)
    result = setup_detection( &pros_res_man );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not set-up detection parameters, terminating" );

        return result;
    }

    //Now everything is set-up, set off the state machine:
    //NOTE: next state is initialised to STATE_PLAY_VOTE_PROMPT
    while( terminate == ACU_OS_FALSE )
    {
        switch( next_state )
        {
        case STATE_PLAY_VOTE_PROMPT:
            //Play a prompt and wait for caller vote
            next_state = play_prompt_wait_for_vote( &pros_res_man );
            break;
        case STATE_WAIT_FOR_VOTE:
            //wait for the caller to vote
            next_state = wait_for_vote( &pros_res_man );
            break;
        case STATE_PLAY_HANGUP_PROMPT:
            //caller voted but not hungup
            next_state = play_prompt_wait_for_hangup( &pros_res_man );
            break;
        case STATE_WAIT_HANG_UP:
            //wait for caller to hangup
            next_state = wait_for_hangup( &pros_res_man );
            break;
        case STATE_COMPLETE:
            //caller has hungup
            //drop through
        case STATE_APP_EXIT:
            //Drop Through
        default:
            //Application is terminating OR we received and invalid state
            terminate = ACU_OS_TRUE;
            break;
        }
    }

    //remove the resources we allocated for detection
    remove_detection( &pros_res_man );

    //Free the resources we allocated
    acu_os_destroy_wait_object( pros_res_man.prompt );

    //Set-up the return code based on what our last state was
    if( next_state == STATE_COMPLETE )
    {
        //our caller hung up so everything was successfull:

        result = 0;
    }
    else
    {
        //Either the thread is terminating or something failed. In either case
        // set-up a negative return code

        result = -1;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////
//
// setup_detection
//
// Setup the detection parameters for the given channel. If the detection
// is set-up successfully, the function will create a wait object (from a prosody event)
// that is associated with the detection criteria
//
// Returns 0 for sucess, or a negative value indicating the error
//
/////////////////////////////////////////////////////////////////////////////////////////

ACU_INT setup_detection( PROS_RES_MAN* pros_res_man )
{
    ACU_ERR    result = 0;

    SM_LISTEN_FOR_PARMS            detection_parms;
    SM_CHANNEL_SET_EVENT_PARMS    event_parms;

    //Cleat the API structs
    INIT_ACU_SM_STRUCT( &detection_parms );
    INIT_ACU_SM_STRUCT( &event_parms );


    ////////////////////////////////////////////////////////////////////////////////////
    //
    // A brief description of what the following parameters will setup
    //
    //    - Tone set 0 is the DTMF tone set
    //    - kSMToneDectionMinDuration64 requires that digits be at least 64 milliseconds
    //    to be regarded as valid
    //    - kSMDTMFToneSetDigitMapping causes the recognised tone to be converted into the
    //      equivalent digit
    //
    /////////////////////////////////////////////////////////////////////////////////////
    detection_parms.channel                = pros_res_man->channel;
    detection_parms.active_tone_set_id    = 0;
    detection_parms.tone_detection_mode    = kSMToneDetectionMinDuration64;
    detection_parms.map_tones_to_digits    = kSMDTMFToneSetDigitMapping;

    result = sm_listen_for( &detection_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not set-up detection parameters <%d>", result );
    }
    else
    {
        //Create a Prosody event that will be signalled for recognition events on this channel
        pros_res_man->event = acu_os_alloc( sizeof(tSMEventId) );

        result = smd_ev_create( pros_res_man->event,            //the event to create
                                pros_res_man->channel,            //the event's channel
                                kSMEventTypeRecog,                //the type of event(recognition)
                                kSMChannelSpecificEvent );        //mapping between event & channel

        if( result != 0 )
        {
            //Fatal Error
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not create Prosody Event <%d>", result );

            exit( -1 );
        }
        else
        {
            //Created the event, now we've got set it up
            //NOTE: The following arguments have the same meaning as those in smd_ev_create()
            event_parms.channel        = pros_res_man->channel;
            event_parms.event        = *(pros_res_man->event);
            event_parms.event_type    = kSMEventTypeRecog;
            event_parms.issue_events =kSMChannelSpecificEvent;

            result = sm_channel_set_event( &event_parms );
            if( result != 0 )
            {
                //Fatal Error
                acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not associate channel and event <%d>", result );

                exit( -1 );
            }
            else
            {
                //Finally we can convert the Prosody event to a wait object that we can use in acu_os_wait_for_wait_object()
                pros_res_man->digits = acu_os_create_wait_object_from_prosody( *(pros_res_man->event) );
               
                if( pros_res_man->digits == NULL )
                {
                    //This is a fatal error, we we won't be able to wait on this event
                    acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not conver Prosody event ");

                    exit( -1 );
                }
            }
        }
    }

    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    play_prompt_wait_for_vote
//
//    State machine function called from "stars_in_their_eyes".  This
//    function plays a prompt and waits for the caller to select an
//    option.
//
//    Returns the next state to move to.
//
//////////////////////////////////////////////////////////////////////
ACU_INT play_prompt_wait_for_vote( PROS_RES_MAN *pros_res_man )
{
    ACU_INT                result = 0;
    ACU_OS_BOOL            got_digit        = NO_DIGIT;    // VALID_DIGIT, INVALID_DIGIT, NO_DIGIT
    ACU_INT                next_state        = STATE_PLAY_VOTE_PROMPT;    // Default next state
    ACU_INT                current_state    = STATE_PLAY_VOTE_PROMPT;    // State we're currently in
    ACU_SDK_WAIT_OBJECT    *pros_events[4];    // Array of the events we want to wait on
    ACU_OS_BOOL            pros_events_sig[4];    // Array that indicates which of our events
                                            // were signalled

    // Start off the prompt:
    result = start_prompt( pros_res_man, VOTE_PROMPT_FILE );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not start prompt <%d>, terminating", result);
        exit(1);
    }

    // Set-up our wait object array:
    pros_events[0] = pros_res_man->kill_me;    // Wait object 0 is application termination
    pros_events[1] = pros_res_man->hungup;    // Wait object 1 is caller hung-up
    pros_events[2] = pros_res_man->digits;    // Wait object 2 is digits detected
    pros_events[3] = pros_res_man->prompt;    // Wait object 3 is prompt finished
   
    // Make sure there are no stale DTMF events on the line
    result = sm_discard_recognised( pros_res_man->channel );
    ///////////////////////////////////////////////////////
    //
    //    We'll drop out of this while loop if:
    //    a) The caller has made a valid vote
    //    b) The prompt finished
    //    c) The caller hung-up.
    //
    //    NOTE: Barge in will only occur if the caller makes
    //          a valid vote.
    //
    ///////////////////////////////////////////////////////
    while ( current_state == next_state )
    {
        /////////////////////////////////////////////////////////////////////////////////
        //
        // Wait for any one of our events to be signalled.  When this function returns
        // the entries in 'pros_events_sig' array will be set to indicate whether
        // or not the corresponding event was signalled
        // For example, if 'pros_events[2]' signalled then 'pros_events_sig[2]' would be
        // set to ACU_OS_TRUE.
        //
        /////////////////////////////////////////////////////////////////////////////////
        result = acu_os_wait_for_any_wait_object( 4,    // Number of events we're waiting on
                                                  pros_events,        // The events we're waiting for
                                                  pros_events_sig,    // The events signalled state
                                                  ACU_OS_INFINITE);    // How long to wait

        if ( result != ERR_ACU_OS_NO_ERROR )
        {
            // This is a fatal error, as there's a chance that at least one of our
            // wait objects is invalid, safer to drop out.
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Resource thread failed to wait, terminating");
            exit(1);
        }
        else
        {
            // At least one of our wait objects was signalled, find out which one(s)
            if ( pros_events_sig[0] == ACU_OS_TRUE )
            {
                // Application is terminating:
                next_state = STATE_APP_EXIT;
            }
            else if ( pros_events_sig[1] == ACU_OS_TRUE )
            {
                // Caller has hung-up, clear down normally
                next_state = STATE_COMPLETE;
            }
            else if ( pros_events_sig[2] == ACU_OS_TRUE )
            {
                // We've detected a digit, collect it and determine if it's valid.
                got_digit = get_digit( pros_res_man );
                if ( got_digit == VALID_DIGIT )
                {
                    // Got a valid vote, update state and wait for caller to hang-up:
                    next_state = STATE_PLAY_HANGUP_PROMPT;
                }
                else if ( got_digit == INVALID_DIGIT )
                {
                    // Got an invalid vote, remain in this state:
                    next_state = STATE_PLAY_VOTE_PROMPT;
                }
                else // got_digit == NO_DIGIT
                {
                    // Did not get a digit.  loop round and try again
                    next_state = current_state;
                }
            }
            else
            {
                // It must be the prompt thread finishing wait for the caller to vote:
                next_state = STATE_WAIT_FOR_VOTE;
            }
        }
    } // End while

    // Whatever happened to drop us out of the while we need to
    // halt the prompt:
    if ( next_state != STATE_PLAY_VOTE_PROMPT )
    {
        stop_prompt( pros_res_man );
    }

    return next_state;
}

//////////////////////////////////////////////////////////////////////
//
//    wait_for_vote
//
//    State machine function called from "stars_in_their_eyes".  This
//    function is called when a prompt has finished playing but the
//    caller is yet to make a selection.
//
//    Returns the next state to move to.
//
//////////////////////////////////////////////////////////////////////
ACU_INT wait_for_vote( PROS_RES_MAN *pros_res_man )
{
    ACU_ERR                result        = 0;
    ACU_OS_BOOL            got_digit    = ACU_OS_FALSE;    // True if we've recieved a valid vote
    ACU_INT                next_state    = STATE_WAIT_HANG_UP;    // Default next state
    ACU_INT                current_state = STATE_WAIT_FOR_VOTE;
    ACU_SDK_WAIT_OBJECT    *pros_events[3];    // Array of the events we want to wait on
    ACU_OS_BOOL            pros_events_sig[3];    // Array that indicates which of our events
                                            // were signalled

    // Set-up our wait object array:
    pros_events[0] = pros_res_man->kill_me;    // Wait object 0 is application termination
    pros_events[1] = pros_res_man->hungup;    // Wait object 1 is caller hung-up
    pros_events[2] = pros_res_man->digits;    // Wait object 2 is digits detected
   

    /////////////////////////////////////////////////////////////////////////////////
    //
    // Wait for any one of our events to be signalled.  When this function returns
    // the entries in 'pros_events_sig' array will be set to indicate whether
    // or not the corresponding event was signalled
    // For example, if 'pros_events[2]' signalled then 'pros_events_sig[2]' would be
    // set to ACU_OS_TRUE.
    //
    /////////////////////////////////////////////////////////////////////////////////
    result = acu_os_wait_for_any_wait_object( 3,    // Number of events we're waiting on
                                              pros_events,        // The events we're waiting for
                                              pros_events_sig,    // The events singalled state
                                              REPROMPT_DELAY);    // How long to wait

    if ( result == ERR_ACU_OS_SIGNAL )
    {
        // This is a fatal error, as there's a chance that at least one of our
        // wait objects is invalid, safer to drop out.
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Resource thread failed to wait, terminating");
        exit(-1);
    }
    else if ( result == ERR_ACU_OS_TIMED_OUT )
    {
        // The caller hasn't entered an option after 10 seconds, so replay prompt:
        next_state = STATE_PLAY_VOTE_PROMPT;
    }
    else
    {
        // At least one of our wait objects was signalled, find out which one(s)
        if ( pros_events_sig[0] == ACU_OS_TRUE )
        {
            // Application is terminating:
            next_state = STATE_APP_EXIT;
        }
        else if ( pros_events_sig[1] == ACU_OS_TRUE )
        {
            // Caller has hung-up, clear down normally
            next_state = STATE_COMPLETE;
        }
        else
        {
            // We've detected a digit, collect it and determine if
            // it's valid.  If it is then we'll drop out of the loop
            got_digit = get_digit( pros_res_man );
            if ( got_digit == VALID_DIGIT )
            {
                // Got a valid vote, update state and wait for caller to hang-up:
                next_state = STATE_PLAY_HANGUP_PROMPT;
            }
            else if ( got_digit == INVALID_DIGIT )
            {
                // Got an invalid vote, remain in this state:
                next_state = STATE_PLAY_VOTE_PROMPT;
            }
            else // got_digit == NO_DIGIT
            {
                // Did not get a digit.  loop round and try again
                next_state = current_state;
            }
        }
    }

    return next_state;
}

//////////////////////////////////////////////////////////////////////
//
//    play_prompt_wait_for_hangup
//
//    State machine function called from "stars_in_their_eyes".  This
//    function is called when the caller has made a vote but not yet
//    hung-up.  Play a prompt to the caller informing them to hang-up.
//
//    Returns the next state to move to.
//
//////////////////////////////////////////////////////////////////////
ACU_INT play_prompt_wait_for_hangup( PROS_RES_MAN *pros_res_man )
{
    ACU_ERR                    result = 0;
    ACU_INT                    next_state = STATE_WAIT_HANG_UP;    // Default next state
    ACU_SDK_WAIT_OBJECT    *pros_events[3];    // Array of the events we want to wait on
    ACU_OS_BOOL            pros_events_sig[3];    // Array that indicates which of our events
                                            // were signalled

    // Start off the prompt:
    result = start_prompt( pros_res_man, HANGUP_PROMPT_FILE );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not start prompt <%d>, terminating", result);
        exit(-1);
    }

    // Set-up our wait object array:
    pros_events[0] = pros_res_man->kill_me;    // Wait object 0 is application termination
    pros_events[1] = pros_res_man->hungup;    // Wait object 1 is caller hung-up
    pros_events[2] = pros_res_man->prompt;    // Wait object 2 is prompt finished
   

    /////////////////////////////////////////////////////////////////////////////////
    //
    // Wait for any one of our events to be signalled.  When this function returns
    // the entries in 'pros_events_sig' array will be set to indicate whether
    // or not the corresponding event was signalled
    // For example, if 'pros_events[2]' signalled then 'pros_events_sig[2]' would be
    // set to ACU_OS_TRUE.
    //
    /////////////////////////////////////////////////////////////////////////////////
    result = acu_os_wait_for_any_wait_object( 3,    // Number of events we're waiting on
                                              pros_events,        // The events we're waiting for
                                              pros_events_sig,    // The events singalled state
                                              ACU_OS_INFINITE);    // How long to wait

    if ( result != ERR_ACU_OS_NO_ERROR )
    {
        // This is a fatal error, as there's a chance that at least one of our
        // wait objects is invalid, safer to drop out.
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Resource thread failed to wait, terminating");
        exit(-1);
    }
    else
    {
        // At least one of our wait objects was signalled, find out which one(s)
        if ( pros_events_sig[0] == ACU_OS_TRUE )
        {
            // Application is terminating:
            next_state = STATE_APP_EXIT;
        }
        else if ( pros_events_sig[1] == ACU_OS_TRUE )
        {
            // Caller has hung-up, clear down normally
            next_state = STATE_COMPLETE;
        }
        else
        {
            // It must be the prompt thread finishing wait for the caller to hang_up:
            next_state = STATE_WAIT_HANG_UP;
        }
    }

    // Whatever the reason we dropped out of the wait, we need to terminate
    // the prompt (either prematurely or normally):
    stop_prompt( pros_res_man );

    return next_state;
}

//////////////////////////////////////////////////////////////////////
//
//    wait_for_hangup
//
//    State machine function called from "stars_in_their_eyes".  This
//    function is called when the caller has made a vote but has not yet
//    hung-up
//
//    Returns the next state to move to.
//
//////////////////////////////////////////////////////////////////////
ACU_INT wait_for_hangup( PROS_RES_MAN *pros_res_man )
{
    ACU_ERR                result = 0;
    ACU_INT                next_state = STATE_COMPLETE;    // Default next state
    ACU_SDK_WAIT_OBJECT    *pros_events[2];    // Array of the events we want to wait on
    ACU_OS_BOOL            pros_events_sig[2];    // Array that indicates which of our events
                                            // were signalled

    // Set-up our wait object array:
    pros_events[0] = pros_res_man->kill_me;    // Wait object 0 is application termination
    pros_events[1] = pros_res_man->hungup;    // Wait object 1 is caller hung-up

    /////////////////////////////////////////////////////////////////////////////////
    //
    // Wait for any one of our events to be signalled.  When this function returns
    // the entries in 'pros_events_sig' array will be set to indicate whether
    // or not the corresponding event was signalled
    // For example, if 'pros_events[1]' signalled then 'pros_events_sig[1]' would be
    // set to ACU_OS_TRUE.
    //
    /////////////////////////////////////////////////////////////////////////////////
    result = acu_os_wait_for_any_wait_object( 2,    // Number of events we're waiting on
          pros_events,        // The events we're waiting for
                pros_events_sig,    // The events singalled state
          REPROMPT_DELAY);    // How long to wait

    if ( result == ERR_ACU_OS_SIGNAL )
    {
        // This is a fatal error, as there's a chance that at least one of our
        // wait objects is invalid, safer to drop out.
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Resource thread failed to wait, terminating");
        exit(-1);
    }
    else if ( result == ERR_ACU_OS_TIMED_OUT )
    {
        // Caller hasn't hung-up after 10 seconds, replay hang-up prompt
        next_state = STATE_PLAY_HANGUP_PROMPT;
    }
    else
    {
        // At least one of our wait objects was signalled, find out which one(s)
        if ( pros_events_sig[0] == ACU_OS_TRUE )
        {
            // Application is terminating:
            next_state = STATE_APP_EXIT;
        }
        else
        {
            // Caller has hung-up, clear down normally
            next_state = STATE_COMPLETE;
        }
    }

    return next_state;
}

//////////////////////////////////////////////////////////////////////
//
//    remove_detection
//
//    Free the resources allocated for digit detection.
//
//////////////////////////////////////////////////////////////////////
void remove_detection( PROS_RES_MAN *pros_res_man )
{
    ACU_ERR                        result = 0;
    SM_CHANNEL_SET_EVENT_PARMS    event_parms;

    // Clear the API structure:
    INIT_ACU_SM_STRUCT( &event_parms );

    // Clear the Prosody event mapping:
    event_parms.channel         = pros_res_man->channel;
    event_parms.issue_events = kSMChannelNoEvent;
    event_parms.event         = *(pros_res_man->event);
    event_parms.event_type     = kSMEventTypeRecog;

    result = sm_channel_set_event( &event_parms );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not disassociate channel and event <%d>", result);
    }

    // Free the event from the Prosody API:
    smd_ev_free( *pros_res_man->event );

    // Free the memory that we used for the Prosody event:
    if ( pros_res_man->event != NULL )
    {
        acu_os_free( pros_res_man->event );
    }

    // Now free the event we created from the Prosody event:
    if ( pros_res_man->digits != NULL )
    {
        acu_os_destroy_wait_object(pros_res_man->digits);
    }
}


//////////////////////////////////////////////////////////////////////
//
//    start_prompt
//
//    This function sets up the required Prosody resources for starting
//    the specified prompt (in "filename").  If this is successfull the
//    function spawns a thread that will block until the prompt has
//    completed.
//
//    Returns 0 for success, or a negative value indicating the error.
//
//////////////////////////////////////////////////////////////////////
ACU_INT start_prompt( PROS_RES_MAN *pros_res_man, char *filename )
{
    ACU_ERR result = 0;
    SM_FILE_REPLAY_PARMS    *prompt_parms;

    // Allocate API structure
    // NOTE: acu_os_alloc initialises the memory for us
    prompt_parms = acu_os_alloc( sizeof(SM_FILE_REPLAY_PARMS) );
    if ( prompt_parms == NULL )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not create prompt structure");
        exit(-1);
    }

    // Attempt to start the prompt first:
    // NOTE: The remaing values are either left as default or are set-up by
    // the API call sm_replay_wav_start()
    prompt_parms->replay_parms.channel    = pros_res_man->channel;
    prompt_parms->replay_parms.type        = kSMDataFormat8KHzALawPCM;
   
    result = sm_replay_wav_start( filename, prompt_parms );
    if ( result != 0 )
    {
        // Fatal error: couldn't initiate wav replay
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not initiate wav %s replay <%d>", filename, result);
    }
    else
    {
        // Initiated prompt, now we can set up the information required to progress
        // the prompt:
        pros_res_man->prompt_parms = prompt_parms;

        // Spawn prompt thread:
        pros_res_man->prompt_thread_id = acu_os_create_thread( prompt_playing, pros_res_man );
        if ( pros_res_man->prompt_thread_id == NULL )
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not create prompt thread, terminating");
            exit(-1);
        }
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    prompt_playing
//
//    This is a thread function which blocks playing a prompt.  When
//    the prompt is finished, the thread signals its' caller.
//
//    Returns 0 for success, or a negative value indicating the error.
//
//////////////////////////////////////////////////////////////////////
ACU_INT prompt_playing( void *parameters )
{
    ACU_ERR result = 0;
    PROS_RES_MAN    *pros_res_man = (PROS_RES_MAN *)parameters;    // Covert argument to something meaningful

    result = sm_replay_file_complete( pros_res_man->prompt_parms );
    if( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to complete file replay -> <%d>", result );

        return result;
    }

    // Prompt finished:
    result = acu_os_signal_wait_object( pros_res_man->prompt );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not signal prompt");
    }

    return result;
}


//////////////////////////////////////////////////////////////////////
//
//    get_digit
//
//    Get a detected digit from the Prosody queue and determines if the
//    digit is in the range of valid digits.
//
//    Returns VALID_DIGIT if the digit is valid, INVALID_DIGIT if
//  digit is invalid, NO_DIGIT otherwise
//
//////////////////////////////////////////////////////////////////////
ACU_INT get_digit( PROS_RES_MAN *pros_res_man )
{
    ACU_INT result = 0;
    tSM_INT                digit = 0;
    ACU_OS_BOOL            valid = ACU_OS_FALSE;
    SM_RECOGNISED_PARMS    recog_parms;

    // Clear API structure:
    INIT_ACU_SM_STRUCT( &recog_parms );

    recog_parms.channel = pros_res_man->channel;

    result = sm_get_recognised( &recog_parms );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not collect digit <%d>", result);
    }
    else
    {
        if ( recog_parms.type == kSMRecognisedDigit )
        {
            // Got digit determine if it's valid:
            valid = validate_digit( recog_parms.param0 );
            if ( valid )
            {
                // Set-up the digit:
                digit = recog_parms.param0 - '0';

                // Update the stats:
                update_votes( digit );
                result = VALID_DIGIT;
            }
            else
            {
                result = INVALID_DIGIT;
            }
        }
        else if( recog_parms.type == kSMRecognisedNothing )
        {
            result = NO_DIGIT;
        }
    }

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    stop_prompt
//
//    Determine if there's currently a prompt in progress for the given
//    resource, if so, terminate it.
//
//    Returns 0 for success, or a negative value indicating the error.
//
//////////////////////////////////////////////////////////////////////
ACU_INT stop_prompt( PROS_RES_MAN *pros_res_man )
{
    ACU_ERR result = 0;

    // Abort the replay job:
    result = sm_replay_file_stop( pros_res_man->prompt_parms );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not abort prompt <%d>", result);
    }

    // Wait for prompt thread to termiante:
    // NOTE: The thread may already have terminated, in which case the function
    //         will return immdeiatley
    result = acu_os_wait_for_thread( pros_res_man->prompt_thread_id );

    // Free thread resources:
    acu_os_destroy_thread( pros_res_man->prompt_thread_id );

    // Tidy up other resources:
    result = sm_replay_wav_close( pros_res_man->prompt_parms );
    if ( result != 0 )
    {
        acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Could not close wav file <%d>", result);
    }

    // Make sure the prompt event is not signalled:
    acu_os_unsignal_wait_object( pros_res_man->prompt );

    // Free the memory allocated for the prompt parameters:
    acu_os_free( pros_res_man->prompt_parms );
    pros_res_man->prompt_parms = NULL;

    return result;
}

//////////////////////////////////////////////////////////////////////
//
//    validate_digit
//
//    Check the given digit against this application's range of valid
//    values.
//
//    Return ACU_OS_TRUE if digit is valid, ACU_OS_FALSE otherwise.
//
//////////////////////////////////////////////////////////////////////
ACU_OS_BOOL validate_digit( tSM_INT digit )
{
    ACU_OS_BOOL    valid = ACU_OS_FALSE;
   
    // Valid digits for this application are 1.. NUM_CONTESTANTS
    if ( (digit >= '1') && (digit <= (NUM_CONTESTANTS + '0') ) )
    {
        valid = ACU_OS_TRUE;
    }

    return valid;
}


//////////////////////////////////////////////////////////////////////
//
//    update_votes
//
//    Update the vote count for the specified contestant.
//   
//    NOTE: As this function can be called by any of the Prosody threads
//          we need to control access to the totals that are being
//          updated.
//
//////////////////////////////////////////////////////////////////////
void update_votes( ACU_INT contestant )
{
    // We need to synchronise access to the count:
    acu_os_lock_critical_section( vote_lock );
   
    // Update this contestants votes:
    // NOTE: Because the votes are from 1 to NUM_CONTESTANTS and the array is
    //         indexed from 0 to (NUM_CONTESTANTS - 1) we need to subtract 1 from
    //         the contestant number.
    ++votes[contestant - 1];

    // Now update the display for this contestant
    acu_os_printf_at( VOTE_COLUMN, VOTE_ROW + (contestant - 1), "%d", votes[contestant - 1]);
   
    acu_os_unlock_critical_section( vote_lock );
}

/////////////////////////////////////////////////////////////////////
//
//    setup_prosody_module
//
//    This function sets up a Prosody module first opens the module then
//    allocates a number of channels and spawns a thread for each of these
//  channels that have been allocated.
//
//    Return 0 for success - a negative value indicating an error
//
//////////////////////////////////////////////////////////////////////

ACU_INT setup_prosody_module( MODULE_DATA* module_data , CARD_DATA* card_data)
{
        PROS_DATA* pros_data    = NULL;

        ACU_ERR        result        = 0;
        ACU_INT        stream        = 0;
        ACU_INT        timeslot    = 0;
        ACU_INT        channels    = CHANNELS_PER_MODULE;
       
        ACU_OS_BOOL    using_2nd_stream    = ACU_OS_FALSE;

        ACU_OS_THREAD*    thread_id;

        SM_OPEN_MODULE_PARMS        open_module;
        SM_MODULE_INFO_PARMS        mod_info;
       
        INIT_ACU_SM_STRUCT( &open_module );

        open_module.module_ix    = module_data->module_ix;
        open_module.card_id        = card_data->card_id;

        //open the module so we get a module_id
        result = sm_open_module( &open_module );
        if( result != 0 )
        {

            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: Unable to open Prosody Module <%d>", result);

            acu_os_free( module_data );

            return result;
        }

        //record the module_id returned by the API
        module_data->module_id    = open_module.module_id;
        module_data->card_id    = card_data->card_id;

        //find out the BASE MODULE STREAM HERE.....
        INIT_ACU_SM_STRUCT( &mod_info );

        mod_info.module = module_data->module_id;

        result = sm_get_module_info( &mod_info );
        if( result != 0 )
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Failed to obtain module information %d", result );
           
            exit(1);
        }

        module_data->min_stream = mod_info.min_stream;

    // Allocate and setup all of the channels for the specified modules.
    // For each channel we allocate, start a resource thread and pass in
    // the required info.
    ////////////////////////////////////////////////////////////////
    //
    // Now we can allocate the Prosody switching resources.
    // First, we need to determine what our base stream is.  We do
    // this by using our card module index, and multiplying this
    // value by 2 and adding the result to the base stream value. 
    // This will give us streams on an even offset, which is what we
    // want as each module has an even & and odd stream (starting
    // with the even value).  For example, Module 3 on a card would
    // give us: (2 * 2) + 48 = 52 (remember modules are 0 based).
    //
    /////////////////////////////////////////////////////////////////
    stream = (module_data->module_ix * 2) + module_data->min_stream;

    for ( timeslot = 0; timeslot < channels; timeslot++ )
    {
        //now check if we need to use the 2nd channel
        if( timeslot >= TIMESLOTS_PER_STREAM )
        {
            //Need to use the 2nd stream on the module, make sure we are not already using it
            if( using_2nd_stream == ACU_OS_TRUE )
            {
                return ERR_SM_NO_RESOURCES; //Error code from the Speech API
            }

            //adjust the counters
            stream += 1; //2nd stream
            channels -= TIMESLOTS_PER_STREAM;    //numbers of channels remaining
            timeslot = 0;    //reset timeslot to 0 (1st ts on the 2nd stream
        }

        //Now we've got a stream and timeslot, allocate a channel and its data
        result = allocate_channel( module_data, stream , timeslot, &pros_data );
        if( result != 0 )
        {
            acu_os_printf_at( ERROR_COLUMN, ERROR_ROW, "Error: could not allocate channel <%d>", result );

            return result;
        }

        //Now that a channel has been set-up start it's thread:
        //NOTE: The Prosody Resource will indicate when it's ready to be used

        thread_id = acu_os_create_thread( prosody_resource_thread, pros_data );
        if( thread_id == NULL )
        {
            acu_os_printf_at( ERROR_COLUMN , ERROR_ROW, "Error: Could not create resource thread for Prosody Channel" );

            return ERR_ACU_OS_ERROR;
        }
        else
        {
            //record the thread_id for the thread just spawned
            pros_data->thread_id = thread_id;
        }
       
    }
    return result;
}

/////////////////////////////////////////////////////////////////
//
//    free_prosody_resources_on_module
//
//    This function frees all the resources currently on the
//    free_resource_queue
//
//
/////////////////////////////////////////////////////////////////

void free_prosody_resources_on_module( MODULE_DATA* module_data )
{
    PROSODY_NODE* pros_node = NULL;

    PROS_DATA *pros_data = NULL;

    acu_os_lock_critical_section( free_resource_lock );

    //find the first prosody resource on the module indicated by modile_data->module_id
    pros_node = acu_qq_iterate( &free_pros_resources, find_pros_res_on_module, (void*)&(module_data->module_id), 0 );

    acu_os_unlock_critical_section( free_resource_lock );

    //make a copy of the pros_data
    pros_data = pros_node->pros_data;

    //remove this node from the linked list
    acu_qq_extract( &free_pros_resources, &pros_node->node );

    //free the associated pros_node memory
    acu_os_free( pros_node );

    while( pros_data != NULL )
    {
        // Signal this thread to stop:
        acu_os_signal_wait_object( pros_data->kill_me );

        // Now wait for the thread to terminate:
        acu_os_wait_for_thread( pros_data->thread_id );

        // Now that the thread has terminated, free it's resource:
        acu_os_destroy_thread( pros_data->thread_id );

        // Finally, free the Prosody data:
        acu_os_free( pros_data );

        pros_data = NULL;

        acu_os_lock_critical_section( free_resource_lock );

        pros_node = acu_qq_iterate( &free_pros_resources, find_pros_res_on_module, (void*)&(module_data->module_id), 0 );

        acu_os_unlock_critical_section( free_resource_lock );

        if( pros_node != NULL )
        {
            //make a copy of the pros_data
            pros_data = pros_node->pros_data;
           
            //remove this node from the linked list
            acu_qq_extract( &free_pros_resources, &pros_node->node );
           
            //free the associated pros_node data memory
            acu_os_free( pros_node );
        }
    }
}