Changeset 88720 in vbox for trunk/src/VBox
- Timestamp:
- Apr 26, 2021 10:46:21 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 144038
- Location:
- trunk/src/VBox/Devices/Audio
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified trunk/src/VBox/Devices/Audio/AudioMixer.cpp ¶
r88452 r88720 1830 1830 space available, this should always write the whole buffer. */ 1831 1831 uint32_t cbDstWritten = 0; 1832 int rc2 = pMixStream->pConn->pfnStream Write(pMixStream->pConn, pMixStream->pStream,1833 1832 int rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream, 1833 &Buf, cbDstPeeked, &cbDstWritten); 1834 1834 Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame, 1835 1835 cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName)); … … 1876 1876 1877 1877 /* 1878 * Iterate buffers (pfnStreamPlay is not used any more).1878 * Iterate buffers. 1879 1879 */ 1880 1880 RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node) -
TabularUnified trunk/src/VBox/Devices/Audio/DrvAudio.cpp ¶
r88719 r88720 541 541 AssertFailed(); 542 542 #endif 543 }544 545 546 /**547 * The no-mixing-buffers worker for drvAudioStreamWrite and548 * drvAudioStreamIterateInternal.549 *550 * The buffer is NULL and has a zero length when called from the interate551 * function. This only occures when there is pre-buffered audio data that need552 * to be pushed to the backend due to a pending disabling of the stream.553 *554 * Caller owns the lock.555 */556 static int drvAudioStreamWriteNoMixBufs(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx,557 const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten)558 {559 Log3Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbBuf));560 561 /*562 * Are we pre-buffering?563 *564 * Note! We do not restart pre-buffering in this version, as we'd565 * need some kind of cooperation with the backend buffer566 * managment to correctly detect an underrun.567 */568 bool fJustStarted = false;569 uint32_t cbWritten = 0;570 int rc;571 if ( pStreamEx->fThresholdReached572 && pStreamEx->Out.cbPreBuffered == 0)573 {574 /* not-prebuffering, likely after a while at least */575 rc = VINF_SUCCESS;576 }577 else578 {579 /*580 * Copy as much as we can to the pre-buffer.581 */582 uint32_t cbFree = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered;583 AssertReturn((int32_t)cbFree >= 0, VERR_INTERNAL_ERROR_2);584 if (cbFree > 0 && cbBuf > 0)585 {586 cbWritten = RT_MIN(cbFree, cbBuf);587 cbWritten = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, cbWritten);588 memcpy(&pStreamEx->Out.pbPreBuf[pStreamEx->Out.cbPreBuffered], pbBuf, cbWritten);589 pStreamEx->Out.cbPreBuffered += cbWritten;590 cbBuf -= cbWritten;591 pbBuf += cbWritten;592 pStreamEx->offInternal += cbWritten;593 }594 595 /*596 * Get the special case of buggy backend drivers out of the way.597 * We get here if we couldn't write out all the pre-buffered data when598 * we hit the threshold.599 */600 if (pStreamEx->fThresholdReached)601 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: cbBuf=%#x cbPreBuffered=%#x\n",602 pStreamEx->offInternal, pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered));603 /*604 * Did we reach the backend's playback (pre-buffering) threshold?605 * Can be 0 if no pre-buffering desired.606 */607 else if (pStreamEx->Out.cbPreBuffered + cbBuf >= pStreamEx->Out.cbPreBufThreshold)608 {609 LogRel2(("Audio: @%#RX64: Stream '%s' buffering complete! (%#x + %#x bytes)\n",610 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered, cbBuf));611 pStreamEx->fThresholdReached = fJustStarted = true;612 }613 /*614 * Some audio files are shorter than the pre-buffering level (e.g. the615 * "click" Explorer sounds on some Windows guests), so make sure that we616 * also play those by checking if the stream already is pending disable617 * mode, even if we didn't hit the pre-buffering watermark yet.618 *619 * Try play "Windows Navigation Start.wav" on Windows 7 (2824 samples).620 */621 else if ( (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE)622 && pStreamEx->Out.cbPreBuffered > 0)623 {624 LogRel2(("Audio: @%#RX64: Stream '%s' buffering complete - short sound! (%#x + %#x bytes)\n",625 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered, cbBuf));626 pStreamEx->fThresholdReached = fJustStarted = true;627 }628 /*629 * Not yet, so still buffering audio data.630 */631 else632 {633 LogRel2(("Audio: @%#RX64: Stream '%s' is buffering (%RU8%% complete)...\n", pStreamEx->offInternal,634 pStreamEx->Core.szName, (100 * pStreamEx->Out.cbPreBuffered) / pStreamEx->Out.cbPreBufThreshold));635 Assert(cbBuf == 0);636 *pcbWritten = cbWritten;637 return VINF_SUCCESS;638 }639 640 /*641 * Write the pre-buffered chunk.642 */643 uint32_t off = 0;644 uint32_t cbPreBufWritten;645 do646 {647 cbPreBufWritten = 0;648 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, &pStreamEx->Out.pbPreBuf[off],649 pStreamEx->Out.cbPreBuffered - off, &cbPreBufWritten);650 AssertRCBreak(rc);651 off += cbPreBufWritten;652 } while (off < pStreamEx->Out.cbPreBuffered && cbPreBufWritten != 0);653 654 if (off >= pStreamEx->Out.cbPreBuffered)655 {656 Assert(off == pStreamEx->Out.cbPreBuffered);657 LogFunc(("@%#RX64: Wrote all %#x bytes of pre-buffered audio data.\n", pStreamEx->offInternal, off));658 pStreamEx->Out.cbPreBuffered = 0;659 }660 else661 {662 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: wrote %#x out of %#x + %#x%s - rc=%Rrc *pcbWritten=%#x\n",663 pStreamEx->offInternal, pStreamEx->Core.szName, off, pStreamEx->Out.cbPreBuffered, cbBuf,664 fJustStarted ? " (just started)" : "", rc, cbWritten));665 AssertMsg(!fJustStarted || RT_FAILURE(rc),666 ("Buggy host driver buffer reporting: off=%#x cbPreBuffered=%#x\n", off, pStreamEx->Out.cbPreBuffered));667 if (off > 0)668 {669 memmove(pStreamEx->Out.pbPreBuf, &pStreamEx->Out.pbPreBuf[off], pStreamEx->Out.cbPreBuffered - off);670 pStreamEx->Out.cbPreBuffered -= off;671 }672 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();673 *pcbWritten = cbWritten;674 return cbWritten ? VINF_SUCCESS : rc;675 }676 677 if (RT_FAILURE(rc))678 {679 *pcbWritten = cbWritten;680 return rc;681 }682 }683 684 /*685 * Do the writing.686 */687 uint32_t cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);688 pStreamEx->Out.Stats.cbBackendWritableBefore = cbWritable;689 690 uint8_t const cbFrame = PDMAudioPropsFrameSize(&pStreamEx->Core.Props);691 while (cbBuf >= cbFrame && cbWritable >= cbFrame)692 {693 uint32_t const cbToWrite = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, RT_MIN(cbBuf, cbWritable));694 uint32_t cbWrittenNow = 0;695 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToWrite, &cbWrittenNow);696 if (RT_SUCCESS(rc))697 {698 if (cbWrittenNow != cbToWrite)699 Log3Func(("%s: @%#RX64: Wrote less bytes than requested: %#x, requested %#x\n",700 pStreamEx->Core.szName, pStreamEx->offInternal, cbWrittenNow, cbToWrite));701 #ifdef DEBUG_bird702 Assert(cbWrittenNow == cbToWrite);703 #endif704 AssertStmt(cbWrittenNow <= cbToWrite, cbWrittenNow = cbToWrite);705 cbWritten += cbWrittenNow;706 cbBuf -= cbWrittenNow;707 pbBuf += cbWrittenNow;708 pStreamEx->offInternal += cbWrittenNow;709 }710 else711 {712 *pcbWritten = cbWritten;713 LogFunc(("%s: @%#RX64: pfnStreamPlay failed writing %#x bytes (%#x previous written, %#x writable): %Rrc\n",714 pStreamEx->Core.szName, pStreamEx->offInternal, cbToWrite, cbWritten, cbWritable, rc));715 return cbWritten ? VINF_SUCCESS : rc;716 }717 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend);718 }719 720 *pcbWritten = cbWritten;721 pStreamEx->Out.Stats.cbBackendWritableAfter = cbWritable;722 if (cbWritten)723 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS();724 725 Log3Func(("%s: @%#RX64: Wrote %#x bytes (%#x bytes left)\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbWritten, cbBuf));726 return rc;727 543 } 728 544 … … 2272 2088 2273 2089 /** 2274 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead} 2275 */ 2276 static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, 2277 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead) 2278 { 2279 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector); 2280 AssertPtr(pThis); 2281 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream; 2282 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER); 2283 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2284 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 2285 AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER); 2286 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2287 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2288 AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN, 2289 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n", 2290 pStreamEx->Core.szName, pStreamEx->Core.enmDir)); 2291 2292 int rc = RTCritSectEnter(&pThis->CritSect); 2293 AssertRCReturn(rc, rc); 2294 2295 /* 2296 * ... 2297 */ 2298 uint32_t cbReadTotal = 0; 2299 2300 do 2301 { 2302 uint32_t cfReadTotal = 0; 2303 2304 const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf); 2305 2306 if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */ 2307 { 2308 if (!PDMAudioStrmStatusCanRead(pStream->fStatus)) 2309 { 2310 rc = VERR_AUDIO_STREAM_NOT_READY; 2311 break; 2312 } 2313 2314 /* 2315 * Read from the parent buffer (that is, the guest buffer) which 2316 * should have the audio data in the format the guest needs. 2317 */ 2318 uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf)); 2319 while (cfToRead) 2320 { 2321 uint32_t cfRead; 2322 rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf, 2323 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 2324 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead); 2325 if (RT_FAILURE(rc)) 2326 break; 2327 2328 #ifdef VBOX_WITH_STATISTICS 2329 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead); 2330 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, cbRead); 2331 STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead); 2332 STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead); 2333 #endif 2334 Assert(cfToRead >= cfRead); 2335 cfToRead -= cfRead; 2336 2337 cfReadTotal += cfRead; 2338 2339 AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead); 2340 } 2341 2342 if (cfReadTotal) 2343 { 2344 if (pThis->In.Cfg.Dbg.fEnabled) 2345 AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead, 2346 pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */); 2347 2348 AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal); 2349 } 2350 } 2351 2352 /* If we were not able to read as much data as requested, fill up the returned 2353 * data with silence. 2090 * The no-mixing-buffers worker for drvAudioStreamWrite and 2091 * drvAudioStreamIterateInternal. 2092 * 2093 * The buffer is NULL and has a zero length when called from the interate 2094 * function. This only occures when there is pre-buffered audio data that need 2095 * to be pushed to the backend due to a pending disabling of the stream. 2096 * 2097 * Caller owns the lock. 2098 */ 2099 static int drvAudioStreamWriteNoMixBufs(PDRVAUDIO pThis, PDRVAUDIOSTREAM pStreamEx, 2100 const uint8_t *pbBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2101 { 2102 Log3Func(("%s: @%#RX64: cbBuf=%#x\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbBuf)); 2103 2104 /* 2105 * Are we pre-buffering? 2106 * 2107 * Note! We do not restart pre-buffering in this version, as we'd 2108 * need some kind of cooperation with the backend buffer 2109 * managment to correctly detect an underrun. 2110 */ 2111 bool fJustStarted = false; 2112 uint32_t cbWritten = 0; 2113 int rc; 2114 if ( pStreamEx->fThresholdReached 2115 && pStreamEx->Out.cbPreBuffered == 0) 2116 { 2117 /* not-prebuffering, likely after a while at least */ 2118 rc = VINF_SUCCESS; 2119 } 2120 else 2121 { 2122 /* 2123 * Copy as much as we can to the pre-buffer. 2124 */ 2125 uint32_t cbFree = pStreamEx->Out.cbPreBufAlloc - pStreamEx->Out.cbPreBuffered; 2126 AssertReturn((int32_t)cbFree >= 0, VERR_INTERNAL_ERROR_2); 2127 if (cbFree > 0 && cbBuf > 0) 2128 { 2129 cbWritten = RT_MIN(cbFree, cbBuf); 2130 cbWritten = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, cbWritten); 2131 memcpy(&pStreamEx->Out.pbPreBuf[pStreamEx->Out.cbPreBuffered], pbBuf, cbWritten); 2132 pStreamEx->Out.cbPreBuffered += cbWritten; 2133 cbBuf -= cbWritten; 2134 pbBuf += cbWritten; 2135 pStreamEx->offInternal += cbWritten; 2136 } 2137 2138 /* 2139 * Get the special case of buggy backend drivers out of the way. 2140 * We get here if we couldn't write out all the pre-buffered data when 2141 * we hit the threshold. 2142 */ 2143 if (pStreamEx->fThresholdReached) 2144 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: cbBuf=%#x cbPreBuffered=%#x\n", 2145 pStreamEx->offInternal, pStreamEx->Core.szName, cbBuf, pStreamEx->Out.cbPreBuffered)); 2146 /* 2147 * Did we reach the backend's playback (pre-buffering) threshold? 2148 * Can be 0 if no pre-buffering desired. 2149 */ 2150 else if (pStreamEx->Out.cbPreBuffered + cbBuf >= pStreamEx->Out.cbPreBufThreshold) 2151 { 2152 LogRel2(("Audio: @%#RX64: Stream '%s' buffering complete! (%#x + %#x bytes)\n", 2153 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered, cbBuf)); 2154 pStreamEx->fThresholdReached = fJustStarted = true; 2155 } 2156 /* 2157 * Some audio files are shorter than the pre-buffering level (e.g. the 2158 * "click" Explorer sounds on some Windows guests), so make sure that we 2159 * also play those by checking if the stream already is pending disable 2160 * mode, even if we didn't hit the pre-buffering watermark yet. 2354 2161 * 2355 * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */ 2356 if (cfReadTotal < cfBuf) 2357 { 2358 Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName, 2359 PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal), 2360 PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf))); 2361 2362 PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props, 2363 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 2364 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal), 2365 cfBuf - cfReadTotal); 2366 2367 cfReadTotal = cfBuf; 2368 } 2369 2370 cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal); 2371 2372 pStreamEx->nsLastReadWritten = RTTimeNanoTS(); 2373 2374 Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc)); 2375 2376 } while (0); 2377 2378 RTCritSectLeave(&pThis->CritSect); 2379 2380 if (RT_SUCCESS(rc) && pcbRead) 2381 *pcbRead = cbReadTotal; 2382 return rc; 2383 } 2384 2385 2386 /** 2387 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamWrite} 2388 */ 2389 static DECLCALLBACK(int) drvAudioStreamWrite(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, 2390 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2391 { 2392 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector); 2393 AssertPtr(pThis); 2394 2395 /* 2396 * Check input and sanity. 2397 */ 2398 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2399 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream; 2400 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER); 2401 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2402 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 2403 uint32_t uTmp; 2404 if (!pcbWritten) 2405 pcbWritten = &uTmp; 2406 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER); 2407 2408 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2409 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2410 AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, 2411 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n", 2412 pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED); 2413 Assert(pStreamEx->fNoMixBufs); 2414 2415 AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf), 2416 ("Stream '%s' got a non-frame-aligned write (%RU32 bytes)\n", pStreamEx->Core.szName, cbBuf)); 2417 2418 int rc = RTCritSectEnter(&pThis->CritSect); 2419 AssertRCReturn(rc, rc); 2420 2421 /* 2422 * First check that we can write to the stream, and if not, 2423 * whether to just drop the input into the bit bucket. 2424 */ 2425 if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus)) 2426 { 2427 if ( !pThis->Out.fEnabled /* (see @bugref{9882}) */ 2428 || pThis->pHostDrvAudio == NULL /* (we used to work this condition differently) */ 2429 || !PDMAudioStrmStatusCanWrite(pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend))) 2430 { 2431 Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName, 2432 !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet")); 2433 *pcbWritten = cbBuf; 2434 pStreamEx->offInternal += cbBuf; 2162 * Try play "Windows Navigation Start.wav" on Windows 7 (2824 samples). 2163 */ 2164 else if ( (pStreamEx->Core.fStatus & PDMAUDIOSTREAMSTS_FLAGS_PENDING_DISABLE) 2165 && pStreamEx->Out.cbPreBuffered > 0) 2166 { 2167 LogRel2(("Audio: @%#RX64: Stream '%s' buffering complete - short sound! (%#x + %#x bytes)\n", 2168 pStreamEx->offInternal, pStreamEx->Core.szName, pStreamEx->Out.cbPreBuffered, cbBuf)); 2169 pStreamEx->fThresholdReached = fJustStarted = true; 2435 2170 } 2436 2171 /* 2437 * No-mixing buffer mode: Write the data directly to the backend, unless 2438 * we're prebuffering. There will be no pfnStreamPlay call in this mode. 2172 * Not yet, so still buffering audio data. 2439 2173 */ 2440 2174 else 2441 2175 { 2442 uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore); 2443 rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten); 2444 Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal); 2445 if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc)) 2446 { /* likely */ } 2447 else 2448 AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */); 2449 } 2450 } 2451 else 2452 rc = VERR_AUDIO_STREAM_NOT_READY; 2453 2454 RTCritSectLeave(&pThis->CritSect); 2176 LogRel2(("Audio: @%#RX64: Stream '%s' is buffering (%RU8%% complete)...\n", pStreamEx->offInternal, 2177 pStreamEx->Core.szName, (100 * pStreamEx->Out.cbPreBuffered) / pStreamEx->Out.cbPreBufThreshold)); 2178 Assert(cbBuf == 0); 2179 *pcbWritten = cbWritten; 2180 return VINF_SUCCESS; 2181 } 2182 2183 /* 2184 * Write the pre-buffered chunk. 2185 */ 2186 uint32_t off = 0; 2187 uint32_t cbPreBufWritten; 2188 do 2189 { 2190 cbPreBufWritten = 0; 2191 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, &pStreamEx->Out.pbPreBuf[off], 2192 pStreamEx->Out.cbPreBuffered - off, &cbPreBufWritten); 2193 AssertRCBreak(rc); 2194 off += cbPreBufWritten; 2195 } while (off < pStreamEx->Out.cbPreBuffered && cbPreBufWritten != 0); 2196 2197 if (off >= pStreamEx->Out.cbPreBuffered) 2198 { 2199 Assert(off == pStreamEx->Out.cbPreBuffered); 2200 LogFunc(("@%#RX64: Wrote all %#x bytes of pre-buffered audio data.\n", pStreamEx->offInternal, off)); 2201 pStreamEx->Out.cbPreBuffered = 0; 2202 } 2203 else 2204 { 2205 LogRel2(("Audio: @%#RX64: Stream '%s' pre-buffering commit problem: wrote %#x out of %#x + %#x%s - rc=%Rrc *pcbWritten=%#x\n", 2206 pStreamEx->offInternal, pStreamEx->Core.szName, off, pStreamEx->Out.cbPreBuffered, cbBuf, 2207 fJustStarted ? " (just started)" : "", rc, cbWritten)); 2208 AssertMsg(!fJustStarted || RT_FAILURE(rc), 2209 ("Buggy host driver buffer reporting: off=%#x cbPreBuffered=%#x\n", off, pStreamEx->Out.cbPreBuffered)); 2210 if (off > 0) 2211 { 2212 memmove(pStreamEx->Out.pbPreBuf, &pStreamEx->Out.pbPreBuf[off], pStreamEx->Out.cbPreBuffered - off); 2213 pStreamEx->Out.cbPreBuffered -= off; 2214 } 2215 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS(); 2216 *pcbWritten = cbWritten; 2217 return cbWritten ? VINF_SUCCESS : rc; 2218 } 2219 2220 if (RT_FAILURE(rc)) 2221 { 2222 *pcbWritten = cbWritten; 2223 return rc; 2224 } 2225 } 2226 2227 /* 2228 * Do the writing. 2229 */ 2230 uint32_t cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend); 2231 pStreamEx->Out.Stats.cbBackendWritableBefore = cbWritable; 2232 2233 uint8_t const cbFrame = PDMAudioPropsFrameSize(&pStreamEx->Core.Props); 2234 while (cbBuf >= cbFrame && cbWritable >= cbFrame) 2235 { 2236 uint32_t const cbToWrite = PDMAudioPropsFloorBytesToFrame(&pStreamEx->Core.Props, RT_MIN(cbBuf, cbWritable)); 2237 uint32_t cbWrittenNow = 0; 2238 rc = pThis->pHostDrvAudio->pfnStreamPlay(pThis->pHostDrvAudio, pStreamEx->pBackend, pbBuf, cbToWrite, &cbWrittenNow); 2239 if (RT_SUCCESS(rc)) 2240 { 2241 if (cbWrittenNow != cbToWrite) 2242 Log3Func(("%s: @%#RX64: Wrote less bytes than requested: %#x, requested %#x\n", 2243 pStreamEx->Core.szName, pStreamEx->offInternal, cbWrittenNow, cbToWrite)); 2244 #ifdef DEBUG_bird 2245 Assert(cbWrittenNow == cbToWrite); 2246 #endif 2247 AssertStmt(cbWrittenNow <= cbToWrite, cbWrittenNow = cbToWrite); 2248 cbWritten += cbWrittenNow; 2249 cbBuf -= cbWrittenNow; 2250 pbBuf += cbWrittenNow; 2251 pStreamEx->offInternal += cbWrittenNow; 2252 } 2253 else 2254 { 2255 *pcbWritten = cbWritten; 2256 LogFunc(("%s: @%#RX64: pfnStreamPlay failed writing %#x bytes (%#x previous written, %#x writable): %Rrc\n", 2257 pStreamEx->Core.szName, pStreamEx->offInternal, cbToWrite, cbWritten, cbWritable, rc)); 2258 return cbWritten ? VINF_SUCCESS : rc; 2259 } 2260 cbWritable = pThis->pHostDrvAudio->pfnStreamGetWritable(pThis->pHostDrvAudio, pStreamEx->pBackend); 2261 } 2262 2263 *pcbWritten = cbWritten; 2264 pStreamEx->Out.Stats.cbBackendWritableAfter = cbWritable; 2265 if (cbWritten) 2266 pStreamEx->nsLastPlayedCaptured = RTTimeNanoTS(); 2267 2268 Log3Func(("%s: @%#RX64: Wrote %#x bytes (%#x bytes left)\n", pStreamEx->Core.szName, pStreamEx->offInternal, cbWritten, cbBuf)); 2455 2269 return rc; 2456 2270 } … … 2806 2620 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay} 2807 2621 */ 2808 static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, uint32_t *pcFramesPlayed) 2809 { 2810 RT_NOREF(pInterface, pStream, pcFramesPlayed); 2811 AssertFailed(/* OBSOLETE! */); 2812 return VERR_NOT_SUPPORTED; 2622 static DECLCALLBACK(int) drvAudioStreamPlay(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, 2623 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2624 { 2625 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector); 2626 AssertPtr(pThis); 2627 2628 /* 2629 * Check input and sanity. 2630 */ 2631 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2632 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream; 2633 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER); 2634 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2635 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 2636 uint32_t uTmp; 2637 if (!pcbWritten) 2638 pcbWritten = &uTmp; 2639 AssertPtrReturn(pcbWritten, VERR_INVALID_PARAMETER); 2640 2641 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2642 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2643 AssertMsgReturn(pStreamEx->Core.enmDir == PDMAUDIODIR_OUT, 2644 ("Stream '%s' is not an output stream and therefore cannot be written to (direction is '%s')\n", 2645 pStreamEx->Core.szName, PDMAudioDirGetName(pStreamEx->Core.enmDir)), VERR_ACCESS_DENIED); 2646 Assert(pStreamEx->fNoMixBufs); 2647 2648 AssertMsg(PDMAudioPropsIsSizeAligned(&pStreamEx->Guest.Cfg.Props, cbBuf), 2649 ("Stream '%s' got a non-frame-aligned write (%RU32 bytes)\n", pStreamEx->Core.szName, cbBuf)); 2650 2651 int rc = RTCritSectEnter(&pThis->CritSect); 2652 AssertRCReturn(rc, rc); 2653 2654 /* 2655 * First check that we can write to the stream, and if not, 2656 * whether to just drop the input into the bit bucket. 2657 */ 2658 if (PDMAudioStrmStatusIsReady(pStreamEx->Core.fStatus)) 2659 { 2660 if ( !pThis->Out.fEnabled /* (see @bugref{9882}) */ 2661 || pThis->pHostDrvAudio == NULL /* (we used to work this condition differently) */ 2662 || !PDMAudioStrmStatusCanWrite(pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pStreamEx->pBackend))) 2663 { 2664 Log3Func(("[%s] Backend stream %s, discarding the data\n", pStreamEx->Core.szName, 2665 !pThis->Out.fEnabled ? "disabled" : !pThis->pHostDrvAudio ? "not attached" : "not ready yet")); 2666 *pcbWritten = cbBuf; 2667 pStreamEx->offInternal += cbBuf; 2668 } 2669 /* 2670 * No-mixing buffer mode: Write the data directly to the backend, unless 2671 * we're prebuffering. There will be no pfnStreamPlay call in this mode. 2672 */ 2673 else 2674 { 2675 uint64_t offInternalBefore = pStreamEx->offInternal; RT_NOREF(offInternalBefore); 2676 rc = drvAudioStreamWriteNoMixBufs(pThis, pStreamEx, (uint8_t const *)pvBuf, cbBuf, pcbWritten); 2677 Assert(offInternalBefore + *pcbWritten == pStreamEx->offInternal); 2678 if (!pThis->Out.Cfg.Dbg.fEnabled || RT_FAILURE(rc)) 2679 { /* likely */ } 2680 else 2681 AudioHlpFileWrite(pStreamEx->Out.Dbg.pFilePlayNonInterleaved, pvBuf, *pcbWritten, 0 /* fFlags */); 2682 } 2683 } 2684 else 2685 rc = VERR_AUDIO_STREAM_NOT_READY; 2686 2687 RTCritSectLeave(&pThis->CritSect); 2688 return rc; 2689 } 2690 2691 2692 /** 2693 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamRead} 2694 */ 2695 static DECLCALLBACK(int) drvAudioStreamRead(PPDMIAUDIOCONNECTOR pInterface, PPDMAUDIOSTREAM pStream, 2696 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead) 2697 { 2698 PDRVAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVAUDIO, IAudioConnector); 2699 AssertPtr(pThis); 2700 PDRVAUDIOSTREAM pStreamEx = (PDRVAUDIOSTREAM)pStream; 2701 AssertPtrReturn(pStreamEx, VERR_INVALID_POINTER); 2702 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); 2703 AssertReturn(cbBuf, VERR_INVALID_PARAMETER); 2704 AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER); 2705 AssertReturn(pStreamEx->Core.uMagic == PDMAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2706 AssertReturn(pStreamEx->uMagic == DRVAUDIOSTREAM_MAGIC, VERR_INVALID_MAGIC); 2707 AssertMsg(pStreamEx->Core.enmDir == PDMAUDIODIR_IN, 2708 ("Stream '%s' is not an input stream and therefore cannot be read from (direction is 0x%x)\n", 2709 pStreamEx->Core.szName, pStreamEx->Core.enmDir)); 2710 2711 int rc = RTCritSectEnter(&pThis->CritSect); 2712 AssertRCReturn(rc, rc); 2713 2714 /* 2715 * ... 2716 */ 2717 uint32_t cbReadTotal = 0; 2718 2719 do 2720 { 2721 uint32_t cfReadTotal = 0; 2722 2723 const uint32_t cfBuf = AUDIOMIXBUF_B2F(&pStreamEx->Guest.MixBuf, cbBuf); 2724 2725 if (pThis->In.fEnabled) /* Input for this audio driver enabled? See #9822. */ 2726 { 2727 if (!PDMAudioStrmStatusCanRead(pStream->fStatus)) 2728 { 2729 rc = VERR_AUDIO_STREAM_NOT_READY; 2730 break; 2731 } 2732 2733 /* 2734 * Read from the parent buffer (that is, the guest buffer) which 2735 * should have the audio data in the format the guest needs. 2736 */ 2737 uint32_t cfToRead = RT_MIN(cfBuf, AudioMixBufLive(&pStreamEx->Guest.MixBuf)); 2738 while (cfToRead) 2739 { 2740 uint32_t cfRead; 2741 rc = AudioMixBufAcquireReadBlock(&pStreamEx->Guest.MixBuf, 2742 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 2743 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfToRead), &cfRead); 2744 if (RT_FAILURE(rc)) 2745 break; 2746 2747 #ifdef VBOX_WITH_STATISTICS 2748 const uint32_t cbRead = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfRead); 2749 STAM_COUNTER_ADD(&pThis->Stats.TotalBytesRead, cbRead); 2750 STAM_COUNTER_ADD(&pStreamEx->In.Stats.TotalFramesRead, cfRead); 2751 STAM_COUNTER_INC(&pStreamEx->In.Stats.TotalTimesRead); 2752 #endif 2753 Assert(cfToRead >= cfRead); 2754 cfToRead -= cfRead; 2755 2756 cfReadTotal += cfRead; 2757 2758 AudioMixBufReleaseReadBlock(&pStreamEx->Guest.MixBuf, cfRead); 2759 } 2760 2761 if (cfReadTotal) 2762 { 2763 if (pThis->In.Cfg.Dbg.fEnabled) 2764 AudioHlpFileWrite(pStreamEx->In.Dbg.pFileStreamRead, 2765 pvBuf, AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 0 /* fFlags */); 2766 2767 AudioMixBufFinish(&pStreamEx->Guest.MixBuf, cfReadTotal); 2768 } 2769 } 2770 2771 /* If we were not able to read as much data as requested, fill up the returned 2772 * data with silence. 2773 * 2774 * This is needed to keep the device emulation DMA transfers up and running at a constant rate. */ 2775 if (cfReadTotal < cfBuf) 2776 { 2777 Log3Func(("[%s] Filling in silence (%RU64ms / %RU64ms)\n", pStream->szName, 2778 PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf - cfReadTotal), 2779 PDMAudioPropsFramesToMilli(&pStreamEx->Guest.Cfg.Props, cfBuf))); 2780 2781 PDMAudioPropsClearBuffer(&pStreamEx->Guest.Cfg.Props, 2782 (uint8_t *)pvBuf + AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal), 2783 AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfBuf - cfReadTotal), 2784 cfBuf - cfReadTotal); 2785 2786 cfReadTotal = cfBuf; 2787 } 2788 2789 cbReadTotal = AUDIOMIXBUF_F2B(&pStreamEx->Guest.MixBuf, cfReadTotal); 2790 2791 pStreamEx->nsLastReadWritten = RTTimeNanoTS(); 2792 2793 Log3Func(("[%s] fEnabled=%RTbool, cbReadTotal=%RU32, rc=%Rrc\n", pStream->szName, pThis->In.fEnabled, cbReadTotal, rc)); 2794 2795 } while (0); 2796 2797 RTCritSectLeave(&pThis->CritSect); 2798 2799 if (RT_SUCCESS(rc) && pcbRead) 2800 *pcbRead = cbReadTotal; 2801 return rc; 2813 2802 } 2814 2803 … … 3556 3545 pThis->IAudioConnector.pfnStreamRelease = drvAudioStreamRelease; 3557 3546 pThis->IAudioConnector.pfnStreamControl = drvAudioStreamControl; 3558 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead;3559 pThis->IAudioConnector.pfnStreamWrite = drvAudioStreamWrite;3560 3547 pThis->IAudioConnector.pfnStreamIterate = drvAudioStreamIterate; 3561 3548 pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable; … … 3564 3551 pThis->IAudioConnector.pfnStreamSetVolume = drvAudioStreamSetVolume; 3565 3552 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay; 3553 pThis->IAudioConnector.pfnStreamRead = drvAudioStreamRead; 3566 3554 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture; 3567 3555 #ifdef VBOX_WITH_AUDIO_CALLBACKS
Note:
See TracChangeset
for help on using the changeset viewer.