Skip to content

Multi-Instance.c

Examples

/****************************************************************************
 *
 *      Multi-Instance
 *      --------------
 *
 ****************************************************************************
 *
 *  Description:    Demonstrate Multi Instance capabilities of the API. Supports single socket mode, and two socket mode.
 *
 *  Copyright: (c) 2020 DSP Concepts, Inc. All rights reserved.
 *                                  3235 Kifer Road
 *                                  Santa Clara, CA 95054-1527
 *
 * This example demonstrates the multi instance capabilities of AWE Core OS, as well as tuning interface logging. 
 * There are two modes, single socket (default) and uniquesocket.
 * 
 * Single socket mode (default) creates NUM_INSTANCES AWEOSInstances and a single Multi-Instance TCP tuning interface. 
 * You can specify the port number with cmd line option -portno:<int>, but a default of 15002 is used.
 * Upon connection to AWE Server, a dropdown menu will appear in the top left corner, which toggles between the two instances.
 * Try configuring the instances individually and recompiling, then reconnect. Notice how the information in Server changes as you toggle between instances. (see the pName changes below)
 * With logging enabled (-enableLogging), a single log file will be created for both of the instances.
 * 
 * Unique socket mode (running with "-uniquesockets") will also create NUM_INSTANCES AWEOSInstances instances, but each with their own individual tuning interface TCP socket.
 * By default, the first instance's socket is opened on port 15002, and the second on 15004 and so on with even number increments. Port numbers can be specified with "-portnos:[<array of ints>]"
 * Windows only allows one instance of AWE Server running on the PC, so to toggle between instances you must disconnect and reconnect to each individual port. 
 * With logging enabled (-enableLogging) in unique socket mode, individual log files will be created per socket.
 * 
 * This example does not have RT audio capabilities, it is simply meant to demonstrate multi instance functionality, however module regression tests can be run on each individual AWEOSInstance. 
 * Routing RT audio between AWE Core OS instances is the responsibility of the integrator. 
 * 
***************************************************************************/
#define _GNU_SOURCE
#include <pthread.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#ifndef __QNX__
#include <sys/prctl.h>
#include <sys/sysinfo.h>
#include <sys/syscall.h>
#endif /* __QNX__ */

#include <errno.h>
#include <math.h>

#ifndef SYS_gettid
#warning "SYS_gettid unavailable on this system"
#else
#define gettid() ((pid_t)syscall(SYS_gettid))
#endif

#include "AWECoreOS.h"
#include "ModuleList.h"
#include "AWECoreUtils.h"

#define NUM_CHANNELS    (2)
#define SAMPLE_RATE     (48000.f)
#define NUM_INSTANCES   (3)
#define CORE_SPEED      (1.2e9)
#define PROFILE_SPEED   (10e6)


#define AUDIO_CALLBACK_PRIO_OFFSET     (0)
#define AUDIO_PUMPALL_PRIO_OFFSET      (1)


#ifndef PI
#define PI              (3.141592653589793f)
#endif

//globals for command line args
UINT32 portNo = 15002;
UINT32 portNos[NUM_INSTANCES];
UINT32 uniqueSocketsFlag = 0;
UINT32 enableLoggingFlag = 0;
UINT32 blockSize = 32;

INT32 * inputBuffer;
INT32 * outputBuffer;

static UINT32 quiet = 0;
UINT64 count; 

//Step 1: Declare an Array of AWEOSInstance pointers. Memory for the instances are allocated by aweOS_init()
AWEOSInstance* g_AWEOSInstanceArray[NUM_INSTANCES];

//Step 2: Declare an AWEOSConfigParameters structure. The members of this structure determine the configuration of the AWEInstance members. For this example, it will be populated with defaults
static AWEOSConfigParameters configParams[NUM_INSTANCES];

//Step 3: Define the module descriptor table from ModuleList.h that contains all modules
static const void* moduleDescriptorTable[] =
{
    LISTOFCLASSOBJECTS
};
UINT32 moduleDescriptorTableSize = sizeof(moduleDescriptorTable) / sizeof(moduleDescriptorTable[0]);

// Global thread handles and mutexes
pthread_t audioCallbackThreadHandle;
pthread_mutex_t audioThreadMutex;
pthread_cond_t audioThreadCond;
pthread_t audioPumpAllThreadHandles[NUM_INSTANCES];

sem_t pumpSems[NUM_INSTANCES];

// Global variables
INT32 audioStarted = 0;
INT32 exitAudioCallbackThread = 0;
INT32 pumpActive[NUM_INSTANCES];
UINT32 pumpCnts[NUM_INSTANCES];
UINT32 errMask;
UINT32 setCPUAffinity = 0;
UINT32 threadCpuToSet = 1;
AWEOSThreadPIDs_t threadPIDs;

void sig_handler(int signo)
{   
    if (!quiet)
    {
        printf("Exiting Multi-Instance App\n");
    }
    exit(0);    
}


static void usage(const char *program)
{
    printf(
        "Usage: %s [args]\n"
        "       -portno:<uint>                          Port Number to open the single multi-instance tuning interface socket. Default 15002\n"
        "       -uniquesockets                          If this flag is defined, then an individual tuning interface socket will be opened on each instance\n"
        "       -portnos:<comma separated uints>        Multi sockets mode only - comma separated port numbers to open the socket for each instance. Default 15002,15004,15006 and so on\n"
        "       -enableLogging                          Enable tuning interface logging. with -uniquesockets enabled, an individual tuning log file is generated per instance. Disabled by default\n"
        "       -bsize                                  Block size (default 32)\n"
        "       -enableCPUAffinity                      Enable the logic in audioStart callback which will query thread PIDs and attempt to set CPU affinity per layout \n"
        "       -help                                   Show usage.\n"
        "This program demonstrates multi instance functionality in AWE Core OS.\n",
        program);
        exit(0);
}

static INT32 getTargetPriorityOffset(INT32 instanceNum)
{
    UINT32 priorityOffset = 0;
    while (--instanceNum >= 0)
    {
        // + 1 for the pumpall thread per instance
        priorityOffset += (configParams[instanceNum].numThreads + 1);
    }
    return priorityOffset;
}

static INT32 getInstanceNumber(AWEOSInstance *pAWEOS)
{
    INT32 i = 0;
    while (i < NUM_INSTANCES)
    {
        if (pAWEOS == g_AWEOSInstanceArray[i])
        {
            break;
        }
        i++;
    }
    return i;
}

void* audioCallbackSimulator(void * args)
{
    // Simple real-time audio simulator. Would be ALSA, PortAudio, etc callback
    // Will not achieve exact realtime interrupts
    struct timespec ts;
    struct timespec ts_tmp;

    long long accumulated_time1;
    long long accumulated_time2;
    long long overshoot = 0.0; 

    (void) args;

    pthread_mutex_lock(&audioThreadMutex);
    audioStarted = 1;
    pthread_cond_signal(&audioThreadCond);
    pthread_mutex_unlock(&audioThreadMutex);

    // Calculate sleep time for fundamental blocksize
    const long time_nsec = (long) ((float)1000000000L * ((float)blockSize / SAMPLE_RATE));

    ts.tv_sec = 0;
    ts_tmp.tv_sec = 0;
    ts_tmp.tv_nsec = 0;

    // Set this thread to run at real time priority
    int max_priority = sched_get_priority_max(SCHED_FIFO);
    struct sched_param audio_sched_param = {0};
    pthread_t currentHandle = pthread_self();
    int target_priority = max_priority - AUDIO_CALLBACK_PRIO_OFFSET;
    audio_sched_param.sched_priority = target_priority;
    pthread_setschedparam(currentHandle, SCHED_FIFO, &audio_sched_param);

    int policy;
    pthread_getschedparam(currentHandle, &policy, &audio_sched_param);
    if ((SCHED_FIFO != policy) ||
      (audio_sched_param.sched_priority != target_priority)) 
    {
        printf("Warning: Failed to increase priority of audio simulation thread to %d\nTry running as root\n", target_priority);
    }

#if defined(SYS_gettid)
    if (setCPUAffinity)
    {
        // Set the pump all threads to CPU 0 if requested. This handles case of low-latency mode
        cpu_set_t callbackthread_set;      
        pid_t callbackPID;
        INT32 ret; 
        static const int cpuToSet = 0;
        CPU_ZERO(&callbackthread_set);
        CPU_SET(cpuToSet, &callbackthread_set);         
        callbackPID = gettid();
        ret = sched_setaffinity(callbackPID, sizeof(cpu_set_t), &callbackthread_set);   
        if (ret == 0)
        {
            printf("Set audio callback thread (PID = %d) to CPU %d \n", callbackPID, cpuToSet); 
        }
        else 
        {
            printf("Could not set CPU affinity of audio callback thread (PID = %d) to CPU %d , errno: %d \n", callbackPID, cpuToSet, errno);
        }
    }
#endif    

    while (!exitAudioCallbackThread)
    {
        INT32 i;
        // Kick off all the audio threads
        // This app assumes all instances are the same block size and driven by the same audio device
        for (i = 0; i < NUM_INSTANCES; i++)
        {
            sem_post(&pumpSems[i]);
        }

        // Might need to post more than once due to clock resolution 
        while (overshoot > time_nsec)
        {
            overshoot -= time_nsec;
            for (i = 0; i < NUM_INSTANCES; i++)
            {            
                sem_post(&pumpSems[i]);
            }
        }

        ts.tv_nsec = time_nsec;

        clock_gettime(CLOCK_MONOTONIC, &ts_tmp);
        accumulated_time1 = ((long long)ts_tmp.tv_sec*1000000000) + ts_tmp.tv_nsec + time_nsec;

        while(1)
        {
            clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);

            clock_gettime(CLOCK_MONOTONIC, &ts_tmp);

            accumulated_time2 = ((long long)ts_tmp.tv_sec*1000000000) + ts_tmp.tv_nsec;

            if (accumulated_time2 >= accumulated_time1)
            {
                overshoot += (accumulated_time2 - accumulated_time1);
                break;
            }
            else
            {
                ts.tv_nsec = (long)(accumulated_time1 - accumulated_time2);
            }
        }
    }

    return NULL;
}


void* aweOSuser_pumpAudio(void * args)
{
    // Represents the callback function from the audio framework
    INT32 schedPolicy, ret, targetPriority, priorityOffset;
    struct sched_param schedParam;

    AWEOSInstance * pAWEOS = (AWEOSInstance *) args;
    INT32 instanceNum = getInstanceNumber(pAWEOS);
    priorityOffset = getTargetPriorityOffset(instanceNum);

    // Set this thread to run at real time priority
    pthread_t currentHandle = pthread_self();
    int max_priority = sched_get_priority_max(SCHED_FIFO);
    targetPriority = max_priority - AUDIO_PUMPALL_PRIO_OFFSET - priorityOffset;
    schedParam.sched_priority = targetPriority;
    schedPolicy = SCHED_FIFO;
    pthread_setschedparam(currentHandle, schedPolicy, &schedParam);
    int policy;
    pthread_getschedparam(currentHandle, &policy, &schedParam);
    if ((SCHED_FIFO != policy) || (schedParam.sched_priority != targetPriority))
    {
        printf("Warning: Failed to increase priority of instance %d pumpall thread to %d\nTry running as root\n", instanceNum, targetPriority);
    }

    char threadNameBuffer[16]; 
    snprintf(threadNameBuffer, 16, "awe%d_pumpall", instanceNum);    
    ret = pthread_setname_np(audioPumpAllThreadHandles[instanceNum], threadNameBuffer);

#if defined(SYS_gettid)
    if (setCPUAffinity)
    {
        // Set the pump all threads to CPU 0 if requested. This handles case of low-latency mode
        cpu_set_t pumpthread_set;      
        pid_t pumpAllPID;
        INT32 ret; 
        static const int cpuToSet = 0;
        CPU_ZERO(&pumpthread_set);
        CPU_SET(cpuToSet, &pumpthread_set);         
        pumpAllPID = gettid();
        ret = sched_setaffinity(pumpAllPID, sizeof(cpu_set_t), &pumpthread_set);    
        if (ret == 0)
        {
            printf("Set audio callback thread (PID = %d) to CPU %d \n", pumpAllPID, cpuToSet); 
        }
        else 
        {
            printf("Could not set CPU affinity of pumpall thread (PID = %d) to CPU %d , errno: %d \n", pumpAllPID, cpuToSet, errno);
        }
    }
#endif

    //Prepare the stereo input and output buffers for the audio processing
    // The I/O buffers are allocated based on the target info / configParams 
    size_t inSize = configParams[instanceNum].inChannels * configParams[instanceNum].fundamentalBlockSize;
    size_t outSize = configParams[instanceNum].outChannels * configParams[instanceNum].fundamentalBlockSize;

    UINT32 * inputBuffer, * outputBuffer;
    inputBuffer = malloc(inSize * sizeof(UINT32));
    outputBuffer = malloc(outSize * sizeof(UINT32));

    // Fill audio input buffers with sin waves scaled by -12 dB
    for (int i = 0; i < blockSize; i++)
    {
        for (int j = 0; j < NUM_CHANNELS; j++) 
        {
            inputBuffer[i * NUM_CHANNELS + j] = float_to_fract32(sinf(2.f * PI * (j + 1) * (SAMPLE_RATE / blockSize) * (i / SAMPLE_RATE))) >> 2;
        }
    }


    if (!quiet)
    {
        printf("Starting pump thread for instance %d\n", instanceNum);
    }
    while (1)
    {
        // Wait for signal to run thread
        sem_wait(&pumpSems[instanceNum]);
        if (aweOS_layoutIsValid(pAWEOS) && aweOS_audioIsStarted(pAWEOS))
        {
            // Import new samples
            for (int i = 0; i < configParams[instanceNum].inChannels ; i++)
            {
                // Import new samples
                ret = aweOS_audioImportSamples(pAWEOS, &inputBuffer[i], configParams[instanceNum].inChannels, i, Sample32bit);
                if (ret)
                {
                    printf("Error: instance %d aweOS_audioImportSamples() failed, channel = %d, error = %d\n", instanceNum, i, ret);
                }
            }

            // Pump the sublayout
            aweOS_audioPumpAll(pAWEOS);

            // Export samples
            for (int i = 0; i < configParams[instanceNum].outChannels ; i++)
            {
                // Import new samples
                ret = aweOS_audioExportSamples(pAWEOS, &outputBuffer[i], configParams[instanceNum].outChannels, i, Sample32bit);
                if (ret)
                {  
                    printf("Error: instance %d aweOS_audioExportSamples() failed, channel = %d, error = %d\n", instanceNum, i, ret);
                }
            }
            pumpCnts[instanceNum]++;
        } // if layout valid
    }
    free(inputBuffer);
    free(outputBuffer);
    return NULL;
}


INT32 aweOSuser_audioStart(AWEOSInstance *pAWEOS)
{
    INT32 ret;
    UINT32 layoutInChannels, layoutOutChannels, blockSize;
    FLOAT32 sampleRate;

    INT32 instanceNum = getInstanceNumber(pAWEOS);

    // Audio start callback. Can be used to start audio stream from audio framework.
    // Could be ALSA, Pulse, Jack, Portaudio, etc. 

    ret = aweOS_layoutGetChannelCount(pAWEOS, &layoutInChannels, &layoutOutChannels);
    if (E_SUCCESS != ret) 
    {
        printf("Failed to get layout channel counts: ret = %d: %s\n", ret, aweOS_errorToString(ret));
    }

    ret = aweOS_layoutGetBlockSize(pAWEOS, &blockSize);
    if (E_SUCCESS != ret) 
    {
        printf("Failed to get layout block size: ret = %d: %s\n", ret, aweOS_errorToString(ret));
    }   

    ret = aweOS_layoutGetSampleRate(pAWEOS, &sampleRate);
    if (E_SUCCESS != ret) 
    {
        printf("Failed to get layout sample rate: ret = %d: %s\n", ret, aweOS_errorToString(ret));
    }

    printf("Loaded layout on instance %d with inChans = %u, outChans = %u, blockSize = %u, sampleRate = %f\n", 
            instanceNum, layoutInChannels, layoutOutChannels, blockSize, sampleRate);   
    #if defined(SYS_gettid) 
    // Set the core affinity for each processing thread if requested
        if (setCPUAffinity)
        {
            // Get the internally running thread PIDs and print them
            ret = aweOS_getThreadPIDs(pAWEOS, &threadPIDs);
            if (ret != 0)
            {
                printf("aweOS_getThreadPIDs failed with error %d. Not setting CPU affinity... \n ", ret);
            }
            else
            {
                INT32 availableProcessors =  get_nprocs();
                for (INT32 i = 0; i < threadPIDs.numPumpThreads; i++)
                {
                    INT32 cpuToSet;
                    cpu_set_t pumpthread_set;                  
                    // Audio streaming thread always set to first cpu
                    CPU_ZERO(&pumpthread_set);
                    cpuToSet = threadCpuToSet++ % availableProcessors;
                    CPU_SET(cpuToSet, &pumpthread_set);

                    ret = sched_setaffinity(threadPIDs.pumpThreadPIDs[i], sizeof(cpu_set_t), &pumpthread_set);  
                    if (ret == 0)
                    {
                        if (!quiet)
                        {   
                            printf("Set pumpThread %d (PID = %u) to CPU %d \n", i, threadPIDs.pumpThreadPIDs[i], cpuToSet); 
                        }
                    }
                    else 
                    {
                        printf("Could not set CPU affinity of pumpThread %d to CPU %d , errno: %d \n", i, cpuToSet, errno);
                    }
                }
            }
        }
    #endif

    // Kick off real-time audio simulation thread, if not already started
    pthread_mutex_lock(&audioThreadMutex);
    pumpActive[instanceNum] = 1;
    if (!audioStarted)
    {
        pthread_mutex_unlock(&audioThreadMutex);    

        exitAudioCallbackThread = 0;
        ret = pthread_create(&audioCallbackThreadHandle, NULL, audioCallbackSimulator, NULL);
        if (0 != ret) 
        {
            printf("Error creating audio callback simulator! ret = %d\n", ret);
            return 0;
        }

        // Reset the count to 0, at the start of audio processing
        memset(pumpCnts, 0, NUM_INSTANCES*sizeof(UINT32));

        // Wait for thread to have been created
        pthread_mutex_lock(&audioThreadMutex);
        while (audioStarted != 1)
        {
            pthread_cond_wait(&audioThreadCond, &audioThreadMutex);
        }
        pthread_mutex_unlock(&audioThreadMutex);
    }
    else 
    {
        pthread_mutex_unlock(&audioThreadMutex);
    }
    printf("audio start, instance %d, active %d\n", instanceNum, pumpActive[instanceNum]);
    return 0;
}


INT32 aweOSuser_audioStop(AWEOSInstance *pAWEOS)
{
    INT32 i, anyActive = 0;
    INT32 ret, realStop = 0;
    INT32 instanceNum = getInstanceNumber(pAWEOS);

    printf("audio stop, instance %d, active %d\n", instanceNum, pumpActive[instanceNum]);
    // Audio stop callback. Can be used to clean up audio stream.
    pthread_mutex_lock(&audioThreadMutex);
    // Preserve previous pumpActive state to control prints
    realStop = pumpActive[instanceNum];
    pumpActive[instanceNum] = 0;
    for (i = 0; i < NUM_INSTANCES; i++)
    {
        anyActive |= pumpActive[i];
    }
    if (!anyActive && audioStarted)
    {
        pthread_mutex_unlock(&audioThreadMutex);

        exitAudioCallbackThread = 1;
        ret = pthread_join(audioCallbackThreadHandle, NULL);
        if (0 != ret) 
        {
            printf("Failed to join audio simulator thread: ret = %d\n", ret);
        }

        pthread_mutex_lock(&audioThreadMutex);
        audioStarted = 0;
        pthread_mutex_unlock(&audioThreadMutex);
        errMask = 0;

        threadCpuToSet = 1;
    }
    else
    {
        pthread_mutex_unlock(&audioThreadMutex);
    }

    if (!quiet && realStop)
    {
        UINT32 avgCycles;

        printf("\nAudio Stopped on instance %d\n", instanceNum);

        float clockRatios = configParams[instanceNum].coreSpeed / configParams[instanceNum].profileSpeed;
        ret = aweOS_getAverageLayoutCycles(g_AWEOSInstanceArray[instanceNum], 0, &avgCycles);
        if (ret != E_SUCCESS)
        {
            avgCycles = 0;
        }
        printf("-- Pumped instance %d for %u times, average cycles per pump: %f\n", instanceNum, pumpCnts[instanceNum], 
            (avgCycles >> 8) * clockRatios);
    }


    return 0;
}

int main(int argc, char **argv)
{
    UINT32 i;
    INT32 ret = E_SUCCESS;
    char *token = NULL;

    // Initialize default unique socket port numbers for each instance
    for (i = 0; i < NUM_INSTANCES; i++)
    {
        portNos[i] = 15002 + (i * 2);
    }

    if (1 == argc)
    {
        printf("No command line options specified. Opening in default single socket mode with TCP socket on port number %u\n", portNo);     
    }
    else 
    {
        for (i = 1; i < argc; i++)
        {
            char *arg = argv[i];

            if (0 == strncmp(arg, "-portno:", 8))
            {
                portNo = atoi(arg + 8);
            }
            else if ((0 == strncmp(arg, "-portnos:", 9)))
            {
                // Walkthrough the list of port numbers and initialize the portNos array.
                // If the port numbers entered larger than NUM_INSTANCES, ignore excess values.

                i = 0;

                /* get the first token */
                token = strtok(arg + 9, ",");

                /* walk through other tokens */
                while ( token != NULL )
                {
                    portNos[i] = atoi(token);
                    i++;
                    if (i >= NUM_INSTANCES)
                    {
                        // If NUM_INSTANCES reached, break the loop
                        break;
                    }
                    token = strtok(NULL, ",");
                }
            }
            else if (0 == strncmp(arg, "-uniquesockets", 14))
            {
                uniqueSocketsFlag = 1;
            }
            else if ((0 == strncmp(arg, "-enableLogging", 14)))
            {
                enableLoggingFlag = 1;
                printf("enableLogging set to true. Tuning logging enabled\n");
            }
            else if ((0 == strncmp(arg, "-bsize:", 7)))
            {
                blockSize = atoi(arg + 7);;
                printf("blockSize set to %d\n", (INT32)blockSize);
            }
            else if ((0 == strncmp(arg, "-enableCPUAffinity", 13)))
            {
                setCPUAffinity = 1;
                printf(" enableCPUAffinity activated\n");
            }                   
            else if ((0 == strncmp(arg, "-help", 5)))
            {
                usage(argv[0]);
            }
            else
            {
                printf("main: unknown option '%s'\n", arg);
            }
        }

        if (uniqueSocketsFlag)
        {
            printf("uniquesockets mode enabled. Opening an individual socket for each of the %d AWEOSInstances\n", NUM_INSTANCES);

            for (i = 0; i < NUM_INSTANCES; i++)
            {
                printf("Instance %u on port no: %u\n", i, portNos[i]);
            }
        }
        else
        {
            printf("singlesocket mode: portno for socket is %u\n", portNo);
        }
    }
    //Call this function with an arg to the user declared AWEOSConfigParameters structure.
    //This populates the configParams structure with DSPC defined default values. 
    for (i = 0; i < NUM_INSTANCES; i++)
    {
        aweOS_getParamDefaults(&configParams[i]);

        // Reduce the heap sizes according to the NUM_INSTANCES
        configParams[i].fastHeapASize = configParams[i].fastHeapASize / NUM_INSTANCES;
        configParams[i].fastHeapBSize = configParams[i].fastHeapBSize / NUM_INSTANCES;
        configParams[i].slowHeapSize  = configParams[i].slowHeapSize / NUM_INSTANCES;
    }

    //if the user is running with unique individual tuning sockets per instance, then assign the instanceID of configParams[N>0] to 0. 
    //This is because when using multiple tuning sockets, AWE Server sees each instance as its own individual target.
    //For a single tuning interface that supports multiple instances, we set configParams[1].instanceId to 16 and so on, this lets Server know that there are NUM_INSTANCES AWEOSInstances on the same socket.
    if (uniqueSocketsFlag == 1)
    {
        // Loop through NUM_INSTANCES and configure params accordingly
        for (i = 0; i < NUM_INSTANCES; i++)
        {
            configParams[i].instanceId = 0;
            configParams[i].pName = malloc(sizeof(char) * 8);
            sprintf((char*)configParams[i].pName, "#%u-soc%u", (i+1), (i+1));
        }
    }
    else 
    {
        //Overwrite the first instances default name to "#1" and the second to "#2" so that we can see this reflected in Server
        for (i = 0; i < NUM_INSTANCES; i++)
        {
             // Note that the instance ID is 16 as opposed to 1. AWE Server takes instanceIds in the order of 0, 16, 32, etc...
            configParams[i].instanceId = i * 16;
            configParams[i].pName = malloc(sizeof(char) * 8);
            sprintf((char*)configParams[i].pName, "aweOS#%u", i);
        }
    }

    // Assign start/stop callbacks to each instance
    for (i = 0; i < NUM_INSTANCES; i++)
    {
        configParams[i].cbAudioStart = aweOSuser_audioStart;
        configParams[i].cbAudioStop = aweOSuser_audioStop;
        configParams[i].fundamentalBlockSize = blockSize;
    }

    //Initialize the AWEOSInstance's with their own parameters that were previously set in the config structures.
    // The init allocates memory for the aweos instances internally
    for (i = 0; i < NUM_INSTANCES; i++)
    {    
        ret = aweOS_init(&g_AWEOSInstanceArray[i], &configParams[i], moduleDescriptorTable, moduleDescriptorTableSize); 
        //Check if the aweOS_init succeeded. If it didn't then terminate the executable.
        if (ret) 
        {
            printf("aweOS init failed for instance %d, ret = %d: %s\nExiting application\n", (INT32)i, ret, aweOS_errorToString(ret));
        }
    }

    if (!uniqueSocketsFlag) //Open in single socket mode
    {
        printf("Single Socket Mode\n");
        if (enableLoggingFlag)
        {
            INT32 loggingRet = aweOS_tuningLoggingEnable(g_AWEOSInstanceArray[0], "tuning_logs", "aweTuning", TUNING_LOG_INFO);
            if (loggingRet != 0)
            {
                printf("failed to open tuning log: ret = %d\n", ret);
            }
            else
            {
                printf("Tuning Logging enabled, a single log file will be generated for both AWEOSInstances.\n");
            }               
        }
        //Open the tuning interface. 
        INT32 interfaceRet = aweOS_tuningSocketOpen(g_AWEOSInstanceArray, portNo, NUM_INSTANCES); 
        if (interfaceRet != 0)
        {
            printf("failed to open aweOS integrated tuning interface\n");
        }
        else
        {
            printf("Succesfully opened a TCP single socket tuning interface on port number %u\n ", portNo);
        }
    }
    else //Open in uniquesockets mode
    {
        for (i = 0; i < NUM_INSTANCES; i++)
        {
            if (enableLoggingFlag) //enable logging on the first socket
            {
                INT32 loggingRet = aweOS_tuningLoggingEnable(g_AWEOSInstanceArray[i], "tuning_logs", "aweTuning", TUNING_LOG_INFO);
                if (loggingRet != 0)
                {
                    printf("failed to open tuning log: ret = %d\n", ret);
                }
                else
                {
                    printf("Tuning Logging enabled, individual log files created for socket %u\n", portNos[i]);
                }
            }

            //Open the tuning interface (on the first socket)
            INT32 interfaceRet = aweOS_tuningSocketOpen(&g_AWEOSInstanceArray[i], portNos[i], 1); //open the tuning socket
            if (interfaceRet != 0)
            {
                printf("failed to open aweOS integrated tuning interface\n");
            }
            else
            {
                printf("Successfully opened a TCP socket for the AWEOSInstance %u on port number %u\n", i, portNos[i]);
            }
        }
    }

    // Link the instances so profiling is correct (run this app with taskset or -enableCPUAffinity for this to work)
    aweOS_setInstancesInfo(g_AWEOSInstanceArray, NUM_INSTANCES);

    for (i = 0; i < NUM_INSTANCES; i++)
    {
        pumpActive[i] = 0;
    }

    // Start audio pumpall threads for all instances
    for (i = 0; i < NUM_INSTANCES; i++)
    {
        sem_init(&pumpSems[i], 0, 0);
        pthread_create(&audioPumpAllThreadHandles[i], NULL, aweOSuser_pumpAudio, g_AWEOSInstanceArray[i]);
    }    

    // Close it all down once ready
    for (i = 0; i < NUM_INSTANCES; i++)
    {
        pthread_join(audioPumpAllThreadHandles[i], NULL);
    }    

    for (i = 0; i < NUM_INSTANCES; i++)
    {
        aweOS_destroy(&g_AWEOSInstanceArray[i]);
    }

    return 0;
}

Filename: Multi-Instance.c


Updated on 2026-02-10 at 15:44:55 -0500