VirtualBox

Changeset 88720 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Apr 26, 2021 10:46:21 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
144038
Message:

Audio: Merged pfnStreamWrite and pfnStreamPlay in PDMIAUDIOCONNECTOR. Moved pfnStreamRead down to pfnStreamCapture. bugref:9890

Location:
trunk/src/VBox/Devices/Audio
Files:
2 edited

Legend:

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

    r88452 r88720  
    18301830                           space available, this should always write the whole buffer. */
    18311831                        uint32_t cbDstWritten = 0;
    1832                         int rc2 = pMixStream->pConn->pfnStreamWrite(pMixStream->pConn, pMixStream->pStream,
    1833                                                                     &Buf, cbDstPeeked, &cbDstWritten);
     1832                        int rc2 = pMixStream->pConn->pfnStreamPlay(pMixStream->pConn, pMixStream->pStream,
     1833                                                                   &Buf, cbDstPeeked, &cbDstWritten);
    18341834                        Log3Func(("%s: %#x L %#x => %#x bytes; wrote %#x rc2=%Rrc %s\n", pSink->pszName, offSrcFrame,
    18351835                                  cSrcFramesPeeked - cSrcFramesPeeked, cbDstPeeked, cbDstWritten, rc2, pMixStream->pszName));
     
    18761876
    18771877    /*
    1878      * Iterate buffers (pfnStreamPlay is not used any more).
     1878     * Iterate buffers.
    18791879     */
    18801880    RTListForEach(&pSink->lstStreams, pMixStream, AUDMIXSTREAM, Node)
  • TabularUnified trunk/src/VBox/Devices/Audio/DrvAudio.cpp

    r88719 r88720  
    541541        AssertFailed();
    542542#endif
    543 }
    544 
    545 
    546 /**
    547  * The no-mixing-buffers worker for drvAudioStreamWrite and
    548  * drvAudioStreamIterateInternal.
    549  *
    550  * The buffer is NULL and has a zero length when called from the interate
    551  * function.  This only occures when there is pre-buffered audio data that need
    552  * 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'd
    565      *       need some kind of cooperation with the backend buffer
    566      *       managment to correctly detect an underrun.
    567      */
    568     bool     fJustStarted = false;
    569     uint32_t cbWritten = 0;
    570     int      rc;
    571     if (   pStreamEx->fThresholdReached
    572         && pStreamEx->Out.cbPreBuffered == 0)
    573     {
    574         /* not-prebuffering, likely after a while at least */
    575         rc = VINF_SUCCESS;
    576     }
    577     else
    578     {
    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 when
    598          * 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. the
    615          * "click" Explorer sounds on some Windows guests), so make sure that we
    616          * also play those by checking if the stream already is pending disable
    617          * 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         else
    632         {
    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         do
    646         {
    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         else
    661         {
    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_bird
    702             Assert(cbWrittenNow == cbToWrite);
    703 #endif
    704             AssertStmt(cbWrittenNow <= cbToWrite, cbWrittenNow = cbToWrite);
    705             cbWritten += cbWrittenNow;
    706             cbBuf     -= cbWrittenNow;
    707             pbBuf     += cbWrittenNow;
    708             pStreamEx->offInternal += cbWrittenNow;
    709         }
    710         else
    711         {
    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;
    727543}
    728544
     
    22722088
    22732089/**
    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 */
     2099static 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.
    23542161         *
    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;
    24352170        }
    24362171        /*
    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.
    24392173         */
    24402174        else
    24412175        {
    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));
    24552269    return rc;
    24562270}
     
    28062620 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay}
    28072621 */
    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;
     2622static 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 */
     2695static 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;
    28132802}
    28142803
     
    35563545    pThis->IAudioConnector.pfnStreamRelease     = drvAudioStreamRelease;
    35573546    pThis->IAudioConnector.pfnStreamControl     = drvAudioStreamControl;
    3558     pThis->IAudioConnector.pfnStreamRead        = drvAudioStreamRead;
    3559     pThis->IAudioConnector.pfnStreamWrite       = drvAudioStreamWrite;
    35603547    pThis->IAudioConnector.pfnStreamIterate     = drvAudioStreamIterate;
    35613548    pThis->IAudioConnector.pfnStreamGetReadable = drvAudioStreamGetReadable;
     
    35643551    pThis->IAudioConnector.pfnStreamSetVolume   = drvAudioStreamSetVolume;
    35653552    pThis->IAudioConnector.pfnStreamPlay        = drvAudioStreamPlay;
     3553    pThis->IAudioConnector.pfnStreamRead        = drvAudioStreamRead;
    35663554    pThis->IAudioConnector.pfnStreamCapture     = drvAudioStreamCapture;
    35673555#ifdef VBOX_WITH_AUDIO_CALLBACKS
Note: See TracChangeset for help on using the changeset viewer.

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