summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Audio/DevSB16.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Audio/DevSB16.cpp')
-rw-r--r--src/VBox/Devices/Audio/DevSB16.cpp159
1 files changed, 125 insertions, 34 deletions
diff --git a/src/VBox/Devices/Audio/DevSB16.cpp b/src/VBox/Devices/Audio/DevSB16.cpp
index a405f311..7df081e7 100644
--- a/src/VBox/Devices/Audio/DevSB16.cpp
+++ b/src/VBox/Devices/Audio/DevSB16.cpp
@@ -110,6 +110,8 @@ typedef struct SB16STATE *PSB16STATE;
*/
typedef struct SB16STREAMSTATE
{
+ /** Critical section for this stream. */
+ RTCRITSECT CritSect;
/** Flag indicating whether this stream is in enabled state or not. */
bool fEnabled;
/** Set if we've registered the asynchronous update job. */
@@ -335,6 +337,8 @@ static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable
static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream);
static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
+DECLINLINE(void) sb16StreamLock(PSB16STREAM pStream);
+DECLINLINE(void) sb16StreamUnlock(PSB16STREAM pStream);
DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx);
static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cSamples);
static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead);
@@ -1923,6 +1927,20 @@ static void sb16RemoveDrv(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16DRIVER pDrv
* Stream handling *
*********************************************************************************************************************************/
+/**
+ * Reads DMA output data into the internal DMA buffer.
+ *
+ * @returns VBox status code.
+ * @param pThis The SB16 state.
+ * @param pStream The SB16 stream to read DMA output data from.
+ * @param uDmaChan DMA channel to read from.
+ * @param offDma DMA offset (in bytes) to start reading from.
+ * @param cbDma DMA area size in bytes.
+ * @param cbToRead How much bytes to read in total.
+ * @param pcbRead Where to return the DMA bytes read.
+ *
+ * @thread EMT
+ */
static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDmaChan, uint32_t offDma, uint32_t cbDma,
uint32_t cbToRead, uint32_t *pcbRead)
{
@@ -1930,6 +1948,8 @@ static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDma
//Assert(cbToRead <= cbFree); /** @todo Add statistics for overflows. */
cbToRead = RT_MIN(cbToRead, cbFree);
+ sb16StreamLock(pStream);
+
uint32_t cbReadTotal = 0;
while (cbToRead)
{
@@ -1948,6 +1968,8 @@ static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDma
if (cbReadTotal > 0)
break;
*pcbRead = 0;
+
+ sb16StreamUnlock(pStream);
return rc;
}
@@ -1956,8 +1978,12 @@ static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDma
else
AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, pv, cbRead);
+ Log3Func(("Writing %#RX32 bytes to DMA buffer\n", cbRead));
+
RTCircBufReleaseWriteBlock(pStream->State.pCircBuf, cbRead);
+ Log3Func(("Now %#RX32 bytes in buffer\n", RTCircBufUsed(pStream->State.pCircBuf)));
+
Assert(cbToRead >= cbRead);
pStream->State.offWrite += cbRead;
offDma = (offDma + cbRead) % cbDma;
@@ -1970,6 +1996,9 @@ static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDma
/* Update buffer stats. */
pStream->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
+ Log3Func(("[SD%RU8] cbCircBufUsed=%#RX64\n", pStream->uIdx, RTCircBufUsed(pStream->State.pCircBuf)));
+
+ sb16StreamUnlock(pStream);
return VINF_SUCCESS;
}
@@ -2011,8 +2040,19 @@ static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable,
}
else
{
- rc = AudioMixerSinkDrainAndStop(pSink, pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0);
+ /* We need to lock the stream here in order to synchronize the amount of bytes we have to drain
+ * here with the mixer's async I/O thread. See @bugref{10354}. */
+ sb16StreamLock(pStream);
+
+ uint32_t const cbToDrain = pStream->State.pCircBuf ? (uint32_t)RTCircBufUsed(pStream->State.pCircBuf) : 0;
+ Log3Func(("cbToDrain=%#RX32\n", cbToDrain));
+
+ sb16StreamUnlock(pStream);
+
+ rc = AudioMixerSinkDrainAndStop(pSink, cbToDrain);
AssertRCReturn(rc, rc);
+
+
}
pStream->State.fEnabled = fEnable;
@@ -2068,6 +2108,9 @@ static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, uint8_t uIdx)
{
LogFlowFuncEnter();
+ int rc = RTCritSectInit(&pStream->State.CritSect);
+ AssertRCReturn(rc, rc);
+
pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
@@ -2111,6 +2154,9 @@ static int sb16StreamDestroy(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM p
pStream->State.fRegisteredAsyncUpdateJob = false;
}
+ int rc2 = RTCritSectDelete(&pStream->State.CritSect);
+ AssertRC(rc2);
+
if (pStream->State.pCircBuf)
{
RTCircBufDestroy(pStream->State.pCircBuf);
@@ -2177,6 +2223,28 @@ static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream)
}
/**
+ * Locks a SB16 stream for serialized access.
+ *
+ * @param pStream The SB16 stream to lock.
+ */
+DECLINLINE(void) sb16StreamLock(PSB16STREAM pStream)
+{
+ int rc2 = RTCritSectEnter(&pStream->State.CritSect);
+ AssertRC(rc2);
+}
+
+/**
+ * Unlocks a formerly locked SB16 stream.
+ *
+ * @param pStream The SB16 stream to unlock.
+ */
+DECLINLINE(void) sb16StreamUnlock(PSB16STREAM pStream)
+{
+ int rc2 = RTCritSectLeave(&pStream->State.CritSect);
+ AssertRC(rc2);
+}
+
+/**
* Opens a SB16 stream with its current mixer settings.
*
* @returns VBox status code.
@@ -2209,47 +2277,59 @@ static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStr
PDMAudioPropsChannels(&pStream->Cfg.Props), pStream->Cfg.Props.fSigned ? "S" : "U",
PDMAudioPropsSampleBits(&pStream->Cfg.Props)));
- /* (Re-)create the stream's internal ring buffer. */
- if (pStream->State.pCircBuf)
- {
- RTCircBufDestroy(pStream->State.pCircBuf);
- pStream->State.pCircBuf = NULL;
- }
-
/** @todo r=bird: two DMA periods is probably too little. */
const uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props,
(RT_MS_1SEC / pStream->uTimerHz) * 2 /* Use double buffering here */);
+ int rc = VINF_SUCCESS;
- int rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
- AssertRCReturn(rc, rc);
- pStream->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStream->State.pCircBuf);
+ /**
+ * Note: Only re-create the DMA buffer if the size actually has changed.
+ *
+ * Otherwise do *not* reset the stream's circular buffer here, as the audio mixer still relies on
+ * previously announced DMA data (via AudioMixerSinkDrainAndStop()) and processes it asynchronously.
+ * Resetting the buffer here will cause a race condition. See @bugref{10354}. */
+ if (pStream->State.StatDmaBufSize != cbCircBuf)
+ {
+ Log3Func(("Re-creating ring buffer (%#RX32 -> %#RX32)\n", pStream->State.StatDmaBufSize, cbCircBuf));
- /* Set scheduling hint. */
- pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / RT_MIN(pStream->uTimerHz, 1);
+ /* (Re-)create the stream's internal ring buffer. */
+ if (pStream->State.pCircBuf)
+ {
+ RTCircBufDestroy(pStream->State.pCircBuf);
+ pStream->State.pCircBuf = NULL;
+ }
- PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
- AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
+ rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
+ AssertRCReturn(rc, rc);
+ pStream->State.StatDmaBufSize = (uint32_t)RTCircBufSize(pStream->State.pCircBuf);
- sb16RemoveDrvStreams(pDevIns, pThis,
- sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.enmPath);
+ /* Set scheduling hint. */
+ pStream->Cfg.Device.cMsSchedulingHint = RT_MS_1SEC / RT_MIN(pStream->uTimerHz, 1);
- rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
- if (RT_SUCCESS(rc))
- {
- if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
- { /* likely */ }
- else
+ PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
+ AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
+
+ sb16RemoveDrvStreams(pDevIns, pThis,
+ sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.enmPath);
+
+ rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
+ if (RT_SUCCESS(rc))
{
- /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
- if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
+ if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
+ { /* likely */ }
+ else
{
- AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
- AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
- }
+ /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
+ if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
+ {
+ AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
+ AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
+ }
- int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
- &pStream->Cfg.Props);
- AssertRC(rc2);
+ int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
+ &pStream->Cfg.Props);
+ AssertRC(rc2);
+ }
}
}
@@ -2302,11 +2382,18 @@ static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream
*
* @param pStream The SB16 stream.
* @param pSink The mixer sink to push to.
+ *
+ * @thread MixAIO-n
+ *
+ * @note Takes the stream's lock.
*/
static void sb16StreamPushToMixer(PSB16STREAM pStream, PAUDMIXSINK pSink)
{
+ sb16StreamLock(pStream);
+
#ifdef LOG_ENABLED
uint64_t const offReadOld = pStream->State.offRead;
+ Log3Func(("[SD%RU8] cbCircBufUsed=%#RX64\n", pStream->uIdx, RTCircBufUsed(pStream->State.pCircBuf)));
#endif
pStream->State.offRead = AudioMixerSinkTransferFromCircBuf(pSink,
pStream->State.pCircBuf,
@@ -2315,11 +2402,13 @@ static void sb16StreamPushToMixer(PSB16STREAM pStream, PAUDMIXSINK pSink)
/** @todo pStream->Dbg.Runtime.fEnabled
? pStream->Dbg.Runtime.pFileStream :*/ NULL);
- Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64\n", pStream->uIdx,
- pStream->State.offRead - offReadOld, pStream->State.offRead));
+ Log3Func(("[SD%RU8] transferred=%#RX64 bytes -> @%#RX64 (cbCircBufUsed=%#RX64)\n", pStream->uIdx,
+ pStream->State.offRead - offReadOld, pStream->State.offRead, RTCircBufUsed(pStream->State.pCircBuf)));
/* Update buffer stats. */
pStream->State.StatDmaBufUsed = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
+
+ sb16StreamUnlock(pStream);
}
@@ -2327,8 +2416,10 @@ static void sb16StreamPushToMixer(PSB16STREAM pStream, PAUDMIXSINK pSink)
* @callback_method_impl{FNAUDMIXSINKUPDATE}
*
* For output streams this moves data from the internal DMA buffer (in which
- * ichac97R3StreamUpdateDma put it), thru the mixer and to the various backend
+ * sb16StreamDoDmaOutput put it), thru the mixer and to the various backend
* audio devices.
+ *
+ * @thread MixAIO-n
*/
static DECLCALLBACK(void) sb16StreamUpdateAsyncIoJob(PPDMDEVINS pDevIns, PAUDMIXSINK pSink, void *pvUser)
{