Copyright (C) Kevin Larke 2009-2020

This file is part of libcm.

libcm is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

libcm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

See the GNU General Public License distributed with the libcm package or look here: .


cmApBuf : Thread safe audio buffer class.

This file defines an audio buffer class which handles buffering incoming (recording) and outgoing (playback) samples in a thread-safe manner.

Usage example and testing code: See cmApBufTest() and cmAudioSysTest(). cmApBuf.c cmApBufExample

Notes on channel flags: Disabled channels: kChFl is cleared cmApBufGet()
in - return NULL buffer pointers
out - return NULL buffer points

cmApBufUpdate() in - incoming samples are set to 0. out - outgoing samples are set to 0.

Muted channels: kMuteFl is set cmApBufUpdate() in - incoming samples are set to 0. out - outgoing samples are set to 0.

Tone channels: kToneFl is set cmApBufUpdate() in - incoming samples are filled with a 1k sine tone out - outgoing samples are filled with a 1k sine tone


typedef cmRC_t cmAbRC_t;   // Result code type

enum
{
  kOkAbRC = 0
};

// Allocate and initialize an audio buffer.
// devCnt - count of devices this buffer will handle.
// meterMs - length of the meter buffers in milliseconds (automatically limit to the range:10 to 1000)
cmAbRC_t cmApBufInitialize( unsigned devCnt, unsigned meterMs );

// Deallocate and release any resource held by an audio buffer allocated via cmApBufInitialize().
cmAbRC_t cmApBufFinalize();

// Configure a buffer for a given device.  
cmAbRC_t cmApBufSetup( 
unsigned devIdx,               // device to setup
double   srate,                // device sample rate (only required for synthesizing the correct test-tone frequency)
unsigned dspFrameCnt,          // dspFrameCnt - count of samples in channel buffers returned via cmApBufGet() 
unsigned cycleCnt,             // number of audio port cycles to store 
unsigned inChCnt,              // input channel count on this device
unsigned inFramesPerCycle,     // maximum number of incoming sample frames on an audio port cycle
unsigned outChCnt,             // output channel count on this device
unsigned outFramesPerCycle,    // maximum number of outgoing sample frames in an audio port cycle
int      srateMult );          // sample rate cvt (positive for upsample, negative for downsample)

// Prime the buffer with 'audioCycleCnt' * outFramesPerCycle samples ready to be played
cmAbRC_t cmApBufPrimeOutput( unsigned devIdx, unsigned audioCycleCnt );

// Notify the audio buffer that a device is being enabled or disabled.
void     cmApBufOnPortEnable( unsigned devIdx, bool enabelFl );

// This function is called asynchronously by the audio device driver to transfer incoming samples to the
// the buffer and to send outgoing samples to the DAC. This function is 
// intended to be called from the audio port callback function (\see cmApCallbackPtr_t).
// This function is thread-safe under the condition where the audio device uses
// different threads for input and output.
//
// Enable Flag: 
// Input: If an input channel is disabled then the incoming samples are replaced with zeros.
// Output: If an output channel is disabled then the packet samples are set to zeros.
//
// Tone Flag:
// Input: If the tone flag is set on an input channel then the incoming samples are set to a sine tone.
// Output: If the tone flag is set on an output channel then the packet samples are set to a sine tone.
//
// The enable flag has higher precedence than the tone flag therefore disabled channels
// will be set to zero even if the tone flag is set.
cmAbRC_t cmApBufUpdate(
cmApAudioPacket_t* inPktArray,   // full audio packets from incoming audio (from ADC)
unsigned           inPktCnt,     // count of incoming audio packets
cmApAudioPacket_t* outPktArray,  // empty audio packet for outgoing audio (to DAC) 
unsigned           outPktCnt     // count of outgoing audio packets
);
// Channel flags
enum
{
  kInApFl     = 0x01,   // Identify an input channel
  kOutApFl    = 0x02,   // Identify an output channel
  kEnableApFl = 0x04,   // Set to enable a channel, Clear to disable. 
  
  kChApFl     = 0x08,   // Used to enable/disable a channel
  kMuteApFl   = 0x10,   // Mute this channel
  kToneApFl   = 0x20,   // Generate a tone on this channel
  kMeterApFl  = 0x40,   // Turn meter's on/off
  kPassApFl   = 0x80    // Pass input channels throught to the output. Must use cmApBufGetIO() to implement this functionality.
  
};

// Return the meter window period as set by cmApBufInitialize()
unsigned cmApBufMeterMs();

// Set the meter update period. THis function limits the value to between 10 and 1000.
void     cmApBufSetMeterMs( unsigned meterMs );

// Returns the channel count set via cmApBufSetup().
unsigned cmApBufChannelCount( unsigned devIdx, unsigned flags );

// Set chIdx to -1 to enable all channels on this device.
// Set flags to {kInApFl | kOutApFl} | {kChApFl | kToneApFl | kMeterFl} | { kEnableApFl=on | 0=off }  
void cmApBufSetFlag( unsigned devIdx, unsigned chIdx, unsigned flags );

// Return true if the the flags is set.
bool cmApBufIsFlag( unsigned devIdx, unsigned chIdx, unsigned flags );

// Set chIdx to -1 to enable all channels on this device.
void  cmApBufEnableChannel(   unsigned devIdx, unsigned chIdx, unsigned flags );

// Returns true if an input/output channel is enabled on the specified device.
bool  cmApBufIsChannelEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );

// Set the state of the tone generator on the specified channel.
// Set chIdx to -1 to apply the change to all channels on this device.
// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
void  cmApBufEnableTone(   unsigned devIdx, unsigned chIdx, unsigned flags );

// Returns true if an input/output tone is enabled on the specified device.
bool  cmApBufIsToneEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );

// Mute a specified channel.
// Set chIdx to -1 to apply the change to all channels on this device.
// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
void  cmApBufEnableMute(   unsigned devIdx, unsigned chIdx, unsigned flags );

// Returns true if an input/output channel is muted on the specified device.
bool  cmApBufIsMuteEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );

// Set the specified channel to pass through.
// Set chIdx to -1 to apply the change to all channels on this device.
// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
void  cmApBufEnablePass(   unsigned devIdx, unsigned chIdx, unsigned flags );

// Returns true if pass through is enabled on the specified channel.
bool  cmApBufIsPassEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );

// Turn meter data collection on and off.
// Set chIdx to -1 to apply the change to all channels on this device.
// Set flags to {kInApFl | kOutApFl} | { kEnableApFl=on | 0=off }
void  cmApBufEnableMeter(   unsigned devIdx, unsigned chIdx, unsigned flags );

// Returns true if an input/output tone is enabled on the specified device.
bool  cmApBufIsMeterEnabled(unsigned devIdx, unsigned chIdx, unsigned flags );

// Return the meter value for the requested channel.
// Set flags to kInApFl | kOutApFl.
cmApSample_t cmApBufMeter(unsigned devIdx, unsigned chIdx, unsigned flags );

// Set chIdx to -1 to apply the gain to all channels on the specified device.
void cmApBufSetGain( unsigned devIdx, unsigned chIdx, unsigned flags, double gain );

// Return the current gain seting for the specified channel.
double cmApBufGain( unsigned devIdx, unsigned chIdx, unsigned flags ); 

// Get the meter and fault status of the channel input or output channel array of a device.
// Set 'flags' to { kInApFl | kOutApFl }.
// The returns value is the count of channels actually written to meterArray.
// If 'faultCntPtr' is non-NULL then it is set to the faultCnt of the associated devices input or output buffer.
unsigned cmApBufGetStatus( unsigned devIdx, unsigned flags, double* meterArray, unsigned meterCnt, unsigned* faultCntPtr );

// Do all enabled input/output channels on this device have samples available?
// 'flags' can be set to either or both kInApFl and kOutApFl
bool  cmApBufIsDeviceReady( unsigned devIdx, unsigned flags ); 

// This function is called by the application to get full incoming sample buffers and
// to fill empty outgoing sample buffers.
// Upon return each element in bufArray[bufChCnt] holds a pointer to a buffer assoicated 
// with an audio channel or to NULL if the channel is disabled.
// 'flags' can be set to kInApFl or kOutApFl but not both.
// The buffers pointed to by bufArray[] each contain 'dspFrameCnt' samples. Where 
// 'dspFrameCnt' was set in the earlier call to cmApBufSetup() for this device.
// (see cmApBufInitialize()).
// Note that this function just returns audio information it does not
// change any cmApBuf() internal states.
void cmApBufGet(     unsigned devIdx, unsigned flags, cmApSample_t* bufArray[], unsigned bufChCnt );

// This function replaces calls to cmApBufGet() and implements pass-through and output 
// buffer zeroing: 
// 
// 1) cmApBufGet(in);
// 2) cmApBufGet(out);
// 3) Copy through channels marked for 'pass' and set the associated oBufArray[i] channel to NULL.
// 4) Zero all other enabled output channels.
//
// Notes:
// 1) The oBufArray[] channels that are disabled or marked for pass-through will 
// be set to NULL.
// 2) The client is required to use this function to implement pass-through internally.
// 3) This function just returns audio information it does not
// change any cmApBuf() internal states.
// 4) The timestamp pointers are optional.
void cmApBufGetIO(   unsigned iDevIdx, cmApSample_t* iBufArray[], unsigned iBufChCnt, cmTimeSpec_t* iTimeStampPtr, 
unsigned oDevIdx, cmApSample_t* oBufArray[], unsigned oBufChCnt, cmTimeSpec_t* oTimeStampPtr );


// The application calls this function each time it completes processing of a bufArray[]
// returned from cmApBufGet(). 'flags' can be set to either or both kInApFl and kOutApFl.
// This function should only be called from the client thread.
void cmApBufAdvance( unsigned devIdx, unsigned flags );

// Copy all available samples incoming samples from an input device to an output device.
// The source code for this example is a good example of how an application should use cmApBufGet()
// and cmApBufAdvance().
void cmApBufInputToOutput( unsigned inDevIdx, unsigned outDevIdx );

// Print the current buffer state.
void cmApBufReport( cmRpt_t* rpt );

// Run a buffer usage simulation to test the class. cmAudioPortTest.c calls this function.
void cmApBufTest( cmRpt_t* rpt );