Changeset 89132 in vbox
- Timestamp:
- May 17, 2021 11:58:37 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioCoreAudio.cpp
r89055 r89132 65 65 * - Maybe make sure the threads are immediately stopped if playing/recording stops. 66 66 */ 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 67 75 68 76 … … 142 150 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 143 151 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 */ 161 typedef 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. */ 171 typedef COREAUDIOBUF *PCOREAUDIOBUF; 172 144 173 145 174 /** … … 173 202 /** List node for the device's stream list. */ 174 203 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 be191 * better off with a variable buffer count using the device's interval192 * hint for the size (though we should at least have two buffers ofc). */193 AudioQueueBufferRef apAudioBuffers[2];194 204 /** The acquired (final) audio format for this stream. 195 205 * @note This what the device requests, we don't alter anything. */ 196 206 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 197 215 /** The audio unit for this stream. */ 198 216 struct … … 216 234 * during the runtime. */ 217 235 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 220 253 /** Critical section for serializing access between thread + callbacks. */ 221 254 RTCRITSECT CritSect; 255 /** Buffer that drvHostAudioCaStreamStatusString uses. */ 256 char szStatus[64]; 222 257 } COREAUDIOSTREAM; 223 258 … … 265 300 * Internal Functions * 266 301 *********************************************************************************************************************************/ 267 static int drvHostAudioCaStreamControlInternal(PCOREAUDIOSTREAM pStreamCA, PDMAUDIOSTREAMCMD enmStreamCmd);268 269 302 /* DrvHostAudioCoreAudioAuth.mm: */ 270 303 DECLHIDDEN(int) coreAudioInputPermissionCheck(void); 271 304 272 305 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 */ 313 static 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*/ 273 360 274 361 … … 715 802 716 803 /********************************************************************************************************************************* 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 else764 {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. The792 * circular buffer can deal with concurrent access. Might help with793 * some life-cycle issues, but that should be serialized by the794 * thread destruction. Only would be concurrent calls to795 * 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 else849 {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 Audio895 * 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 else917 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 signal939 * our creator to say that we're done. The runloop reference is the940 * 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 else958 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 else967 LogRel(("CoreAudio: Failed to associate device with queue: %#x (%d)\n", orc, orc));968 969 AudioQueueDispose(pStreamCA->hAudioQueue, TRUE /*inImmediate*/);970 }971 else972 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 else1004 {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 /*********************************************************************************************************************************1017 804 * PDMIHOSTAUDIO * 1018 805 *********************************************************************************************************************************/ … … 1692 1479 1693 1480 /** 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 */ 1493 static 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 */ 1525 static 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 /** 1694 1545 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 1695 1546 */ … … 1704 1555 AssertReturn(pCfgReq->enmDir == PDMAUDIODIR_IN || pCfgReq->enmDir == PDMAUDIODIR_OUT, VERR_INVALID_PARAMETER); 1705 1556 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. */ 1706 1562 1707 1563 /* … … 1730 1586 * Basic structure init. 1731 1587 */ 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; 1738 1596 1739 1597 rc = RTCritSectInit(&pStreamCA->CritSect); … … 1750 1608 ? "Capturing queue format" 1751 1609 : "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) 1756 1624 { 1757 1625 /* 1758 * Start the thread.1626 * Assign device to the queue. 1759 1627 */ 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) 1766 1631 { 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) 1770 1651 { 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 } 1775 1658 } 1659 else 1660 { 1661 cQueueBuffers = COREAUDIO_MIN_BUFFERS; 1662 cFramesQueueBuffer = cFramesBufferSize / COREAUDIO_MIN_BUFFERS; 1663 } 1664 1665 cFramesBufferSize = cQueueBuffers * cFramesBufferSize; 1776 1666 1777 1667 /* 1778 * Failed, clean up.1668 * Allocate the audio queue buffers. 1779 1669 */ 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; 1789 1723 } 1790 1724 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*/); 1794 1727 } 1795 1728 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)); 1797 1730 RTCritSectDelete(&pStreamCA->CritSect); 1798 1731 } … … 1802 1735 else 1803 1736 { 1804 Log Func(("No device for stream.\n"));1737 LogRelMax(64, ("CoreAudio: No device for %s stream.\n", PDMAudioDirGetName(pCfgReq->enmDir))); 1805 1738 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE; 1806 1739 } … … 1830 1763 1831 1764 /* 1832 * Disable (stop) the stream just in case it's running.1765 * Change the stream state and stop the stream (just to be sure). 1833 1766 */ 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 } 1837 1774 1838 1775 /* 1839 * Change the state (cannot do before the stop).1840 1776 * Enter and leave the critsect afterwards for paranoid reasons. 1841 1777 */ 1842 ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_IN_UNINIT);1843 1778 RTCritSectEnter(&pStreamCA->CritSect); 1844 1779 RTCritSectLeave(&pStreamCA->CritSect); 1845 1780 1846 1781 /* 1847 * Bring down the queue thread.1782 * Free the queue buffers and the queue. 1848 1783 */ 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++) 1856 1788 { 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; 1865 1792 } 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; 1875 1805 } 1876 1806 1877 1807 /* 1878 * Kill the circular buffer and NULL essential variable.1808 * Release the device and delete the critsect. 1879 1809 */ 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! */ 1887 1811 1888 1812 RTCritSectDelete(&pStreamCA->CritSect); … … 1893 1817 ASMAtomicWriteU32(&pStreamCA->enmInitState, COREAUDIOINITSTATE_UNINIT); 1894 1818 } 1819 else 1820 LogFunc(("Wrong stream init state for %p: %d - leaking it\n", pStream, enmInitState)); 1895 1821 1896 1822 LogFunc(("returns\n")); … … 1899 1825 1900 1826 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 */ 1830 static 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 */ 1912 1861 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 */ 1901 static 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 */ 1941 static 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 */ 1987 static 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 */ 2020 static 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 */ 2069 static 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). */ 1913 2076 switch (enmStreamCmd) 1914 2077 { 1915 2078 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); 1916 2084 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*/ 1934 2093 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 */ 2102 static DECLCALLBACK(uint32_t) drvHostAudioCaHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1968 2103 { 1969 2104 RT_NOREF(pInterface); 1970 2105 PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream; 1971 2106 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 */ 2151 static DECLCALLBACK(uint32_t) drvHostAudioCaHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) 1981 2152 { 1982 2153 RT_NOREF(pInterface); 1983 2154 PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream; 1984 2155 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 */ 2198 static 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 */ 2246 static DECLCALLBACK(int) drvHostAudioCaHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 2247 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2003 2248 { 2004 2249 RT_NOREF(pInterface); 2005 2250 PCOREAUDIOSTREAM pStreamCA = (PCOREAUDIOSTREAM)pStream; 2006 2251 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); 2051 2264 *pcbWritten = 0; 2265 LogFunc(("Skipping %#x byte write to disabled stream {%s}\n", cbBuf, drvHostAudioCaStreamStatusString(pStreamCA) )); 2052 2266 return VINF_SUCCESS; 2053 2267 } 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 */ } 2084 2292 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; 2086 2374 #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) )); 2117 2409 return rc; 2118 2410 } … … 2126 2418 { 2127 2419 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); 2130 2424 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); 2135 2434 *pcbRead = 0; 2435 LogFunc(("Skipping %#x byte read from disabled stream {%s}\n", cbBuf, drvHostAudioCaStreamStatusString(pStreamCA))); 2136 2436 return VINF_SUCCESS; 2137 2437 } 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; 2162 2539 2163 2540 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; 2165 2553 } 2166 2554
Note:
See TracChangeset
for help on using the changeset viewer.