]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - processor-sdk/performance-audio-sr.git/blobdiff - pasdk/test_arm/framework/aspDecOpCircBuf_slave.c
PASDK-218:CB updates for THD 192 kHz
[processor-sdk/performance-audio-sr.git] / pasdk / test_arm / framework / aspDecOpCircBuf_slave.c
index e0d2e14247fc0e38d97f59f55b54e7673858044f..79c645bf5bf92f409e311f979c3ef236091ad078 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
-Copyright (c) 2016, Texas Instruments Incorporated - http://www.ti.com/
+Copyright (c) 2017, Texas Instruments Incorporated - http://www.ti.com/
 All rights reserved.
 
 * Redistribution and use in source and binary forms, with or without 
@@ -45,6 +45,16 @@ All rights reserved.
 //#include "pafsp.h"
 #include "aspDecOpCircBuf_slave.h"
 
+#include "evmc66x_gpio_dbg.h" // Debug
+
+// Init last audio frame configuration info 
+static Void cbInitLastAfInfo(
+    PAF_AST_DecOpCircBuf *pCb,  // decoder output circular buffer control
+    PAF_AudioFrame *pAfInit     // audio frame used for init
+);
+
+
+#if 0 // FL: moved to common
 // Initialize circular buffer control
 Int cbCtlInit(
     PAF_AST_DecOpCircBufCtl *pCbCtl,    // decoder output circular buffer control
@@ -63,16 +73,262 @@ Int cbCtlInit(
     }
     else
     {
-       pCbCtl->gateHandle = NULL;
+        pCbCtl->gateHandle = NULL;
         return ASP_DECOP_CB_CTL_INIT_INV_GATE;
     }
     
-    pCbCtl->pXDecOpCb = pXDecOpCb;
+    pCbCtl->numCb = numCb;          // init number of circular buffers
+    pCbCtl->pXDecOpCb = pXDecOpCb;  // init base address of circular buffers
     
     return ASP_DECOP_CB_SOK;
+}
+#endif
+
+// debug
+//Int8 gCbInitDecWriteCnt=0;
+//Int8 gCbInitDecWriteThdCnt=0;
+
+/// Initialize circular buffer for Decoder writes
+Int cbInitDecWrite(
+    PAF_AST_DecOpCircBufCtl *pCbCtl,    // decoder output circular buffer control
+    Int8 cbIdx,                         // decoder output circular buffer index
+    Int8 sourceSelect,                  // source select (PCM, DDP, etc.)
+    Int16 decOpFrameLen,                // decoder output frame length (PCM samples)
+    Int8 resetRwFlags,                  // whether to reset reader, writer, and drain flags
+    PAF_AudioFrame *pDecInitAf          // pointer to Dec output audio frame used for CB initialization
+)
+{
+    IArg key;
+    GateMP_Handle gateHandle;
+    PAF_AST_DecOpCircBuf *pCb;
+    PAF_AudioFrame *pAfCb;
+    PAF_AudioData *pPcmBuf;
+    UInt8 *pMetaBuf;
+    Int8 n;
+    Int8 i;
+
+    //gCbInitDecWriteCnt++; // debug
+    
+    // Get gate handle
+    gateHandle = pCbCtl->gateHandle;
+    // Enter gate
+    key = GateMP_enter(gateHandle);
+
+    // Get circular buffer base pointer
+    pCb = &((*pCbCtl->pXDecOpCb)[cbIdx]);
+    
+    // Invalidate circular buffer configuration
+    Cache_inv(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+    Cache_wait();
+
+    //Log_info1("cbInitDecWrite:afCb=0x%04x", (IArg)pCb->afCb); // debug
+
+    // Set source select
+    pCb->sourceSel = sourceSelect;
+
+    // Set input frame length
+    pCb->decOpFrameLen = decOpFrameLen;
+    
+    //pCb->afInitialLag = 0;  // default No lag
+    //pCb->afLagIdx = 0;
+    // Initialize CB primed flag
+    pCb->primedFlag = 0;
+    // Initialize delta samples
+    pCb->deltaSamps = 0;
+    
+    // Initialize circular buffer:
+    //  - maximum number of AFs
+    //  - target nominal delay
+    //  - AF write, read indices
+    //  - maximum AF channel and sample counts
+    //  - maximum number of PCM samples per channel
+    if (sourceSelect == PAF_SOURCE_PCM)
+    {
+        pCb->maxNumAfCb = ASP_DECOP_CB_MAX_NUM_AF_PCM;
+        
+        //pCb->afInitialLag = ASP_DECOP_CB_INIT_LAG_PCM;
+        pCb->targetNDSamps = ASP_DECOP_CB_TARGET_ND_SAMPS_48kPCM;
+        
+        pCb->afWrtIdx = ASP_DECOP_CB_INIT_WRTIDX_PCM;
+        pCb->afRdIdx = ASP_DECOP_CB_INIT_RDIDX_PCM;
+        
+        pCb->maxAFChanNum = ASP_DECOP_CB_MAX_NUM_PCM_CH;
+        pCb->maxAFSampCount = DEF_DEC_OP_FRAME_LEN; 
+
+        pCb->maxNumPcmSampsPerCh = (Int32)(pCb->pcmBufEnd - pCb->pcmBuf)/pCb->maxAFChanNum;
+    }
+    else if ((sourceSelect == PAF_SOURCE_DDP) || (sourceSelect == PAF_SOURCE_AC3))
+    {
+        pCb->maxNumAfCb = ASP_DECOP_CB_MAX_NUM_AF_DDP;
+        
+        //pCb->afInitialLag = ASP_DECOP_CB_INIT_LAG_DDP;
+        pCb->targetNDSamps = ASP_DECOP_CB_TARGET_ND_SAMPS_DDP;
+        
+        pCb->afWrtIdx = ASP_DECOP_CB_INIT_WRTIDX_DDP;
+        pCb->afRdIdx = ASP_DECOP_CB_INIT_RDIDX_DDP;
+        
+        pCb->maxAFChanNum = ASP_DECOP_CB_MAX_NUM_PCM_CH_DDP;
+        pCb->maxAFSampCount = ASP_DECOP_CB_MAX_PCM_FRAME_LEN_48kDDP;
+
+        pCb->maxNumPcmSampsPerCh = (Int32)(pCb->pcmBufEnd - pCb->pcmBuf)/pCb->maxAFChanNum;
+    }
+    else if (sourceSelect == PAF_SOURCE_THD)
+    {
+        //gCbInitSourceSelThdCnt++; //debug
+        
+        pCb->maxNumAfCb = ASP_DECOP_CB_MAX_NUM_AF_THD;
+        
+        //pCb->afInitialLag = ASP_DECOP_CB_INIT_LAG_THD;
+        // FL: (***) set nominal delay per sampling rate -- need to review these settings
+        switch (pDecInitAf->sampleRate)
+        {
+            case PAF_SAMPLERATE_44100HZ:
+            case PAF_SAMPLERATE_48000HZ:
+                pCb->targetNDSamps = ASP_DECOP_CB_TARGET_ND_SAMPS_48kTHD;
+                break;
+            case PAF_SAMPLERATE_88200HZ:
+            case PAF_SAMPLERATE_96000HZ:
+                pCb->targetNDSamps = ASP_DECOP_CB_TARGET_ND_SAMPS_96kTHD;
+                break;
+            case PAF_SAMPLERATE_176400HZ:
+            case PAF_SAMPLERATE_192000HZ:
+                pCb->targetNDSamps = ASP_DECOP_CB_TARGET_ND_SAMPS_192kTHD;
+                break;
+            default:
+                pCb->targetNDSamps = ASP_DECOP_CB_TARGET_ND_SAMPS_48kTHD;
+                break;
+        }
+        
+        pCb->afWrtIdx = ASP_DECOP_CB_INIT_WRTIDX_THD;
+        pCb->afRdIdx = ASP_DECOP_CB_INIT_RDIDX_THD;
+        
+        pCb->maxAFChanNum = ASP_DECOP_CB_MAX_NUM_PCM_CH_MAT;
+        pCb->maxAFSampCount = ASP_DECOP_CB_MAX_PCM_FRAME_LEN_48kMAT;        
+
+        pCb->maxNumPcmSampsPerCh = (Int32)(pCb->pcmBufEnd - pCb->pcmBuf)/pCb->maxAFChanNum;
+    }
+    else
+    {
+        //
+        // Currently unsupported source select
+        //
+        
+        SW_BREAKPOINT; // debug
+        
+        // Leave the gate
+        GateMP_leave(gateHandle, key);
+
+        return ASP_DECOP_CB_INIT_INV_SOURCE_SEL;
+    }
+
+    // Initialize circular buffer:
+    //  - PCM read index
+    //  - Private metadata read index
+    //  - number of PCM samples in CB
+    pCb->pcmRdIdx = 0;
+    pCb->prvMdRdIdx = 0;
+    pCb->numPcmSampsPerCh = 0;
+
+    // Initialize audio frames
+    for (n = 0; n < pCb->maxNumAfCb; n++)
+    {
+        pAfCb = &pCb->afCb[n]; // get pointer to CB AF
+        
+        // Dec init AF sample count not correct for CB AFs.
+        // Dec Op frame length is computed in framework based on selected source.
+        pAfCb->sampleCount = decOpFrameLen;
+
+        // initialize CB AF using Dec init AF
+        pAfCb->sampleDecode = pDecInitAf->sampleDecode;
+        PAF_PROCESS_COPY(pAfCb->sampleProcess, pDecInitAf->sampleProcess);
+        pAfCb->sampleRate = pDecInitAf->sampleRate;
+        pAfCb->channelConfigurationRequest.full = pDecInitAf->channelConfigurationRequest.full;
+        pAfCb->channelConfigurationStream.full = pDecInitAf->channelConfigurationStream.full;
+        
+        // initialize metadata information updated by decoder
+        pAfCb->bsMetadata_type     = PAF_bsMetadata_none;           /* non zero if metadata is attached. */
+        pAfCb->pafBsMetadataUpdate = 0;                             /* indicates whether bit-stream metadata update */
+        pAfCb->numPrivateMetadata  = 0;                             /* number of valid private metadata (0 or 1 if metadata filtering enabled) */
+        pAfCb->bsMetadata_offset   = 0;                             /* offset into audio frame for change in bsMetadata_type field */
+    }
+    
+    // Initialize circular buffer current number of frames
+    pCb->numAfCb = pCb->afWrtIdx - pCb->afRdIdx;
     
+    // Initialize audio frame PCM buffers
+    pPcmBuf = pCb->pcmBuf;
+    pMetaBuf = pCb->metaBuf;
+    for (n=0; n<pCb->maxNumAfCb; n++)
+    {
+        pAfCb = &pCb->afCb[n]; // get pointer to CB AF
+        
+        pAfCb->data.nChannels = pCb->maxAFChanNum;
+        pAfCb->data.nSamples = decOpFrameLen;
+        for (i=0; i<pCb->maxAFChanNum; i++)
+        {
+            pAfCb->data.sample[i] = pPcmBuf;
+            memset(pAfCb->data.sample[i], 0, pCb->maxAFSampCount);
+            pPcmBuf += pCb->maxAFSampCount;
+            
+            pAfCb->data.samsiz[i] = 0;
+        }
+        
+        // Initialize metadata buffers
+        for (i=0; i<PAF_MAX_NUM_PRIVATE_MD; i++)
+        {
+            pAfCb->pafPrivateMetadata[i].offset = 0; 
+            pAfCb->pafPrivateMetadata[i].size   = 0; 
+            pAfCb->pafPrivateMetadata[i].pMdBuf = pMetaBuf;
+            pMetaBuf += PAF_MAX_PRIVATE_MD_SZ;
+        }
+    }
+    
+    // Initialize last audio frame configuration info
+    cbInitLastAfInfo(pCb, pDecInitAf);
+    
+    // Reset read/write flags
+    if (resetRwFlags != 0)
+    {
+        pCb->writerActiveFlag = 0;
+        pCb->readerActiveFlag = 0;
+        pCb->drainFlag = 0;
+    }
+    
+    // Reset stats
+    pCb->readAfWriterInactiveCnt = 0;
+    pCb->readAfNdCnt = 0;
+    pCb->wrtAfReaderInactiveCnt = 0;
+    pCb->wrtAfZeroSampsCnt = 0;
+    pCb->errAfUndCnt = 0;
+    pCb->errAfOvrCnt = 0;
+    pCb->errPcmUndCnt = 0;
+    pCb->errPcmOvrCnt = 0;
+    
+    // Write back circular buffer configuration
+    Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+    // Write back AF circular buffer
+    Cache_wb(pCb->afCb, pCb->maxNumAfCb*sizeof(PAF_AudioFrame), Cache_Type_ALLD, 0);
+    // Write back PCM data
+    for (n=0; n<pCb->maxNumAfCb; n++)
+    {
+        pAfCb = &pCb->afCb[n];
+        Cache_wb(pAfCb->data.samsiz, pCb->maxAFChanNum*sizeof(PAF_AudioSize), Cache_Type_ALLD, 0);
+        Cache_wb(pAfCb->data.sample, pCb->maxAFChanNum*sizeof(PAF_AudioData *), Cache_Type_ALLD, 0);
+        for (i=0; i<pCb->maxAFChanNum; i++)
+        {
+            Cache_wb(pAfCb->data.sample[i], pCb->maxAFSampCount*sizeof(PAF_AudioData), Cache_Type_ALLD, 0);
+        }
+    }
+    Cache_wait();
+
+    // Leave the gate
+    GateMP_leave(gateHandle, key);
+    
+    return ASP_DECOP_CB_SOK;
 }
 
+//Int8 gCbWriteStartCnt=0; // debug
+
 // Start writes to circular buffer
 Int cbWriteStart(
     PAF_AST_DecOpCircBufCtl *pCbCtl,    // decoder output circular buffer control
@@ -86,6 +342,8 @@ Int cbWriteStart(
     Int8 n;
     //Int8 i;
 
+    //gCbWriteStartCnt++; // debug
+    
     // Get gate handle
     gateHandle = pCbCtl->gateHandle;
     // Enter gate
@@ -94,13 +352,12 @@ Int cbWriteStart(
     // Get circular buffer base pointer
     pCb = &((*pCbCtl->pXDecOpCb)[cbIdx]);
 
-    // (***) FL: revisit
     // Invalidate circular buffer configuration.
     // NOTE: Probably only a subset of this information needs to be updated.
     Cache_inv(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
     Cache_wait();
     
-    //Log_info1("cbWriteStart:afCb=0x%04x", (IArg)pCb->afCb); // FL: debug
+    //Log_info1("cbWriteStart:afCb=0x%04x", (IArg)pCb->afCb); // debug
     
     // Invalidate AF circular buffer
     Cache_inv(pCb->afCb, pCb->maxNumAfCb*sizeof(PAF_AudioFrame), Cache_Type_ALLD, 0);
@@ -113,9 +370,9 @@ Int cbWriteStart(
             
     // update flags
     pCb->writerActiveFlag = 1;
-    pCb->emptyFlag = 0;
+    pCb->drainFlag = 0;
+    //pCb->afLagIdx = 0;
     
-    // (***) FL: revisit
     // Write back circular buffer configuration
     Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
     Cache_wait();
@@ -148,13 +405,12 @@ Int cbWriteStop(
     Cache_inv(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
     Cache_wait();
 
-    //Log_info1("cbWriteStop:afCb=0x%04x", (IArg)pCb->afCb);  // FL: debug
+    //Log_info1("cbWriteStop:afCb=0x%04x", (IArg)pCb->afCb);  // debug
     
     // update flags
     pCb->writerActiveFlag = 0;
-    pCb->emptyFlag = 1;
+    pCb->drainFlag = 1;
 
-    // (***) FL: revisit
     // Write back circular buffer configuration
     Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
     Cache_wait();
@@ -165,6 +421,12 @@ Int cbWriteStop(
     return ASP_DECOP_CB_SOK;
 }
 
+// debug
+//Int16 gSampleCountBuf[10];
+//Int16 gCalcDeltaSampsBuf[10];
+//Int8 gPrimedFlagCnt=0;
+
+// (***) FL: revisit
 // Write audio frame to circular buffer
 Int cbWriteAf(
     PAF_AST_DecOpCircBufCtl *pCbCtl,    // decoder output circular buffer control
@@ -179,228 +441,366 @@ Int cbWriteAf(
     PAF_ChannelMask_HD streamMask;
     Int8 i;
     Int16 j;
-    PAF_AudioData *pPcmBuf;UInt8 *pMetaBuf; int nextWrtIdx;PAF_AudioFrame *pAfCbNextAf; 
-
+    PAF_AudioData *pPcmBuf; UInt8 *pMetaBuf; int nextWrtIdx;PAF_AudioFrame *pAfCbNextAf; 
+    PAF_AudioFrame *pAfCbRd;
+    PAF_AudioData *pPcmBufRd, *pPcmBufWrt;
+    
     // Get gate handle
     gateHandle = pCbCtl->gateHandle;
     // Enter gate
     key = GateMP_enter(gateHandle);
 
-    //Log_info2("cbWriteAf:gate enter, gateHandle=0x%04x, key=%d", (IArg)gateHandle, (IArg)key); // FL: debug
+    //Log_info2("cbWriteAf:gate enter, gateHandle=0x%04x, key=%d", (IArg)gateHandle, (IArg)key); // debug
 
     // Get circular buffer base pointer
     pCb = &((*pCbCtl->pXDecOpCb)[cbIdx]);
-    //Log_info1("cbWriteAf:pCb=0x%04x", (IArg)pCb); // FL: debug
+    //Log_info1("cbWriteAf:pCb=0x%04x", (IArg)pCb); // debug
 
-    // (***) FL: revisit
     // Invalidate circular buffer configuration.
-    // NOTE: Probably only a subset of this information nexeds to be updated.
+    // NOTE: Probably only a subset of this information needs to be updated.
     Cache_inv(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
     Cache_wait();
 
-    //Log_info1("cbWriteAf:afCb=0x%04x", (IArg)pCb->afCb); // FL: debug
-    //Log_info2("cbWriteAf:pCb->readerActiveFlag=%d, pCb->writerActiveFlag=%d", (IArg)pCb->readerActiveFlag, (IArg)pCb->writerActiveFlag); // FL: debug
-
-    if ((pCb->readerActiveFlag == 1) && (pAfWrt->sampleCount)) //QIN ?
-    {
-        //
-        // Normal case, reader active.
-        // If reader not active, don't write to circular buffer or check OVRflow.
+    //Log_info1("cbWriteAf:afCb=0x%04x", (IArg)pCb->afCb); // debug
+    //Log_info2("cbWriteAf:pCb->readerActiveFlag=%d, pCb->writerActiveFlag=%d", (IArg)pCb->readerActiveFlag, (IArg)pCb->writerActiveFlag); // debug
 
-#if 0        
-        if (pCb->cbWriteAfInit == 0)
+    //if (pCb->readerActiveFlag == 1)
+    //{
+    //    //
+    //    // Normal case, reader active.
+    //    //
+        
+        if (pAfWrt->sampleCount != 0)
         {
-            // Invalidate AF circular buffer
-            Cache_inv(pCb->afCb, pCb->maxNumAfCb*sizeof(PAF_AudioFrame), Cache_Type_ALLD, 0);
-            for (n=0; n<pCb->maxNumAfCb; n++)
-            {
-                pAfCb = &pCb->afCb[n];
-                Cache_inv(pAfCb->data.sample, pCb->maxAFChanNum*sizeof(PAF_AudioData *), Cache_Type_ALLD, 0);
-            }
-            Cache_wait();
+            //Log_info2("cbWriteAf:pCb->numAfCb=%d, pCb->maxNumAfCb=%d", (IArg)pCb->readerActiveFlag, (IArg)pCb->maxNumAfCb); // debug
 
-            pCb->cbWriteAfInit = 1;
-        }
-#endif        
-
-        //Log_info2("cbWriteAf:pCb->numAfCb=%d, pCb->maxNumAfCb=%d", (IArg)pCb->readerActiveFlag, (IArg)pCb->maxNumAfCb); // FL: debug
-
-        // check overflow
-        //while (pCb->numAfCb >= pCb->maxNumAfCb); // FL: debug
-        if (pCb->numAfCb >= pCb->maxNumAfCb)
-        {
-            pCb->errOvrCnt++;
-
-            //SW_BREAKPOINT;
-            Log_info1("cbWriteAf: ERROR: overflow, numAfCb=%d", pCb->numAfCb);
+            // check AF overflow
+            if (pCb->numAfCb >= pCb->maxNumAfCb)
+            {
+                pCb->errAfOvrCnt++;
 
-            // Write back circular buffer configuration
-            Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+                //SW_BREAKPOINT;
+                Log_info1("cbWriteAf: ERROR: AF CB overflow, numAfCb=%d", pCb->numAfCb);
 
-            // Leave the gate
-            GateMP_leave(gateHandle, key);
+                // Write back circular buffer configuration
+                Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+                Cache_wait();
 
-            //Log_info2("cbWriteAf:gate leave, gateHandle=0x%04x, key=%d", (IArg)gateHandle, (IArg)key); // FL: debug
+                // Leave the gate
+                GateMP_leave(gateHandle, key);
 
-            return ASP_DECOP_CB_WRITE_OVERFLOW;
-        }
+                //Log_info2("cbWriteAf:gate leave, gateHandle=0x%04x, key=%d", (IArg)gateHandle, (IArg)key); // debug
 
-        pAfCb = &pCb->afCb[pCb->afWrtIdx];
-        pPcmBuf = pAfCb->data.sample[0];
-        pMetaBuf = pAfCb->pafPrivateMetadata[0].pMdBuf;
-        if((pPcmBuf + (pAfWrt->sampleCount * pCb->maxAFChanNum )) > (pCb->pcmBufEnd))
-        {
-            pPcmBuf = pCb->pcmBuf;
-        }
+                return ASP_DECOP_CB_AF_WRITE_OVERFLOW;
+            }
+            
+            // FL: this won't reliably detect overflow because of PCM buffer write address wrap
+            // check PCM overflow
+            //if ((pCb->numPcmSampsPerCh + pAfWrt->sampleCount) > pCb->maxNumPcmSampsPerCh)
+            //{
+            //    pCb->errPcmOvrCnt++;
+            //
+            //    Log_info3("cbWriteAf: ERROR: PCM CB overflow, sampleCount=%d, numPcmSampsPerCh=%d, maxNumPcmSampsPerCh=%d",
+            //        pCb->numPcmSampsPerCh, pAfWrt->sampleCount, pCb->maxNumPcmSampsPerCh);
+            //
+            //    // Write back circular buffer configuration
+            //    Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+            //    Cache_wait();
+            //
+            //    // Leave the gate
+            //    GateMP_leave(gateHandle, key);
+            //
+            //    return ASP_DECOP_CB_PCM_WRITE_OVERFLOW;
+            //}
+
+            pAfCb = &pCb->afCb[pCb->afWrtIdx]; // get CB AF to be written
+            
+            // Get reader current PCM buffer location for PCM buffer overflow check
+            pAfCbRd = &pCb->afCb[pCb->afRdIdx];
+            //pPcmBufRd = pAfCbRd->data.sample[0];                    // FL: starting location of PCM samples for AF being read
+            pPcmBufRd = pAfCbRd->data.sample[0] + pCb->pcmRdIdx;    // FL: current location of PCM samples for AF being read
+            
+            pPcmBufWrt = pAfCb->data.sample[0];             // get current location in PCM buffer to be written
+            // (***) FL: currently no metadata buffer overflow detection
+            pMetaBuf = pAfCb->pafPrivateMetadata[0].pMdBuf; // get current location in MD buffer to be written
+            
+            // check PCM buffer overflow
+            pPcmBuf = pPcmBufWrt;
+            for (i = 0; i < pCb->maxAFChanNum; i++)
+            {
+                // check PCM buffer wrap
+                if ((pPcmBuf + pAfWrt->sampleCount) >= pCb->pcmBufEnd)
+                {
+                    pPcmBuf = pCb->pcmBuf;
+                }
+                
+                // check PCM buffer overflow
+                if ((pPcmBuf < pPcmBufRd) && 
+                    ((pPcmBuf + pAfWrt->sampleCount) >= pPcmBufRd))
+                {
+                    pCb->errPcmOvrCnt++;;
+                    
+                    Log_info2("cbWriteAf: ERROR: PCM CB overflow, sampleCount=%d, numPcmSampsPerCh=%d", 
+                         pAfWrt->sampleCount, pCb->numPcmSampsPerCh);
+            
+                    //SW_BREAKPOINT; // debug
+                    
+                    // Write back circular buffer configuration
+                    Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+                    Cache_wait();
+            
+                    // Leave the gate
+                    GateMP_leave(gateHandle, key);
+            
+                    return ASP_DECOP_CB_PCM_WRITE_OVERFLOW;
+                }
+                
+                pPcmBuf += pAfWrt->sampleCount;
+            }
+            
+            // configure AF sample pointers
+            pPcmBuf = pPcmBufWrt;
+            for (i = 0; i < pCb->maxAFChanNum; i++)
+            {
+                // check PCM buffer wrap
+                if ((pPcmBuf + pAfWrt->sampleCount) >= pCb->pcmBufEnd)
+                {
+                    pPcmBuf = pCb->pcmBuf;
+                }
+                
+                pAfCb->data.sample[i] = pPcmBuf;                
+                pPcmBuf += pAfWrt->sampleCount;
+                pAfCb->data.samsiz[i] = 0;
+            }
+            Cache_inv(pAfCb->data.sample, pCb->maxAFChanNum*sizeof(PAF_AudioData *), Cache_Type_ALLD, 0); // FL: this is write back and invalidate??
+            Cache_wait();
 
-        for (i=0; i<pCb->maxAFChanNum; i++)
-        {
+            // FL: brute force reset of all metadata in CB AF?
+            for (i=0; i<PAF_MAX_NUM_PRIVATE_MD; i++)
+            {
+                pAfCb->pafPrivateMetadata[i].offset = 0;
+                pAfCb->pafPrivateMetadata[i].size   = 0;
+                pAfCb->pafPrivateMetadata[i].pMdBuf = pMetaBuf;
+                pMetaBuf += PAF_MAX_PRIVATE_MD_SZ;
+            }
 
-            pAfCb->data.sample[i] = pPcmBuf;
-            pPcmBuf += pAfWrt->sampleCount;
-            pAfCb->data.samsiz[i] = 0;
-        }
-        Cache_inv(pAfCb->data.sample, pCb->maxAFChanNum*sizeof(PAF_AudioData *), Cache_Type_ALLD, 0);
-        Cache_wait();
+            nextWrtIdx = 0;
+            if ((pCb->afWrtIdx + 1) >= pCb->maxNumAfCb)
+            {
+                //Log_info0("cbWriteAf: AF Wrap around **** ");
+                // next audio frame will be audio frame 0
+                nextWrtIdx = 0;
+            }
+            else
+            {
+                // next audio frame will be current audio frame + 1
+                nextWrtIdx = pCb->afWrtIdx + 1;
+            }
 
-        for (i=0; i<pCb->maxAFChanNum; i++){
-        }
-        for (i=0; i<PAF_MAX_NUM_PRIVATE_MD; i++)
-        {
-            pAfCb->pafPrivateMetadata[i].offset = 0;
-            pAfCb->pafPrivateMetadata[i].size   = 0;
-            pAfCb->pafPrivateMetadata[i].pMdBuf = pMetaBuf;
-            pMetaBuf += PAF_MAX_PRIVATE_MD_SZ;
-        }
+            pAfCbNextAf = &pCb->afCb[nextWrtIdx]; // +1 or last AF if overflow
+            pAfCbNextAf->data.sample[0] = &pAfCb->data.sample[pCb->maxAFChanNum - 1][pAfWrt->sampleCount];// pAfCb->data.sample[15] + (pAfCb->sampleCount * sizeof(PAF_AudioData));
+
+            // write audio frame information updated by decoder
+            pAfCb->sampleDecode = pAfWrt->sampleDecode;
+            PAF_PROCESS_COPY(pAfCb->sampleProcess, pAfWrt->sampleProcess);
+            pAfCb->sampleRate = pAfWrt->sampleRate;
+            pAfCb->sampleCount = pAfWrt->sampleCount;
+            pAfCb->channelConfigurationRequest = pAfWrt->channelConfigurationRequest;
+            pAfCb->channelConfigurationStream = pAfWrt->channelConfigurationStream;
+            // write metadata information updated by decoder
+            pAfCb->bsMetadata_type     = pAfWrt->bsMetadata_type;        /* non zero if metadata is attached. */
+            pAfCb->pafBsMetadataUpdate = pAfWrt->pafBsMetadataUpdate;    /* indicates whether bit-stream metadata update */
+            pAfCb->numPrivateMetadata  = pAfWrt->numPrivateMetadata;     /* number of valid private metadata (0 or 1 if metadata filtering enabled) */
+            pAfCb->bsMetadata_offset   = pAfWrt->bsMetadata_offset;      /* offset into audio frame for change in bsMetadata_type field */
+            // write PCM samples
+            streamMask = pAfWrt->fxns->channelMask(pAfWrt, pAfCb->channelConfigurationStream);
+            for (i = 0; i < pCb->maxAFChanNum; i++)
+            {
+                if ((streamMask >> i) & 0x1)
+                {
+                    for (j = 0; j < pAfWrt->sampleCount; j++)
+                    {
+                        pAfCb->data.sample[i][j] = pAfWrt->data.sample[i][j];
+                    }
 
-        nextWrtIdx = 0;
-        if ((pCb->afWrtIdx +1) >= pCb->maxNumAfCb)
-        {
-            //Log_info0("cbWriteAf: AF Wrap around **** ");
-            nextWrtIdx = 0;
-        }else{
-            nextWrtIdx = pCb->afWrtIdx + 1;
-        }
+                    pAfCb->data.samsiz[i] = pAfWrt->data.samsiz[i];
+                }
+            }
+            
+            // Update PCM samples per channel
+            pCb->numPcmSampsPerCh += pAfWrt->sampleCount;
+            
+            #ifdef CB_RW_OP_CAP_PP // debug
+            if (pCb->cb_opCnt < CB_OP_COUNT_MAX)
+            {
+                if ((pCb->cb_samples_op != NULL) && (pCb->cb_op_owner != NULL))
+                {
+                    // log sample count
+                    pCb->cb_samples_op[pCb->cb_opCnt] = pAfWrt->sampleCount;
+                    pCb->cb_op_owner[pCb->cb_opCnt] = CB_OP_W;
+                    // log idxs
+                    pCb->cb_afRdIdx[pCb->cb_opCnt] = pCb->afRdIdx;
+                    pCb->cb_afWrtIdx[pCb->cb_opCnt] = pCb->afWrtIdx;
+                    pCb->cb_numAfCb[pCb->cb_opCnt] = pCb->numAfCb; // numAfCb might not be pointing to this instance
+                    pCb->cb_opCnt++;
+                }
+            }
+            #endif
 
-        pAfCbNextAf = &pCb->afCb[nextWrtIdx]; // +1 or last AF if overflow
-        pAfCbNextAf->data.sample[0] = &pAfCb->data.sample[PAF_MAX_NUM_PRIVATE_MD - 1][pAfWrt->sampleCount];// pAfCb->data.sample[15] + (pAfCb->sampleCount * sizeof(PAF_AudioData));
-
-        // write audio frame information updated by decoder
-        pAfCb->sampleDecode = pAfWrt->sampleDecode;
-        PAF_PROCESS_COPY(pAfCb->sampleProcess, pAfWrt->sampleProcess);
-        pAfCb->sampleRate = pAfWrt->sampleRate;
-        pAfCb->sampleCount = pAfWrt->sampleCount;
-        pAfCb->channelConfigurationRequest = pAfWrt->channelConfigurationRequest;
-        pAfCb->channelConfigurationStream = pAfWrt->channelConfigurationStream;
-        // write metadata information updated by decoder //QIN
-        pAfCb->bsMetadata_type     = pAfWrt->bsMetadata_type;        /* non zero if metadata is attached. */
-        pAfCb->pafBsMetadataUpdate = pAfWrt->pafBsMetadataUpdate;    /* indicates whether bit-stream metadata update */
-        pAfCb->numPrivateMetadata  = pAfWrt->numPrivateMetadata;     /* number of valid private metadata (0 or 1 if metadata filtering enabled) */
-        pAfCb->bsMetadata_offset   = pAfWrt->bsMetadata_offset;      /* offset into audio frame for change in bsMetadata_type field */
-        // write PCM samples
-        streamMask = pAfWrt->fxns->channelMask(pAfWrt, pAfCb->channelConfigurationStream);
-
-        for (i = 0; i < pCb->maxAFChanNum; i++)
-        {
-            if ((streamMask >> i) & 0x1)
+            // prepare metadata buffer pointers according to the metadata and buffer sizes
+            for (i=0; i < pAfWrt->numPrivateMetadata; i++)
             {
-                for (j = 0; j < pAfWrt->sampleCount; j++)
+                UInt8 *nextMdBuf;
+                if(i == 0)
+                    nextMdBuf = (pAfCb->pafPrivateMetadata[0].pMdBuf + pAfWrt->pafPrivateMetadata[0].size);
+                else
+                    nextMdBuf = (pAfCb->pafPrivateMetadata[i-1].pMdBuf + pAfWrt->pafPrivateMetadata[i-1].size);
+                if(nextMdBuf >= pCb->metaBufEnd) // metadata buffer overflow
+                {
+                    pAfCb->pafPrivateMetadata[i].pMdBuf = pCb->metaBuf;
+                }
+                else if(i != 0)
                 {
-                    pAfCb->data.sample[i][j] = pAfWrt->data.sample[i][j];
+                    pAfCb->pafPrivateMetadata[i].pMdBuf = nextMdBuf;
                 }
+                Cache_inv(pAfCb->pafPrivateMetadata[i].pMdBuf, sizeof(UInt8 *), Cache_Type_ALLD, 0);
+            }
 
-                pAfCb->data.samsiz[i] = pAfWrt->data.samsiz[i];
+            // Write metadata to circular buffer
+            for (i = 0; i < pAfWrt->numPrivateMetadata; i++) // only copy numPrivateMetadata
+            {
+                pAfCb->pafPrivateMetadata[i].offset = pAfWrt->pafPrivateMetadata[i].offset;
+                pAfCb->pafPrivateMetadata[i].size   = pAfWrt->pafPrivateMetadata[i].size;
+                memcpy(pAfCb->pafPrivateMetadata[i].pMdBuf, pAfWrt->pafPrivateMetadata[i].pMdBuf, pAfWrt->pafPrivateMetadata[i].size);
             }
-        }
 
+            Cache_inv(pAfCb->pafPrivateMetadata, pAfWrt->numPrivateMetadata*sizeof(PAF_PrivateMetadata *), Cache_Type_ALLD, 0); // FL: this is write back and invalidate??
+            Cache_wait();
+            for (i=0; i<pAfCb->numPrivateMetadata; i++) // only write back numPrivateMetadata
+            {
+                //Log_info4("cbWriteAf: AF: %d nummd: %d offset: %d size: %d ", pCb->afWrtIdx, pAfCb->numPrivateMetadata, pAfCb->pafPrivateMetadata[i].offset,  pAfCb->pafPrivateMetadata[i].size);
+                Cache_wb(pAfCb->pafPrivateMetadata[i].pMdBuf, pAfCb->pafPrivateMetadata[i].size, Cache_Type_ALLD, 0);
+            }
+            // update audio frame write index
+            pCb->afWrtIdx++;
+            if (pCb->afWrtIdx >= pCb->maxNumAfCb)
+            {
+                pCb->afWrtIdx = 0;
+            }
 
-        // prepare metadata buffer pointers according to the metadata and buffer sizes
-        for (i=0; i < pAfWrt->numPrivateMetadata; i++)
-        {
-            UInt8 *nextMdBuf;
-            if(i == 0)
-               nextMdBuf = (pAfCb->pafPrivateMetadata[0].pMdBuf + pAfWrt->pafPrivateMetadata[0].size);
+            pCb->afCb[pCb->afWrtIdx].data.sample[0] = &pAfCb->data.sample[pCb->maxAFChanNum - 1][pAfWrt->sampleCount];
+            if(pAfWrt->numPrivateMetadata > 0)
+            {
+                pCb->afCb[pCb->afWrtIdx].pafPrivateMetadata[0].pMdBuf = pAfCb->pafPrivateMetadata[pAfWrt->numPrivateMetadata - 1].pMdBuf + pAfWrt->pafPrivateMetadata[pAfWrt->numPrivateMetadata - 1].size;
+            }
             else
-               nextMdBuf = (pAfCb->pafPrivateMetadata[i-1].pMdBuf + pAfWrt->pafPrivateMetadata[i-1].size);
-            if(nextMdBuf >= pCb->metaBufEnd) // metadata buffer overflow
             {
-                pAfCb->pafPrivateMetadata[i].pMdBuf = pCb->metaBuf;
+                pCb->afCb[pCb->afWrtIdx].pafPrivateMetadata[0].pMdBuf = pAfCb->pafPrivateMetadata[0].pMdBuf;
+                Cache_wb(pCb->afCb , ASP_DECOP_CB_MAX_NUM_PCM_FRAMES*sizeof(PAF_AudioFrame *), Cache_Type_ALLD, 0);
+                Cache_wait();
             }
-            else if(i != 0)
+            Cache_inv(pCb->afCb[pCb->afWrtIdx].pafPrivateMetadata[0].pMdBuf, sizeof(UInt8 *), Cache_Type_ALLD, 0);
+            Cache_wait();
+            // update number of audio frames in circular buffer
+            pCb->numAfCb++;
+            
+            // Update CB Lag index 
+            //if (pCb->afLagIdx < pCb->afInitialLag)
+            //{
+            //    pCb->afLagIdx += 1;
+            //}
+            
+            // Update CB primed flag
+            // calculate number of delta samples before allowing CB read
+            if (pCb->primedFlag == 0)
             {
-                pAfCb->pafPrivateMetadata[i].pMdBuf = nextMdBuf;
+                pCb->primedFlag = 1;
+                
+                // Calculate number of output frames to block reader.
+                // This is sample count reader waits before allowed to actually read samples from the CB.
+                //pCb->deltaSamps = (pCb->targetNDSamps - pAfWrt->sampleCount + (pCb->strFrameLen-1)) / pCb->strFrameLen * pCb->strFrameLen;
+                // FL: CB read decrements by strFrameLen and tests for >0, so rounding to strFrameLen is unnecessary
+                pCb->deltaSamps = pCb->targetNDSamps - pAfWrt->sampleCount;
+                
+                // debug
+                //gSampleCountBuf[gPrimedFlagCnt] = pAfWrt->sampleCount;
+                //gCalcDeltaSampsBuf[gPrimedFlagCnt] = pCb->deltaSamps;
+                //if (gPrimedFlagCnt < 10)
+                //    gPrimedFlagCnt++;
             }
-            Cache_inv(pAfCb->pafPrivateMetadata[i].pMdBuf, sizeof(UInt8 *), Cache_Type_ALLD, 0);
-        }
 
-        // Write metadata to circular buffer
-        for (i = 0; i < pAfWrt->numPrivateMetadata; i++) // FL: only copy numPrivateMetadata
-        {
-            pAfCb->pafPrivateMetadata[i].offset = pAfWrt->pafPrivateMetadata[i].offset;
-            pAfCb->pafPrivateMetadata[i].size   = pAfWrt->pafPrivateMetadata[i].size;
-            memcpy(pAfCb->pafPrivateMetadata[i].pMdBuf, pAfWrt->pafPrivateMetadata[i].pMdBuf, pAfWrt->pafPrivateMetadata[i].size);
-        }
+            // Write back circular buffer configuration
+            Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
+            // write back audio frame
+            Cache_wb(pAfCb, sizeof(PAF_AudioFrame), Cache_Type_ALLD, 0);
+            Cache_wb(pAfCb->data.samsiz, pCb->maxAFChanNum*sizeof(PAF_AudioSize), Cache_Type_ALLD, 0);
+            Cache_wb(pAfCb->pafPrivateMetadata, pAfWrt->numPrivateMetadata*sizeof(PAF_PrivateMetadata *), Cache_Type_ALLD, 0);
+            Cache_wait();
+            // write back PCM data
+            for (i = 0; i < pCb->maxAFChanNum; i++)
+            {
+                if ((streamMask >> i) & 0x1)
+                {
+                    Cache_wb(pAfCb->data.sample[i], pAfWrt->sampleCount * sizeof(PAF_AudioData), Cache_Type_ALLD, 0);
+                }
+            }
+            Cache_wait();
 
-        Cache_inv(pAfCb->pafPrivateMetadata, pAfWrt->numPrivateMetadata*sizeof(PAF_PrivateMetadata *), Cache_Type_ALLD, 0);
-        Cache_wait();
-        for (i=0; i<pAfCb->numPrivateMetadata; i++) // FL: only write back numPrivateMetadata
-        {
-            //Log_info4("cbWriteAf: AF: %d nummd: %d offset: %d size: %d ", pCb->afWrtIdx, pAfCb->numPrivateMetadata, pAfCb->pafPrivateMetadata[i].offset,  pAfCb->pafPrivateMetadata[i].size);
-            Cache_wb(pAfCb->pafPrivateMetadata[i].pMdBuf, pAfCb->pafPrivateMetadata[i].size, Cache_Type_ALLD, 0);
-        }
-        // update audio frame write index
-        pCb->afWrtIdx++;
-        if (pCb->afWrtIdx >= pCb->maxNumAfCb)
-        {
-            pCb->afWrtIdx = 0;
-        }
+#if 0 // (***) FL: shows timing of CB write
+            // debug
+            {
+                static Uint8 toggleState = 0;
+               if (toggleState == 0)
+                   GPIOSetOutput(GPIO_PORT_0, GPIO_PIN_99);
+               else
+                   GPIOClearOutput(GPIO_PORT_0, GPIO_PIN_99);
+               toggleState = ~(toggleState);
+            }
+#endif
 
-        pCb->afCb[pCb->afWrtIdx].data.sample[0] = &pAfCb->data.sample[pCb->maxAFChanNum - 1][pAfWrt->sampleCount];
-        if(pAfWrt->numPrivateMetadata > 0)
-        {
-            pCb->afCb[pCb->afWrtIdx].pafPrivateMetadata[0].pMdBuf = pAfCb->pafPrivateMetadata[pAfWrt->numPrivateMetadata - 1].pMdBuf + pAfWrt->pafPrivateMetadata[pAfWrt->numPrivateMetadata - 1].size;
+            Log_info3("wrote %d samples into AF %d sourceSel: %d", pAfCb->sampleCount, pCb->afWrtIdx, pCb->sourceSel);
+            Log_info4("CBWMETA num=%d  size=%d  offset=%d chrequest=0x%04x", pAfCb->numPrivateMetadata, pAfCb->pafPrivateMetadata[0].size, pAfCb->pafPrivateMetadata[0].offset, pAfCb->channelConfigurationRequest.full);
         }
         else
         {
-            pCb->afCb[pCb->afWrtIdx].pafPrivateMetadata[0].pMdBuf = pAfCb->pafPrivateMetadata[0].pMdBuf;
-            Cache_wb(pCb->afCb , ASP_DECOP_CB_MAX_NUM_PCM_FRAMES*sizeof(PAF_AudioFrame *), Cache_Type_ALLD, 0);
+            //
+            // Skip write in case of 0 sample count
+            //
+            
+            // writing audio frame w/ zero samples
+            // update stat
+            pCb->wrtAfZeroSampsCnt++;
+
+            // Write back circular buffer configuration
+            Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
             Cache_wait();
         }
-        Cache_inv(pCb->afCb[pCb->afWrtIdx].pafPrivateMetadata[0].pMdBuf, sizeof(UInt8 *), Cache_Type_ALLD, 0);
-        Cache_wait();
-        // update number of audio frames in circular buffer
-        pCb->numAfCb++;
+    //}
+    //else if (pCb->readerActiveFlag == 0)
+    if (pCb->readerActiveFlag == 0)
+    {
+        //
+        // Reader inactive, don't write to circular buffer or check OVRflow.
+        //
+        
+        // writing AF w/ inactive reader
+        // update stat
+        pCb->wrtAfReaderInactiveCnt++;
 
-        // (***) FL: revisit
         // Write back circular buffer configuration
         Cache_wb(pCb, sizeof(PAF_AST_DecOpCircBuf), Cache_Type_ALLD, 0);
-        // write back audio frame
-        Cache_wb(pAfCb, sizeof(PAF_AudioFrame), Cache_Type_ALLD, 0);
-        Cache_wb(pAfCb->data.samsiz, pCb->maxAFChanNum*sizeof(PAF_AudioSize), Cache_Type_ALLD, 0);
-        Cache_wb(pAfCb->pafPrivateMetadata, pAfWrt->numPrivateMetadata*sizeof(PAF_PrivateMetadata *), Cache_Type_ALLD, 0);
         Cache_wait();
-        // write back PCM data
-        for (i = 0; i < pCb->maxAFChanNum; i++)
-        {
-            if ((streamMask >> i) & 0x1)
-            {
-                Cache_wb(pAfCb->data.sample[i], pAfWrt->sampleCount * sizeof(PAF_AudioData), Cache_Type_ALLD, 0);
-            }
-        }
-        Cache_wait();
-        Log_info3("wrote %d samples into AF %d sourceSel: %d", pAfCb->sampleCount, pCb->afWrtIdx, pCb->sourceSel);
-        // write back private metadata // QIN
-        Log_info4("CBWMETA num=%d  size=%d  offset=%d chrequest=0x%04x", pAfCb->numPrivateMetadata, pAfCb->pafPrivateMetadata[0].size, pAfCb->pafPrivateMetadata[0].offset, pAfCb->channelConfigurationRequest.full);
     }
 
     // Leave the gate
     GateMP_leave(gateHandle, key);
 
-    //Log_info2("cbWriteAf:gate leave, gateHandle=0x%04x, key=%d", (IArg)gateHandle, (IArg)key); // FL: debug
+    //Log_info2("cbWriteAf:gate leave, gateHandle=0x%04x, key=%d", (IArg)gateHandle, (IArg)key); // debug
 
     return ASP_DECOP_CB_SOK;
 }
 
+#if 0
 // Get next audio frame to write in circular buffer
 Int cbGetNextWriteAf(
     PAF_AST_DecOpCircBufCtl *pCbCtl,    // decoder output circular buffer control
@@ -435,3 +835,18 @@ Int cbGetNextWriteAf(
 
     return ASP_DECOP_CB_SOK;
 }
+#endif
+
+// Init last audio frame configuration info 
+static Void cbInitLastAfInfo(
+    PAF_AST_DecOpCircBuf *pCb,  // decoder output circular buffer control
+    PAF_AudioFrame *pAfInit     // audio frame used for init
+)
+{
+    memset(&pCb->lastAf, 0, sizeof(PAF_AudioFrame));
+    
+    pCb->lastAf.sampleDecode = pAfInit->sampleDecode;
+    pCb->lastAf.sampleRate = pAfInit->sampleRate;
+    pCb->lastAf.channelConfigurationRequest.full = pAfInit->channelConfigurationRequest.full;
+    pCb->lastAf.channelConfigurationStream.full = pAfInit->channelConfigurationStream.full;
+}