AWECore 8.C.1 API Document
LinuxApp.c

Examples

/****************************************************************************
*
* Simple AWECore Linux Application
* ---------------------
*
****************************************************************************
*
* Description: Sample app to show basic usage of AWECore
* A single instance AWECore integration with simulated real-time audio on Linux.
* Includes an ethernet/TCPIP tuning interface to connect to AWE Server on port 15092
*
* This is a low latency enabled example, meaning that there can either be 0 or 1 blocks of latency based on certain conditions...
* -If the user loads a layout with a blocksize which does NOT equal the AWEInstance's fundamental BS, there will be 1 block of latency.
* -If the user's layout blocksize matches the AWEInstance's fundamental blocksize, then there will be 0 blocks of latency
*
* See the Latency section of the docs for more info.
*
* Copyright: (c) 2020 DSP Concepts, Inc. All rights reserved.
* 3235 Kifer Road
* Santa Clara, CA 95054-1527
*
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <pthread.h>
#include <math.h>
#include <time.h>
#include "AWECore.h"
#include "ProxyIDs.h"
#include "AWECoreUtils.h"
#include "TargetInfo.h"
// Global AWEInstance structure and pins
AWEInstance aweInstance;
static IOPinDescriptor aweInputPin;
static IOPinDescriptor aweOutputPin;
// LISTOFCLASSOBJECTS comes from ModuleList.h
const void* module_descriptor_table[] =
{
LISTOFCLASSOBJECTS
};
// Global packet buffers
UINT32 awePacketBuffer[MAX_COMMAND_BUFFER_LEN];
UINT32 awePacketBufferReply[MAX_COMMAND_BUFFER_LEN];
// Global heaps
UINT32 fastHeapA[FASTA_HEAP_SIZE];
UINT32 fastHeapB[FASTB_HEAP_SIZE];
UINT32 slowHeap[SLOW_HEAP_SIZE];
// Audio buffers
UINT32 audioInputBuffer[NUM_INPUT_CHANNELS * BASE_BLOCK_SIZE];
UINT32 audioOutputBuffer[NUM_OUTPUT_CHANNELS * BASE_BLOCK_SIZE];
// Global thread handles and mutexes
pthread_t tuningThreadHandle;
pthread_t processThreadHandle;
pthread_t audioCallbackThreadHandle;
pthread_t audioPumpAllThreadHandle;
pthread_mutex_t pumpAllMutex, packetMutex;
pthread_cond_t pumpAllCond;
INT32 pumpAll = 0;
INT32 audioStarted = 0;
INT32 exitAudioCallbackThread = 0;
// Tuning parameters
int sockfd, newsockfd;
int exitTuning = 0;
#define TUNING_PORT 15092
// increase priority levels for AWE threads
// Audio callback functions must be highest priority to avoid
// missing real time requirements. Pump is right after,
// followed by tuning interface where actions can be performed
// in the background.
#define AUDIO_CALLBACK_PRIO 9
#define AUDIO_PUMP_PRIO 8
#define TUNING_THREAD_PRIO 7
void error(const char *msg)
{
perror(msg);
exit(1);
}
void sig_handler(int signo)
{
if (signo == SIGINT)
{
// Cought user termination signal
exitTuning = 1;
shutdown(newsockfd, SHUT_RDWR);
close(newsockfd);
shutdown(sockfd, SHUT_RDWR);
close(sockfd);
// Wait until thread ends
pthread_join(tuningThreadHandle, NULL);
printf("Exiting LinuxApp\n");
exit(0);
}
}
void* tuningPacketThread(void* arg)
{
/*
Tuning packets can be recieved and processed here. Another implementation
may choose to process the packet in another thread.
*/
(void) arg;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
INT32 schedPolicy, ret;
struct sched_param schedParam;
int socketOption;
// Set this thread to run at real time priority
pthread_t currentHandle = pthread_self();
schedParam.sched_priority = TUNING_THREAD_PRIO;
schedPolicy = SCHED_FIFO;
ret = pthread_setschedparam(currentHandle, schedPolicy, &schedParam);
if (ret != 0)
{
printf("Failed to increase priority of tuning thread with error: %s \nTry running with sudo\n", strerror(ret));
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
error("ERROR opening socket");
}
// Set the socket connection option to immediately allow reuse of port upon losing connection
socketOption = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &socketOption, sizeof(socketOption));
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(TUNING_PORT);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
{
error("ERROR on binding");
}
while (!exitTuning)
{
listen(sockfd,5);
printf("Listening for connection on port %d\n", TUNING_PORT);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
{
error("ERROR on accept");
}
/* Enter a tuning loop. This will be in a separate thread from audio processing */
printf( "Found connection!\n");
while(1)
{
unsigned int plen;
ssize_t readBytes, n;
readBytes = read(newsockfd, aweInstance.pPacketBuffer, MAX_COMMAND_BUFFER_LEN * sizeof(aweInstance.pPacketBuffer[0]));
if (readBytes == 0)
{
// No message to read
break;
}
else if (readBytes < 0)
{
error("ERROR reading from socket");
}
// May not have received the whole message. Read again if not
plen = PACKET_LENGTH_BYTES(aweInstance.pPacketBuffer);
while (readBytes < plen)
{
//printf("Didn't read the entire packet! readBytes = %d, plen = %d\nReading again\n", readBytes, plen);
n = read(newsockfd,&((char*)aweInstance.pPacketBuffer)[readBytes], MAX_COMMAND_BUFFER_LEN * sizeof(aweInstance.pPacketBuffer[0]));
if (n == 0)
{
break;
}
else if (n < 0)
{
error("ERROR reading from socket");
}
else
{
readBytes += n;
}
}
// Don't process the packet if audio is pumping
pthread_mutex_lock(&packetMutex);
awe_packetProcess(&aweInstance);
pthread_mutex_unlock(&packetMutex);
plen = PACKET_LENGTH_WORDS(aweInstance.pReplyBuffer);
n = write(newsockfd, aweInstance.pReplyBuffer, plen * sizeof(aweInstance.pReplyBuffer[0]));
if (n < plen)
{
error("ERROR writing to socket");
}
}
}
return NULL;
}
void* aweuser_pumpAllAudio(void * args)
{
// Represents the callback function from the audio framework
(void) args;
INT32 pumpMask;
INT32 ix;
INT32 schedPolicy, ret;
struct sched_param schedParam;
// Set this thread to run at real time priority
pthread_t currentHandle = pthread_self();
schedParam.sched_priority = AUDIO_PUMP_PRIO;
schedPolicy = SCHED_FIFO;
ret = pthread_setschedparam(currentHandle, schedPolicy, &schedParam);
if (ret != 0)
{
printf("Failed to increase priority of audio pump thread to real time with error: %s \nTry running with sudo\n", strerror(ret));
}
printf("Starting pump all thread\n");
while (1)
{
// Wait for signal to run thread
pthread_mutex_lock(&pumpAllMutex);
while (pumpAll != 1) {
pthread_cond_wait(&pumpAllCond, &pumpAllMutex);
}
pthread_mutex_unlock(&pumpAllMutex);
if (awe_layoutIsValid(&aweInstance) && awe_audioIsStarted(&aweInstance))
{
// Import new samples
awe_audioImportSamples(&aweInstance, audioInputBuffer, NUM_INPUT_CHANNELS, 0, Sample32bit);
awe_audioImportSamples(&aweInstance, &audioInputBuffer[1], NUM_INPUT_CHANNELS, 1, Sample32bit);
// Pump any required sublayouts
pumpMask = awe_audioGetPumpMask(&aweInstance);
// Lock the instance so packet processing doesn't occur simultaneously
pthread_mutex_lock(&packetMutex);
for (ix = 0; ix < NUM_THREADS; ix++)
{
if (pumpMask & (1U << ix))
{
// Pump the sublayout. Each pump may be in its own thread depending on the application
ret |= awe_audioPump(&aweInstance, ix);
}
}
// Do deferred processing if needed
if (ret > 0)
{
// This could be done in a loop until ret == 0, but doing one per fundamental period
// is more conservative at the cost of potentially taking longer to update a module parameter
ret = awe_deferredSetCall(&aweInstance);
}
// Export samples
awe_audioExportSamples(&aweInstance, audioOutputBuffer, NUM_OUTPUT_CHANNELS, 0, Sample32bit);
awe_audioExportSamples(&aweInstance, &audioOutputBuffer[1], NUM_OUTPUT_CHANNELS, 1, Sample32bit);
pthread_mutex_unlock(&packetMutex);
}
pthread_mutex_lock(&pumpAllMutex);
pumpAll = 0;
pthread_mutex_unlock(&pumpAllMutex);
}
return NULL;
}
void* audioCallbackSimulator(void * args)
{
// Simple real-time audio simulator. Would be ALSA, PortAudio, etc callback
// Will not achieve exact realtime interrupts
struct timespec ts;
(void) args;
INT32 schedPolicy, ret;
struct sched_param schedParam;
// Setup sleep time for fundamental blocksize
ts.tv_sec = 0;
ts.tv_nsec = (long) ((float)1000000000L * ((float)BASE_BLOCK_SIZE / SAMPLE_RATE));
// Set this thread to run at real time priority
pthread_t currentHandle = pthread_self();
schedParam.sched_priority = AUDIO_CALLBACK_PRIO;
schedPolicy = SCHED_FIFO;
ret = pthread_setschedparam(currentHandle, schedPolicy, &schedParam);
if (ret != 0)
{
printf("Failed to increase priority of audio callback thread with error: %s \nTry running with sudo\n", strerror(ret));
}
while (!exitAudioCallbackThread)
{
pthread_mutex_lock(&pumpAllMutex);
pumpAll = 1;
pthread_cond_signal(&pumpAllCond);
pthread_mutex_unlock(&pumpAllMutex);
nanosleep(&ts, &ts);
}
return NULL;
}
INT32 aweuser_audioStart(AWEInstance *pAWE)
{
(void) pAWE;
// Audio start callback. Can be used to start audio stream from audio framework.
// Could be ALSA, Pulse, Jack, Portaudio, etc.
pthread_mutex_lock(&pumpAllMutex);
pumpAll = 0;
pthread_mutex_unlock(&pumpAllMutex);
// Kick off real-time audio simulation thread
exitAudioCallbackThread = 0;
pthread_create(&audioCallbackThreadHandle, NULL, audioCallbackSimulator, NULL);
audioStarted = 1;
printf("Audio Started\n");
return 0;
}
INT32 aweuser_audioStop(AWEInstance *pAWE)
{
(void) pAWE;
if(audioCallbackThreadHandle)
{
// Audio stop callback. Can be used to clean up audio stream.
pthread_mutex_lock(&pumpAllMutex);
pumpAll = 0;
pthread_mutex_unlock(&pumpAllMutex);
exitAudioCallbackThread = 1;
pthread_join(audioCallbackThreadHandle, NULL);
if (audioStarted)
{
printf("Audio Stopped\n");
audioStarted = 0;
}
}
return 0;
}
void InitializeAWEInstance()
{
int ret = 0;
int i, j;
UINT32 module_descriptor_table_size;
//set memory for awe instance and initialize to 0's
memset(&aweInstance, 0, sizeof(AWEInstance));
aweInstance.pInputPin = &aweInputPin;
aweInstance.pOutputPin = &aweOutputPin;
// Set pointers to the separate packet buffers in this example
// Could set both pointer to same buffer if desired
aweInstance.pPacketBuffer = awePacketBuffer;
aweInstance.pReplyBuffer = awePacketBufferReply;
aweInstance.packetBufferSize = MAX_COMMAND_BUFFER_LEN;
aweInstance.pModuleDescriptorTable = module_descriptor_table;
module_descriptor_table_size = sizeof(module_descriptor_table) / sizeof(module_descriptor_table[0]);
aweInstance.numModules = module_descriptor_table_size;
aweInstance.numThreads = NUM_THREADS;
aweInstance.sampleRate = SAMPLE_RATE;
aweInstance.fundamentalBlockSize = BASE_BLOCK_SIZE;
aweInstance.pFlashFileSystem = HAS_FLASHFILESYSTEM;
aweInstance.fastHeapASize = FASTA_HEAP_SIZE;
aweInstance.fastHeapBSize = FASTB_HEAP_SIZE;
aweInstance.slowHeapSize = SLOW_HEAP_SIZE;
aweInstance.pFastHeapA = fastHeapA;
aweInstance.pFastHeapB = fastHeapB;
aweInstance.pSlowHeap = slowHeap;
aweInstance.cbAudioStart = aweuser_audioStart;
aweInstance.cbAudioStop = aweuser_audioStop;
aweInstance.coreSpeed = CORE_SPEED;
aweInstance.profileSpeed = PROFILE_SPEED;
aweInstance.pName = TARGET_NAME;
ret = awe_initPin(&aweInputPin, NUM_INPUT_CHANNELS, NULL);
if (ret != 0)
{
printf("awe_initPin inputPin failed\n");
}
ret = awe_initPin(&aweOutputPin, NUM_OUTPUT_CHANNELS, NULL);
if (ret != 0)
{
printf("awe_initPin outputPin failed\n");
}
ret = awe_init(&aweInstance);
if (ret != 0)
{
printf("awe_init instance 1 failed\n");
}
#ifndef PI
#define PI 3.141592653589793
#endif
// Fill audio input buffers with full scale sin waves
for (i = 0; i < BASE_BLOCK_SIZE; i++)
{
for (j = 0; j < NUM_INPUT_CHANNELS; j++)
{
audioInputBuffer[i * NUM_INPUT_CHANNELS + j] = float_to_fract32(sinf(2.f*PI*(j+1)*((float)SAMPLE_RATE/BASE_BLOCK_SIZE) * (i / (float)SAMPLE_RATE)));
}
}
}
int main( int argc, const char* argv[] )
{
(void) argc;
(void) argv;
// Setup signal handler to kill sockets
if (signal(SIGINT, sig_handler) == SIG_ERR)
{
printf("Can't catch SIGINT (%d)\n", SIGINT);
}
// Initialize the AWEInstance
InitializeAWEInstance();
// Initialize mutex and condition variable objects
pthread_mutex_init(&packetMutex, NULL);
pthread_mutex_init(&pumpAllMutex, NULL);
pthread_cond_init (&pumpAllCond, NULL);
// Start tuning thread
pthread_create(&tuningThreadHandle, NULL, tuningPacketThread, NULL);
// Start audio pump all thread
pthread_create(&audioPumpAllThreadHandle, NULL, aweuser_pumpAllAudio, NULL);
// Wait until all threads end
pthread_join(tuningThreadHandle, NULL);
pthread_join(audioPumpAllThreadHandle, NULL);
return 0;
}
AWEInstance::cbAudioStop
INT32(* cbAudioStop)(struct AWEInstance *pAWE)
OPTIONAL.
Definition: AWECore.h:141
AWEInstance::sampleRate
float sampleRate
Default sample rate of this instance.
Definition: AWECore.h:226
AWEInstance::numThreads
UINT32 numThreads
Number of threads supported for multithreaded systems(1-4).
Definition: AWECore.h:223
AWEInstance::fastHeapBSize
UINT32 fastHeapBSize
The fast heap B size.
Definition: AWECore.h:124
awe_initPin
INT32 awe_initPin(IOPinDescriptor *pPin, UINT32 channels, const char *name)
Initialize an input or output pin.
AWEInstance::coreSpeed
float coreSpeed
A BSP author will set this to the speed of the CPU they are integrating into.
Definition: AWECore.h:208
awe_layoutIsValid
INT32 awe_layoutIsValid(const AWEInstance *pAWE)
Determines if a layout is loaded and valid.
AWECoreUtils.h
The AWECore Helper Functions File.
awe_audioGetPumpMask
INT32 awe_audioGetPumpMask(const AWEInstance *pAWE)
Test if AWE is ready to run.
AWEInstance::numModules
UINT32 numModules
Number of modules in module table.
Definition: AWECore.h:166
PACKET_LENGTH_BYTES
#define PACKET_LENGTH_BYTES(x)
This will determine the length of a packet in bytes.
Definition: AWECoreUtils.h:40
float_to_fract32
INT32 float_to_fract32(FLOAT32 x)
Convert audio data from floating point to Fract32 sample by sample.
awe_audioExportSamples
INT32 awe_audioExportSamples(const AWEInstance *pAWE, void *outSamples, INT32 outStride, INT32 channel, SampleType outType)
Export samples to a user buffer from a channel.
AWEInstance::pReplyBuffer
UINT32 * pReplyBuffer
Reply buffer pointer.
Definition: AWECore.h:193
AWEInstance::pInputPin
IOPinDescriptor * pInputPin
A BSP author must define/allocate an input pin in their BSP and assign it to this member NOTE: AudioW...
Definition: AWECore.h:150
AWEInstance::pOutputPin
IOPinDescriptor * pOutputPin
A BSP author must define/allocate an output pin in their BSP and assign it to this member NOTE: Audio...
Definition: AWECore.h:156
AWEInstance::pFlashFileSystem
AWEFlashFSInstance * pFlashFileSystem
DSPC Flash file system instance.
Definition: AWECore.h:235
AWEInstance::pName
const char * pName
Name of this instance as a string
Definition: AWECore.h:219
awe_init
INT32 awe_init(AWEInstance *pAWE)
Initialize the instance.
AWEInstance::pPacketBuffer
UINT32 * pPacketBuffer
The Packet buffer pointer.
Definition: AWECore.h:184
AWEInstance::packetBufferSize
UINT32 packetBufferSize
Packet buffer size.
Definition: AWECore.h:200
AWEInstance
The AWE instance.
Definition: AWECore.h:101
awe_audioPump
INT32 awe_audioPump(AWEInstance *pAWE, UINT32 layoutIndex)
Audio pump function.
AWEInstance::pSlowHeap
UINT32 * pSlowHeap
The slow heap.
Definition: AWECore.h:116
AWECore.h
The AWECore API Header File.
PACKET_LENGTH_WORDS
#define PACKET_LENGTH_WORDS(x)
This will determine the length of a packet in words.
Definition: AWECoreUtils.h:37
AWEInstance::cbAudioStart
INT32(* cbAudioStart)(struct AWEInstance *PAWE)
OPTIONAL This callback is invoked when a layout is run or when a StartAudio command is sent.
Definition: AWECore.h:135
ProxyIDs.h
A list of all AWE Server Commands.
awe_audioIsStarted
INT32 awe_audioIsStarted(const AWEInstance *pAWE)
Check if this instance is running.
AWEInstance::profileSpeed
float profileSpeed
Profiling clock speed in Hz.
Definition: AWECore.h:211
AWEInstance::fastHeapASize
UINT32 fastHeapASize
The fast heap A size.
Definition: AWECore.h:121
awe_audioImportSamples
INT32 awe_audioImportSamples(const AWEInstance *pAWE, const void *inSamples, INT32 inStride, INT32 channel, SampleType inType)
Import samples from a user buffer to a channel.
awe_packetProcess
INT32 awe_packetProcess(AWEInstance *pAWE)
Process an AWEInstance's newly received tuning packet.
AWEInstance::slowHeapSize
UINT32 slowHeapSize
The slow heap size.
Definition: AWECore.h:127
AWEInstance::pFastHeapA
UINT32 * pFastHeapA
Fast heap A.
Definition: AWECore.h:110
AWEInstance::fundamentalBlockSize
UINT32 fundamentalBlockSize
Base frame size of this instance.
Definition: AWECore.h:230
awe_deferredSetCall
INT32 awe_deferredSetCall(AWEInstance *pAWE)
Perform deferred awe set on a module.
IOPinDescriptor
AWE IO Pin type.
Definition: AWECore.h:43
AWEInstance::pFastHeapB
UINT32 * pFastHeapB
The second fast heap, B .
Definition: AWECore.h:113
AWEInstance::pModuleDescriptorTable
const void * pModuleDescriptorTable
Pointer to module table.
Definition: AWECore.h:174