AWE Core OS 8.B.16 Documentation
RTAudio-alsa.c

Examples.

Examples

/****************************************************************************
*
* RTAudio-alsa
* --------------
*
****************************************************************************
*
* Description: Pump realtime audio through a layout using the linux alsa API
*
* Copyright: (c) 2022 DSP Concepts, Inc. All rights reserved.
* 3235 Kifer Road
* Santa Clara, CA 95054-1527
*
* This example is demonstrates integration of a realtime audio framework, specifically the alsa API.
* It brings in samples of audio from an alsa callback, and pumps them through a layout with the aweOS_audioPumpAll function.
* This example is meant to show the pumpAll function, and the surrounding import/exportSamples API's
*
* NOTE: AudioStream.c and all alsa related code written by DSPC Concepts is simply for reference, and is not considered robust or full featured...
* The alsa code is meant to be a bare minimum, and the focus of this example should be on this file, and the AWE Core OS API functions.
* See the alsa api doc for more details about realtime audio with alsa
*
* NOTE: This example must be run with superuser priorities, to ensure that we can we set scheduling parameters of the internal threads.
* Please give privileges or run with sudo. aweOS_audioPumpAll will return E_SCHEDULER_PERMISSION_DENIED as a warning.
*
* By default, this example opens the alsa input and output devices "hw:0,0", but any device id can be passed in with cmd line arguments. The device id string must be in ""
*
* NOTE: This device id string is an alsa specific parameter (https://www.alsa-project.org/wiki/Documentation), where "hw:" means the device type, 0 is Card #0, and 0 is Device #0.
* To find the device id string of an output device, enter the command "aplay -l" into the console. This will display alsa's list of all available output Cards/Devices
* Then, enter aplay -L to see the available types. A usb audio device may support both types "hw" and "plughw". For USB devices, use "plughw"
* For example, a USB device on Card 1, Device 0 would take the cmd line arg -outputdevice:"plughw:1,0"
* The process is the same for finding the input device id, but use the commands "arecord -l" and "arecord -L" instead of "aplay -l" and "aplay -L".
* For example, a USB device on Card 1, Device 0 would take the cmd line arg -inputdevice:"plughw:1,0"
*
* Block size, sample rate, and channel counts of the HW device can also be set with cmd line arguments.
* Audio logging can also be enabled with cmd line options, but is disabled by default.
*
* The example opens a tuning interface on port 15002, unless specified otherwise with the -portno: cmd line option.
* An awb can be loaded at run time with the -load: option. If no awb file path is given, then the app waits for one over the tuning interface.
*
***************************************************************************/
#define _GNU_SOURCE
#include <pthread.h>
#include <sys/prctl.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>
#include <sys/sysinfo.h>
#include <sys/syscall.h>
#ifndef SYS_gettid
#warning "SYS_gettid unavailable on this system"
#else
#define gettid() ((pid_t)syscall(SYS_gettid))
#endif
#include "AWECoreOS.h"
#include "AudioStream.h"
#include "ModuleList.h"
AudioStream *as;
AWEOSInstance *g_AWEOSInstance;
static AWEOSConfigParameters configParams;
UINT32 hwInputChannels = 1;
UINT32 hwOutputChannels = 2;
char* inputDeviceName;
char* outputDeviceName;
UINT32 portNo = 15002;
UINT32 fundamentalBlockSize = 32;
double sampleRate = 48000.0;
UINT32 enableAudioLogging = 0;
const char* awbPath;
UINT32 loadAWB = 0;
float coreSpeed = 1e9f;
float profileSpeed = 10e6f;
UINT32 setCPUs;
AWEOSThreadPIDs_t threadPIDs;
UINT32 firstCallback = 1;
void sig_handler(int signo)
{
printf("Exiting RTAudio-alsa\n");
exit(0);
}
static const void* moduleDescriptorTable[] =
{
LISTOFCLASSOBJECTS
};
UINT32 moduleDescriptorTableSize = sizeof(moduleDescriptorTable) / sizeof(moduleDescriptorTable[0]);
INT32 audioStartCallback(AWEOSInstance *pAWEOS)
{
INT32 i, availableProcessors, cpuToSet, ret;
cpu_set_t pumpthread_set;
printf("Audio Start Callback: Starting audio stream \n");
if (setCPUs)
{
// Get the internally running thread PIDs and print them
ret = aweOS_getThreadPIDs(g_AWEOSInstance, &threadPIDs);
if (ret != 0)
{
printf("aweOS_getThreadPIDs failed with error %d. Not setting CPU affinity... \n ", ret);
}
else
{
availableProcessors = get_nprocs();
for (i = 0; i < threadPIDs.numPumpThreads; i++)
{
// Audio streaming thread always set to first cpu
cpuToSet = (i + 1) % availableProcessors;
CPU_ZERO(&pumpthread_set);
CPU_SET(cpuToSet, &pumpthread_set);
ret = sched_setaffinity(threadPIDs.pumpThreadPIDs[i], sizeof(cpu_set_t), &pumpthread_set);
if (ret == 0)
{
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);
}
}
}
}
// Start audio stream
AudioStream_start(as);
return 0;
}
//the user defined callback for audio stop that will be passed into the param structure. Stop PA here
INT32 audioStopCallback(AWEOSInstance *pAWEOS)
{
printf("Audio Stop Callback: Stopping audio stream \n");
AudioStream_stop(as);
firstCallback = 1;
return 0;
}
void audioRecordNotifyCallback(AWEOSAudioRecordNotification_t * recordingNotification)
{
if (recordingNotification->notificationStatus == INPUT_OVERRUN)
{
printf("Got xrun on audio recording input stream: total xruns = %u, time of xrun = %f\n", recordingNotification->xruns, recordingNotification->xrunTime);
}
else if (recordingNotification->notificationStatus == OUTPUT_OVERRUN)
{
printf("Got xrun on audio recording output stream: total xruns = %u, time of xrun = %f\n", recordingNotification->xruns, recordingNotification->xrunTime);
}
else
{
printf("Got audio recording error notification: %d\n", recordingNotification->error);
}
}
void loadAWBFile(AWEOSInstance *pAWEOS, const char* filePath)
{
//Load the AWB from the file
UINT32 position = 0;
INT32 ret = aweOS_loadAWBFile(pAWEOS,filePath, &position);
if (0 == ret)
{
printf("The layout %s loaded succesfully\n", filePath);
}
else
{
printf("The layout %s downloaded unsuccessfully at position %u with error = %s \n", filePath, position, strerror(ret));
exit(1);
}
}
//AudioStream audio callback function. This is where the importing/exporting/pumping will happen
AudioStream_CallbackResult audio_callback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
AudioStream_CallbackFlag statusFlags,
void *userData)
{
/*suppress warnings for unused variables timeinfo, userData */
(void)userData;
UINT32 i, j;
INT32 pumpRet; //return for pump
if (statusFlags != 0)
{
// Indicate an xrun. These may not appear until after the audio stream is stopped. Enable logging for more details
printf("xrun ");
return AudioStream_CallbackResult_Continue; //Even though there was an xrun, tell the stream to continue. This will cause audio dropouts under high CPU load when xruns are occuring.
}
if (firstCallback && setCPUs)
{
#if defined(SYS_gettid)
// Set the audio callback to CPU 0 if requested. This handles case of low-latency mode
cpu_set_t pumpthread_set;
pid_t audioCallbackPID;
INT32 ret;
static const int cpuToSet = 0;
CPU_ZERO(&pumpthread_set);
CPU_SET(cpuToSet, &pumpthread_set);
audioCallbackPID = gettid();
ret = sched_setaffinity(audioCallbackPID, sizeof(cpu_set_t), &pumpthread_set);
if (ret == 0)
{
printf("Set audio callback thread (PID = %d) to CPU %d \n", audioCallbackPID, cpuToSet);
}
else
{
printf("Could not set CPU affinity of audo callback thread (PID = %d) to CPU %d , errno: %d \n", audioCallbackPID, cpuToSet, errno);
}
#endif
firstCallback = 0;
}
if (aweOS_layoutIsValid(g_AWEOSInstance))
{
if (aweOS_audioIsStarted(g_AWEOSInstance))
{
INT32 ret;
// import samples for each input device channel
for (i = 0; i < hwInputChannels; i++)
{
ret = aweOS_audioImportSamples(g_AWEOSInstance, ((UINT32*)inputBuffer) + i, hwInputChannels, i, Sample32bit);
if (ret != 0)
{
printf("error import samples: error: %d -- %s\n", ret, aweOS_errorToString(ret));
}
}
// pump the audio through the loaded layout
pumpRet = aweOS_audioPumpAll(g_AWEOSInstance);
if (pumpRet < 0)
{
// Ignore E_CALLBACK_NOT_REALTIME error
if (pumpRet != E_CALLBACK_NOT_REALTIME)
{
printf("pumpAll failing with error code: %d -- %s\n", pumpRet, aweOS_errorToString(pumpRet));
}
}
// export latest samples for each device output channel
for (j = 0; j < hwOutputChannels; j++)
{
ret = aweOS_audioExportSamples(g_AWEOSInstance, ((UINT32*)outputBuffer) + j, hwOutputChannels, j, Sample32bit);
if (ret != 0)
{
printf("Failed on export samples: error: %d -- %s\n", ret, aweOS_errorToString(ret));
}
}
}
}
return AudioStream_CallbackResult_Continue;
}
int initializeAudioStream()
{
as = AudioStream_create();
if (as == NULL)
{
printf("Error initializing AudioStream\n");
}
else
{
printf("AudioStream init success\n");
}
AudioStream_open(as, inputDeviceName, hwInputChannels,
outputDeviceName, hwOutputChannels,
AudioStream_SampleFormat_S32_LE,
sampleRate, fundamentalBlockSize,
audio_callback, NULL);
return 0;
}
static void usage(const char *program)
{
printf(
"Usage: %s [args]\n"
" -load:<file> AWB file to load. If no AWB is specified, then app waits for a layout from tuning interface\n"
" -inputdevice:<str> The alsa device id for the input device. Default hw:0,0 \n"
" -outputdevice:<str> The alsa device id for the output device. Default hw:0,0\n"
" -hwinchannels:<int> The number of hw input channels for the alsa audio device \n"
" -hwoutchannels:<int> The number of hw output channels for the alsa audio device\n"
" -blocksize:<int> The fundamental hw blocksize of the alsa device (DMA)\n"
" -samplerate:<int> The samplerate of the audio hw\n"
" -enabledAudioLog Enable audio logging. Logs will be saved to <appdirectory>/audio-logs/\n"
" -portno:<int> Port number to open the TCP tuning socket on. Default 15002\n"
" -coreSpeed:<float> Processor clock speed in Hz. Default 1 GHz\n"
" -profileSpeed:<float> Application profiling speed in Hz. Default 10 MHz \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 shows realtime audio integration with the alsa API.\n",
program);
exit(0);
}
int main(int argc, char **argv)
{
// Setup signal handler
if (signal(SIGINT, sig_handler) == SIG_ERR)
{
printf("Can't catch SIGINT (%d)\n", SIGINT);
}
inputDeviceName = "hw:0,0";
outputDeviceName = "hw:0,0";
setCPUs = 0;
UINT32 i;
if (1 == argc)
{
printf("No command line options specified. Using defaults\n");
}
else
{
for (i = 1; i < argc; i++)
{
const char *arg = argv[i];
if ((0 == strncmp(arg, "-inputdevice:", 13)))
{
inputDeviceName = (char*)(arg + 13);
printf(" cmd line arg set inputdevice to %s \n", inputDeviceName);
}
else if ((0 == strncmp(arg, "-outputdevice:", 14)))
{
outputDeviceName = (char*)(arg + 14);
printf(" cmd line arg set outputdevice to %s \n", outputDeviceName);
}
else if ((0 == strncmp(arg, "-hwinchannels:", 14)))
{
hwInputChannels = atoi(arg + 14);
printf(" hardware input channels set to %u \n", hwInputChannels);
}
else if ((0 == strncmp(arg, "-hwoutchannels:", 15)))
{
hwOutputChannels = atoi(arg + 15);
printf(" hardware output channels set to %u \n", hwOutputChannels);
}
else if ((0 == strncmp(arg, "-blocksize:", 11)))
{
fundamentalBlockSize = atoi(arg + 11);
printf(" fundamental block size set to %u \n", fundamentalBlockSize);
}
else if ((0 == strncmp(arg, "-samplerate:", 12)))
{
sampleRate = atof(arg + 12);
printf(" sample rate set to %f \n", sampleRate);
}
else if ((0 == strncmp(arg, "-portno:", 8)))
{
portNo = atoi(arg + 8);
printf(" portno for socket is %u \n", portNo);
}
else if (0 == strncmp(arg, "-load:", 6))
{
awbPath = arg + 6;
loadAWB = 1;
printf(" load: %s\n",awbPath);
}
else if ((0 == strncmp(arg, "-enableAudioLog", 15)))
{
enableAudioLogging = 1;
printf(" audio logging enabled \n");
}
else if ((0 == strncmp(arg, "-coreSpeed:", 11)))
{
coreSpeed = atof(arg + 11);
printf(" coreSpeed set to %f\n", coreSpeed);
}
else if ((0 == strncmp(arg, "-profileSpeed:", 14)))
{
profileSpeed = atof(arg + 14);
printf(" profileSpeed set to %f \n", profileSpeed);
}
else if ((0 == strncmp(arg, "-enableCPUAffinity", 13)))
{
setCPUs = 1;
printf(" enableCPUAffinity activated. query for PIDs in audiostart callback and attempt to set CPU affinity \n");
}
else if ((0 == strncmp(arg, "-help", 5)))
{
usage(argv[0]);
}
else
{
printf("main: unknown option '%s'\n", arg);
}
}
}
aweOS_getParamDefaults(&configParams);
configParams.inChannels = hwInputChannels;
configParams.outChannels = hwOutputChannels;
configParams.sampleRate = sampleRate;
configParams.fundamentalBlockSize = fundamentalBlockSize;
configParams.numThreads = 8;
configParams.cbAudioStart = audioStartCallback;
configParams.cbAudioStop = audioStopCallback;
configParams.instanceId = 0;
configParams.coreSpeed = coreSpeed;
configParams.profileSpeed = profileSpeed;
INT32 ret;
ret = aweOS_init(&g_AWEOSInstance, &configParams, moduleDescriptorTable, moduleDescriptorTableSize);
if (ret != 0) //if the initialization fails, don't do anything and exit the app
{
printf("FATAL: aweOS_init failed. exiting application \n");
exit(1);
}
else
{
initializeAudioStream();
if (enableAudioLogging)
{
ret = aweOS_audioRecordingEnable(g_AWEOSInstance, "audio-logs", "rtaudio-alsa", 100, Sample32bit);
if (ret != 0)
{
printf("failed to enable audio logging: ret = %d\n", ret);
}
ret = aweOS_audioRecordingRegisterNotificationCallback(g_AWEOSInstance, audioRecordNotifyCallback, AUDIO_RECORDING_NOTIFICATION_ALL);
if (ret != 0)
{
printf("Failed to register audio logging callback function: ret = %d\n", ret);
}
}
INT32 tuningRet = aweOS_tuningSocketOpen(&g_AWEOSInstance, portNo, 1);
if (tuningRet < 0)
{
printf("Failing opening tuning interface with error %s \n", aweOS_errorToString(tuningRet));
}
else
{
printf("Opened TCP tuning interface on port %u: Waiting for AWE Server Connection from PC... \n", portNo);
}
if (loadAWB)
{
loadAWBFile(g_AWEOSInstance, awbPath);
}
while (1)
{
usleep(100000);
}
}
}
The AWE Core OS API header file.
#define E_CALLBACK_NOT_REALTIME
Audio Callback is not at realtime priority.
Definition: Errors.h:339
@ Sample32bit
Data is 32 bit PCM .
Definition: StandardDefs.h:231
#define AUDIO_RECORDING_NOTIFICATION_ALL
Audio recording: Notify on errors and overruns.
Definition: AWECoreOS.h:77
void AWEOSInstance
The AWE Core OS Instance instance type.
Definition: AWECoreOS.h:103
INT32 aweOS_audioExportSamples(AWEOSInstance *pAWEOS, void *outSamples, INT32 outStride, INT32 channel, SampleType outType)
Export samples to a user buffer from a specific channel of the AWEOSInstance's output pin.
INT32 aweOS_audioPumpAll(AWEOSInstance *pAWEOS)
Pump one fundamental block size of audio through the loaded layout and all of its sublayouts.
INT32 aweOS_audioIsStarted(const AWEOSInstance *pAWEOS)
Check if this instance has received an Audio Start command.
INT32 aweOS_init(AWEOSInstance **pAWEOS, const AWEOSConfigParameters *aweParams, const void *pModuleDescriptorTable, UINT32 moduleDescriptorTableSize)
Initialize the AWEOSInstance with the specified configuration parameters.
INT32 aweOS_audioRecordingRegisterNotificationCallback(AWEOSInstance *pAWEOS, recordNotificationCallbackFunction recordNotificationCallback, UINT32 recordNotificationMask)
Register a callback function for audio recording event notificiations.
INT32 aweOS_layoutIsValid(const AWEOSInstance *pAWEOS)
Determines if a layout is loaded and valid.
INT32 aweOS_audioRecordingEnable(AWEOSInstance *pAWEOS, char *path, char *baseName, UINT32 bufferLength, SampleType sampleType)
Enables recording of all input and output audio of AWEOSInstance.
INT32 aweOS_tuningSocketOpen(AWEOSInstance **pAWEOS, INT32 portNo, UINT32 numInstances)
Initialize and open an integrated TCP/IP tuning interface socket.
UINT32 aweOS_getThreadPIDs(AWEOSInstance *pAWEOS, AWEOSThreadPIDs_t *threadPIDs)
Return the PIDs of all internally spawned AWE Core OS threads.
const char * aweOS_errorToString(INT32 errorCode)
Convert an error code (INT32) to its corresponding error string.
INT32 aweOS_audioImportSamples(AWEOSInstance *pAWEOS, void *inSamples, INT32 inStride, INT32 channel, SampleType inType)
Import samples from an audio buffer to a specific channel of the AWEOSInstance's input pin.
INT32 aweOS_getParamDefaults(AWEOSConfigParameters *aweParams)
Populates an AWEOSConfigParameters structure with defaults.
INT32 aweOS_loadAWBFile(AWEOSInstance *pAWEOS, const char *binaryFile, UINT32 *pErrorOffset)
Executes packet commands from an AWB file on the filesystem.
Audio recording notification callback argument structure.
Definition: AWECoreOS.h:62
STATUS notificationStatus
type of notification being reported
Definition: AWECoreOS.h:63
FLOAT32 xrunTime
time in seconds of xrun in recording - 0 if error
Definition: AWECoreOS.h:66
UINT32 xruns
total xruns on recording stream - 0 if error
Definition: AWECoreOS.h:65
INT32 error
error value - 0 if xrun
Definition: AWECoreOS.h:64
Internal threading PID structure, meant to be used with aweOS_getThreadPIDs.
Definition: AWECoreOS.h:81
UINT32 numPumpThreads
The number of running pump threads.
Definition: AWECoreOS.h:84
UINT32 * pumpThreadPIDs
Pointer to an array of the running pump thread PIDs of size pumpThreadPIDs[numPumpThreads].
Definition: AWECoreOS.h:85
AWEOSConfigParameters.
Definition: AWECoreOS.h:114
float profileSpeed
Application profiling speed in Hz.
Definition: AWECoreOS.h:129
INT32 instanceId
ID number of instance.
Definition: AWECoreOS.h:136
float coreSpeed
Processor clock speed in Hz.
Definition: AWECoreOS.h:128
UINT32 numThreads
Maximum number of supported sublayouts.
Definition: AWECoreOS.h:131
UINT32 fundamentalBlockSize
Fundamental block size of audio driver.
Definition: AWECoreOS.h:133
UINT32 outChannels
Number of output channels of audio device.
Definition: AWECoreOS.h:135
INT32(* cbAudioStop)(AWEOSInstance *pAWEOS)
Pointer to user created callback function for audio stop commands.
Definition: AWECoreOS.h:123
float sampleRate
Sample rate of audio device.
Definition: AWECoreOS.h:132
UINT32 inChannels
Number of input channels of audio device.
Definition: AWECoreOS.h:134
INT32(* cbAudioStart)(AWEOSInstance *pAWEOS)
Pointer to user created callback function for audio start commands.
Definition: AWECoreOS.h:122