123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- // cmRtSys.h
- // Implements a real-time audio processing engine.
- //
- // The audio system is composed a collection of independent sub-systems.
- // Each sub-system maintains a thread which runs asynchrounsly
- // from the application, the MIDI devices, and the audio devices.
- // To faciliate communication between these components each sub-system maintains
- // two thread-safe data buffers one for control information and a second
- // for audio data.
- //
- // The audio devices are the primary driver for the system.
- // Callbacks from the audio devices (See #cmApCallbackPtr_t)
- // inserts incoming audio samples into the audio
- // record buffers and extracts samples from the playback buffer.
- // When sufficient incoming samples and outgoing empty buffer space exists
- // a sub-system thread is waken up by the callback. This triggers a DSP audio
- // processing cycle which empties/fills the audio buffers. During a DSP
- // processing cycle control messages from the application and MIDI are blocked and
- // buffered. Upon completetion of the DSP cycle a control message
- // transfer cycles occurs - buffered incoming messages are passed to
- // the DSP system and messages originating in the DSP system are
- // buffered by the audio system for later pickup by the application
- // or MIDI system.
- //
- // Note that control messages that arrive when the DSP cycle is not
- // occurring can pass directly through to the DSP system.
- //
- // The DSP system sends messages back to the host by calling
- // cmRtDspToHostFunc_t provided by cmRtSysCtx_t. These
- // calls are always made from within an audio system call to
- // audio or control update within cmRtCallback_t. cmRtDspToHostFunc_t
- // simply stores the message in a message buffer. The host picks
- // up the message at some later time when it notices that messages
- // are waiting via polling cmRtSysIsMsgWaiting().
- //
- // Implementation: \n
- // The audio sub-systems work by maintaining an internal thread
- // which blocks on a mutex condition variable.
- // While the thread is blocked the mutex is unlocked allowing messages
- // to pass directly through to the DSP procedure via cmRtCallback().
- //
- // Periodic calls from running audio devices update the audio buffer.
- // When the audio buffer has input samples waiting and output space
- // available the condition variable is signaled, the mutex is
- // then automatically locked by the system, and the DSP execution
- // procedure is called via cmRtCallback().
- //
- // Messages arriving while the mutex is locked are queued and
- // delivered to the DSP procedure at the end of the DSP execution
- // procedure.
- //
- // Usage example and testing code:
- // See cmRtSysTest().
- // \snippet cmRtSys.c cmRtSysTest
-
- #ifndef cmRtSys_h
- #define cmRtSys_h
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- // Audio system result codes
- enum
- {
- kOkRtRC = cmOkRC,
- kThreadErrRtRC,
- kMutexErrRtRC,
- kTsQueueErrRtRC,
- kMsgEnqueueFailRtRC,
- kAudioDevSetupErrRtRC,
- kAudioBufSetupErrRtRC,
- kAudioDevStartFailRtRC,
- kAudioDevStopFailRtRC,
- kBufTooSmallRtRC,
- kNoMsgWaitingRtRC,
- kMidiSysFailRtRC,
- kMsgSerializeFailRtRC,
- kStateBufFailRtRC,
- kInvalidArgRtRC,
- kNotInitRtRC,
- kTimeOutErrRtRC
- };
-
- enum
- {
- kAsDfltMsgQueueByteCnt = 0xffff,
- kAsDfltDevFramesPerCycle = 512,
- kAsDfltDspFramesPerCycle = 64,
- kAsDfltBufCnt = 3,
- kAsDfltSrate = 44100,
- kAsDfltSyncToInputFl = 1,
- kAsDfltMinMeterMs = 10,
- kAsDfltMeterMs = 50,
- kAsDfltMaxMeterMs = 1000
- };
-
- typedef cmHandle_t cmRtSysH_t; //< Audio system handle type
- typedef unsigned cmRtRC_t; //< Audio system result code
-
- struct cmRtSysCtx_str;
-
- //
- // DSP system callback function.
- //
- // This is the sole point of entry into the DSP system while the audio system is running.
- //
- // ctxPtr is pointer to a cmRtSysCtx_t record.
- //
- // This function is called under two circumstances:
- //
- // 1) To notify the DSP system that the audio input/output buffers need to be serviced.
- // This is a perioidic request which the DSP system uses as its execution trigger.
- // cmRtSysCtx_t.audioRateFl is set to true to indicate this type of callback.
- //
- // 2) To pass messages from the host application to the DSP system.
- // The DSP system is asyncronous with the host because it executes in the
- // audio system thread rather than the host thread. The cmRtSysDeliverMsg()
- // function synchronizes incoming messages with the internal audio system
- // thread to prevent thread collisions.
- //
- // Notes:
- // This callback is always made with the internal audio system mutex locked.
- //
- // The signal time covered by the callback is from
- // ctx->begSmpIdx to ctx->begSmpIdx+cfg->dspFramesPerCycle.
- //
- // The return value is currently not used.
- typedef cmRC_t (*cmRtCallback_t)(void* ctxPtr, unsigned msgByteCnt, const void* msgDataPtr );
-
-
- // Audio device sub-sytem configuration record
- typedef struct cmRtSysArgs_str
- {
- cmRpt_t* rpt; // system console object
- unsigned inDevIdx; // input audio device
- unsigned outDevIdx; // output audio device
- bool syncInputFl; // true/false sync the DSP update callbacks with audio input/output
- unsigned msgQueueByteCnt; // Size of the internal msg queue used to buffer msgs arriving via cmRtSysDeliverMsg().
- unsigned devFramesPerCycle; // (512) Audio device samples per channel per device update buffer.
- unsigned dspFramesPerCycle; // (64) Audio samples per channel per DSP cycle.
- unsigned audioBufCnt; // (3) Audio device buffers.
- double srate; // Audio sample rate.
- } cmRtSysArgs_t;
-
- // Audio sub-system configuration record.
- // This record is provided by the host to configure the audio system
- // via cmRtSystemAllocate() or cmRtSystemInitialize().
- typedef struct cmRtSysSubSys_str
- {
- cmRtSysArgs_t args; // Audio device configuration
- cmRtCallback_t cbFunc; // DSP system entry point function.
- void* cbDataPtr; // Host provided data for the DSP system callback.
- } cmRtSysSubSys_t;
-
-
- // Signature of a callback function provided by the audio system to receive messages
- // from the DSP system for later dispatch to the host application.
- // This declaration is used by the DSP system implementation and the audio system.
- // Note that this function is intended to convey one message broken into multiple parts.
- // See cmTsQueueEnqueueSegMsg() for the equivalent interface.
- typedef cmRtRC_t (*cmRtDspToHostFunc_t)(struct cmRtSysCtx_str* p, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt);
-
- // Record passed with each call to the DSP callback function cmRtCallback_t
- typedef struct cmRtSysCtx_str
- {
- void* reserved; // used internally by the audio system
-
- bool audioRateFl; // true if this is an audio update callback
-
- unsigned srcNetNodeId; // Source net node if this is a msg callback originating from a remote network node.
- unsigned rtSubIdx; // index of the sub-system this DSP process is serving
-
- cmRtSysSubSys_t* ss; // ptr to a copy of the cfg recd used to initialize the audio system
- unsigned begSmpIdx; // gives signal time as a sample count
-
- cmRtDspToHostFunc_t dspToHostFunc; // Callback used by the DSP process to send messages to the host
- // via the audio system. Returns a cmRtRC_t result code.
-
- // output (playback) buffers
- cmSample_t** oChArray; // each ele is a ptr to buffer with cfg.dspFramesPerCycle samples
- unsigned oChCnt; // count of output channels (ele's in oChArray[])
-
- // input (recording) buffers
- cmSample_t** iChArray; // each ele is a ptr to buffer with cfg.dspFramesPerCycle samples
- unsigned iChCnt; // count of input channels (ele's in iChArray[])
-
- } cmRtSysCtx_t;
-
-
- // Audio system configuration record used by cmRtSysAllocate().
- typedef struct cmRtSysCfg_str
- {
- cmRtSysSubSys_t* ssArray; // sub-system cfg record array
- unsigned ssCnt; // count of sub-systems
- unsigned meterMs; // Meter sample period in milliseconds
- void* clientCbData; // User arg. for clientCbFunc().
- cmTsQueueCb_t clientCbFunc; // Called by cmRtSysReceiveMsg() to deliver internally generated msg's to the host.
- // Set to NULL if msg's will be directly returned by buffers passed to cmRtSysReceiveMsg().
- cmUdpH_t udpH;
- } cmRtSysCfg_t;
-
- extern cmRtSysH_t cmRtSysNullHandle;
-
- // Allocate and initialize an audio system as a collection of 'cfgCnt' sub-systems.
- // Prior to call this function the audio audio ports system must be initalized
- // (via cmApInitialize()) and the MIDI port system must be initialized
- // (via cmMpInitialize()). Note also that cmApFinalize() and cmMpFinalize()
- // cannot be called prior to cmRtSysFree().
- // See cmRtSystemTest() for a complete example.
- cmRtRC_t cmRtSysAllocate( cmRtSysH_t* hp, cmRpt_t* rpt, const cmRtSysCfg_t* cfg );
-
- // Finalize and release any resources held by the audio system.
- cmRtRC_t cmRtSysFree( cmRtSysH_t* hp );
-
- // Returns true if 'h' is a handle which was successfully allocated by
- // cmRtSysAllocate().
- bool cmRtSysHandleIsValid( cmRtSysH_t h );
-
- // Reinitialize a previously allocated audio system. This function
- // begins with a call to cmRtSysFinalize().
- // Use cmRtSysEnable(h,true) to begin processing audio following this call.
- cmRtRC_t cmRtSysInitialize( cmRtSysH_t h, const cmRtSysCfg_t* cfg );
-
- // Complements cmRtSysInitialize(). In general there is no need to call this function
- // since calls to cmRtSysInitialize() and cmRtSysFree() automaticatically call it.
- cmRtRC_t cmRtSysFinalize( cmRtSysH_t h );
-
- // Returns true if the audio system has been successfully initialized.
- bool cmRtSysIsInitialized( cmRtSysH_t );
-
- // Returns true if the audio system is enabled.
- bool cmRtSysIsEnabled( cmRtSysH_t h );
-
- // Enable/disable the audio system. Enabling the starts audio stream
- // in/out of the system.
- cmRtRC_t cmRtSysEnable( cmRtSysH_t h, bool enableFl );
-
- //
- // DSP to Host delivery function
- //
-
- // This function is used to pass messages from a DSP process to the HOST it
- // is always called from within the real-time thread.
- cmRtRC_t cmRtSysDspToHostSegMsg( cmRtSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt);
- cmRtRC_t cmRtSysDspToHost( cmRtSysH_t h, const void* msgDataPtr, unsigned msgByteCnt);
-
-
- //
- // Host to DSP delivery functions
- //
-
- // Deliver a message from the host application to the DSP process. (host -> DSP);
- // The message is formed as a concatenation of the bytes in each of the segments
- // pointed to by 'msgDataPtrArrary[segCnt][msgByteCntArray[segCnt]'.
- // This is the canonical msg delivery function in so far as the other host->DSP
- // msg delivery function are written in terms of this function.
- // The first 4 bytes in the first segment must contain the index of the audio sub-system
- // which is to receive the message.
- cmRtRC_t cmRtSysDeliverSegMsg( cmRtSysH_t h, const void* msgDataPtrArray[], unsigned msgByteCntArray[], unsigned msgSegCnt, unsigned srcNetNodeId );
-
- // Deliver a single message from the host to the DSP system.
- cmRtRC_t cmRtSysDeliverMsg( cmRtSysH_t h, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId );
-
- // Deliver a single message from the host to the DSP system.
- // Prior to delivery the 'id' is prepended to the message.
- cmRtRC_t cmRtSysDeliverIdMsg( cmRtSysH_t h, unsigned rtSubIdx, unsigned id, const void* msgPtr, unsigned msgByteCnt, unsigned srcNetNodeId );
-
-
- //
- // DSP to Host message functions
- //
-
- // Is a msg from the DSP waiting to be picked up by the host? (host <- DSP)
- // 0 = no msgs are waiting or the msg queue is locked by the DSP process.
- // >0 = the size of the buffer required to hold the next msg returned via
- // cmRtSysReceiveMsg().
- unsigned cmRtSysIsMsgWaiting( cmRtSysH_t h );
-
- // Copy the next available msg sent from the DSP process to the host into the host supplied msg buffer
- // pointed to by 'msgBufPtr'. Set 'msgDataPtr' to NULL to receive msg by callback from cmRtSysCfg_t.clientCbFunc.
- // Returns kBufTooSmallRtRC if msgDataPtr[msgByteCnt] is too small to hold the msg.
- // Returns kNoMsgWaitingRtRC if no messages are waiting for delivery or the msg queue is locked by the DSP process.
- // Returns kOkRtRC if a msg was delivered.
- // Call cmRtSysIsMsgWaiting() prior to calling this function to get
- // the size of the data buffer required to hold the next message.
- cmRtRC_t cmRtSysReceiveMsg( cmRtSysH_t h, void* msgDataPtr, unsigned msgByteCnt );
-
-
- // Fill an audio system status record.
- void cmRtSysStatus( cmRtSysH_t h, unsigned rtSubIdx, cmRtSysStatus_t* statusPtr );
-
- // Enable cmRtSysStatus_t notifications to be sent periodically to the host.
- // Set rtSubIdx to cmInvalidIdx to enable/disable all sub-systems.
- // The notifications occur approximately every cmRtSysCfg_t.meterMs milliseconds.
- void cmRtSysStatusNotifyEnable( cmRtSysH_t, unsigned rtSubIdx, bool enableFl );
-
- // Return a pointer the context record associated with a sub-system
- cmRtSysCtx_t* cmRtSysContext( cmRtSysH_t h, unsigned rtSubIdx );
-
- // Return the count of audio sub-systems.
- // This is the same as the count of cfg recds passed to cmRtSystemInitialize().
- unsigned cmRtSysSubSystemCount( cmRtSysH_t h );
-
- // Audio system test and example function.
- void cmRtSysTest( cmRpt_t* rpt, int argc, const char* argv[] );
-
-
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif
|