//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org> 
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
//( {file_desc: "Thread safe audio buffer class." kw:[rt audio]}
//
// 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 audio::device::test()
// \snippet cwAudioBuf.c cwAudioBufExample
//
// Notes on channel flags:
// Disabled channels:  kChFl is cleared
//   get()     
//      in  - return NULL buffer pointers  
//      out - return NULL buffer points
//
//   update()
//      in  - incoming samples are set to 0. 
//      out - outgoing samples are set to 0.
//
// Muted channels: kMuteFl is set 
//   update()
//      in  - incoming samples are set to 0. 
//      out - outgoing samples are set to 0.
//
// Tone channels: kToneFl is set 
//   update()
//      in  - incoming samples are filled with a 1k sine tone
//      out - outgoing samples are filled with a 1k sine tone
//)

#ifndef cwAudioBuf_H
#define cwAudioBuf_H

#include "cwAudioBufDecls.h"

namespace cw
{
  namespace audio
  {
    namespace buf
    {
  
      //(

      typedef device::sample_t            sample_t;
      typedef handle<struct audioBuf_str> handle_t;

      // Channel flags
      enum
      {
       kInFl     = 0x01,  //< Identify an input channel
       kOutFl    = 0x02,  //< Identify an output channel
       kEnableFl = 0x04,  //< Set to enable a channel, Clear to disable. 

       kChFl     = 0x08,  //< Used to enable/disable a channel
       kMuteFl   = 0x10,  //< Mute this channel
       kToneFl   = 0x20,  //< Generate a tone on this channel
       kMeterFl  = 0x40,  //< Turn meter's on/off
       kPassFl   = 0x80   //< Pass input channels throught to the output. Must use getIO() to implement this functionality.
  
      };

      // 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)
      rc_t create( handle_t& hRef, unsigned devCnt, unsigned meterMs, float meterDampCoeff=0.4 );

      // Deallocate and release any resource held by an audio buffer allocated via initialize().
      rc_t destroy( handle_t& hRef );

      // Configure a buffer for a given device.  
      rc_t setup(
        handle_t h,
        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 get() 
        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
                             );

      // Prime the buffer with 'audioCycleCnt' * outFramesPerCycle samples ready to be played
      rc_t primeOutput( handle_t h, unsigned devIdx, unsigned audioCycleCnt );

      // Notify the audio buffer that a device is being enabled or disabled.
      void onPortEnable( handle_t h, 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 auido::device::cbFunc_t).
      // This function is thread-safe under the condition that 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.
      rc_t update(
        handle_t               h, 
        device::audioPacket_t* inPktArray,  //< full audio packets from incoming audio (from ADC)
        unsigned               inPktCnt,    //< count of incoming audio packets
        device::audioPacket_t* outPktArray, //< empty audio packet for outgoing audio (to DAC)  
        unsigned               outPktCnt);  //< count of outgoing audio packets
                             
      // Return the meter window period as set by initialize()
      unsigned meterMs(handle_t h);
  
      // Set the meter update period. This function limits the value to between 10 and 1000.
      void     setMeterMs( handle_t h, unsigned meterMs );

      // Returns the channel count set via setup().
      unsigned channelCount( handle_t h, unsigned devIdx, unsigned flags );

      // Set chIdx to -1 to enable all channels on this device.
      // Set flags to kInFl | kOutFl | {kChFl | kToneFl | kMeterFl} | { kEnableFl=on | 0=off }  
      void setFlag( handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );
  
      // Return true if the the flags is set.
      bool isFlag( handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );

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

      // Returns true if an input/output channel is enabled on the specified device.
      bool  isChannelEnabled(handle_t h, 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 {kInFl | kOutFl} | { kEnableFl=on | 0=off }
      void  enableTone(   handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );

      // Returns true if an input/output tone is enabled on the specified device.
      bool  isToneEnabled(handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );
      void  toneFlags(    handle_t h, unsigned devIdx, unsigned flags, bool* enableFlA, unsigned enableFlN );

      // Mute a specified channel.
      // Set chIdx to -1 to apply the change to all channels on this device.
      // Set flags to {kInFl | kOutFl} | { kEnableFl=on | 0=off }
      void  enableMute(   handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );

      // Returns true if an input/output channel is muted on the specified device.
      bool  isMuteEnabled(handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );      
      void  muteFlags(    handle_t h, unsigned devIdx, unsigned flags, bool* muteFlA, unsigned muteFlN );

      // Set the specified channel to pass through.
      // Set chIdx to -1 to apply the change to all channels on this device.
      // Set flags to {kInFl | kOutFl} | { kEnableFl=on | 0=off }
      void  enablePass(   handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );

      // Returns true if pass through is enabled on the specified channel.
      bool  isPassEnabled(handle_t h, 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 {kInFl | kOutFl} | { kEnableFl=on | 0=off }
      void  enableMeter(   handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );

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

      // Return the meter value for the requested channel.
      // Set flags to kInFl | kOutFl.
      sample_t meter(handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags );
      void     meter(handle_t h, unsigned devIdx, unsigned flags, sample_t* meterA, unsigned meterN ); 

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

      // Return the current gain seting for the specified channel.
      double gain( handle_t h, unsigned devIdx, unsigned chIdx, unsigned flags ); 
      void   gain( handle_t h, unsigned devIdx, unsigned flags, double* gainA, unsigned gainN ); 

      
      // Get the meter and fault status of the channel input or output channel array of a device.
      // Set 'flags' to { kInFl | kOutFl }.
      // 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 getStatus( handle_t h, 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 kInFl and kOutFl
      bool  isDeviceReady( handle_t h, 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 kInFl or kOutFl but not both.
      // The buffers pointed to by bufArray[] each contain 'dspFrameCnt' samples. Where 
      // 'dspFrameCnt' was set in the earlier call to setup() for this device.
      // (see initialize()).
      // Note that this function just returns audio information it does not
      // change any internal states.
      void get( handle_t h, unsigned devIdx, unsigned flags, sample_t* bufArray[], unsigned bufChCnt );

      // This function replaces calls to get() and implements pass-through and output 
      // buffer zeroing: 
      // 
      // 1) get(in);
      // 2) get(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 internal states.
      // 4) The timestamp pointers are optional.
      void getIO(   handle_t h,
        unsigned iDevIdx, sample_t* iBufArray[], unsigned iBufChCnt, time::spec_t* iTimeStampPtr, 
        unsigned oDevIdx, sample_t* oBufArray[], unsigned oBufChCnt, time::spec_t* oTimeStampPtr );


      // The application calls this function each time it completes processing of a bufArray[]
      // returned from get(). 'flags' can be set to either or both kInFl and kOutFl.
      // This function should only be called from the client thread.
      void advance( handle_t h, 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 get()
      // and advance().
      void inputToOutput( handle_t h, unsigned inDevIdx, unsigned outDevIdx );

      // Print the current buffer state.
      void report( handle_t h );

      // Run a buffer usage simulation to test the class. cmAudioPortTest.c calls this function.
      rc_t test(const test::test_args_t& args);

      //)

    }
  }
}

#endif
