VirtualBox

Changeset 89132 in vbox


Ignore:
Timestamp:
May 17, 2021 11:58:37 PM (4 years ago)
Author:
vboxsync
Message:

DrvHostAudioCoreAudio: Rewrote buffer handling, kicking out the double buffering and the thread. This lead to having to reimplement the playback, capture, control, state getter, and readable/writable getters. We now properly support draining. Code needs more work as stream creation is too slow. bugref:9890

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Audio/DrvHostAudioCoreAudio.cpp

    r89055 r89132  
    6565 * - Maybe make sure the threads are immediately stopped if playing/recording stops.
    6666 */
     67
     68/*********************************************************************************************************************************
     69*   Defined Constants And Macros                                                                                                 *
     70*********************************************************************************************************************************/
     71/** The max number of queue buffers we'll use. */
     72#define COREAUDIO_MAX_BUFFERS       1024
     73/** The minimum number of queue buffers. */
     74#define COREAUDIO_MIN_BUFFERS       4
    6775
    6876
     
    142150#endif /* VBOX_WITH_AUDIO_CA_CONVERTER */
    143151
     152/**
     153 * Core audio buffer tracker.
     154 *
     155 * For output buffer we'll be using AudioQueueBuffer::mAudioDataByteSize to
     156 * track how much we've written.  When a buffer is full, or if we run low on
     157 * queued bufferes, it will be queued.
     158 *
     159 * For input buffer we'll be using offRead to track how much we've read.
     160 */
     161typedef struct COREAUDIOBUF
     162{
     163    /** The buffer. */
     164    AudioQueueBufferRef     pBuf;
     165    /** Set if the buffer is queued. */
     166    bool volatile           fQueued;
     167    /** The buffer read offset (input only). */
     168    uint32_t                offRead;
     169} COREAUDIOBUF;
     170/** Pointer to a core audio buffer tracker. */
     171typedef COREAUDIOBUF *PCOREAUDIOBUF;
     172
    144173
    145174/**
     
    173202    /** List node for the device's stream list. */
    174203    RTLISTNODE                  Node;
    175     /** The stream's thread handle for maintaining the audio queue. */
    176     RTTHREAD                    hThread;
    177     /** The runloop of the queue thread. */
    178     CFRunLoopRef                hRunLoop;
    179     /** Flag indicating to start a stream's data processing. */
    180     bool                        fRun;
    181     /** Whether the stream is in a running (active) state or not.
    182      *  For playback streams this means that audio data can be (or is being) played,
    183      *  for capturing streams this means that audio data is being captured (if available). */
    184     bool                        fIsRunning;
    185     /** Thread shutdown indicator. */
    186     bool volatile               fShutdown;
    187     /** The actual audio queue being used. */
    188     AudioQueueRef               hAudioQueue;
    189     /** The audio buffers which are used with the above audio queue.
    190      * @todo r=bird: Two buffers is a bit to granular, isn't it?  I'd think we'd be
    191      *       better off with a variable buffer count using the device's interval
    192      *       hint for the size (though we should at least have two buffers ofc). */
    193     AudioQueueBufferRef         apAudioBuffers[2];
    194204    /** The acquired (final) audio format for this stream.
    195205     * @note This what the device requests, we don't alter anything. */
    196206    AudioStreamBasicDescription BasicStreamDesc;
     207    /** The actual audio queue being used. */
     208    AudioQueueRef               hAudioQueue;
     209
     210    /** Number of buffers. */
     211    uint32_t                    cBuffers;
     212    /** The array of buffer. */
     213    PCOREAUDIOBUF               paBuffers;
     214
    197215    /** The audio unit for this stream. */
    198216    struct
     
    216234     * during the runtime. */
    217235    volatile uint32_t           enmInitState;
    218     /** An internal ring buffer for transferring data from/to the rendering callbacks. */
    219     PRTCIRCBUF                  pCircBuf;
     236    /** The current buffer being written to / read from. */
     237    uint32_t                    idxBuffer;
     238    /** Set if the stream is enabled. */
     239    bool                        fEnabled;
     240    /** Set if the stream is started (playing/capturing). */
     241    bool                        fStarted;
     242    /** Set if the stream is draining (output only). */
     243    bool                        fDraining;
     244    /** Set if we should restart the stream on resume (saved pause state). */
     245    bool                        fRestartOnResume;
     246//    /** Set if we're switching to a new output/input device. */
     247//    bool                        fSwitchingDevice;
     248    /** Internal stream offset (bytes). */
     249    uint64_t                    offInternal;
     250    /** The RTTimeMilliTS() at the end of the last transfer. */
     251    uint64_t                    msLastTransfer;
     252
    220253    /** Critical section for serializing access between thread + callbacks. */
    221254    RTCRITSECT                  CritSect;
     255    /** Buffer that drvHostAudioCaStreamStatusString uses. */
     256    char                        szStatus[64];
    222257} COREAUDIOSTREAM;
    223258
     
    265300*   Internal Functions                                                                                                           *
    266301*********************************************************************************************************************************/
    267 static int drvHostAudioCaStreamControlInternal(PCOREAUDIOSTREAM pStreamCA, PDMAUDIOSTREAMCMD enmStreamCmd);
    268 
    269302/* DrvHostAudioCoreAudioAuth.mm: */
    270303DECLHIDDEN(int) coreAudioInputPermissionCheck(void);
    271304
    272305
     306#ifdef LOG_ENABLED
     307/**
     308 * Gets the stream status.
     309 *
     310 * @returns Pointer to stream status string.
     311 * @param   pStreamCA   The stream to get the status for.
     312 */
     313static const char *drvHostAudioCaStreamStatusString(PCOREAUDIOSTREAM pStreamCA)
     314{
     315    static RTSTRTUPLE const s_aInitState[5] =
     316    {
     317        { RT_STR_TUPLE("UNINIT")    },
     318        { RT_STR_TUPLE("IN_INIT")   },
     319        { RT_STR_TUPLE("INIT")      },
     320        { RT_STR_TUPLE("IN_UNINIT") },
     321        { RT_STR_TUPLE("BAD")       },
     322    };
     323    uint32_t enmInitState = pStreamCA->enmInitState;
     324    PCRTSTRTUPLE pTuple = &s_aInitState[RT_MIN(enmInitState, RT_ELEMENTS(s_aInitState) - 1)];
     325    memcpy(pStreamCA->szStatus, pTuple->psz, pTuple->cch);
     326    size_t off = pTuple->cch;
     327
     328    static RTSTRTUPLE const s_aEnable[2] =
     329    {
     330        { RT_STR_TUPLE("DISABLED") },
     331        { RT_STR_TUPLE("ENABLED ") },
     332    };
     333    pTuple = &s_aEnable[pStreamCA->fEnabled];
     334    memcpy(pStreamCA->szStatus, pTuple->psz, pTuple->cch);
     335    off += pTuple->cch;
     336
     337    static RTSTRTUPLE const s_aStarted[2] =
     338    {
     339        { RT_STR_TUPLE(" STOPPED") },
     340        { RT_STR_TUPLE(" STARTED") },
     341    };
     342    pTuple = &s_aStarted[pStreamCA->fStarted];
     343    memcpy(&pStreamCA->szStatus[off], pTuple->psz, pTuple->cch);
     344    off += pTuple->cch;
     345
     346    static RTSTRTUPLE const s_aDraining[2] =
     347    {
     348        { RT_STR_TUPLE("")          },
     349        { RT_STR_TUPLE(" DRAINING") },
     350    };
     351    pTuple = &s_aDraining[pStreamCA->fDraining];
     352    memcpy(&pStreamCA->szStatus[off], pTuple->psz, pTuple->cch);
     353    off += pTuple->cch;
     354
     355    Assert(off < sizeof(pStreamCA->szStatus));
     356    pStreamCA->szStatus[off] = '\0';
     357    return pStreamCA->szStatus;
     358}
     359#endif /*LOG_ENABLED*/
    273360
    274361
     
    715802
    716803/*********************************************************************************************************************************
    717 *   Queue Thread                                                                                                                 *
    718 *********************************************************************************************************************************/
    719 
    720 /**
    721  * Processes output data of a Core Audio stream into an audio queue buffer.
    722  *
    723  * @param   pStreamCA       Core Audio stream to process output data for.
    724  * @param   pAudioBuffer    Audio buffer to store data into.
    725  */
    726 static void drvHostAudioCaOutputQueueFillBuffer(PCOREAUDIOSTREAM pStreamCA, AudioQueueBufferRef pAudioBuffer)
    727 {
    728     PRTCIRCBUF pCircBuf = pStreamCA->pCircBuf;
    729     AssertPtr(pCircBuf);
    730 
    731     /*
    732      * Copy out the data from the circular buffer..
    733      */
    734     size_t offDst = 0;
    735     size_t cbLeft = RTCircBufUsed(pCircBuf);
    736     cbLeft = RT_MIN(cbLeft, pAudioBuffer->mAudioDataBytesCapacity);
    737     while (cbLeft > 0)
    738     {
    739         /* Get the next ring buffer block and copy the data from it. */
    740         void  *pvSrc = NULL;
    741         size_t cbSrc = 0;
    742         RTCircBufAcquireReadBlock(pCircBuf, cbLeft, &pvSrc, &cbSrc);
    743         AssertBreakStmt(cbSrc > 0, RTCircBufReleaseReadBlock(pCircBuf, 0));
    744         Assert(cbSrc <= cbLeft);
    745 
    746         memcpy((uint8_t *)pAudioBuffer->mAudioData + offDst, pvSrc, cbSrc);
    747 
    748         /* Advance. */
    749         RTCircBufReleaseReadBlock(pCircBuf, cbSrc);
    750         offDst += cbSrc;
    751         cbLeft -= cbSrc;
    752     }
    753 
    754     /*
    755      * Zero any remaining buffer space.
    756      */
    757     if (offDst >= pAudioBuffer->mAudioDataBytesCapacity)
    758     {
    759         pAudioBuffer->mAudioDataByteSize = offDst;
    760         Log3Func(("pStreamCA=%p RTCircBufUsed=%#zx pAudioBuffer=%p cbCapacity=%#zx full\n",
    761                   pStreamCA, RTCircBufUsed(pCircBuf), offDst));
    762     }
    763     else
    764     {
    765         RT_BZERO((uint8_t *)pAudioBuffer->mAudioData + offDst, pAudioBuffer->mAudioDataBytesCapacity - offDst);
    766         pAudioBuffer->mAudioDataByteSize = pAudioBuffer->mAudioDataBytesCapacity;
    767         LogFunc(("pStreamCA=%p RTCircBufUsed=%#zx pAudioBuffer=%p cbCapacity=%#zx offDst=%#zx (zeroed %#zx bytes)\n", pStreamCA,
    768                  RTCircBufUsed(pCircBuf), pAudioBuffer->mAudioDataBytesCapacity, offDst, pAudioBuffer->mAudioDataBytesCapacity - offDst));
    769         /** @todo overflow statistics */
    770         /** @todo do we really need to operate this way here? */
    771     }
    772 }
    773 
    774 
    775 /**
    776  * Output audio queue callback.
    777  *
    778  * Called whenever an audio queue is ready to process more output data.
    779  *
    780  * @param   pvUser          User argument.
    781  * @param   hAudioQueue     Audio queue to process output data for.
    782  * @param   pAudioBuffer    Audio buffer to store output data in.
    783  *
    784  * @thread  queue thread.
    785  */
    786 static void drvHostAudioCaOutputQueueCallback(void *pvUser, AudioQueueRef hAudioQueue, AudioQueueBufferRef pAudioBuffer)
    787 {
    788     PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pvUser;
    789     AssertPtr(pStreamCA);
    790 
    791     /** @todo r=bird: The locking is probably not really necessary. The
    792      *        circular buffer can deal with concurrent access.  Might help with
    793      *        some life-cycle issues, but that should be serialized by the
    794      *        thread destruction.  Only would be concurrent calls to
    795      *        drvHostAudioCaOutputQueueFillBuffer on different threads. */
    796     int rc = RTCritSectEnter(&pStreamCA->CritSect);
    797     AssertRC(rc);
    798 
    799     drvHostAudioCaOutputQueueFillBuffer(pStreamCA, pAudioBuffer);
    800     OSStatus orc = AudioQueueEnqueueBuffer(hAudioQueue, pAudioBuffer, 0, NULL);
    801     AssertMsg(orc == noErr, ("%#x (%d)\n", orc, orc)); NOREF(orc);
    802 
    803     rc = RTCritSectLeave(&pStreamCA->CritSect);
    804     AssertRC(rc);
    805 }
    806 
    807 
    808 /**
    809  * Processes input data of an audio queue buffer and stores it into a Core Audio stream.
    810  *
    811  * @param   pStreamCA       Core Audio stream to store input data into.
    812  * @param   pAudioBuffer    Audio buffer to process input data from.
    813  */
    814 static void drvHostAudioCaInputQueueReadBuffer(PCOREAUDIOSTREAM pStreamCA, AudioQueueBufferRef pAudioBuffer)
    815 {
    816     PRTCIRCBUF pCircBuf = pStreamCA->pCircBuf;
    817     AssertPtr(pCircBuf);
    818 
    819     /*
    820      * Copy data out of the buffer.
    821      */
    822     size_t offSrc = 0;
    823     size_t cbLeft = RTCircBufFree(pCircBuf);
    824     cbLeft = RT_MIN(cbLeft, pAudioBuffer->mAudioDataByteSize);
    825     while (cbLeft > 0)
    826     {
    827         /* Get the next ring buffer block and copy the data into it. */
    828         void  *pvDst = NULL;
    829         size_t cbDst = 0;
    830         RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, &pvDst, &cbDst);
    831         AssertBreakStmt(cbDst > 0, RTCircBufReleaseWriteBlock(pCircBuf, 0));
    832         Assert(cbDst <= cbLeft);
    833 
    834         memcpy(pvDst, (uint8_t const *)pAudioBuffer->mAudioData + offSrc, cbDst);
    835 
    836         /* Advance. */
    837         RTCircBufReleaseWriteBlock(pCircBuf, cbDst);
    838         offSrc += cbDst;
    839         cbLeft -= cbDst;
    840     }
    841 
    842     /*
    843      * Log and count overflows.
    844      */
    845     if (offSrc >= pAudioBuffer->mAudioDataByteSize)
    846         Log3Func(("pStreamCA=%p RTCircBufUsed=%#zx pAudioBuffer=%p cbData=%#zx/%#zx all\n",
    847                   pStreamCA, RTCircBufUsed(pCircBuf), pAudioBuffer, offSrc, pAudioBuffer->mAudioDataBytesCapacity));
    848     else
    849     {
    850         LogFunc(("pStreamCA=%p RTCircBufUsed=%#zx pAudioBuffer=%p cbData=%#zx/%#zx overflow copyied %#zx (%#zx left)!\n",
    851                  pStreamCA, RTCircBufUsed(pCircBuf), pAudioBuffer, pAudioBuffer->mAudioDataByteSize,
    852                  pAudioBuffer->mAudioDataBytesCapacity, offSrc, pAudioBuffer->mAudioDataBytesCapacity - offSrc));
    853         /** @todo statistics counter for overflows */
    854     }
    855 }
    856 
    857 
    858 /**
    859  * Input audio queue callback.
    860  *
    861  * Called whenever input data from the audio queue becomes available.
    862  *
    863  * @param   pvUser          User argument.
    864  * @param   hAudioQueue     Audio queue to process input data from.
    865  * @param   pAudioBuffer    Audio buffer to process input data from.
    866  * @param   pAudioTS        Audio timestamp.
    867  * @param   cPacketDesc     Number of packet descriptors.
    868  * @param   paPacketDesc    Array of packet descriptors.
    869  */
    870 static void drvHostAudioCaInputQueueCallback(void *pvUser, AudioQueueRef hAudioQueue, AudioQueueBufferRef pAudioBuffer,
    871                                              const AudioTimeStamp *pAudioTS,
    872                                              UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc)
    873 {
    874     PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pvUser;
    875     RT_NOREF(pAudioTS, cPacketDesc, paPacketDesc);
    876     AssertPtr(pStreamCA);
    877 
    878     int rc = RTCritSectEnter(&pStreamCA->CritSect);
    879     AssertRC(rc);
    880 
    881     drvHostAudioCaInputQueueReadBuffer(pStreamCA, pAudioBuffer);
    882     OSStatus orc = AudioQueueEnqueueBuffer(hAudioQueue, pAudioBuffer, 0, NULL);
    883     AssertMsg(orc == noErr, ("%#x (%d)\n", orc, orc)); NOREF(orc);
    884 
    885     rc = RTCritSectLeave(&pStreamCA->CritSect);
    886     AssertRC(rc);
    887 }
    888 
    889 
    890 /**
    891  * @callback_method_impl{FNRTTHREAD,
    892  * Thread for a Core Audio stream's audio queue handling.}
    893  *
    894  * This thread is required per audio queue to pump data to/from the Core Audio
    895  * stream and handling its callbacks.
    896  */
    897 static DECLCALLBACK(int) drvHostAudioCaQueueThread(RTTHREAD hThreadSelf, void *pvUser)
    898 {
    899     PCOREAUDIOSTREAM           pStreamCA = (PCOREAUDIOSTREAM)pvUser;
    900     AssertPtr(pStreamCA);
    901     const bool                 fIn       = pStreamCA->Cfg.enmDir == PDMAUDIODIR_IN;
    902     PCOREAUDIODEVICEDATA const pDev      = (PCOREAUDIODEVICEDATA)pStreamCA->Unit.pDevice;
    903     CFRunLoopRef const         hRunLoop  = CFRunLoopGetCurrent();
    904     AssertPtr(pDev);
    905 
    906     LogFunc(("Thread started for pStreamCA=%p fIn=%RTbool\n", pStreamCA, fIn));
    907 
    908     /*
    909      * Create audio queue.
    910      */
    911     int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
    912     OSStatus orc;
    913     if (fIn)
    914         orc = AudioQueueNewInput(&pStreamCA->BasicStreamDesc, drvHostAudioCaInputQueueCallback, pStreamCA /* pvData */,
    915                                  CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pStreamCA->hAudioQueue);
    916     else
    917         orc = AudioQueueNewOutput(&pStreamCA->BasicStreamDesc, drvHostAudioCaOutputQueueCallback, pStreamCA /* pvData */,
    918                                   CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pStreamCA->hAudioQueue);
    919     if (orc == noErr)
    920     {
    921         /*
    922          * Assign device to the queue.
    923          */
    924         UInt32 uSize = sizeof(pDev->UUID);
    925         orc = AudioQueueSetProperty(pStreamCA->hAudioQueue, kAudioQueueProperty_CurrentDevice, &pDev->UUID, uSize);
    926         if (orc == noErr)
    927         {
    928             /*
    929              * Allocate audio buffers.
    930              */
    931             const size_t cbBuf = PDMAudioPropsFramesToBytes(&pStreamCA->Cfg.Props, pStreamCA->Cfg.Backend.cFramesPeriod);
    932             size_t iBuf;
    933             for (iBuf = 0; orc == noErr && iBuf < RT_ELEMENTS(pStreamCA->apAudioBuffers); iBuf++)
    934                 orc = AudioQueueAllocateBuffer(pStreamCA->hAudioQueue, cbBuf, &pStreamCA->apAudioBuffers[iBuf]);
    935             if (orc == noErr)
    936             {
    937                 /*
    938                  * Get a reference to our runloop so it can be stopped then signal
    939                  * our creator to say that we're done.  The runloop reference is the
    940                  * success indicator.
    941                  */
    942                 pStreamCA->hRunLoop = hRunLoop;
    943                 CFRetain(hRunLoop);
    944                 RTThreadUserSignal(hThreadSelf);
    945 
    946                 /*
    947                  * The main loop.
    948                  */
    949                 while (!ASMAtomicReadBool(&pStreamCA->fShutdown))
    950                 {
    951                     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 30.0 /*sec*/, 1);
    952                 }
    953 
    954                 AudioQueueStop(pStreamCA->hAudioQueue, fIn ? 1 : 0);
    955                 rc = VINF_SUCCESS;
    956             }
    957             else
    958                 LogRel(("CoreAudio: Failed to allocate %#x byte queue buffer #%u: %#x (%d)\n", cbBuf, iBuf, orc, orc));
    959 
    960             while (iBuf-- > 0)
    961             {
    962                 AudioQueueFreeBuffer(pStreamCA->hAudioQueue, pStreamCA->apAudioBuffers[iBuf]);
    963                 pStreamCA->apAudioBuffers[iBuf] = NULL;
    964             }
    965         }
    966         else
    967             LogRel(("CoreAudio: Failed to associate device with queue: %#x (%d)\n", orc, orc));
    968 
    969         AudioQueueDispose(pStreamCA->hAudioQueue, TRUE /*inImmediate*/);
    970     }
    971     else
    972         LogRel(("CoreAudio: Failed to create audio queue: %#x (%d)\n", orc, orc));
    973 
    974 
    975     RTThreadUserSignal(hThreadSelf);
    976     LogFunc(("Thread ended for pStreamCA=%p fIn=%RTbool: rc=%Rrc (orc=%#x/%d)\n", pStreamCA, fIn, rc, orc, orc));
    977     return rc;
    978 }
    979 
    980 
    981 /**
    982  * Invalidates a Core Audio stream's audio queue.
    983  *
    984  * @returns IPRT status code.
    985  * @param   pStreamCA           Core Audio stream to invalidate its queue for.
    986  *
    987  * @todo r=bird: Which use of the word 'invalidate' is this?
    988  */
    989 static int drvHostAudioCaStreamInvalidateQueue(PCOREAUDIOSTREAM pStreamCA)
    990 {
    991     int rc = VINF_SUCCESS;
    992 
    993     Log3Func(("pStreamCA=%p\n", pStreamCA));
    994 
    995     for (size_t i = 0; i < RT_ELEMENTS(pStreamCA->apAudioBuffers); i++)
    996     {
    997         AudioQueueBufferRef pBuf = pStreamCA->apAudioBuffers[i];
    998         if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_IN) /** @todo to this outside. */
    999         {
    1000             drvHostAudioCaInputQueueReadBuffer(pStreamCA, pBuf);
    1001             AudioQueueEnqueueBuffer(pStreamCA->hAudioQueue, pBuf, 0 /*inNumPacketDescs*/, NULL /*inPacketDescs*/);
    1002         }
    1003         else
    1004         {
    1005             Assert(pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT);
    1006             drvHostAudioCaOutputQueueFillBuffer(pStreamCA, pBuf);
    1007             if (pBuf->mAudioDataByteSize) /** @todo r=bird: pointless. Always set to the capacity. */
    1008                 AudioQueueEnqueueBuffer(pStreamCA->hAudioQueue, pBuf, 0 /*inNumPacketDescs*/, NULL /*inPacketDescs*/);
    1009         }
    1010     }
    1011 
    1012     return rc;
    1013 }
    1014 
    1015 
    1016 /*********************************************************************************************************************************
    1017804*   PDMIHOSTAUDIO                                                                                                                *
    1018805*********************************************************************************************************************************/
     
    16921479
    16931480/**
     1481 * Output audio queue buffer callback.
     1482 *
     1483 * Called whenever an audio queue is done processing a buffer.  This routine
     1484 * will set the data fill size to zero and mark it as unqueued so that
     1485 * drvHostAudioCaHA_StreamPlay knowns it can use it.
     1486 *
     1487 * @param   pvUser          User argument.
     1488 * @param   hAudioQueue     Audio queue to process output data for.
     1489 * @param   pAudioBuffer    Audio buffer to store output data in.
     1490 *
     1491 * @thread  queue thread.
     1492 */
     1493static void drvHostAudioCaOutputQueueBufferCallback(void *pvUser, AudioQueueRef hAudioQueue, AudioQueueBufferRef pAudioBuffer)
     1494{
     1495    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pvUser;
     1496    AssertPtr(pStreamCA);
     1497    Assert(pStreamCA->hAudioQueue == hAudioQueue);
     1498    RT_NOREF(hAudioQueue);
     1499
     1500    uintptr_t idxBuf = (uintptr_t)pAudioBuffer->mUserData;
     1501    Log4Func(("Got back buffer #%zu (%p)\n", idxBuf, pAudioBuffer));
     1502    AssertReturnVoid(   idxBuf < pStreamCA->cBuffers
     1503                     && pStreamCA->paBuffers[idxBuf].pBuf == pAudioBuffer);
     1504
     1505    pAudioBuffer->mAudioDataByteSize = 0;
     1506    bool fWasQueued = ASMAtomicXchgBool(&pStreamCA->paBuffers[idxBuf].fQueued, false);
     1507    Assert(fWasQueued); RT_NOREF(fWasQueued);
     1508}
     1509
     1510
     1511/**
     1512 * Input audio queue buffer callback.
     1513 *
     1514 * Called whenever input data from the audio queue becomes available.  This
     1515 * routine will mark the buffer unqueued so that drvHostAudioCaHA_StreamCapture
     1516 * can read the data from it.
     1517 *
     1518 * @param   pvUser          User argument.
     1519 * @param   hAudioQueue     Audio queue to process input data from.
     1520 * @param   pAudioBuffer    Audio buffer to process input data from.
     1521 * @param   pAudioTS        Audio timestamp.
     1522 * @param   cPacketDesc     Number of packet descriptors.
     1523 * @param   paPacketDesc    Array of packet descriptors.
     1524 */
     1525static void drvHostAudioCaInputQueueBufferCallback(void *pvUser, AudioQueueRef hAudioQueue,
     1526                                                   AudioQueueBufferRef pAudioBuffer, const AudioTimeStamp *pAudioTS,
     1527                                                   UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc)
     1528{
     1529    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pvUser;
     1530    AssertPtr(pStreamCA);
     1531    Assert(pStreamCA->hAudioQueue == hAudioQueue);
     1532    RT_NOREF(hAudioQueue, pAudioTS, cPacketDesc, paPacketDesc);
     1533
     1534    uintptr_t idxBuf = (uintptr_t)pAudioBuffer->mUserData;
     1535    Log4Func(("Got back buffer #%zu (%p) with %#x bytes\n", idxBuf, pAudioBuffer, pAudioBuffer->mAudioDataByteSize));
     1536    AssertReturnVoid(   idxBuf < pStreamCA->cBuffers
     1537                     && pStreamCA->paBuffers[idxBuf].pBuf == pAudioBuffer);
     1538
     1539    bool fWasQueued = ASMAtomicXchgBool(&pStreamCA->paBuffers[idxBuf].fQueued, false);
     1540    Assert(fWasQueued); RT_NOREF(fWasQueued);
     1541}
     1542
     1543
     1544/**
    16941545 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
    16951546 */
     
    17041555    AssertReturn(pCfgReq->enmDir == PDMAUDIODIR_IN || pCfgReq->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
    17051556    int rc;
     1557
     1558    /** @todo This takes too long.  Stats indicates it may take up to 200 ms.
     1559     *        Knoppix guest resets the stream and we hear nada because the
     1560     *        draining is aborted when the stream is destroyed.  Should try use
     1561     *        async init for parts (much) of this. */
    17061562
    17071563    /*
     
    17301586         * Basic structure init.
    17311587         */
    1732         pStreamCA->hThread      = NIL_RTTHREAD;
    1733         pStreamCA->fRun         = false;
    1734         pStreamCA->fIsRunning   = false;
    1735         pStreamCA->fShutdown    = false;
    1736         pStreamCA->Unit.pDevice = pDev;   /** @todo r=bird: How do we protect this against enumeration releasing pDefaultDevOut/In. */
    1737         pStreamCA->enmInitState = COREAUDIOINITSTATE_IN_INIT;
     1588        pStreamCA->fEnabled         = false;
     1589        pStreamCA->fStarted         = false;
     1590        pStreamCA->fDraining        = false;
     1591        pStreamCA->fRestartOnResume = false;
     1592        pStreamCA->offInternal      = 0;
     1593        pStreamCA->idxBuffer        = 0;
     1594        pStreamCA->Unit.pDevice     = pDev;   /** @todo r=bird: How do we protect this against enumeration releasing pDefaultDevOut/In. */
     1595        pStreamCA->enmInitState     = COREAUDIOINITSTATE_IN_INIT;
    17381596
    17391597        rc = RTCritSectInit(&pStreamCA->CritSect);
     
    17501608                                    ? "Capturing queue format"
    17511609                                    : "Playback queue format", &pStreamCA->BasicStreamDesc);
    1752 
    1753             rc = RTCircBufCreate(&pStreamCA->pCircBuf,
    1754                                  PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize));
    1755             if (RT_SUCCESS(rc))
     1610            /*
     1611             * Create audio queue.
     1612             *
     1613             * Documentation says the callbacks will be run on some core audio
     1614             * related thread if we don't specify a runloop here.  That's simpler.
     1615             */
     1616            OSStatus orc;
     1617            if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
     1618                orc = AudioQueueNewOutput(&pStreamCA->BasicStreamDesc, drvHostAudioCaOutputQueueBufferCallback, pStreamCA,
     1619                                          NULL /*hRunLoop*/, NULL /*pStrRlMode*/, 0 /*fFlags - MBZ*/, &pStreamCA->hAudioQueue);
     1620            else
     1621                orc = AudioQueueNewInput(&pStreamCA->BasicStreamDesc, drvHostAudioCaInputQueueBufferCallback, pStreamCA,
     1622                                         NULL /*hRunLoop*/, NULL /*pStrRlMode*/, 0 /*fFlags - MBZ*/, &pStreamCA->hAudioQueue);
     1623            if (orc == noErr)
    17561624            {
    17571625                /*
    1758                  * Start the thread.
     1626                 * Assign device to the queue.
    17591627                 */
    1760                 static uint32_t volatile s_idxThread = 0;
    1761                 uint32_t idxThread = ASMAtomicIncU32(&s_idxThread);
    1762 
    1763                 rc = RTThreadCreateF(&pStreamCA->hThread, drvHostAudioCaQueueThread, pStreamCA, 0 /*cbStack*/,
    1764                                      RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CaQue%u", idxThread);
    1765                 if (RT_SUCCESS(rc))
     1628                UInt32 uSize = sizeof(pDev->UUID);
     1629                orc = AudioQueueSetProperty(pStreamCA->hAudioQueue, kAudioQueueProperty_CurrentDevice, &pDev->UUID, uSize);
     1630                if (orc == noErr)
    17661631                {
    1767                     rc = RTThreadUserWait(pStreamCA->hThread, RT_MS_10SEC);
    1768                     AssertRC(rc);
    1769                     if (RT_SUCCESS(rc) && pStreamCA->hRunLoop != NULL)
     1632                    /*
     1633                     * Sanity-adjust the requested buffer size.
     1634                     */
     1635                    uint32_t cFramesBufferSizeMax = PDMAudioPropsMilliToFrames(&pStreamCA->Cfg.Props, 2 * RT_MS_1SEC);
     1636                    uint32_t cFramesBufferSize    = PDMAudioPropsMilliToFrames(&pStreamCA->Cfg.Props, 32 /*ms*/);
     1637                    cFramesBufferSize = RT_MAX(cFramesBufferSize, pCfgReq->Backend.cFramesBufferSize);
     1638                    cFramesBufferSize = RT_MIN(cFramesBufferSize, cFramesBufferSizeMax);
     1639
     1640                    /*
     1641                     * The queue buffers size is based on cMsSchedulingHint so that we're likely to
     1642                     * have a new one ready/done after each guest DMA transfer.  We must however
     1643                     * make sure we don't end up with too may or too few.
     1644                     */
     1645                    Assert(pCfgReq->Device.cMsSchedulingHint > 0);
     1646                    uint32_t cFramesQueueBuffer = PDMAudioPropsMilliToFrames(&pStreamCA->Cfg.Props,
     1647                                                                             pCfgReq->Device.cMsSchedulingHint > 0
     1648                                                                             ? pCfgReq->Device.cMsSchedulingHint : 10);
     1649                    uint32_t cQueueBuffers;
     1650                    if (cFramesQueueBuffer * COREAUDIO_MIN_BUFFERS <= cFramesBufferSize)
    17701651                    {
    1771                         ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_INIT);
    1772 
    1773                         LogFunc(("returns VINF_SUCCESS\n"));
    1774                         return VINF_SUCCESS;
     1652                        cQueueBuffers      = cFramesBufferSize / cFramesQueueBuffer;
     1653                        if (cQueueBuffers > COREAUDIO_MAX_BUFFERS)
     1654                        {
     1655                            cQueueBuffers = COREAUDIO_MAX_BUFFERS;
     1656                            cFramesQueueBuffer = cFramesBufferSize / COREAUDIO_MAX_BUFFERS;
     1657                        }
    17751658                    }
     1659                    else
     1660                    {
     1661                        cQueueBuffers      = COREAUDIO_MIN_BUFFERS;
     1662                        cFramesQueueBuffer = cFramesBufferSize / COREAUDIO_MIN_BUFFERS;
     1663                    }
     1664
     1665                    cFramesBufferSize = cQueueBuffers * cFramesBufferSize;
    17761666
    17771667                    /*
    1778                      * Failed, clean up.
     1668                     * Allocate the audio queue buffers.
    17791669                     */
    1780                     LogRel(("CoreAudio: Thread failed to initialize in a timely manner (%Rrc).\n", rc));
    1781 
    1782                     ASMAtomicWriteBool(&pStreamCA->fShutdown, true);
    1783                     RTThreadPoke(pStreamCA->hThread);
    1784                     int rcThread = 0;
    1785                     rc = RTThreadWait(pStreamCA->hThread, RT_MS_15SEC, NULL);
    1786                     AssertLogRelRC(rc);
    1787                     LogRel(("CoreAudio: Thread exit code: %Rrc / %Rrc.\n", rc, rcThread));
    1788                     pStreamCA->hThread = NIL_RTTHREAD;
     1670                    pStreamCA->paBuffers = (PCOREAUDIOBUF)RTMemAllocZ(sizeof(pStreamCA->paBuffers[0]) * cQueueBuffers);
     1671                    if (pStreamCA->paBuffers != NULL)
     1672                    {
     1673                        pStreamCA->cBuffers = cQueueBuffers;
     1674
     1675                        cFramesBufferSize = 0;
     1676                        const size_t cbQueueBuffer = PDMAudioPropsFramesToBytes(&pStreamCA->Cfg.Props, cFramesQueueBuffer);
     1677                        for (uint32_t iBuf = 0; iBuf < cQueueBuffers; iBuf++)
     1678                        {
     1679                            AudioQueueBufferRef pBuf = NULL;
     1680                            orc = AudioQueueAllocateBuffer(pStreamCA->hAudioQueue, cbQueueBuffer, &pBuf);
     1681                            if (RT_LIKELY(orc == noErr))
     1682                            {
     1683                                pBuf->mUserData = (void *)(uintptr_t)iBuf;
     1684                                pStreamCA->paBuffers[iBuf].pBuf = pBuf;
     1685                                cFramesBufferSize += PDMAudioPropsBytesToFrames(&pStreamCA->Cfg.Props,
     1686                                                                                pBuf->mAudioDataBytesCapacity);
     1687                                Assert(PDMAudioPropsIsSizeAligned(&pStreamCA->Cfg.Props, pBuf->mAudioDataBytesCapacity));
     1688                            }
     1689                            else
     1690                            {
     1691                                LogRel(("CoreAudio: Out of memory (buffer %#x out of %#x, %#x bytes)\n",
     1692                                        iBuf, cQueueBuffers, cbQueueBuffer));
     1693                                while (iBuf-- > 0)
     1694                                {
     1695                                    AudioQueueFreeBuffer(pStreamCA->hAudioQueue, pStreamCA->paBuffers[iBuf].pBuf);
     1696                                    pStreamCA->paBuffers[iBuf].pBuf = NULL;
     1697                                }
     1698                                break;
     1699                            }
     1700                        }
     1701                        if (orc == noErr)
     1702                        {
     1703                            /*
     1704                             * Update the stream config.
     1705                             */
     1706                            pStreamCA->Cfg.Backend.cFramesBufferSize   = cFramesBufferSize;
     1707                            pStreamCA->Cfg.Backend.cFramesPeriod       = cFramesQueueBuffer; /* whatever */
     1708                            pStreamCA->Cfg.Backend.cFramesPreBuffering =   pStreamCA->Cfg.Backend.cFramesPreBuffering
     1709                                                                         * pStreamCA->Cfg.Backend.cFramesBufferSize
     1710                                                                       / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
     1711
     1712                            PDMAudioStrmCfgCopy(pCfgAcq, &pStreamCA->Cfg);
     1713
     1714                            ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_INIT);
     1715
     1716                            LogFunc(("returns VINF_SUCCESS\n"));
     1717                            return VINF_SUCCESS;
     1718                        }
     1719                        RTMemFree(pStreamCA->paBuffers);
     1720                    }
     1721                    else
     1722                        rc = VERR_NO_MEMORY;
    17891723                }
    17901724                else
    1791                     LogRel(("CoreAudio: Failed to create queue thread for stream: %Rrc\n", rc));
    1792                 RTCircBufDestroy(pStreamCA->pCircBuf);
    1793                 pStreamCA->pCircBuf = NULL;
     1725                    LogRelMax(64, ("CoreAudio: Failed to associate device with queue: %#x (%d)\n", orc, orc));
     1726                AudioQueueDispose(pStreamCA->hAudioQueue, TRUE /*inImmediate*/);
    17941727            }
    17951728            else
    1796                 LogRel(("CoreAudio: Failed to allocate stream buffer: %Rrc\n", rc));
     1729                LogRelMax(64, ("CoreAudio: Failed to create audio queue: %#x (%d)\n", orc, orc));
    17971730            RTCritSectDelete(&pStreamCA->CritSect);
    17981731        }
     
    18021735    else
    18031736    {
    1804         LogFunc(("No device for stream.\n"));
     1737        LogRelMax(64, ("CoreAudio: No device for %s stream.\n", PDMAudioDirGetName(pCfgReq->enmDir)));
    18051738        rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
    18061739    }
     
    18301763
    18311764        /*
    1832          * Disable (stop) the stream just in case it's running.
     1765         * Change the stream state and stop the stream (just to be sure).
    18331766         */
    1834         /** @todo this isn't paranoid enough, the pStreamCA->hAudioQueue is
    1835          *        owned+released by the queue thread. */
    1836         drvHostAudioCaStreamControlInternal(pStreamCA, PDMAUDIOSTREAMCMD_DISABLE);
     1767        OSStatus orc;
     1768        ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_IN_UNINIT);
     1769        if (pStreamCA->hAudioQueue)
     1770        {
     1771            orc = AudioQueueStop(pStreamCA->hAudioQueue, TRUE /*inImmediate/synchronously*/);
     1772            LogFlowFunc(("AudioQueueStop -> %#x\n", orc));
     1773        }
    18371774
    18381775        /*
    1839          * Change the state (cannot do before the stop).
    18401776         * Enter and leave the critsect afterwards for paranoid reasons.
    18411777         */
    1842         ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_IN_UNINIT);
    18431778        RTCritSectEnter(&pStreamCA->CritSect);
    18441779        RTCritSectLeave(&pStreamCA->CritSect);
    18451780
    18461781        /*
    1847          * Bring down the queue thread.
     1782         * Free the queue buffers and the queue.
    18481783         */
    1849         if (pStreamCA->hThread != NIL_RTTHREAD)
    1850         {
    1851             LogFunc(("Waiting for thread ...\n"));
    1852             ASMAtomicXchgBool(&pStreamCA->fShutdown, true);
    1853             int rcThread = VERR_IPE_UNINITIALIZED_STATUS;
    1854             int rc       = VERR_TIMEOUT;
    1855             for (uint32_t iWait = 0; rc == VERR_TIMEOUT && iWait < 60; iWait++)
     1784        if (pStreamCA->paBuffers)
     1785        {
     1786            LogFlowFunc(("Freeing %u buffers ...\n", pStreamCA->cBuffers));
     1787            for (uint32_t iBuf = 0; iBuf < pStreamCA->cBuffers; iBuf++)
    18561788            {
    1857                 LogFunc(("%u ...\n", iWait));
    1858                 if (pStreamCA->hRunLoop != NULL)
    1859                     CFRunLoopStop(pStreamCA->hRunLoop);
    1860                 if (iWait >= 10)
    1861                     RTThreadPoke(pStreamCA->hThread);
    1862 
    1863                 rcThread = VERR_IPE_UNINITIALIZED_STATUS;
    1864                 rc = RTThreadWait(pStreamCA->hThread, RT_MS_1SEC / 2, &rcThread);
     1789                orc = AudioQueueFreeBuffer(pStreamCA->hAudioQueue, pStreamCA->paBuffers[iBuf].pBuf);
     1790                AssertMsg(orc == noErr, ("AudioQueueFreeBuffer(#%u) -> orc=%#x\n", iBuf, orc));
     1791                pStreamCA->paBuffers[iBuf].pBuf = NULL;
    18651792            }
    1866             AssertLogRelRC(rc);
    1867             LogFunc(("Thread stopped with: %Rrc/%Rrc\n", rc, rcThread));
    1868             pStreamCA->hThread = NIL_RTTHREAD;
    1869         }
    1870 
    1871         if (pStreamCA->hRunLoop != NULL)
    1872         {
    1873             CFRelease(pStreamCA->hRunLoop);
    1874             pStreamCA->hRunLoop = NULL;
     1793            RTMemFree(pStreamCA->paBuffers);
     1794            pStreamCA->paBuffers = NULL;
     1795        }
     1796        pStreamCA->cBuffers = 0;
     1797
     1798        if (pStreamCA->hAudioQueue)
     1799        {
     1800            LogFlowFunc(("Disposing of the queue ...\n", orc));
     1801            orc = AudioQueueDispose(pStreamCA->hAudioQueue, TRUE /*inImmediate/synchronously*/);
     1802            LogFlowFunc(("AudioQueueDispose -> %#x (%d)\n", orc, orc));
     1803            AssertMsg(orc == noErr, ("AudioQueueDispose -> orc=%#x\n", orc));
     1804            pStreamCA->hAudioQueue = NULL;
    18751805        }
    18761806
    18771807        /*
    1878          * Kill the circular buffer and NULL essential variable.
     1808         * Release the device and delete the critsect.
    18791809         */
    1880         if (pStreamCA->pCircBuf)
    1881         {
    1882             RTCircBufDestroy(pStreamCA->pCircBuf);
    1883             pStreamCA->pCircBuf = NULL;
    1884         }
    1885 
    1886         pStreamCA->Unit.pDevice = NULL;
     1810        pStreamCA->Unit.pDevice = NULL; /** @todo This bugger must be refcounted! */
    18871811
    18881812        RTCritSectDelete(&pStreamCA->CritSect);
     
    18931817        ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_UNINIT);
    18941818    }
     1819    else
     1820        LogFunc(("Wrong stream init state for %p: %d - leaking it\n", pStream, enmInitState));
    18951821
    18961822    LogFunc(("returns\n"));
     
    18991825
    19001826
    1901 static int drvHostAudioCaStreamControlInternal(PCOREAUDIOSTREAM pStreamCA, PDMAUDIOSTREAMCMD enmStreamCmd)
    1902 {
    1903     uint32_t enmInitState = ASMAtomicReadU32(&pStreamCA->enmInitState);
    1904 
    1905     LogFlowFunc(("enmStreamCmd=%RU32, enmInitState=%RU32\n", enmStreamCmd, enmInitState));
    1906 
    1907     if (enmInitState != COREAUDIOINITSTATE_INIT)
    1908     {
    1909         return VINF_SUCCESS;
    1910     }
    1911 
     1827/**
     1828 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
     1829 */
     1830static DECLCALLBACK(int) drvHostAudioCaHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1831{
     1832    RT_NOREF(pInterface);
     1833    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     1834    LogFlowFunc(("Stream '%s' {%s}\n", pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA)));
     1835    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, VERR_AUDIO_STREAM_NOT_READY);
     1836    RTCritSectEnter(&pStreamCA->CritSect);
     1837
     1838    Assert(!pStreamCA->fEnabled);
     1839    Assert(!pStreamCA->fStarted);
     1840
     1841    /*
     1842     * We always reset the buffer before enabling the stream (normally never necessary).
     1843     */
     1844    OSStatus orc = AudioQueueReset(pStreamCA->hAudioQueue);
     1845    if (orc != noErr)
     1846        LogRelMax(64, ("CoreAudio: Stream reset failed when enabling '%s': %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     1847    Assert(orc == noErr);
     1848    for (uint32_t iBuf = 0; iBuf < pStreamCA->cBuffers; iBuf++)
     1849        Assert(!pStreamCA->paBuffers[iBuf].fQueued);
     1850
     1851    pStreamCA->offInternal      = 0;
     1852    pStreamCA->fDraining        = false;
     1853    pStreamCA->fEnabled         = true;
     1854    pStreamCA->fRestartOnResume = false;
     1855    pStreamCA->idxBuffer        = 0;
     1856
     1857    /*
     1858     * Input streams will start capturing, while output streams will only start
     1859     * playing once we get some audio data to play (see drvHostAudioCaHA_StreamPlay).
     1860     */
    19121861    int rc = VINF_SUCCESS;
     1862    if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_IN)
     1863    {
     1864        /* Zero (probably not needed) and submit all the buffers first. */
     1865        for (uint32_t iBuf = 0; iBuf < pStreamCA->cBuffers; iBuf++)
     1866        {
     1867            AudioQueueBufferRef pBuf = pStreamCA->paBuffers[iBuf].pBuf;
     1868
     1869            RT_BZERO(pBuf->mAudioData, pBuf->mAudioDataBytesCapacity);
     1870            pBuf->mAudioDataByteSize = 0;
     1871
     1872            orc = AudioQueueEnqueueBuffer(pStreamCA->hAudioQueue, pBuf, 0 /*inNumPacketDescs*/, NULL /*inPacketDescs*/);
     1873            AssertLogRelMsgBreak(orc == noErr, ("CoreAudio: AudioQueueEnqueueBuffer(#%u) -> %#x (%d) - stream '%s'\n",
     1874                                                iBuf, orc, orc, pStreamCA->Cfg.szName));
     1875        }
     1876
     1877        /* Start the stream. */
     1878        if (orc == noErr)
     1879        {
     1880            LogFlowFunc(("Start input stream '%s'...\n", pStreamCA->Cfg.szName));
     1881            orc = AudioQueueStart(pStreamCA->hAudioQueue, NULL /*inStartTime*/);
     1882            AssertLogRelMsgStmt(orc == noErr, ("CoreAudio: AudioQueueStart(%s) -> %#x (%d) \n", pStreamCA->Cfg.szName, orc, orc),
     1883                                rc = VERR_AUDIO_STREAM_NOT_READY);
     1884            pStreamCA->fStarted = orc == noErr;
     1885        }
     1886        else
     1887            rc = VERR_AUDIO_STREAM_NOT_READY;
     1888    }
     1889    else
     1890        Assert(pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT);
     1891
     1892    RTCritSectLeave(&pStreamCA->CritSect);
     1893    LogFlowFunc(("returns %Rrc\n", rc));
     1894    return rc;
     1895}
     1896
     1897
     1898/**
     1899 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
     1900 */
     1901static DECLCALLBACK(int) drvHostAudioCaHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1902{
     1903    RT_NOREF(pInterface);
     1904    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     1905    LogFlowFunc(("cMsLastTransfer=%RI64 ms, stream '%s' {%s} \n",
     1906                 pStreamCA->msLastTransfer ? RTTimeMilliTS() - pStreamCA->msLastTransfer : -1,
     1907                 pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA) ));
     1908    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, VERR_AUDIO_STREAM_NOT_READY);
     1909    RTCritSectEnter(&pStreamCA->CritSect);
     1910
     1911    /*
     1912     * Always stop it (draining or no).
     1913     */
     1914    pStreamCA->fEnabled         = false;
     1915    pStreamCA->fRestartOnResume = false;
     1916    Assert(!pStreamCA->fDraining || pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT);
     1917
     1918    int rc = VINF_SUCCESS;
     1919    if (pStreamCA->fStarted)
     1920    {
     1921        OSStatus orc = AudioQueueStop(pStreamCA->hAudioQueue, TRUE /*inImmediate*/);
     1922        LogFlowFunc(("AudioQueueStop(%s,TRUE) returns %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     1923        if (orc != noErr)
     1924        {
     1925            LogRelMax(64, ("CoreAudio: Stopping '%s' failed (disable): %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     1926            rc = VERR_GENERAL_FAILURE;
     1927        }
     1928        pStreamCA->fStarted  = false;
     1929        pStreamCA->fDraining = false;
     1930    }
     1931
     1932    RTCritSectLeave(&pStreamCA->CritSect);
     1933    LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostAudioCaStreamStatusString(pStreamCA)));
     1934    return rc;
     1935}
     1936
     1937
     1938/**
     1939 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
     1940 */
     1941static DECLCALLBACK(int) drvHostAudioCaHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1942{
     1943    RT_NOREF(pInterface);
     1944    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     1945    LogFlowFunc(("cMsLastTransfer=%RI64 ms, stream '%s' {%s} \n",
     1946                 pStreamCA->msLastTransfer ? RTTimeMilliTS() - pStreamCA->msLastTransfer : -1,
     1947                 pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA) ));
     1948    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, VERR_AUDIO_STREAM_NOT_READY);
     1949    RTCritSectEnter(&pStreamCA->CritSect);
     1950
     1951    /*
     1952     * Unless we're draining the stream, pause it if it has started.
     1953     */
     1954    int rc = VINF_SUCCESS;
     1955    if (pStreamCA->fStarted && !pStreamCA->fDraining)
     1956    {
     1957        pStreamCA->fRestartOnResume = true;
     1958
     1959        OSStatus orc = AudioQueuePause(pStreamCA->hAudioQueue);
     1960        LogFlowFunc(("AudioQueuePause(%s) returns %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     1961        if (orc != noErr)
     1962        {
     1963            LogRelMax(64, ("CoreAudio: Pausing '%s' failed: %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     1964            rc = VERR_GENERAL_FAILURE;
     1965        }
     1966        pStreamCA->fStarted = false;
     1967    }
     1968    else
     1969    {
     1970        pStreamCA->fRestartOnResume = false;
     1971        if (pStreamCA->fDraining)
     1972        {
     1973            LogFunc(("Stream '%s' is draining\n", pStreamCA->Cfg.szName));
     1974            Assert(pStreamCA->fStarted);
     1975        }
     1976    }
     1977
     1978    RTCritSectLeave(&pStreamCA->CritSect);
     1979    LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostAudioCaStreamStatusString(pStreamCA)));
     1980    return rc;
     1981}
     1982
     1983
     1984/**
     1985 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
     1986 */
     1987static DECLCALLBACK(int) drvHostAudioCaHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     1988{
     1989    RT_NOREF(pInterface);
     1990    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     1991    LogFlowFunc(("Stream '%s' {%s}\n", pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA)));
     1992    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, VERR_AUDIO_STREAM_NOT_READY);
     1993    RTCritSectEnter(&pStreamCA->CritSect);
     1994
     1995    /*
     1996     * Resume according to state saved by drvHostAudioCaHA_StreamPause.
     1997     */
     1998    int rc = VINF_SUCCESS;
     1999    if (pStreamCA->fRestartOnResume)
     2000    {
     2001        OSStatus orc = AudioQueueStart(pStreamCA->hAudioQueue, NULL /*inStartTime*/);
     2002        LogFlowFunc(("AudioQueueStart(%s, NULL) returns %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     2003        if (orc != noErr)
     2004        {
     2005            LogRelMax(64, ("CoreAudio: Pausing '%s' failed: %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     2006            rc = VERR_AUDIO_STREAM_NOT_READY;
     2007        }
     2008    }
     2009    pStreamCA->fRestartOnResume = false;
     2010
     2011    RTCritSectLeave(&pStreamCA->CritSect);
     2012    LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostAudioCaStreamStatusString(pStreamCA)));
     2013    return rc;
     2014}
     2015
     2016
     2017/**
     2018 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
     2019 */
     2020static DECLCALLBACK(int) drvHostAudioCaHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     2021{
     2022    RT_NOREF(pInterface);
     2023    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     2024    AssertReturn(pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER);
     2025    LogFlowFunc(("cMsLastTransfer=%RI64 ms, stream '%s' {%s} \n",
     2026                 pStreamCA->msLastTransfer ? RTTimeMilliTS() - pStreamCA->msLastTransfer : -1,
     2027                 pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2028    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, VERR_AUDIO_STREAM_NOT_READY);
     2029    RTCritSectEnter(&pStreamCA->CritSect);
     2030
     2031    /*
     2032     * The AudioQueueStop function has both an immediate and a drain mode,
     2033     * so we'll obviously use the latter here.  For checking draining progress,
     2034     * we will just check if all buffers have been returned or not.
     2035     */
     2036    int rc = VINF_SUCCESS;
     2037    if (pStreamCA->fStarted)
     2038    {
     2039        if (!pStreamCA->fDraining)
     2040        {
     2041            OSStatus orc = AudioQueueStop(pStreamCA->hAudioQueue, FALSE /*inImmediate*/);
     2042            LogFlowFunc(("AudioQueueStop(%s, FALSE) returns %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     2043            if (orc == noErr)
     2044                pStreamCA->fDraining = true;
     2045            else
     2046            {
     2047                LogRelMax(64, ("CoreAudio: Stopping '%s' failed (drain): %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     2048                rc = VERR_GENERAL_FAILURE;
     2049            }
     2050        }
     2051        else
     2052            LogFlowFunc(("Already draining '%s' ...\n", pStreamCA->Cfg.szName));
     2053    }
     2054    else
     2055    {
     2056        LogFlowFunc(("Drain requested for '%s', but not started playback...\n", pStreamCA->Cfg.szName));
     2057        AssertStmt(!pStreamCA->fDraining, pStreamCA->fDraining = false);
     2058    }
     2059
     2060    RTCritSectLeave(&pStreamCA->CritSect);
     2061    LogFlowFunc(("returns %Rrc {%s}\n", rc, drvHostAudioCaStreamStatusString(pStreamCA)));
     2062    return rc;
     2063}
     2064
     2065
     2066/**
     2067 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
     2068 */
     2069static DECLCALLBACK(int) drvHostAudioCaHA_StreamControl(PPDMIHOSTAUDIO pInterface,
     2070                                                        PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
     2071{
     2072    /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
     2073     *        replacing it with individual StreamXxxx methods.  That would save us
     2074     *        potentally huge switches and more easily see which drivers implement
     2075     *        which operations (grep for pfnStreamXxxx). */
    19132076    switch (enmStreamCmd)
    19142077    {
    19152078        case PDMAUDIOSTREAMCMD_ENABLE:
     2079            return drvHostAudioCaHA_StreamEnable(pInterface, pStream);
     2080        case PDMAUDIOSTREAMCMD_DISABLE:
     2081            return drvHostAudioCaHA_StreamDisable(pInterface, pStream);
     2082        case PDMAUDIOSTREAMCMD_PAUSE:
     2083            return drvHostAudioCaHA_StreamPause(pInterface, pStream);
    19162084        case PDMAUDIOSTREAMCMD_RESUME:
    1917         {
    1918             LogFunc(("Queue enable\n"));
    1919             if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_IN)
    1920             {
    1921                 rc = drvHostAudioCaStreamInvalidateQueue(pStreamCA);
    1922                 if (RT_SUCCESS(rc))
    1923                 {
    1924                     /* Start the audio queue immediately. */
    1925                     AudioQueueStart(pStreamCA->hAudioQueue, NULL);
    1926                 }
    1927             }
    1928             else if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT)
    1929             {
    1930                 /* Touch the run flag to start the audio queue as soon as
    1931                  * we have anough data to actually play something. */
    1932                 ASMAtomicXchgBool(&pStreamCA->fRun, true);
    1933             }
     2085            return drvHostAudioCaHA_StreamResume(pInterface, pStream);
     2086        case PDMAUDIOSTREAMCMD_DRAIN:
     2087            return drvHostAudioCaHA_StreamDrain(pInterface, pStream);
     2088
     2089        case PDMAUDIOSTREAMCMD_END:
     2090        case PDMAUDIOSTREAMCMD_32BIT_HACK:
     2091        case PDMAUDIOSTREAMCMD_INVALID:
     2092            /* no default*/
    19342093            break;
    1935         }
    1936 
    1937         case PDMAUDIOSTREAMCMD_DISABLE:
    1938         {
    1939             LogFunc(("Queue disable\n"));
    1940             AudioQueueStop(pStreamCA->hAudioQueue, 1 /* Immediately */);
    1941             ASMAtomicXchgBool(&pStreamCA->fRun,       false);
    1942             ASMAtomicXchgBool(&pStreamCA->fIsRunning, false);
    1943             break;
    1944         }
    1945         case PDMAUDIOSTREAMCMD_PAUSE:
    1946         {
    1947             LogFunc(("Queue pause\n"));
    1948             AudioQueuePause(pStreamCA->hAudioQueue);
    1949             ASMAtomicXchgBool(&pStreamCA->fIsRunning, false);
    1950             break;
    1951         }
    1952 
    1953         default:
    1954             rc = VERR_NOT_SUPPORTED;
    1955             break;
    1956     }
    1957 
    1958     LogFlowFuncLeaveRC(rc);
    1959     return rc;
    1960 }
    1961 
    1962 
    1963 /**
    1964  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
    1965  */
    1966 static DECLCALLBACK(int) drvHostAudioCaHA_StreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    1967                                                         PDMAUDIOSTREAMCMD enmStreamCmd)
     2094    }
     2095    return VERR_NOT_SUPPORTED;
     2096}
     2097
     2098
     2099/**
     2100 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
     2101 */
     2102static DECLCALLBACK(uint32_t) drvHostAudioCaHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    19682103{
    19692104    RT_NOREF(pInterface);
    19702105    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
    19712106    AssertPtrReturn(pStreamCA, VERR_INVALID_POINTER);
    1972 
    1973     return drvHostAudioCaStreamControlInternal(pStreamCA, enmStreamCmd);
    1974 }
    1975 
    1976 
    1977 /**
    1978  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
    1979  */
    1980 static DECLCALLBACK(uint32_t) drvHostAudioCaHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     2107    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, 0);
     2108
     2109    uint32_t cbReadable = 0;
     2110    if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_IN)
     2111    {
     2112        RTCritSectEnter(&pStreamCA->CritSect);
     2113        PCOREAUDIOBUF const paBuffers = pStreamCA->paBuffers;
     2114        uint32_t const      cBuffers  = pStreamCA->cBuffers;
     2115        uint32_t const      idxStart  = pStreamCA->idxBuffer;
     2116        uint32_t            idxBuffer = idxStart;
     2117
     2118        if (   cBuffers > 0
     2119            && !paBuffers[idxBuffer].fQueued)
     2120        {
     2121            do
     2122            {
     2123                AudioQueueBufferRef const pBuf    = paBuffers[idxBuffer].pBuf;
     2124                uint32_t const            cbTotal = pBuf->mAudioDataBytesCapacity;
     2125                uint32_t                  cbFill  = pBuf->mAudioDataByteSize;
     2126                AssertStmt(cbFill <= cbTotal, cbFill = cbTotal);
     2127                uint32_t                  off     = paBuffers[idxBuffer].offRead;
     2128                AssertStmt(off < cbFill, off = cbFill);
     2129
     2130                cbReadable += cbFill - off;
     2131
     2132                /* Advance. */
     2133                idxBuffer++;
     2134                if (idxBuffer < cBuffers)
     2135                { /* likely */ }
     2136                else
     2137                    idxBuffer = 0;
     2138            } while (idxBuffer != idxStart && !paBuffers[idxBuffer].fQueued);
     2139        }
     2140
     2141        RTCritSectLeave(&pStreamCA->CritSect);
     2142    }
     2143    Log2Func(("returns %#x for '%s'\n", cbReadable, pStreamCA->Cfg.szName));
     2144    return cbReadable;
     2145}
     2146
     2147
     2148/**
     2149 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
     2150 */
     2151static DECLCALLBACK(uint32_t) drvHostAudioCaHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
    19812152{
    19822153    RT_NOREF(pInterface);
    19832154    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
    19842155    AssertPtrReturn(pStreamCA, VERR_INVALID_POINTER);
    1985 
    1986     if (ASMAtomicReadU32(&pStreamCA->enmInitState) != COREAUDIOINITSTATE_INIT)
    1987         return 0;
    1988 
    1989     if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_IN)
    1990     {
    1991         AssertPtr(pStreamCA->pCircBuf);
    1992         return (uint32_t)RTCircBufUsed(pStreamCA->pCircBuf);
    1993     }
    1994     AssertFailed();
    1995     return 0;
    1996 }
    1997 
    1998 
    1999 /**
    2000  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
    2001  */
    2002 static DECLCALLBACK(uint32_t) drvHostAudioCaHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
     2156    AssertReturn(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, 0);
     2157
     2158    uint32_t cbWritable = 0;
     2159    if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT)
     2160    {
     2161        RTCritSectEnter(&pStreamCA->CritSect);
     2162        PCOREAUDIOBUF const paBuffers = pStreamCA->paBuffers;
     2163        uint32_t const      cBuffers  = pStreamCA->cBuffers;
     2164        uint32_t const      idxStart  = pStreamCA->idxBuffer;
     2165        uint32_t            idxBuffer = idxStart;
     2166
     2167        if (   cBuffers > 0
     2168            && !paBuffers[idxBuffer].fQueued)
     2169        {
     2170            do
     2171            {
     2172                AudioQueueBufferRef const pBuf    = paBuffers[idxBuffer].pBuf;
     2173                uint32_t const            cbTotal = pBuf->mAudioDataBytesCapacity;
     2174                uint32_t                  cbUsed  = pBuf->mAudioDataByteSize;
     2175                AssertStmt(cbUsed <= cbTotal, paBuffers[idxBuffer].pBuf->mAudioDataByteSize = cbUsed = cbTotal);
     2176
     2177                cbWritable += cbTotal - cbUsed;
     2178
     2179                /* Advance. */
     2180                idxBuffer++;
     2181                if (idxBuffer < cBuffers)
     2182                { /* likely */ }
     2183                else
     2184                    idxBuffer = 0;
     2185            } while (idxBuffer != idxStart && !paBuffers[idxBuffer].fQueued);
     2186        }
     2187
     2188        RTCritSectLeave(&pStreamCA->CritSect);
     2189    }
     2190    Log2Func(("returns %#x for '%s'\n", cbWritable, pStreamCA->Cfg.szName));
     2191    return cbWritable;
     2192}
     2193
     2194
     2195/**
     2196 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
     2197 */
     2198static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostAudioCaHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
     2199                                                                             PPDMAUDIOBACKENDSTREAM pStream)
     2200{
     2201    RT_NOREF(pInterface);
     2202    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     2203    AssertPtrReturn(pStreamCA, PDMHOSTAUDIOSTREAMSTATE_INVALID);
     2204
     2205    if (ASMAtomicReadU32(&pStreamCA->enmInitState) == COREAUDIOINITSTATE_INIT)
     2206    {
     2207        if (!pStreamCA->fDraining)
     2208        { /* likely */ }
     2209        else
     2210        {
     2211            /*
     2212             * If we're draining, we're done when we've got all the buffers back.
     2213             */
     2214            RTCritSectEnter(&pStreamCA->CritSect);
     2215            PCOREAUDIOBUF const paBuffers = pStreamCA->paBuffers;
     2216            uintptr_t           idxBuffer = pStreamCA->cBuffers;
     2217            while (idxBuffer-- > 0)
     2218                if (!paBuffers[idxBuffer].fQueued)
     2219                { /* likely */ }
     2220                else
     2221                {
     2222#ifdef LOG_ENABLED
     2223                    uint32_t cQueued = 1;
     2224                    while (idxBuffer-- > 0)
     2225                        cQueued += paBuffers[idxBuffer].fQueued;
     2226                    LogFunc(("Still done draining '%s': %u queued buffers\n", pStreamCA->Cfg.szName, cQueued));
     2227#endif
     2228                    RTCritSectLeave(&pStreamCA->CritSect);
     2229                    return PDMHOSTAUDIOSTREAMSTATE_DRAINING;
     2230                }
     2231
     2232            LogFunc(("Done draining '%s'\n", pStreamCA->Cfg.szName));
     2233            pStreamCA->fDraining = false;
     2234            pStreamCA->fEnabled  = false;
     2235            RTCritSectLeave(&pStreamCA->CritSect);
     2236        }
     2237
     2238        return PDMHOSTAUDIOSTREAMSTATE_OKAY;
     2239    }
     2240    return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING; /** @todo ?? */
     2241}
     2242
     2243/**
     2244 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
     2245 */
     2246static DECLCALLBACK(int) drvHostAudioCaHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
     2247                                                     const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    20032248{
    20042249    RT_NOREF(pInterface);
    20052250    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
    20062251    AssertPtrReturn(pStreamCA, VERR_INVALID_POINTER);
    2007 
    2008 
    2009     uint32_t cbWritable = 0;
    2010     if (ASMAtomicReadU32(&pStreamCA->enmInitState) == COREAUDIOINITSTATE_INIT)
    2011     {
    2012         AssertPtr(pStreamCA->pCircBuf);
    2013 
    2014         if (pStreamCA->Cfg.enmDir == PDMAUDIODIR_OUT)
    2015             cbWritable = (uint32_t)RTCircBufFree(pStreamCA->pCircBuf);
    2016     }
    2017 
    2018     LogFlowFunc(("cbWritable=%RU32\n", cbWritable));
    2019     return cbWritable;
    2020 }
    2021 
    2022 
    2023 /**
    2024  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
    2025  */
    2026 static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostAudioCaHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
    2027                                                                              PPDMAUDIOBACKENDSTREAM pStream)
    2028 {
    2029     RT_NOREF(pInterface);
    2030     PCOREAUDIOSTREAM pStreamCa = (PCOREAUDIOSTREAM)pStream;
    2031     AssertPtrReturn(pStreamCa, PDMHOSTAUDIOSTREAMSTATE_INVALID);
    2032 
    2033     if (ASMAtomicReadU32(&pStreamCa->enmInitState) == COREAUDIOINITSTATE_INIT)
    2034         return PDMHOSTAUDIOSTREAMSTATE_OKAY;
    2035     return PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING; /** @todo ?? */
    2036 }
    2037 
    2038 /**
    2039  * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
    2040  */
    2041 static DECLCALLBACK(int) drvHostAudioCaHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    2042                                                      const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
    2043 {
    2044     PDRVHOSTCOREAUDIO pThis     = RT_FROM_MEMBER(pInterface, DRVHOSTCOREAUDIO, IHostAudio);
    2045     PCOREAUDIOSTREAM  pStreamCA = (PCOREAUDIOSTREAM)pStream;
    2046 
    2047     RT_NOREF(pThis);
    2048 
    2049     if (ASMAtomicReadU32(&pStreamCA->enmInitState) != COREAUDIOINITSTATE_INIT)
    2050     {
     2252    AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
     2253    if (cbBuf)
     2254        AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     2255    Assert(PDMAudioPropsIsSizeAligned(&pStreamCA->Cfg.Props, cbBuf));
     2256    AssertReturnStmt(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, *pcbWritten = 0, VERR_AUDIO_STREAM_NOT_READY);
     2257
     2258    RTCritSectEnter(&pStreamCA->CritSect);
     2259    if (pStreamCA->fEnabled)
     2260    { /* likely */ }
     2261    else
     2262    {
     2263        RTCritSectLeave(&pStreamCA->CritSect);
    20512264        *pcbWritten = 0;
     2265        LogFunc(("Skipping %#x byte write to disabled stream {%s}\n", cbBuf, drvHostAudioCaStreamStatusString(pStreamCA) ));
    20522266        return VINF_SUCCESS;
    20532267    }
    2054 
    2055     int rc = RTCritSectEnter(&pStreamCA->CritSect);
    2056     AssertRCReturn(rc, rc);
    2057 
    2058     size_t cbToWrite = RT_MIN(cbBuf, RTCircBufFree(pStreamCA->pCircBuf));
    2059     Log3Func(("cbToWrite=%zu\n", cbToWrite));
    2060 
    2061     uint32_t cbWrittenTotal = 0;
    2062     while (cbToWrite > 0)
    2063     {
    2064         /* Try to acquire the necessary space from the ring buffer. */
    2065         void    *pvChunk = NULL;
    2066         size_t   cbChunk = 0;
    2067         RTCircBufAcquireWriteBlock(pStreamCA->pCircBuf, cbToWrite, &pvChunk, &cbChunk);
    2068         AssertBreakStmt(cbChunk > 0, RTCircBufReleaseWriteBlock(pStreamCA->pCircBuf, cbChunk));
    2069 
    2070         Assert(cbChunk <= cbToWrite);
    2071         Assert(cbWrittenTotal + cbChunk <= cbBuf);
    2072 
    2073         memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
    2074 
    2075 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
    2076         RTFILE fh;
    2077         rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caPlayback.pcm",
    2078                         RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
    2079         if (RT_SUCCESS(rc))
    2080         {
    2081             RTFileWrite(fh, pvChunk, cbChunk, NULL);
    2082             RTFileClose(fh);
    2083         }
     2268    Log4Func(("cbBuf=%#x stream '%s' {%s}\n", cbBuf, pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2269
     2270    /*
     2271     * Transfer loop.
     2272     */
     2273    PCOREAUDIOBUF const paBuffers = pStreamCA->paBuffers;
     2274    uint32_t const      cBuffers  = pStreamCA->cBuffers;
     2275    AssertMsgReturnStmt(cBuffers >= COREAUDIO_MIN_BUFFERS && cBuffers < COREAUDIO_MAX_BUFFERS, ("%u\n", cBuffers),
     2276                        RTCritSectLeave(&pStreamCA->CritSect), VERR_AUDIO_STREAM_NOT_READY);
     2277
     2278    uint32_t            idxBuffer = pStreamCA->idxBuffer;
     2279    AssertStmt(idxBuffer < cBuffers, idxBuffer %= cBuffers);
     2280
     2281    int                 rc        = VINF_SUCCESS;
     2282    uint32_t            cbWritten = 0;
     2283    while (cbBuf > 0)
     2284    {
     2285        AssertBreakStmt(pStreamCA->hAudioQueue, rc = VERR_AUDIO_STREAM_NOT_READY);
     2286
     2287        /*
     2288         * Check out how much we can put into the current buffer.
     2289         */
     2290        if (!paBuffers[idxBuffer].fQueued)
     2291        { /* likely */ }
    20842292        else
    2085             AssertFailed();
     2293        {
     2294            LogFunc(("@%#RX64: Warning! Out of buffer space! (%#x bytes unwritten)\n", pStreamCA->offInternal, cbBuf));
     2295            /** @todo stats   */
     2296            break;
     2297        }
     2298
     2299        AudioQueueBufferRef const pBuf    = paBuffers[idxBuffer].pBuf;
     2300        AssertPtrBreakStmt(pBuf, rc = VERR_INTERNAL_ERROR_2);
     2301        uint32_t const            cbTotal = pBuf->mAudioDataBytesCapacity;
     2302        uint32_t                  cbUsed  = pBuf->mAudioDataByteSize;
     2303        AssertStmt(cbUsed < cbTotal, cbUsed = cbTotal);
     2304        uint32_t const            cbAvail = cbTotal - cbUsed;
     2305
     2306        /*
     2307         * Copy over the data.
     2308         */
     2309        if (cbBuf < cbAvail)
     2310        {
     2311            Log3Func(("@%#RX64: buffer #%u/%u: %#x bytes, have %#x only - leaving unqueued {%s}\n",
     2312                      pStreamCA->offInternal, idxBuffer, cBuffers, cbAvail, cbBuf, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2313            memcpy((uint8_t *)pBuf->mAudioData + cbUsed, pvBuf, cbBuf);
     2314            pBuf->mAudioDataByteSize = cbUsed + cbBuf;
     2315            cbWritten               += cbBuf;
     2316            pStreamCA->offInternal  += cbBuf;
     2317            /** @todo Maybe queue it anyway if it's almost full or we haven't got a lot of
     2318             *        buffers queued. */
     2319            break;
     2320        }
     2321
     2322        Log3Func(("@%#RX64: buffer #%u/%u: %#x bytes, have %#x - will queue {%s}\n",
     2323                  pStreamCA->offInternal, idxBuffer, cBuffers, cbAvail, cbBuf, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2324        memcpy((uint8_t *)pBuf->mAudioData + cbUsed, pvBuf, cbAvail);
     2325        pBuf->mAudioDataByteSize = cbTotal;
     2326        cbWritten               += cbAvail;
     2327        pStreamCA->offInternal  += cbAvail;
     2328        ASMAtomicWriteBool(&paBuffers[idxBuffer].fQueued, true);
     2329
     2330        OSStatus orc = AudioQueueEnqueueBuffer(pStreamCA->hAudioQueue, pBuf, 0 /*inNumPacketDesc*/, NULL /*inPacketDescs*/);
     2331        if (orc == noErr)
     2332        { /* likely */ }
     2333        else
     2334        {
     2335            LogRelMax(256, ("CoreAudio: AudioQueueEnqueueBuffer('%s', #%u) failed: %#x (%d)\n",
     2336                            pStreamCA->Cfg.szName, idxBuffer, orc, orc));
     2337            ASMAtomicWriteBool(&paBuffers[idxBuffer].fQueued, false);
     2338            pBuf->mAudioDataByteSize -= PDMAudioPropsFramesToBytes(&pStreamCA->Cfg.Props, 1); /* avoid assertions above */
     2339            rc = VERR_AUDIO_STREAM_NOT_READY;
     2340            break;
     2341        }
     2342
     2343        /*
     2344         * Advance.
     2345         */
     2346        idxBuffer += 1;
     2347        if (idxBuffer < cBuffers)
     2348        { /* likely */ }
     2349        else
     2350            idxBuffer = 0;
     2351        pStreamCA->idxBuffer = idxBuffer;
     2352
     2353        pvBuf  = (const uint8_t *)pvBuf + cbAvail;
     2354        cbBuf -= cbAvail;
     2355    }
     2356
     2357    /*
     2358     * Start the stream if we haven't do so yet.
     2359     */
     2360    if (   pStreamCA->fStarted
     2361        || cbWritten == 0
     2362        || RT_FAILURE_NP(rc))
     2363    { /* likely */ }
     2364    else
     2365    {
     2366        UInt32   cFramesPrepared = 0;
     2367#if 0 /* taking too long? */
     2368        OSStatus orc = AudioQueuePrime(pStreamCA->hAudioQueue, 0 /*inNumberOfFramesToPrepare*/, &cFramesPrepared);
     2369        LogFlowFunc(("AudioQueuePrime(%s, 0,) returns %#x (%d) and cFramesPrepared=%u (offInternal=%#RX64)\n",
     2370                     pStreamCA->Cfg.szName, orc, orc, cFramesPrepared, pStreamCA->offInternal));
     2371        AssertMsg(orc == noErr, ("%#x (%d)\n", orc, orc));
     2372#else
     2373        OSStatus orc;
    20862374#endif
    2087 
    2088         /* Release the ring buffer, so the read thread could start reading this data. */
    2089         RTCircBufReleaseWriteBlock(pStreamCA->pCircBuf, cbChunk);
    2090 
    2091         if (RT_FAILURE(rc))
    2092             break;
    2093 
    2094         Assert(cbToWrite >= cbChunk);
    2095         cbToWrite      -= cbChunk;
    2096 
    2097         cbWrittenTotal += cbChunk;
    2098     }
    2099 
    2100     if (    RT_SUCCESS(rc)
    2101         &&  pStreamCA->fRun
    2102         && !pStreamCA->fIsRunning)
    2103     {
    2104         rc = drvHostAudioCaStreamInvalidateQueue(pStreamCA);
    2105         if (RT_SUCCESS(rc))
    2106         {
    2107             AudioQueueStart(pStreamCA->hAudioQueue, NULL);
    2108             pStreamCA->fRun       = false;
    2109             pStreamCA->fIsRunning = true;
    2110         }
    2111     }
    2112 
    2113     int rc2 = RTCritSectLeave(&pStreamCA->CritSect);
    2114     AssertRC(rc2);
    2115 
    2116     *pcbWritten = cbWrittenTotal;
     2375        orc = AudioQueueStart(pStreamCA->hAudioQueue, NULL /*inStartTime*/);
     2376        LogFunc(("AudioQueueStart(%s, NULL) returns %#x (%d)\n", pStreamCA->Cfg.szName, orc, orc));
     2377        if (orc == noErr)
     2378            pStreamCA->fStarted = true;
     2379        else
     2380        {
     2381            LogRelMax(128, ("CoreAudio: Starting '%s' failed: %#x (%d) - %u frames primed, %#x bytes queued\n",
     2382                            pStreamCA->Cfg.szName, orc, orc, cFramesPrepared, pStreamCA->offInternal));
     2383            rc = VERR_AUDIO_STREAM_NOT_READY;
     2384        }
     2385    }
     2386
     2387    /*
     2388     * Done.
     2389     */
     2390#ifdef LOG_ENABLED
     2391    uint64_t const msPrev = pStreamCA->msLastTransfer;
     2392#endif
     2393    uint64_t const msNow  = RTTimeMilliTS();
     2394    if (cbWritten)
     2395        pStreamCA->msLastTransfer = msNow;
     2396
     2397    RTCritSectLeave(&pStreamCA->CritSect);
     2398
     2399    *pcbWritten = cbWritten;
     2400    if (RT_SUCCESS(rc) || !cbWritten)
     2401    { }
     2402    else
     2403    {
     2404        LogFlowFunc(("Suppressing %Rrc to report %#x bytes written\n", rc, cbWritten));
     2405        rc = VINF_SUCCESS;
     2406    }
     2407    LogFlowFunc(("@%#RX64: rc=%Rrc cbWritten=%RU32 cMsDelta=%RU64 (%RU64 -> %RU64) {%s}\n", pStreamCA->offInternal, rc, cbWritten,
     2408                 msPrev ? msNow - msPrev : 0, msPrev, pStreamCA->msLastTransfer, drvHostAudioCaStreamStatusString(pStreamCA) ));
    21172409    return rc;
    21182410}
     
    21262418{
    21272419    RT_NOREF(pInterface);
    2128     PCOREAUDIOSTREAM  pStreamCA = (PCOREAUDIOSTREAM)pStream;
    2129     AssertPtrReturn(pStreamCA, VERR_INVALID_POINTER);
     2420    PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream;
     2421    AssertPtrReturn(pStreamCA, 0);
     2422    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     2423    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
    21302424    AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
    2131 
    2132 
    2133     if (ASMAtomicReadU32(&pStreamCA->enmInitState) != COREAUDIOINITSTATE_INIT)
    2134     {
     2425    Assert(PDMAudioPropsIsSizeAligned(&pStreamCA->Cfg.Props, cbBuf));
     2426    AssertReturnStmt(pStreamCA->enmInitState == COREAUDIOINITSTATE_INIT, *pcbRead = 0, VERR_AUDIO_STREAM_NOT_READY);
     2427
     2428    RTCritSectEnter(&pStreamCA->CritSect);
     2429    if (pStreamCA->fEnabled)
     2430    { /* likely */ }
     2431    else
     2432    {
     2433        RTCritSectLeave(&pStreamCA->CritSect);
    21352434        *pcbRead = 0;
     2435        LogFunc(("Skipping %#x byte read from disabled stream {%s}\n", cbBuf, drvHostAudioCaStreamStatusString(pStreamCA)));
    21362436        return VINF_SUCCESS;
    21372437    }
    2138 
    2139     int rc = RTCritSectEnter(&pStreamCA->CritSect);
    2140     AssertRCReturn(rc, rc);
    2141 
    2142     size_t cbToWrite = RT_MIN(cbBuf, RTCircBufUsed(pStreamCA->pCircBuf));
    2143     Log3Func(("cbToWrite=%zu/%zu\n", cbToWrite, RTCircBufSize(pStreamCA->pCircBuf)));
    2144 
    2145     uint32_t cbReadTotal = 0;
    2146     while (cbToWrite > 0)
    2147     {
    2148         void    *pvChunk = NULL;
    2149         size_t   cbChunk = 0;
    2150         RTCircBufAcquireReadBlock(pStreamCA->pCircBuf, cbToWrite, &pvChunk, &cbChunk);
    2151 
    2152         AssertStmt(cbChunk <= cbToWrite, cbChunk = cbToWrite);
    2153         memcpy((uint8_t *)pvBuf + cbReadTotal, pvChunk, cbChunk);
    2154 
    2155         RTCircBufReleaseReadBlock(pStreamCA->pCircBuf, cbChunk);
    2156 
    2157         cbToWrite      -= cbChunk;
    2158         cbReadTotal    += cbChunk;
    2159     }
    2160 
    2161     *pcbRead = cbReadTotal;
     2438    Log4Func(("cbBuf=%#x stream '%s' {%s}\n", cbBuf, pStreamCA->Cfg.szName, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2439
     2440
     2441    /*
     2442     * Transfer loop.
     2443     */
     2444    uint32_t const      cbFrame   = PDMAudioPropsFrameSize(&pStreamCA->Cfg.Props);
     2445    PCOREAUDIOBUF const paBuffers = pStreamCA->paBuffers;
     2446    uint32_t const      cBuffers  = pStreamCA->cBuffers;
     2447    AssertMsgReturnStmt(cBuffers >= COREAUDIO_MIN_BUFFERS && cBuffers < COREAUDIO_MAX_BUFFERS, ("%u\n", cBuffers),
     2448                        RTCritSectLeave(&pStreamCA->CritSect), VERR_AUDIO_STREAM_NOT_READY);
     2449
     2450    uint32_t            idxBuffer = pStreamCA->idxBuffer;
     2451    AssertStmt(idxBuffer < cBuffers, idxBuffer %= cBuffers);
     2452
     2453    int                 rc        = VINF_SUCCESS;
     2454    uint32_t            cbRead    = 0;
     2455    while (cbBuf > cbFrame)
     2456    {
     2457        AssertBreakStmt(pStreamCA->hAudioQueue, rc = VERR_AUDIO_STREAM_NOT_READY);
     2458
     2459        /*
     2460         * Check out how much we can read from the current buffer (if anything at all).
     2461         */
     2462        if (!paBuffers[idxBuffer].fQueued)
     2463        { /* likely */ }
     2464        else
     2465        {
     2466            LogFunc(("@%#RX64: Warning! Underrun! (%#x bytes unread)\n", pStreamCA->offInternal, cbBuf));
     2467            /** @todo stats   */
     2468            break;
     2469        }
     2470
     2471        AudioQueueBufferRef const pBuf    = paBuffers[idxBuffer].pBuf;
     2472        AssertPtrBreakStmt(pBuf, rc = VERR_INTERNAL_ERROR_2);
     2473        uint32_t const            cbTotal = pBuf->mAudioDataBytesCapacity;
     2474        uint32_t                  cbValid = pBuf->mAudioDataByteSize;
     2475        AssertStmt(cbValid < cbTotal, cbValid = cbTotal);
     2476        uint32_t                  offRead = paBuffers[idxBuffer].offRead;
     2477        uint32_t const            cbLeft  = cbValid - offRead;
     2478
     2479        /*
     2480         * Copy over the data.
     2481         */
     2482        if (cbBuf < cbLeft)
     2483        {
     2484            Log3Func(("@%#RX64: buffer #%u/%u: %#x bytes, want %#x - leaving unqueued {%s}\n",
     2485                      pStreamCA->offInternal, idxBuffer, cBuffers, cbLeft, cbBuf, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2486            memcpy(pvBuf, (uint8_t const *)pBuf->mAudioData + offRead, cbBuf);
     2487            paBuffers[idxBuffer].offRead = offRead + cbBuf;
     2488            cbRead                      += cbBuf;
     2489            pStreamCA->offInternal      += cbBuf;
     2490            break;
     2491        }
     2492
     2493        Log3Func(("@%#RX64: buffer #%u/%u: %#x bytes, want all (%#x) - will queue {%s}\n",
     2494                  pStreamCA->offInternal, idxBuffer, cBuffers, cbLeft, cbBuf, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2495        memcpy(pvBuf, (uint8_t const *)pBuf->mAudioData + offRead, cbLeft);
     2496        cbRead                  += cbLeft;
     2497        pStreamCA->offInternal  += cbLeft;
     2498
     2499        RT_BZERO(pBuf->mAudioData, cbTotal); /* paranoia */
     2500        paBuffers[idxBuffer].offRead = 0;
     2501        pBuf->mAudioDataByteSize     = 0;
     2502        ASMAtomicWriteBool(&paBuffers[idxBuffer].fQueued, true);
     2503
     2504        OSStatus orc = AudioQueueEnqueueBuffer(pStreamCA->hAudioQueue, pBuf, 0 /*inNumPacketDesc*/, NULL /*inPacketDescs*/);
     2505        if (orc == noErr)
     2506        { /* likely */ }
     2507        else
     2508        {
     2509            LogRelMax(256, ("CoreAudio: AudioQueueEnqueueBuffer('%s', #%u) failed: %#x (%d)\n",
     2510                            pStreamCA->Cfg.szName, idxBuffer, orc, orc));
     2511            ASMAtomicWriteBool(&paBuffers[idxBuffer].fQueued, false);
     2512            rc = VERR_AUDIO_STREAM_NOT_READY;
     2513            break;
     2514        }
     2515
     2516        /*
     2517         * Advance.
     2518         */
     2519        idxBuffer += 1;
     2520        if (idxBuffer < cBuffers)
     2521        { /* likely */ }
     2522        else
     2523            idxBuffer = 0;
     2524        pStreamCA->idxBuffer = idxBuffer;
     2525
     2526        pvBuf  = (uint8_t *)pvBuf + cbLeft;
     2527        cbBuf -= cbLeft;
     2528    }
     2529
     2530    /*
     2531     * Done.
     2532     */
     2533#ifdef LOG_ENABLED
     2534    uint64_t const msPrev = pStreamCA->msLastTransfer;
     2535#endif
     2536    uint64_t const msNow  = RTTimeMilliTS();
     2537    if (cbRead)
     2538        pStreamCA->msLastTransfer = msNow;
    21622539
    21632540    RTCritSectLeave(&pStreamCA->CritSect);
    2164     return VINF_SUCCESS;
     2541
     2542    *pcbRead = cbRead;
     2543    if (RT_SUCCESS(rc) || !cbRead)
     2544    { }
     2545    else
     2546    {
     2547        LogFlowFunc(("Suppressing %Rrc to report %#x bytes read\n", rc, cbRead));
     2548        rc = VINF_SUCCESS;
     2549    }
     2550    LogFlowFunc(("@%#RX64: rc=%Rrc cbRead=%RU32 cMsDelta=%RU64 (%RU64 -> %RU64) {%s}\n", pStreamCA->offInternal, rc, cbRead,
     2551                 msPrev ? msNow - msPrev : 0, msPrev, pStreamCA->msLastTransfer, drvHostAudioCaStreamStatusString(pStreamCA) ));
     2552    return rc;
    21652553}
    21662554
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette