VirtualBox

Changeset 88767 in vbox for trunk/src/VBox/Devices


Ignore:
Timestamp:
Apr 29, 2021 9:37:55 AM (4 years ago)
Author:
vboxsync
Message:

Audio/SB16: Implemented support for async I/O (disabled by default); a lot of code cleanup / reorganization to support more streams in the future.

File:
1 edited

Legend:

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

    r88676 r88767  
    55
    66/*
    7  * Copyright (C) 2015-2020 Oracle Corporation
     7 * Copyright (C) 2015-2021 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    6969*   Defined Constants And Macros                                                                                                 *
    7070*********************************************************************************************************************************/
     71/** The maximum number of separate streams we currently implement.
     72 *  Currently we only support one stream only, namely the output stream. */
     73#define SB16_MAX_STREAMS                1
     74/** The (zero-based) index of the output stream in \a aStreams. */
     75#define SB16_IDX_OUT                    0
     76
    7177/** Current saved state version. */
    7278#define SB16_SAVE_STATE_VERSION         2
     
    8793/** Pointer to the SB16 state. */
    8894typedef struct SB16STATE *PSB16STATE;
     95
     96#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     97/**
     98 * Asynchronous I/O state for an SB16 stream.
     99 */
     100typedef struct SB16STREAMSTATEAIO
     101{
     102    PPDMTHREAD              pThread;
     103    /** Event for letting the thread know there is some data to process. */
     104    SUPSEMEVENT             hEvtProcess;
     105    /** Critical section for synchronizing access. */
     106    RTCRITSECT              CritSect;
     107    /** Started indicator. */
     108    volatile bool           fStarted;
     109    /** Shutdown indicator. */
     110    volatile bool           fShutdown;
     111    /** Whether the thread should do any data processing or not. */
     112    volatile bool           fEnabled;
     113    bool                    afPadding[5];
     114} SB16STREAMSTATEAIO;
     115/** Pointer to the async I/O state for a SB16 stream. */
     116typedef SB16STREAMSTATEAIO *PSB16STREAMSTATEAIO;
     117#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
     118
     119/**
     120 * The internal state of a SB16 stream.
     121 */
     122typedef struct SB16STREAMSTATE
     123{
     124    /** Flag indicating whether this stream is in enabled state or not. */
     125    bool                    fEnabled;
     126#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     127    /** Asynchronous I/O state members. */
     128    SB16STREAMSTATEAIO      AIO;
     129#endif
     130    /** DMA cache to read data from / write data to. */
     131    PRTCIRCBUF              pCircBuf;
     132} SB16STREAMSTATE;
     133/** Pointer to internal state of an SB16 stream. */
     134typedef SB16STREAMSTATE *PSB16STREAMSTATE;
    89135
    90136/**
     
    155201typedef struct SB16STREAM
    156202{
     203    /** The stream's own index in \a aStreams of SB16STATE.
     204     *  Set to UINT8_MAX if not set (yet). */
     205    uint8_t                         uIdx;
     206    /** The timer for pumping data thru the attached LUN drivers. */
     207    TMTIMERHANDLE                   hTimerIO;
     208    /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
     209    uint64_t                        cTicksTimerIOInterval;
     210    /** Timestamp of the last timer callback (sb16TimerIO).
     211     * Used to calculate the time actually elapsed between two timer callbacks.
     212     * This currently ASSMUMES that we only have one single (output) stream. */
     213    uint64_t                        tsTimerIO; /** @todo Make this a per-stream value. */
    157214    /** The stream's current configuration. */
    158215    PDMAUDIOSTREAMCFG               Cfg;
     216    /** Internal state of this stream. */
     217    SB16STREAMSTATE                 State;
    159218    /** Debug stuff. */
    160219    SB16STREAMDEBUG                 Dbg;
     
    162221/** Pointer to a SB16 stream */
    163222typedef SB16STREAM *PSB16STREAM;
     223
     224#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     225/**
     226 * Asynchronous I/O thread context (arguments).
     227 */
     228typedef struct SB16STREAMTHREADCTX
     229{
     230    /** The SB16 device state. */
     231    PSB16STATE              pThis;
     232    /** The SB16 stream state. */
     233    PSB16STREAM             pStream;
     234} SB16STREAMTHREADCTX;
     235/** Pointer to the context for an async I/O thread. */
     236typedef SB16STREAMTHREADCTX *PSB16STREAMTHREADCTX;
     237#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
    164238
    165239/**
     
    198272    int ver;
    199273
    200     int in_index;
    201     int out_data_len;
     274    int dsp_in_idx;
     275    int dsp_out_data_len;
    202276    int fmt_stereo;
    203277    int fmt_signed;
     
    210284    int time_const;
    211285    int speaker;
    212     int needed_bytes;
     286    int dsp_in_needed_bytes;
    213287    int cmd;
    214288    int use_hdma;
    215289    int highspeed;
    216     int can_write; /** @todo Value never gets set to 0! */
     290    int can_write; /** @todo r=andy BUGBUG Value never gets set to 0! */
    217291
    218292    int v2x6;
     
    227301    int csp_reg83w;
    228302
    229     uint8_t in2_data[10];
    230     uint8_t out_data[50];
     303    uint8_t dsp_in_data[10];
     304    uint8_t dsp_out_data[50];
    231305    uint8_t test_reg;
    232306    uint8_t last_read_byte;
     
    245319    PDMIBASE            IBase;
    246320
    247     /** Output stream. */
    248     SB16STREAM             StreamOut;
     321    /** Array of all SB16 hardware audio stream. */
     322    SB16STREAM             aStreams[SB16_MAX_STREAMS];
    249323    /** The device's software mixer. */
    250324    R3PTRTYPE(PAUDIOMIXER) pMixer;
    251325    /** Audio sink for PCM output. */
    252326    R3PTRTYPE(PAUDMIXSINK) pSinkOut;
    253 
    254     /** The timer for pumping data thru the attached LUN drivers. */
    255     TMTIMERHANDLE       hTimerIO;
    256     /** The timer interval for pumping data thru the LUN drivers in timer ticks. */
    257     uint64_t            cTicksTimerIOInterval;
    258     /** Timestamp of the last timer callback (sb16TimerIO).
    259      * Used to calculate the time actually elapsed between two timer callbacks. */
    260     uint64_t            tsTimerIO;
    261     /** Number of active (running) SDn streams. */
    262     uint8_t             cStreamsActive;
    263     /** Flag indicating whether the timer is active or not. */
    264     bool volatile       fTimerActive;
    265     uint8_t             u8Padding1[5];
    266327
    267328    /** The two mixer I/O ports (port + 4). */
     
    287348*   Internal Functions                                                                                                           *
    288349*********************************************************************************************************************************/
    289 static int  sb16CheckAndReOpenOut(PPDMDEVINS pDevIns, PSB16STATE pThis);
    290 static int  sb16StreamEnable(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream, bool fEnable);
     350DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx);
     351
     352static int  sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce);
     353static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream);
    291354static int  sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
    292 static void sb16StreamClose(PSB16STATE pThis, PSB16STREAM pStream);
    293 DECLINLINE(PAUDMIXSINK) sb16StreamToSink(PSB16STATE pThis, PSB16STREAM pStream);
    294 static void sb16TimerMaybeStart(PPDMDEVINS pDevIns, PSB16STATE pThis);
    295 static void sb16TimerMaybeStop(PSB16STATE pThis);
    296 
     355static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
     356DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx);
     357static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cSamples);
     358static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink);
     359static int  sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream, int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead);
     360
     361static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser);
     362static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser);
     363DECLINLINE(void) sb16TimerSet(PPDMDEVINS pDevIns, PSB16STREAM pStream, uint64_t cTicksToDeadline);
     364
     365#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     366static int  sb16StreamAsyncIOCreate(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream);
     367static int  sb16StreamAsyncIODestroy(PPDMDEVINS pDevIns, PSB16STREAM pStream);
     368static int  sb16StreamAsyncIONotify(PPDMDEVINS pDevIns, PSB16STREAM pStream);
     369#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
     370
     371static void sb16SpeakerControl(PSB16STATE pThis, int on);
     372static void sb16UpdateVolume(PSB16STATE pThis);
    297373
    298374#if 0 // unused // def DEBUG
     
    311387#endif
    312388
     389#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     390/**
     391 * @callback_method_impl{FNPDMTHREADDEV}
     392 */
     393static DECLCALLBACK(int) sb16StreamAsyncIOThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
     394{
     395    if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
     396        return VINF_SUCCESS;
     397
     398    PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)pThread->pvUser;
     399    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     400
     401    PSB16STATE pThis = pCtx->pThis;
     402    AssertPtrReturn(pThis, VERR_INVALID_POINTER);
     403
     404    PSB16STREAM pStream = pCtx->pStream;
     405    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     406
     407    AssertReturn(pThread == pStream->State.AIO.pThread, VERR_INVALID_PARAMETER);
     408
     409    PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
     410    AssertPtrReturn(pSink, VERR_INVALID_POINTER);
     411
     412    while (   pThread->enmState != PDMTHREADSTATE_TERMINATING
     413           && pThread->enmState != PDMTHREADSTATE_TERMINATED)
     414    {
     415        int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pStream->State.AIO.hEvtProcess, RT_INDEFINITE_WAIT);
     416        if (pStream->State.AIO.fShutdown)
     417            break;
     418        AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
     419        if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
     420            return VINF_SUCCESS;
     421        if (rc == VERR_INTERRUPTED)
     422            continue;
     423
     424        sb16StreamUpdate(pStream, pSink);
     425    }
     426
     427    return VINF_SUCCESS;
     428}
     429
     430/**
     431 * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
     432 */
     433static DECLCALLBACK(int) sb16StreamAsyncIOWakeUp(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
     434{
     435    PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)pThread->pvUser;
     436    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
     437
     438    PSB16STREAM pStream = pCtx->pStream;
     439    AssertPtrReturn(pStream, VERR_INVALID_POINTER);
     440
     441    sb16StreamAsyncIONotify(pDevIns, pStream);
     442
     443    return VINF_SUCCESS;
     444}
     445
     446/**
     447 * Creates the async I/O thread for a specific SB16 audio stream.
     448 *
     449 * @returns VBox status code.
     450 * @param   pDevIns             The device instance.
     451 * @param   pThis               The shared SB16 state.
     452 * @param   pStream             SB16 audio stream to create the async I/O thread for.
     453 */
     454static int sb16StreamAsyncIOCreate(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
     455{
     456    PSB16STREAMSTATEAIO pAIO = &pStream->State.AIO;
     457
     458    int rc;
     459
     460    if (!ASMAtomicReadBool(&pAIO->fStarted))
     461    {
     462        pAIO->fShutdown = false;
     463        pAIO->fEnabled  = true; /* Enabled by default. */
     464
     465        rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pAIO->hEvtProcess);
     466        if (RT_SUCCESS(rc))
     467        {
     468            rc = RTCritSectInit(&pAIO->CritSect);
     469            if (RT_SUCCESS(rc))
     470            {
     471                /** @todo Make the device thread naming more specific once we have more streams here. */
     472
     473                char szDevTag[20];
     474                RTStrPrintf(szDevTag, sizeof(szDevTag), "SB16-%u", pDevIns->iInstance);
     475
     476                PSB16STREAMTHREADCTX pCtx = (PSB16STREAMTHREADCTX)RTMemAllocZ(sizeof(SB16STREAMTHREADCTX));
     477                if (pCtx)
     478                {
     479                    pCtx->pStream = pStream;
     480                    pCtx->pThis   = pThis;
     481
     482                    rc = PDMDevHlpThreadCreate(pDevIns, &pAIO->pThread, pCtx, sb16StreamAsyncIOThread,
     483                                               sb16StreamAsyncIOWakeUp, 0, RTTHREADTYPE_IO, szDevTag);
     484                    if (RT_FAILURE(rc))
     485                        RTMemFree(pCtx);
     486                }
     487                else
     488                    rc = VERR_NO_MEMORY;
     489            }
     490        }
     491    }
     492    else
     493        rc = VINF_SUCCESS;
     494
     495    return rc;
     496}
     497
     498/**
     499 * Lets the stream's async I/O thread know that there is some data to process.
     500 *
     501 * @returns VBox status code.
     502 * @param   pDevIns             The device instance.
     503 * @param   pStream             The SB16 stream to notify async I/O thread.
     504 */
     505static int sb16StreamAsyncIONotify(PPDMDEVINS pDevIns, PSB16STREAM pStream)
     506{
     507    return PDMDevHlpSUPSemEventSignal(pDevIns, pStream->State.AIO.hEvtProcess);
     508}
     509
     510/**
     511 * Destroys the async I/O thread of a specific SB16 audio stream.
     512 *
     513 * @returns VBox status code.
     514 * @param   pDevIns             The device instance.
     515 * @param   pStream             SB16 audio stream to destroy the async I/O thread for.
     516 */
     517static int sb16StreamAsyncIODestroy(PPDMDEVINS pDevIns, PSB16STREAM pStream)
     518{
     519    PSB16STREAMSTATEAIO pAIO = &pStream->State.AIO;
     520
     521    if (!ASMAtomicReadBool(&pAIO->fStarted))
     522        return VINF_SUCCESS;
     523
     524    ASMAtomicWriteBool(&pAIO->fShutdown, true);
     525
     526    int rc = sb16StreamAsyncIONotify(pDevIns, pStream);
     527    AssertRC(rc);
     528
     529    rc = PDMDevHlpThreadDestroy(pDevIns, pAIO->pThread, NULL);
     530    AssertRC(rc);
     531
     532    rc = RTCritSectDelete(&pAIO->CritSect);
     533    AssertRC(rc);
     534
     535    if (pStream->State.AIO.hEvtProcess != NIL_SUPSEMEVENT)
     536    {
     537        PDMDevHlpSUPSemEventSignal(pDevIns, pStream->State.AIO.hEvtProcess);
     538        PDMDevHlpSUPSemEventClose(pDevIns, pStream->State.AIO.hEvtProcess);
     539        pStream->State.AIO.hEvtProcess = NIL_SUPSEMEVENT;
     540    }
     541
     542    pAIO->fShutdown = false;
     543    return rc;
     544}
     545#endif /* VBOX_WITH_AUDIO_SB16_ASYNC_IO */
     546
    313547static void sb16SpeakerControl(PSB16STATE pThis, int on)
    314548{
    315549    pThis->speaker = on;
    316     /* AUD_enable (pThis->voice, on); */
    317550}
    318551
     
    326559    PDMDevHlpDMASetDREQ(pThis->pDevInsR3, dma, hold);
    327560
    328     /* We only support one output stream at the moment, so keep things easy here for now. */
    329     PSB16STREAM pStream = &pThis->StreamOut;
    330     sb16StreamEnable(pDevIns, pThis, pStream, RT_BOOL(hold));
     561    /* We only support one output stream at the moment, so keep things simple here for now. */
     562    PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT];
    331563
    332564    if (hold)
    333565    {
    334         pThis->cStreamsActive++;
    335         sb16TimerMaybeStart(pDevIns, pThis);
    336         PDMDevHlpDMASchedule(pThis->pDevInsR3);
     566        int rc = VINF_SUCCESS;
     567
     568        if (pThis->freq > 0)
     569        {
     570            rc = sb16StreamOpen(pDevIns, pThis, pStream);
     571            if (RT_SUCCESS(rc))
     572                sb16UpdateVolume(pThis);
     573        }
     574
     575        if (RT_SUCCESS(rc))
     576        {
     577            rc = sb16StreamEnable(pThis, pStream, true /* fEnable */, false /* fForce */);
     578            if (RT_SUCCESS(rc))
     579            {
     580                sb16TimerSet(pDevIns, pStream, pStream->cTicksTimerIOInterval);
     581
     582                PDMDevHlpDMASchedule(pThis->pDevInsR3);
     583            }
     584        }
    337585    }
    338586    else
    339587    {
    340         if (pThis->cStreamsActive)
    341             pThis->cStreamsActive--;
    342         sb16TimerMaybeStop(pThis);
    343     }
    344 }
    345 
    346 /**
    347  * @callback_method_impl{PFNTMTIMERDEV}
    348  */
    349 static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
    350 {
    351     PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
    352     RT_NOREF(pvUser, hTimer);
    353 
    354     pThis->can_write = 1;
    355     PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1);
     588        sb16StreamEnable(pThis, pStream, false /* fEnable */, false /* fForce */);
     589    }
    356590}
    357591
     
    361595static void continue_dma8(PPDMDEVINS pDevIns, PSB16STATE pThis)
    362596{
    363     sb16CheckAndReOpenOut(pDevIns, pThis);
    364597    sb16Control(pDevIns, pThis, 1);
    365598}
     
    484717    }
    485718
    486     sb16CheckAndReOpenOut(pDevIns, pThis);
    487719    sb16Control(pDevIns, pThis, 1);
    488720    sb16SpeakerControl(pThis, 1);
    489721}
    490722
    491 static inline void dsp_out_data (PSB16STATE pThis, uint8_t val)
    492 {
    493     LogFlowFunc(("outdata %#x\n", val));
    494     if ((size_t) pThis->out_data_len < sizeof (pThis->out_data)) {
    495         pThis->out_data[pThis->out_data_len++] = val;
    496     }
    497 }
    498 
    499 static inline uint8_t dsp_get_data (PSB16STATE pThis)
    500 {
    501     if (pThis->in_index) {
    502         return pThis->in2_data[--pThis->in_index];
    503     }
    504     LogFlowFunc(("buffer underflow\n"));
     723static inline void dsp_set_data(PSB16STATE pThis, uint8_t val)
     724{
     725    LogFlowFunc(("%#x\n", val));
     726    if ((size_t) pThis->dsp_out_data_len < sizeof (pThis->dsp_out_data))
     727        pThis->dsp_out_data[pThis->dsp_out_data_len++] = val;
     728}
     729
     730static inline uint8_t dsp_get_data(PSB16STATE pThis)
     731{
     732    if (pThis->dsp_in_idx)
     733        return pThis->dsp_in_data[--pThis->dsp_in_idx];
     734    AssertMsgFailed(("DSP input buffer underflow\n"));
    505735    return 0;
    506736}
     
    524754        }
    525755
    526         pThis->needed_bytes = 3;
     756        pThis->dsp_in_needed_bytes = 3;
    527757    }
    528758    else
    529759    {
    530         pThis->needed_bytes = 0;
     760        pThis->dsp_in_needed_bytes = 0;
    531761
    532762        switch (cmd)
    533763        {
    534764            case 0x03:
    535                 dsp_out_data(pThis, 0x10); /* pThis->csp_param); */
     765                dsp_set_data(pThis, 0x10); /* pThis->csp_param); */
    536766                goto warn;
    537767
    538768            case 0x04:
    539                 pThis->needed_bytes = 1;
     769                pThis->dsp_in_needed_bytes = 1;
    540770                goto warn;
    541771
    542772            case 0x05:
    543                 pThis->needed_bytes = 2;
     773                pThis->dsp_in_needed_bytes = 2;
    544774                goto warn;
    545775
     
    549779
    550780            case 0x0e:
    551                 pThis->needed_bytes = 2;
     781                pThis->dsp_in_needed_bytes = 2;
    552782                goto warn;
    553783
    554784            case 0x09:
    555                 dsp_out_data(pThis, 0xf8);
     785                dsp_set_data(pThis, 0xf8);
    556786                goto warn;
    557787
    558788            case 0x0f:
    559                 pThis->needed_bytes = 1;
     789                pThis->dsp_in_needed_bytes = 1;
    560790                goto warn;
    561791
    562792            case 0x10:
    563                 pThis->needed_bytes = 1;
     793                pThis->dsp_in_needed_bytes = 1;
    564794                goto warn;
    565795
    566796            case 0x14:
    567                 pThis->needed_bytes = 2;
     797                pThis->dsp_in_needed_bytes = 2;
    568798                pThis->block_size = 0;
    569799                break;
     
    574804
    575805            case 0x20:              /* Direct ADC, Juice/PL */
    576                 dsp_out_data(pThis, 0xff);
     806                dsp_set_data(pThis, 0xff);
    577807                goto warn;
    578808
     
    584814                pThis->freq = -1;
    585815                pThis->time_const = -1;
    586                 pThis->needed_bytes = 1;
     816                pThis->dsp_in_needed_bytes = 1;
    587817                break;
    588818
     
    590820                pThis->freq = -1;
    591821                pThis->time_const = -1;
    592                 pThis->needed_bytes = 2;
     822                pThis->dsp_in_needed_bytes = 2;
    593823                break;
    594824
     
    596826                pThis->freq = -1;
    597827                pThis->time_const = -1;
    598                 pThis->needed_bytes = 2;
     828                pThis->dsp_in_needed_bytes = 2;
    599829                goto warn;
    600830
    601831            case 0x45:
    602                 dsp_out_data(pThis, 0xaa);
     832                dsp_set_data(pThis, 0xaa);
    603833                goto warn;
    604834
     
    607837
    608838            case 0x48:
    609                 pThis->needed_bytes = 2;
     839                pThis->dsp_in_needed_bytes = 2;
    610840                break;
    611841
    612842            case 0x74:
    613                 pThis->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
     843                pThis->dsp_in_needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
    614844                LogFlowFunc(("0x75 - DMA DAC, 4-bit ADPCM not implemented\n"));
    615845                break;
    616846
    617847            case 0x75:              /* DMA DAC, 4-bit ADPCM Reference */
    618                 pThis->needed_bytes = 2;
     848                pThis->dsp_in_needed_bytes = 2;
    619849                LogFlowFunc(("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n"));
    620850                break;
    621851
    622852            case 0x76:              /* DMA DAC, 2.6-bit ADPCM */
    623                 pThis->needed_bytes = 2;
     853                pThis->dsp_in_needed_bytes = 2;
    624854                LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n"));
    625855                break;
    626856
    627857            case 0x77:              /* DMA DAC, 2.6-bit ADPCM Reference */
    628                 pThis->needed_bytes = 2;
     858                pThis->dsp_in_needed_bytes = 2;
    629859                LogFlowFunc(("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n"));
    630860                break;
     
    641871
    642872            case 0x80:
    643                 pThis->needed_bytes = 2;
     873                pThis->dsp_in_needed_bytes = 2;
    644874                break;
    645875
     
    684914
    685915            case 0xe0:              /* DSP identification */
    686                 pThis->needed_bytes = 1;
     916                pThis->dsp_in_needed_bytes = 1;
    687917                break;
    688918
    689919            case 0xe1:
    690                 dsp_out_data(pThis, pThis->ver & 0xff);
    691                 dsp_out_data(pThis, pThis->ver >> 8);
     920                dsp_set_data(pThis, pThis->ver & 0xff);
     921                dsp_set_data(pThis, pThis->ver >> 8);
    692922                break;
    693923
    694924            case 0xe2:
    695                 pThis->needed_bytes = 1;
     925                pThis->dsp_in_needed_bytes = 1;
    696926                goto warn;
    697927
     
    699929            {
    700930                for (int i = sizeof (e3) - 1; i >= 0; --i)
    701                     dsp_out_data(pThis, e3[i]);
     931                    dsp_set_data(pThis, e3[i]);
    702932
    703933                break;
     
    705935
    706936            case 0xe4:              /* write test reg */
    707                 pThis->needed_bytes = 1;
     937                pThis->dsp_in_needed_bytes = 1;
    708938                break;
    709939
     
    713943
    714944            case 0xe8:              /* read test reg */
    715                 dsp_out_data(pThis, pThis->test_reg);
     945                dsp_set_data(pThis, pThis->test_reg);
    716946                break;
    717947
    718948            case 0xf2:
    719949            case 0xf3:
    720                 dsp_out_data(pThis, 0xaa);
     950                dsp_set_data(pThis, 0xaa);
    721951                pThis->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
    722952                PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
     
    725955            case 0xf8:
    726956                /* Undocumented, used by old Creative diagnostic programs. */
    727                 dsp_out_data (pThis, 0);
     957                dsp_set_data(pThis, 0);
    728958                goto warn;
    729959
    730960            case 0xf9:
    731                 pThis->needed_bytes = 1;
     961                pThis->dsp_in_needed_bytes = 1;
    732962                goto warn;
    733963
    734964            case 0xfa:
    735                 dsp_out_data (pThis, 0);
     965                dsp_set_data(pThis, 0);
    736966                goto warn;
    737967
    738968            case 0xfc:              /* FIXME */
    739                 dsp_out_data (pThis, 0);
     969                dsp_set_data(pThis, 0);
    740970                goto warn;
    741971
     
    746976    }
    747977
    748     if (!pThis->needed_bytes)
     978    if (!pThis->dsp_in_needed_bytes)
    749979        LogFlow(("\n"));
    750980
    751981exit:
    752982
    753      if (!pThis->needed_bytes)
     983     if (!pThis->dsp_in_needed_bytes)
    754984        pThis->cmd = -1;
    755985     else
     
    759989
    760990warn:
    761     LogFlowFunc(("warning: command %#x,%d is not truly understood yet\n", cmd, pThis->needed_bytes));
     991    LogFlowFunc(("warning: command %#x,%d is not truly understood yet\n", cmd, pThis->dsp_in_needed_bytes));
    762992    goto exit;
    763993}
    764994
    765 static uint16_t dsp_get_lohi (PSB16STATE pThis)
    766 {
    767     uint8_t hi = dsp_get_data (pThis);
    768     uint8_t lo = dsp_get_data (pThis);
    769     return (hi << 8) | lo;
    770 }
    771 
    772 static uint16_t dsp_get_hilo (PSB16STATE pThis)
    773 {
    774     uint8_t lo = dsp_get_data (pThis);
    775     uint8_t hi = dsp_get_data (pThis);
    776     return (hi << 8) | lo;
    777 }
    778 
    779 static void complete(PPDMDEVINS pDevIns, PSB16STATE pThis)
    780 {
    781     int d0, d1, d2;
    782     LogFlowFunc(("complete command %#x, in_index %d, needed_bytes %d\n", pThis->cmd, pThis->in_index, pThis->needed_bytes));
     995DECLINLINE(uint16_t) dsp_get_lohi(PSB16STATE pThis)
     996{
     997    const uint8_t hi = dsp_get_data(pThis);
     998    const uint8_t lo = dsp_get_data(pThis);
     999    return RT_MAKE_U16(lo, hi);
     1000}
     1001
     1002DECLINLINE(uint16_t) dsp_get_hilo(PSB16STATE pThis)
     1003{
     1004    const uint8_t lo = dsp_get_data(pThis);
     1005    const uint8_t hi = dsp_get_data(pThis);
     1006    return RT_MAKE_U16(lo, hi);
     1007}
     1008
     1009static void dsp_cmd_complete(PPDMDEVINS pDevIns, PSB16STATE pThis)
     1010{
     1011    LogFlowFunc(("Command %#x, in_index %d, needed_bytes %d\n", pThis->cmd, pThis->dsp_in_idx, pThis->dsp_in_needed_bytes));
     1012
     1013    int v0, v1, v2;
     1014
     1015    PSB16STREAM pStream = &pThis->aStreams[SB16_IDX_OUT]; /** @ŧodo Improve this. */
    7831016
    7841017    if (pThis->cmd > 0xaf && pThis->cmd < 0xd0)
    7851018    {
    786         d2 = dsp_get_data (pThis);
    787         d1 = dsp_get_data (pThis);
    788         d0 = dsp_get_data (pThis);
     1019        v2 = dsp_get_data(pThis);
     1020        v1 = dsp_get_data(pThis);
     1021        v0 = dsp_get_data(pThis);
    7891022
    7901023        if (pThis->cmd & 8)
    791             LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));
     1024            LogFlowFunc(("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, v0, v1, v2));
    7921025        else
    7931026        {
    794             LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, d0, d1, d2));
    795             dma_cmd(pDevIns, pThis, pThis->cmd, d0, d1 + (d2 << 8));
     1027            LogFlowFunc(("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n", pThis->cmd, v0, v1, v2));
     1028            dma_cmd(pDevIns, pThis, pThis->cmd, v0, v1 + (v2 << 8));
    7961029        }
    7971030    }
     
    8001033        switch (pThis->cmd)
    8011034        {
    802         case 0x04:
    803             pThis->csp_mode = dsp_get_data (pThis);
    804             pThis->csp_reg83r = 0;
    805             pThis->csp_reg83w = 0;
    806             LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode));
    807             break;
    808 
    809         case 0x05:
    810             pThis->csp_param = dsp_get_data (pThis);
    811             pThis->csp_value = dsp_get_data (pThis);
    812             LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n", pThis->csp_param, pThis->csp_value));
    813             break;
    814 
    815         case 0x0e:
    816         {
    817             d0 = dsp_get_data(pThis);
    818             d1 = dsp_get_data(pThis);
    819             LogFlowFunc(("write CSP register %d <- %#x\n", d1, d0));
    820             if (d1 == 0x83)
     1035            case 0x04:
    8211036            {
    822                 LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, d0));
    823                 pThis->csp_reg83[pThis->csp_reg83r % 4] = d0;
    824                 pThis->csp_reg83r += 1;
     1037                pThis->csp_mode = dsp_get_data(pThis);
     1038                pThis->csp_reg83r = 0;
     1039                pThis->csp_reg83w = 0;
     1040                LogFlowFunc(("CSP command 0x04: mode=%#x\n", pThis->csp_mode));
     1041                break;
    8251042            }
    826             else
    827                 pThis->csp_regs[d1] = d0;
    828             break;
     1043
     1044            case 0x05:
     1045            {
     1046                pThis->csp_param = dsp_get_data(pThis);
     1047                pThis->csp_value = dsp_get_data(pThis);
     1048                LogFlowFunc(("CSP command 0x05: param=%#x value=%#x\n", pThis->csp_param, pThis->csp_value));
     1049                break;
     1050            }
     1051
     1052            case 0x0e:
     1053            {
     1054                v0 = dsp_get_data(pThis);
     1055                v1 = dsp_get_data(pThis);
     1056                LogFlowFunc(("write CSP register %d <- %#x\n", v1, v0));
     1057                if (v1 == 0x83)
     1058                {
     1059                    LogFlowFunc(("0x83[%d] <- %#x\n", pThis->csp_reg83r, v0));
     1060                    pThis->csp_reg83[pThis->csp_reg83r % 4] = v0;
     1061                    pThis->csp_reg83r += 1;
     1062                }
     1063                else
     1064                    pThis->csp_regs[v1] = v0;
     1065                break;
     1066            }
     1067
     1068            case 0x0f:
     1069            {
     1070                v0 = dsp_get_data(pThis);
     1071                LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", v0, pThis->csp_regs[v0], pThis->csp_mode));
     1072                if (v0 == 0x83)
     1073                {
     1074                    LogFlowFunc(("0x83[%d] -> %#x\n", pThis->csp_reg83w, pThis->csp_reg83[pThis->csp_reg83w % 4]));
     1075                    dsp_set_data(pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]);
     1076                    pThis->csp_reg83w += 1;
     1077                }
     1078                else
     1079                    dsp_set_data(pThis, pThis->csp_regs[v0]);
     1080                break;
     1081            }
     1082
     1083            case 0x10:
     1084                v0 = dsp_get_data(pThis);
     1085                LogFlowFunc(("cmd 0x10 d0=%#x\n", v0));
     1086                break;
     1087
     1088            case 0x14:
     1089                dma_cmd8(pDevIns, pThis, 0, dsp_get_lohi(pThis) + 1);
     1090                break;
     1091
     1092            case 0x22: /* Sets the master volume. */
     1093                /** @todo Setting the master volume is not implemented yet. */
     1094                break;
     1095
     1096            case 0x40: /* Sets the timer constant; SB16 is able to use sample rates via 0x41 instead. */
     1097                pThis->time_const = dsp_get_data(pThis);
     1098                LogFlowFunc(("set time const %d\n", pThis->time_const));
     1099                break;
     1100
     1101            case 0x42: /* Sets the input rate (in Hz). */
     1102#if 0
     1103                LogFlowFunc(("cmd 0x42 might not do what it think it should\n"));
     1104#endif
     1105                RT_FALL_THROUGH(); /** @todo BUGBUG FT2 sets output freq with this, go figure. */
     1106
     1107            case 0x41: /* Sets the output rate (in Hz). */
     1108            {
     1109                pThis->freq = dsp_get_hilo(pThis);
     1110                LogFlowFunc(("set freq %d\n", pThis->freq));
     1111                break;
     1112            }
     1113
     1114            case 0x48:
     1115            {
     1116                pThis->block_size = dsp_get_lohi(pThis) + 1;
     1117                LogFlowFunc(("set dma block len %d\n", pThis->block_size));
     1118                break;
     1119            }
     1120
     1121            case 0x74:
     1122                RT_FALL_THROUGH();
     1123            case 0x75:
     1124                RT_FALL_THROUGH();
     1125            case 0x76:
     1126                RT_FALL_THROUGH();
     1127            case 0x77:
     1128                /* ADPCM stuff, ignore. */
     1129                break;
     1130
     1131            case 0x80: /* Sets the IRQ. */
     1132            {
     1133                sb16StreamTransferScheduleNext(pThis, pStream, dsp_get_lohi(pThis) + 1);
     1134                break;
     1135            }
     1136
     1137            case 0xe0:
     1138            {
     1139                v0 = dsp_get_data(pThis);
     1140                pThis->dsp_out_data_len = 0;
     1141                LogFlowFunc(("E0=%#x\n", v0));
     1142                dsp_set_data(pThis, ~v0);
     1143                break;
     1144            }
     1145
     1146            case 0xe2:
     1147            {
     1148                v0 = dsp_get_data(pThis);
     1149                LogFlowFunc(("E2=%#x\n", v0));
     1150                break;
     1151            }
     1152
     1153            case 0xe4:
     1154                pThis->test_reg = dsp_get_data(pThis);
     1155                break;
     1156
     1157            case 0xf9:
     1158                v0 = dsp_get_data(pThis);
     1159                switch (v0)
     1160                {
     1161                    case 0x0e:
     1162                        dsp_set_data(pThis, 0xff);
     1163                        break;
     1164
     1165                    case 0x0f:
     1166                        dsp_set_data(pThis, 0x07);
     1167                        break;
     1168
     1169                    case 0x37:
     1170                        dsp_set_data(pThis, 0x38);
     1171                        break;
     1172
     1173                    default:
     1174                        dsp_set_data(pThis, 0x00);
     1175                        break;
     1176                }
     1177                break;
     1178
     1179            default:
     1180                LogRel2(("SB16: Unrecognized command %#x, skipping\n", pThis->cmd));
     1181                return;
    8291182        }
    830 
    831         case 0x0f:
    832             d0 = dsp_get_data(pThis);
    833             LogFlowFunc(("read CSP register %#x -> %#x, mode=%#x\n", d0, pThis->csp_regs[d0], pThis->csp_mode));
    834             if (d0 == 0x83)
    835             {
    836                 LogFlowFunc(("0x83[%d] -> %#x\n", pThis->csp_reg83w, pThis->csp_reg83[pThis->csp_reg83w % 4]));
    837                 dsp_out_data(pThis, pThis->csp_reg83[pThis->csp_reg83w % 4]);
    838                 pThis->csp_reg83w += 1;
    839             }
    840             else
    841                 dsp_out_data(pThis, pThis->csp_regs[d0]);
    842             break;
    843 
    844         case 0x10:
    845             d0 = dsp_get_data(pThis);
    846             LogFlowFunc(("cmd 0x10 d0=%#x\n", d0));
    847             break;
    848 
    849         case 0x14:
    850             dma_cmd8(pDevIns, pThis, 0, dsp_get_lohi (pThis) + 1);
    851             break;
    852 
    853         case 0x40:
    854             pThis->time_const = dsp_get_data(pThis);
    855             LogFlowFunc(("set time const %d\n", pThis->time_const));
    856             break;
    857 
    858         case 0x42:              /* FT2 sets output freq with this, go figure */
    859 #if 0
    860             LogFlowFunc(("cmd 0x42 might not do what it think it should\n"));
    861 #endif
    862         case 0x41:
    863             pThis->freq = dsp_get_hilo(pThis);
    864             LogFlowFunc(("set freq %d\n", pThis->freq));
    865             break;
    866 
    867         case 0x48:
    868             pThis->block_size = dsp_get_lohi(pThis) + 1;
    869             LogFlowFunc(("set dma block len %d\n", pThis->block_size));
    870             break;
    871 
    872         case 0x74:
    873         case 0x75:
    874         case 0x76:
    875         case 0x77:
    876             /* ADPCM stuff, ignore */
    877             break;
    878 
    879         case 0x80:
    880         {
    881             uint32_t const freq     = pThis->freq > 0 ? pThis->freq : 11025;
    882             uint32_t const samples  = dsp_get_lohi(pThis) + 1;
    883             uint32_t const bytes    = samples << pThis->fmt_stereo << (pThis->fmt_bits == 16 ? 1 : 0);
    884             uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIRQ);
    885             uint64_t const cTicks   = (bytes * uTimerHz) / freq;
    886             if (cTicks < uTimerHz / 1024)
    887                 PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1);
    888             else
    889                 PDMDevHlpTimerSetRelative(pDevIns, pThis->hTimerIRQ, cTicks, NULL);
    890             LogFlowFunc(("mix silence: %d samples, %d bytes, %RU64 ticks\n", samples, bytes, cTicks));
    891             break;
    892         }
    893 
    894         case 0xe0:
    895             d0 = dsp_get_data(pThis);
    896             pThis->out_data_len = 0;
    897             LogFlowFunc(("E0 data = %#x\n", d0));
    898             dsp_out_data(pThis, ~d0);
    899             break;
    900 
    901         case 0xe2:
    902             d0 = dsp_get_data(pThis);
    903             LogFlow(("SB16:E2 = %#x\n", d0));
    904             break;
    905 
    906         case 0xe4:
    907             pThis->test_reg = dsp_get_data(pThis);
    908             break;
    909 
    910         case 0xf9:
    911             d0 = dsp_get_data(pThis);
    912             LogFlowFunc(("command 0xf9 with %#x\n", d0));
    913             switch (d0) {
    914             case 0x0e:
    915                 dsp_out_data(pThis, 0xff);
    916                 break;
    917 
    918             case 0x0f:
    919                 dsp_out_data(pThis, 0x07);
    920                 break;
    921 
    922             case 0x37:
    923                 dsp_out_data(pThis, 0x38);
    924                 break;
    925 
    926             default:
    927                 dsp_out_data(pThis, 0x00);
    928                 break;
    929             }
    930             break;
    931 
    932         default:
    933             LogFlowFunc(("complete: unrecognized command %#x\n", pThis->cmd));
    934             return;
    935         }
    936     }
    937 
    938     LogFlow(("\n"));
     1183    }
     1184
    9391185    pThis->cmd = -1;
    9401186    return;
     
    9501196    pThis->fmt_stereo = 0;
    9511197
    952     /* At the moment we only have one stream, the output stream. */
    953     PSB16STREAM pStream = &pThis->StreamOut;
    954 
    955     pStream->Cfg.enmDir    = PDMAUDIODIR_OUT;
    956     pStream->Cfg.u.enmDst  = PDMAUDIOPLAYBACKDST_FRONT;
    957     pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
    958 
    959     PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, pThis->freq);
    960     RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
    961 
    962     sb16StreamClose(pThis, pStream);
     1198    /*
     1199     * Reset all streams.
     1200     */
     1201    for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
     1202    {
     1203        sb16StreamEnable(pThis, &pThis->aStreams[i], false /* fEnable */, false /* fForce */);
     1204        sb16StreamReset(pThis, &pThis->aStreams[i]);
     1205    }
    9631206}
    9641207
     
    9741217    pThis->mixer_regs[0x82] = 0;
    9751218    pThis->dma_auto = 0;
    976     pThis->in_index = 0;
    977     pThis->out_data_len = 0;
     1219    pThis->dsp_in_idx = 0;
     1220    pThis->dsp_out_data_len = 0;
    9781221    pThis->left_till_irq = 0;
    979     pThis->needed_bytes = 0;
     1222    pThis->dsp_in_needed_bytes = 0;
    9801223    pThis->block_size = -1;
    9811224    pThis->nzero = 0;
     
    9841227    pThis->cmd = -1;
    9851228
    986     dsp_out_data(pThis, 0xaa);
     1229    dsp_set_data(pThis, 0xaa);
    9871230    sb16SpeakerControl(pThis, 0);
    9881231
     
    10361279
    10371280                case 0x39:
    1038                     dsp_out_data(pThis, 0x38);
     1281                    dsp_set_data(pThis, 0x38);
    10391282                    sb16CmdReset(pDevIns, pThis);
    10401283                    pThis->v2x6 = 0x39;
     
    10521295                break;
    10531296#endif
    1054             if (0 == pThis->needed_bytes)
     1297            if (0 == pThis->dsp_in_needed_bytes)
    10551298            {
    10561299                sb16HandleCommand(pDevIns, pThis, u32);
     
    10631306            else
    10641307            {
    1065                 if (pThis->in_index == sizeof (pThis->in2_data))
     1308                if (pThis->dsp_in_idx == sizeof (pThis->dsp_in_data))
    10661309                {
    1067                     LogFlowFunc(("in data overrun\n"));
     1310                    AssertMsgFailed(("DSP input data overrun\n"));
    10681311                }
    10691312                else
    10701313                {
    1071                     pThis->in2_data[pThis->in_index++] = u32;
    1072                     if (pThis->in_index == pThis->needed_bytes)
     1314                    pThis->dsp_in_data[pThis->dsp_in_idx++] = u32;
     1315                    if (pThis->dsp_in_idx == pThis->dsp_in_needed_bytes)
    10731316                    {
    1074                         pThis->needed_bytes = 0;
    1075                         complete(pDevIns, pThis);
     1317                        pThis->dsp_in_needed_bytes = 0;
     1318                        dsp_cmd_complete(pDevIns, pThis);
    10761319#if 0
    10771320                        log_dsp (pThis);
     
    11121355
    11131356        case 4:                     /* read data */
    1114             if (pThis->out_data_len)
     1357            if (pThis->dsp_out_data_len)
    11151358            {
    1116                 retval = pThis->out_data[--pThis->out_data_len];
     1359                retval = pThis->dsp_out_data[--pThis->dsp_out_data_len];
    11171360                pThis->last_read_byte = retval;
    11181361            }
     
    11361379
    11371380        case 8:                     /* data available status | irq 8 ack */
    1138             retval = (!pThis->out_data_len || pThis->highspeed) ? 0 : 0x80;
     1381            retval = (!pThis->dsp_out_data_len || pThis->highspeed) ? 0 : 0x80;
    11391382            if (pThis->mixer_regs[0x82] & 1)
    11401383            {
     
    11681411
    11691412
    1170 /* -=-=-=-=-=- Mixer -=-=-=-=-=- */
     1413/*********************************************************************************************************************************
     1414*   Mixer functions                                                                                                              *
     1415*********************************************************************************************************************************/
    11711416
    11721417static uint8_t sb16MixRegToVol(PSB16STATE pThis, int reg)
     
    12741519    /* Update the master (mixer) and PCM out volumes. */
    12751520    sb16UpdateVolume(pThis);
     1521
     1522    /*
     1523     * Reset mixer sinks.
     1524     *
     1525     * Do the reset here instead of in sb16StreamReset();
     1526     * the mixer sink(s) might still have data to be processed when an audio stream gets reset.
     1527     */
     1528    if (pThis->pSinkOut)
     1529        AudioMixerSinkReset(pThis->pSinkOut);
    12761530}
    12771531
     
    15491803
    15501804
    1551 /* -=-=-=-=-=- DMA -=-=-=-=-=- */
     1805/*********************************************************************************************************************************
     1806*   DMA handling                                                                                                                 *
     1807*********************************************************************************************************************************/
    15521808
    15531809/**
    15541810 * Worker for sb16DMARead.
    15551811 */
    1556 static int sb16WriteAudio(PSB16STATE pThis,
    1557                           PSB16STREAM pStream, int nchan, uint32_t dma_pos, uint32_t dma_len, uint32_t len, uint32_t *pcbWritten)
    1558 {
    1559     uint8_t abBuf[_4K]; /** @todo Have a buffer on the heap. */
    1560 
    1561     uint32_t cbToWrite      = len;
    1562     uint32_t cbWrittenTotal = 0;
    1563 
    1564     PAUDMIXSINK pDstMixSink = pThis->pSinkOut;
    1565     AssertPtrReturn(pDstMixSink, VERR_INVALID_POINTER);
    1566 
    1567     int rc = VINF_SUCCESS;
    1568 
    1569     while (cbToWrite)
    1570     {
    1571         uint32_t cbToRead = RT_MIN(dma_len - dma_pos, cbToWrite);
    1572         if (cbToRead > (int)sizeof(abBuf)) /** @todo BUGBUG Clean this up. */
    1573             cbToRead = (int)sizeof(abBuf);
    1574 
    1575         uint32_t cbRead = 0;
    1576         rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, nchan, abBuf, dma_pos, cbToRead, &cbRead);
    1577         AssertMsgRCReturn(rc, ("Reading from DMA failed, rc=%Rrc\n", rc), rc);
    1578 
    1579         if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
    1580         { /* likely */ }
    1581         else
    1582             AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, abBuf, cbRead, 0 /* fFlags */);
    1583 
    1584         /*
    1585          * Write data to the backends.
    1586          */
    1587         uint32_t cbWritten = 0;
    1588         rc = AudioMixerSinkWrite(pDstMixSink, AUDMIXOP_COPY, abBuf, cbRead, &cbWritten);
    1589         if (   !cbWritten /* Nothing written? */
    1590             || RT_FAILURE(rc))
    1591             break;
    1592 
    1593         Assert(cbToWrite >= cbWritten);
    1594         cbToWrite      -= cbWritten;
    1595         dma_pos         = (dma_pos + cbWritten) % dma_len;
    1596         cbWrittenTotal += cbWritten;
    1597     }
    1598 
    1599     if (pcbWritten)
    1600         *pcbWritten = cbWrittenTotal;
    1601 
    1602     return rc;
    1603 }
    16041812
    16051813/**
     
    16101818
    16111819{
    1612     PSB16STATE  pThis    = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
     1820    PSB16STATE  pThis   = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
    16131821    AssertPtr(pThis);
    16141822    PSB16STREAM pStream = (PSB16STREAM)pvUser;
     
    16311839    till = pThis->left_till_irq;
    16321840
    1633 #ifdef DEBUG_SB16_MOST
    1634     LogFlowFunc(("pos:%06d %d till:%d len:%d\n", off, free, till, cb));
    1635 #endif
     1841    Log4Func(("pos=%d %d, till=%d, len=%d\n", off, free, till, cb));
    16361842
    16371843    if (copy >= till)
     
    16501856    STAM_COUNTER_ADD(&pThis->StatBytesRead, copy);
    16511857
    1652     uint32_t cbWritten;
    1653     int rc = sb16WriteAudio(pThis, pStream, uChannel, off, cb, copy, &cbWritten);
     1858    uint32_t written = 0; /* Shut up GCC. */
     1859    int rc = sb16StreamDoDmaOutput(pThis, pStream, uChannel, off, cb, copy, &written);
    16541860    AssertRC(rc);
    16551861
    16561862    /** @todo Convert the rest to uin32_t / size_t. */
    1657     off = (off + (int)cbWritten) % cb;
    1658     pThis->left_till_irq -= (int)cbWritten;
     1863    off = (off + (int)written) % cb;
     1864    pThis->left_till_irq -= (int)written; /** @todo r=andy left_till_irq can be < 0. Correct? Revisit this. */
     1865
     1866    Log3Func(("pos %d/%d, free=%d, till=%d, copy=%d, written=%RU32, block_size=%d\n",
     1867              off, cb, free, pThis->left_till_irq, copy, copy, pThis->block_size));
    16591868
    16601869    if (pThis->left_till_irq <= 0)
    16611870    {
    16621871        pThis->mixer_regs[0x82] |= (uChannel & 4) ? 2 : 1;
     1872
    16631873        PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
    1664         if (0 == pThis->dma_auto)
     1874
     1875        if (0 == pThis->dma_auto) /** @todo r=andy BUGBUG Why do we first assert the IRQ if dma_auto is 0? Revisit this. */
    16651876        {
    16661877            sb16Control(pDevIns, pThis, 0);
     
    16691880    }
    16701881
    1671     Log3Func(("pos %d/%d, free=%5d, till=%5d, copy=%5d, written=%RU32, block_size=%5d\n",
    1672               off, cb, free, pThis->left_till_irq, copy, cbWritten, pThis->block_size));
    1673 
    16741882    while (pThis->left_till_irq <= 0)
    16751883        pThis->left_till_irq += pThis->block_size;
     
    16791887
    16801888
    1681 /* -=-=-=-=-=- I/O timer -=-=-=-=-=- */
    1682 
    1683 static void sb16TimerMaybeStart(PPDMDEVINS pDevIns, PSB16STATE pThis)
    1684 {
    1685     LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
    1686 
    1687     if (pThis->cStreamsActive == 0) /* Only start the timer if there are no active streams. */
    1688         return;
    1689 
    1690     /* Set timer flag. */
    1691     ASMAtomicWriteBool(&pThis->fTimerActive, true);
    1692 
    1693     /* Update current time timestamp. */
    1694     uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pThis->hTimerIO);
    1695     pThis->tsTimerIO = tsNow;
    1696 
    1697     /* Arm the timer. */
    1698     PDMDevHlpTimerSet(pDevIns, pThis->hTimerIO, tsNow + pThis->cTicksTimerIOInterval);
    1699 }
    1700 
    1701 /**
    1702  * This clears fTimerActive if no streams are active, so that the timer won't be
    1703  * rearmed then next time it fires.
    1704  */
    1705 static void sb16TimerMaybeStop(PSB16STATE pThis)
    1706 {
    1707     LogFlowFunc(("cStreamsActive=%RU8\n", pThis->cStreamsActive));
    1708 
    1709     if (pThis->cStreamsActive) /* Some streams still active? Bail out. */
    1710         return;
    1711 
    1712     /* Clear the timer flag. */
    1713     ASMAtomicWriteBool(&pThis->fTimerActive, false);
     1889/*********************************************************************************************************************************
     1890*   Timer-related code                                                                                                           *
     1891*********************************************************************************************************************************/
     1892
     1893/**
     1894 * @callback_method_impl{PFNTMTIMERDEV}
     1895 */
     1896static DECLCALLBACK(void) sb16TimerIRQ(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
     1897{
     1898    PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
     1899    RT_NOREF(pvUser, hTimer);
     1900
     1901    LogFlowFuncEnter();
     1902
     1903    pThis->can_write = 1;
     1904    PDMDevHlpISASetIrq(pDevIns, pThis->irq, 1);
     1905}
     1906
     1907/**
     1908 * Sets the stream's I/O timer to a new expiration time.
     1909 *
     1910 * @param   pDevIns             The device instance.
     1911 * @param   pStream             SB16 stream to set timer for.
     1912 * @param   cTicksToDeadline    The number of ticks to the new deadline.
     1913 */
     1914DECLINLINE(void) sb16TimerSet(PPDMDEVINS pDevIns, PSB16STREAM pStream, uint64_t cTicksToDeadline)
     1915{
     1916    int rc = PDMDevHlpTimerSetRelative(pDevIns, pStream->hTimerIO, cTicksToDeadline, NULL /*pu64Now*/);
     1917    AssertRC(rc);
    17141918}
    17151919
     
    17191923static DECLCALLBACK(void) sb16TimerIO(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
    17201924{
    1721     RT_NOREF(pvUser);
     1925    PSB16STREAM pStream = (PSB16STREAM)pvUser;
     1926    AssertPtrReturnVoid(pStream);
    17221927
    17231928    PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
    17241929    STAM_PROFILE_START(&pThis->StatTimerIO, a);
    1725     Assert(hTimer == pThis->hTimerIO);
    1726 
    1727     uint64_t cTicksNow     = PDMDevHlpTimerGet(pDevIns, hTimer);
    1728 
    1729     pThis->tsTimerIO = cTicksNow;
    1730 
    1731     int rc2 = AudioMixerSinkUpdate(pThis->pSinkOut);
    1732     AssertRC(rc2);
    1733 
    1734     bool fTimerActive = ASMAtomicReadBool(&pThis->fTimerActive);
    1735     bool fArmTimer    = fTimerActive;
     1930    AssertReturnVoid(hTimer == pStream->hTimerIO);
     1931
     1932    const uint64_t cTicksNow = PDMDevHlpTimerGet(pDevIns, pStream->hTimerIO);
     1933
     1934    pStream->tsTimerIO = cTicksNow;
     1935
     1936    PAUDMIXSINK pSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
     1937    AssertPtrReturnVoid(pSink);
     1938
     1939    const bool fSinkActive = AudioMixerSinkIsActive(pSink);
     1940
     1941    LogFlowFunc(("fSinkActive=%RTbool\n", fSinkActive));
     1942
     1943#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     1944    sb16StreamAsyncIONotify(pDevIns, pStream);
     1945#else
     1946    sb16StreamUpdate(pStream, pSink);
     1947#endif
    17361948
    17371949    /* Schedule the next transfer. */
    17381950    PDMDevHlpDMASchedule(pDevIns);
    17391951
    1740     /* Arm the timer at least one more time. */
    1741     fArmTimer = true;
    1742 
    1743     /*
    1744      * Recording.
    1745      */
    1746     /** @todo Implement recording. */
    1747 
    1748     if (fArmTimer)
    1749     {
    1750         /* Arm the timer again. */
    1751         uint64_t cTicks = pThis->cTicksTimerIOInterval;
     1952    if (fSinkActive)
     1953    {
    17521954        /** @todo adjust cTicks down by now much cbOutMin represents. */
    1753         PDMDevHlpTimerSet(pDevIns, hTimer, cTicksNow + cTicks);
     1955        sb16TimerSet(pDevIns, pStream, pStream->cTicksTimerIOInterval);
    17541956    }
    17551957
     
    17581960
    17591961
    1760 /* -=-=-=-=-=- Streams? -=-=-=-=-=- */
     1962/*********************************************************************************************************************************
     1963*   Driver handling                                                                                                              *
     1964*********************************************************************************************************************************/
    17611965
    17621966/**
     
    19052109        if (pDrvStream->pMixStrm)
    19062110        {
    1907             LogFlowFunc(("[LUN#%RU8]\n", pDrv));
     2111            LogFlowFunc(("[LUN#%RU8]\n", pDrv->uLUN));
    19082112
    19092113            AudioMixerSinkRemoveStream(pMixSink, pDrvStream->pMixStrm);
     
    19462150    int rc = VINF_SUCCESS;
    19472151
    1948     if (AudioHlpStreamCfgIsValid(&pThis->StreamOut.Cfg))
    1949         rc = sb16AddDrvStream(pDevIns, pThis->pSinkOut, &pThis->StreamOut.Cfg, pDrv);
     2152    for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
     2153    {
     2154        if (AudioHlpStreamCfgIsValid(&pThis->aStreams[i].Cfg))
     2155        {
     2156            int rc2 = sb16AddDrvStream(pDevIns,
     2157                                       sb16StreamIndexToSink(pThis, pThis->aStreams[i].uIdx), &pThis->aStreams[i].Cfg, pDrv);
     2158            if (RT_SUCCESS(rc))
     2159                rc = rc2;
     2160        }
     2161    }
    19502162
    19512163    return rc;
     
    19762188}
    19772189
    1978 /**
    1979  * Checks if the output stream needs to be (re-)created and does so if needed.
    1980  *
    1981  * @return  VBox status code.
    1982  * @param   pDevIns         The device instance.
    1983  * @param   pThis           SB16 state.
    1984  */
    1985 static int sb16CheckAndReOpenOut(PPDMDEVINS pDevIns, PSB16STATE pThis)
    1986 {
    1987     AssertPtr(pThis);
     2190
     2191/*********************************************************************************************************************************
     2192*   Stream handling                                                                                                              *
     2193*********************************************************************************************************************************/
     2194
     2195static int sb16StreamDoDmaOutput(PSB16STATE pThis, PSB16STREAM pStream,
     2196                                 int uDmaChan, uint32_t offDma, uint32_t cbDma, uint32_t cbToRead, uint32_t *pcbRead)
     2197{
     2198    uint32_t cbFree      = (uint32_t)RTCircBufFree(pStream->State.pCircBuf);
     2199    uint32_t cbReadTotal = 0;
     2200
     2201    //Assert(cbToRead <= cbFree); /** @todo Add STAM value for overflows. */
     2202
     2203    cbToRead = RT_MIN(cbToRead, cbFree);
    19882204
    19892205    int rc = VINF_SUCCESS;
    19902206
    1991     PSB16STREAM pStream = &pThis->StreamOut;
    1992 
    1993     if (pThis->freq > 0)
    1994     {
    1995         sb16StreamEnable(pDevIns, pThis, pStream, false /* fEnable */);
    1996 
    1997         sb16StreamEnable(pDevIns, pThis, pStream, true /* fEnable */);
    1998     }
    1999     else
    2000         sb16StreamEnable(pDevIns, pThis, pStream, false /* fEnable */);
    2001 
    2002     LogFlowFuncLeaveRC(rc);
     2207    void  *pv;
     2208    size_t cb;
     2209
     2210    while (cbToRead)
     2211    {
     2212        uint32_t cbChunk = RT_MIN(cbDma - offDma, cbToRead);
     2213
     2214        RTCircBufAcquireWriteBlock(pStream->State.pCircBuf, cbChunk, &pv, &cb);
     2215
     2216        uint32_t cbRead;
     2217        rc = PDMDevHlpDMAReadMemory(pThis->pDevInsR3, uDmaChan, pv, offDma, cb, &cbRead);
     2218        AssertMsgRCReturn(rc, ("Reading from DMA failed, rc=%Rrc\n", rc), rc);
     2219        Assert(cbRead == cb);
     2220
     2221        if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
     2222        { /* likely */ }
     2223        else
     2224            AudioHlpFileWrite(pStream->Dbg.Runtime.pFileDMA, pv, cbRead, 0 /* fFlags */);
     2225
     2226        RTCircBufReleaseWriteBlock(pStream->State.pCircBuf, cbRead);
     2227
     2228        Assert(cbToRead >= cbRead);
     2229        cbToRead    -= cbRead;
     2230        offDma       = (offDma + cbRead) % cbDma;
     2231        cbReadTotal += cbRead;
     2232    }
     2233
     2234    if (pcbRead)
     2235        *pcbRead = cbReadTotal;
     2236
    20032237    return rc;
    20042238}
     
    20082242 *
    20092243 * @returns VBox status code.
    2010  * @param   pDevIns     The device instance.
    20112244 * @param   pThis       The SB16 state.
    20122245 * @param   pStream     The SB16 stream to enable or disable.
    20132246 * @param   fEnable     Whether to enable or disable the stream.
    2014  */
    2015 static int sb16StreamEnable(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream, bool fEnable)
    2016 {
    2017     int rc = VINF_SUCCESS;
    2018 
    2019     if (fEnable)
     2247 * @param   fForce      Whether to force re-opening the stream or not.
     2248 *                      Otherwise re-opening only will happen if the PCM properties have changed.
     2249 */
     2250static int sb16StreamEnable(PSB16STATE pThis, PSB16STREAM pStream, bool fEnable, bool fForce)
     2251{
     2252    if (   !fForce
     2253        && fEnable == pStream->State.fEnabled)
     2254        return VINF_SUCCESS;
     2255
     2256    LogFlowFunc(("fEnable=%RTbool, fForce=%RTbool, fStreamEnabled=%RTbool\n", fEnable, fForce, pStream->State.fEnabled));
     2257
     2258    /* First, enable or disable the stream and the stream's sink. */
     2259    int rc = AudioMixerSinkCtl(sb16StreamIndexToSink(pThis, pStream->uIdx),
     2260                               fEnable ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE);
     2261    AssertRCReturn(rc, rc);
     2262
     2263    pStream->State.fEnabled = fEnable;
     2264
     2265    return rc;
     2266}
     2267
     2268/**
     2269 * Retrieves the audio mixer sink of a corresponding SB16 stream.
     2270 *
     2271 * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid.
     2272 * @param   pThis               The SB16 state.
     2273 * @param   uIdx                Stream index to get audio mixer sink for.
     2274 */
     2275DECLINLINE(PAUDMIXSINK) sb16StreamIndexToSink(PSB16STATE pThis, uint8_t uIdx)
     2276{
     2277    AssertReturn(uIdx <= SB16_MAX_STREAMS, NULL);
     2278
     2279    /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
     2280    if (uIdx == SB16_IDX_OUT)
     2281        return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */
     2282
     2283    AssertMsgFailed(("No sink attached (yet) for index %RU8\n", uIdx));
     2284    return NULL;
     2285}
     2286
     2287/**
     2288 * Returns the audio direction of a specified stream descriptor.
     2289 *
     2290 * @returns Audio direction.
     2291 * @param   uIdx                Stream index to get audio direction for.
     2292 */
     2293DECLINLINE(PDMAUDIODIR) sb16GetDirFromIndex(uint8_t uIdx)
     2294{
     2295    AssertReturn(uIdx <= SB16_MAX_STREAMS, PDMAUDIODIR_INVALID);
     2296
     2297    /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
     2298    if (uIdx == SB16_IDX_OUT)
     2299        return PDMAUDIODIR_OUT;
     2300
     2301    return PDMAUDIODIR_INVALID;
     2302}
     2303
     2304/**
     2305 * Creates a SB16 audio stream.
     2306 *
     2307 * @returns VBox status code.
     2308 * @param   pThisCC             The SB16 state.
     2309 * @param   pStream             The SB16 stream to create.
     2310 * @param   uIdx                Stream index to assign.
     2311 */
     2312static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, uint8_t uIdx)
     2313{
     2314    LogFlowFuncEnter();
     2315
     2316    pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
     2317
     2318    int rc2;
     2319
     2320#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     2321    rc2 = sb16StreamAsyncIOCreate(pThis->pDevInsR3, pThis, pStream);
     2322    AssertRCReturn(rc2, rc2);
     2323#endif
     2324
     2325    if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
     2326    { /* likely */ }
     2327    else
     2328    {
     2329        char szFile[64];
     2330
     2331        if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_IN)
     2332            RTStrPrintf(szFile, sizeof(szFile), "sb16StreamWriteSD%RU8", pStream->uIdx);
     2333        else
     2334            RTStrPrintf(szFile, sizeof(szFile), "sb16StreamReadSD%RU8", pStream->uIdx);
     2335
     2336        char szPath[RTPATH_MAX];
     2337        rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThis->Dbg.pszOutPath, szFile,
     2338                                  0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
     2339        AssertRC(rc2);
     2340        rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStream->Dbg.Runtime.pFileDMA);
     2341        AssertRC(rc2);
     2342
     2343        /* Delete stale debugging files from a former run. */
     2344        AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
     2345    }
     2346
     2347    pStream->uIdx = uIdx;
     2348
     2349    return VINF_SUCCESS;
     2350}
     2351
     2352/**
     2353 * Destroys a SB16 audio stream.
     2354 *
     2355 * @returns VBox status code.
     2356 * @param   pDevIns             The device instance.
     2357 * @param   pThis               The SB16 state.
     2358 * @param   pStream             The SB16 stream to destroy.
     2359 */
     2360static int sb16StreamDestroy(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
     2361{
     2362    LogFlowFuncEnter();
     2363
     2364    sb16StreamClose(pDevIns, pThis, pStream);
     2365
     2366#ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     2367    int rc = sb16StreamAsyncIODestroy(pDevIns, pStream);
     2368    AssertRCReturn(rc, rc);
     2369#else
     2370    RT_NOREF(pDevIns);
     2371#endif
     2372
     2373    if (pStream->State.pCircBuf)
     2374    {
     2375        RTCircBufDestroy(pStream->State.pCircBuf);
     2376        pStream->State.pCircBuf = NULL;
     2377    }
     2378
     2379    if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
     2380    { /* likely */ }
     2381    else
     2382    {
     2383        AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA);
     2384        pStream->Dbg.Runtime.pFileDMA = NULL;
     2385    }
     2386
     2387    pStream->uIdx = UINT8_MAX;
     2388
     2389    return VINF_SUCCESS;
     2390}
     2391
     2392/**
     2393 * Resets a SB16 stream.
     2394 *
     2395 * @param   pThis               The SB16 state.
     2396 * @param   pStream             The SB16 stream to reset.
     2397 */
     2398static void sb16StreamReset(PSB16STATE pThis, PSB16STREAM pStream)
     2399{
     2400    LogFlowFuncEnter();
     2401
     2402    switch (pStream->uIdx)
     2403    {
     2404        case SB16_IDX_OUT:
     2405        {
     2406            pStream->Cfg.enmDir    = PDMAUDIODIR_OUT;
     2407            pStream->Cfg.u.enmDst  = PDMAUDIOPLAYBACKDST_FRONT;
     2408            pStream->Cfg.enmLayout = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
     2409
     2410            PDMAudioPropsInit(&pStream->Cfg.Props, 1 /* 8-bit */, false /* fSigned */, 1 /* Mono */, pThis->freq);
     2411            RTStrCopy(pStream->Cfg.szName, sizeof(pStream->Cfg.szName), "Output");
     2412
     2413            break;
     2414        }
     2415
     2416        default:
     2417            AssertFailed();
     2418            break;
     2419    }
     2420
     2421    /** @todo Also reset corresponding DSP values here? */
     2422}
     2423
     2424/**
     2425 * Opens a SB16 stream with its current mixer settings.
     2426 *
     2427 * @returns VBox status code.
     2428 * @param   pDevIns     The device instance.
     2429 * @param   pThis       The SB16 device state.
     2430 * @param   pStream     The SB16 stream to open.
     2431 *
     2432 * @note    This currently only supports the one and only output stream.
     2433 */
     2434static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
     2435{
     2436    LogFlowFuncEnter();
     2437
     2438    PDMAUDIOSTREAMCFG Cfg;
     2439    RT_ZERO(Cfg);
     2440
     2441    /** @todo Implement mixer sink selection (and it's in/out + destination mapping) here once we add more streams. */
     2442    PDMAudioPropsInit(&Cfg.Props, pThis->fmt_bits / 8, pThis->fmt_signed != 0, 1 << pThis->fmt_stereo, pThis->freq);
     2443
     2444    /* Bail out early if the PCM properties did not change. */
     2445    if (PDMAudioPropsAreEqual(&Cfg.Props, &pStream->Cfg.Props))
     2446        return VINF_SUCCESS;
     2447
     2448    PDMAUDIODSTSRCUNION dstSrc;
     2449    PDMAUDIODIR         enmDir;
     2450
     2451    switch (pStream->uIdx)
     2452    {
     2453        case SB16_IDX_OUT:
     2454        {
     2455            Cfg.enmDir      = PDMAUDIODIR_OUT;
     2456            Cfg.u.enmDst    = PDMAUDIOPLAYBACKDST_FRONT;
     2457            Cfg.enmLayout   = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
     2458
     2459            RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Output");
     2460
     2461            dstSrc.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
     2462            enmDir        = PDMAUDIODIR_OUT;
     2463            break;
     2464        }
     2465
     2466        default:
     2467            AssertFailed();
     2468            break;
     2469    }
     2470
     2471    LogRel2(("SB16: (Re-)Opening stream '%s' (%RU32Hz, %RU8 channels, %s%RU8)\n", Cfg.szName, Cfg.Props.uHz,
     2472             PDMAudioPropsChannels(&Cfg.Props), Cfg.Props.fSigned ? "S" : "U",  PDMAudioPropsSampleBits(&Cfg.Props)));
     2473
     2474    int rc = PDMAudioStrmCfgCopy(&pStream->Cfg, &Cfg);
     2475    if (RT_FAILURE(rc))
     2476        return rc;
     2477
     2478    /* (Re-)create the stream's internal ring buffer. */
     2479    if (pStream->State.pCircBuf)
     2480    {
     2481        RTCircBufDestroy(pStream->State.pCircBuf);
     2482        pStream->State.pCircBuf = NULL;
     2483    }
     2484
     2485    const uint32_t cbCircBuf = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, 2 * 10 /* ms */);
     2486
     2487    rc = RTCircBufCreate(&pStream->State.pCircBuf, cbCircBuf);
     2488    AssertRCReturn(rc, rc);
     2489
     2490    /* Set scheduling hint (if available). */
     2491    if (pStream->cTicksTimerIOInterval)
     2492        pStream->Cfg.Device.cMsSchedulingHint = 1000 /* ms */
     2493                                              / (  PDMDevHlpTimerGetFreq(pDevIns, pStream->hTimerIO)
     2494                                              / RT_MIN(pStream->cTicksTimerIOInterval, 1));
     2495
     2496    PAUDMIXSINK pMixerSink = sb16StreamIndexToSink(pThis, pStream->uIdx);
     2497    AssertPtrReturn(pMixerSink, VERR_INVALID_POINTER);
     2498
     2499    sb16RemoveDrvStreams(pDevIns, pThis,
     2500                         sb16StreamIndexToSink(pThis, pStream->uIdx), pStream->Cfg.enmDir, pStream->Cfg.u);
     2501
     2502    rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
     2503
     2504    if (RT_SUCCESS(rc))
    20202505    {
    20212506        if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
     
    20232508        else
    20242509        {
    2025             if (!AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
     2510            /* Make sure to close + delete a former debug file, as the PCM format has changed (e.g. U8 -> S16). */
     2511            if (AudioHlpFileIsOpen(pStream->Dbg.Runtime.pFileDMA))
    20262512            {
    2027                 int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
    2028                                            &pStream->Cfg.Props);
    2029                 AssertRC(rc2);
     2513                AudioHlpFileClose(pStream->Dbg.Runtime.pFileDMA);
     2514                AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
    20302515            }
     2516
     2517            int rc2 = AudioHlpFileOpen(pStream->Dbg.Runtime.pFileDMA, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS,
     2518                                       &pStream->Cfg.Props);
     2519            AssertRC(rc2);
    20312520        }
    2032 
    2033         rc = sb16StreamOpen(pDevIns, pThis, pStream);
    2034     }
    2035     else
    2036         sb16StreamClose(pThis, pStream);
    2037 
    2038     if (RT_SUCCESS(rc))
    2039     {
    2040         rc = AudioMixerSinkCtl(sb16StreamToSink(pThis, pStream),
    2041                                fEnable ? AUDMIXSINKCMD_ENABLE : AUDMIXSINKCMD_DISABLE);
    2042     }
    2043 
    2044     return rc;
    2045 }
    2046 
    2047 /**
    2048  * Retrieves the audio mixer sink of a corresponding SB16 stream.
    2049  *
    2050  * @returns Pointer to audio mixer sink if found, or NULL if not found / invalid.
    2051  * @param   pThis               The SB16 state.
    2052  * @param   pStream             Stream to get audio mixer sink for.
    2053  */
    2054 DECLINLINE(PAUDMIXSINK) sb16StreamToSink(PSB16STATE pThis, PSB16STREAM pStream)
    2055 {
    2056     /* Dead simple for now; make this more sophisticated if we have more stuff to cover. */
    2057     if (pStream->Cfg.enmDir == PDMAUDIODIR_OUT)
    2058         return pThis->pSinkOut; /* Can be NULL if not configured / set up yet. */
    2059 
    2060     AssertFailed();
    2061     return NULL;
    2062 }
    2063 
    2064 /**
    2065  * Creates a SB16 audio stream.
    2066  *
    2067  * @returns VBox status code.
    2068  * @param   pThisCC             The SB16 state.
    2069  * @param   pStream             The SB16 stream to create.
    2070  * @param   fIn                 Whether this is the input (recording) or output (playback) stream.
    2071  */
    2072 static int sb16StreamCreate(PSB16STATE pThis, PSB16STREAM pStream, bool fIn)
    2073 {
    2074     LogFlowFuncEnter();
    2075 
    2076     pStream->Dbg.Runtime.fEnabled = pThis->Dbg.fEnabled;
    2077 
    2078     if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
    2079     { /* likely */ }
    2080     else
    2081     {
    2082         char szFile[64];
    2083         RTStrPrintf(szFile, sizeof(szFile), "sb16Stream%s", fIn ? "In" : "Out");
    2084 
    2085         char szPath[RTPATH_MAX];
    2086         int rc2 = AudioHlpFileNameGet(szPath, sizeof(szPath), pThis->Dbg.pszOutPath, szFile,
    2087                                       0 /* uInst */, AUDIOHLPFILETYPE_WAV, AUDIOHLPFILENAME_FLAGS_NONE);
    2088         AssertRC(rc2);
    2089         rc2 = AudioHlpFileCreate(AUDIOHLPFILETYPE_WAV, szPath, AUDIOHLPFILE_FLAGS_NONE, &pStream->Dbg.Runtime.pFileDMA);
    2090         AssertRC(rc2);
    2091 
    2092         /* Delete stale debugging files from a former run. */
    2093         AudioHlpFileDelete(pStream->Dbg.Runtime.pFileDMA);
    2094     }
    2095 
    2096     return VINF_SUCCESS;
    2097 }
    2098 
    2099 /**
    2100  * Destroys a SB16 audio stream.
    2101  *
    2102  * @returns VBox status code.
    2103  * @param   pThis               The SB16 state.
    2104  * @param   pStream             The SB16 stream to destroy.
    2105  */
    2106 static int sb16StreamDestroy(PSB16STATE pThis, PSB16STREAM pStream)
    2107 {
    2108     RT_NOREF(pThis);
    2109 
    2110     LogFlowFuncEnter();
    2111 
    2112     sb16StreamClose(pThis, pStream);
    2113 
    2114     if (RT_LIKELY(!pStream->Dbg.Runtime.fEnabled))
    2115     { /* likely */ }
    2116     else
    2117     {
    2118         AudioHlpFileDestroy(pStream->Dbg.Runtime.pFileDMA);
    2119         pStream->Dbg.Runtime.pFileDMA = NULL;
    2120     }
    2121 
    2122     return VINF_SUCCESS;
    2123 }
    2124 
    2125 /**
    2126  * Opens a SB16 stream with its current mixer settings.
    2127  *
    2128  * @returns VBox status code.
    2129  * @param   pDevIns     The device instance.
    2130  * @param   pThis       The SB16 device state.
    2131  * @param   pStream     The SB16 stream to open.
    2132  *
    2133  * @note    This currently only supports the one and only output stream.
    2134  */
    2135 static int sb16StreamOpen(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
    2136 {
    2137     LogFlowFuncEnter();
    2138 
    2139     PAUDMIXSINK         pMixerSink;
    2140     PDMAUDIODSTSRCUNION dstSrc;
    2141     PDMAUDIODIR         enmDir;
    2142 
    2143     int rc = VINF_SUCCESS;
    2144 
    2145     {
    2146         /** @todo Implement mixer sink selection (and it's in/out + destination mapping) here once we add more streams. */
    2147         PDMAUDIOSTREAMCFG Cfg;
    2148         RT_ZERO(Cfg);
    2149         PDMAudioPropsInit(&Cfg.Props, pThis->fmt_bits / 8, pThis->fmt_signed != 0, 1 << pThis->fmt_stereo, pThis->freq);
    2150 
    2151         if (!PDMAudioStrmCfgMatchesProps(&Cfg, &pStream->Cfg.Props))
    2152         {
    2153             Cfg.enmDir      = PDMAUDIODIR_OUT;
    2154             Cfg.u.enmDst    = PDMAUDIOPLAYBACKDST_FRONT;
    2155             Cfg.enmLayout   = PDMAUDIOSTREAMLAYOUT_NON_INTERLEAVED;
    2156 
    2157             RTStrCopy(Cfg.szName, sizeof(Cfg.szName), "Output");
    2158 
    2159             rc = PDMAudioStrmCfgCopy(&pStream->Cfg, &Cfg);
    2160         }
    2161 
    2162         pMixerSink    = pThis->pSinkOut;
    2163         dstSrc.enmDst = PDMAUDIOPLAYBACKDST_FRONT;
    2164         enmDir        = PDMAUDIODIR_OUT;
    2165     }
    2166 
    2167     if (RT_FAILURE(rc))
    2168         return rc;
    2169 
    2170     /* Set scheduling hint (if available). */
    2171     if (pThis->cTicksTimerIOInterval)
    2172         pStream->Cfg.Device.cMsSchedulingHint = 1000 /* ms */
    2173                                               / (  PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIO)
    2174                                               / RT_MIN(pThis->cTicksTimerIOInterval, 1));
    2175 
    2176     sb16RemoveDrvStreams(pDevIns, pThis, pMixerSink, enmDir, dstSrc);
    2177 
    2178     rc = sb16AddDrvStreams(pDevIns, pThis, pMixerSink, &pStream->Cfg);
    2179     if (RT_SUCCESS(rc))
    2180         sb16UpdateVolume(pThis);
     2521    }
    21812522
    21822523    LogFlowFuncLeaveRC(rc);
     
    21872528 * Closes a SB16 stream.
    21882529 *
     2530 * @param   pDevIns         The device instance.
    21892531 * @param   pThis           SB16 state.
    21902532 * @param   pStream         The SB16 stream to close.
    21912533 */
    2192 static void sb16StreamClose(PSB16STATE pThis, PSB16STREAM pStream)
    2193 {
    2194     RT_NOREF(pThis, pStream);
     2534static void sb16StreamClose(PPDMDEVINS pDevIns, PSB16STATE pThis, PSB16STREAM pStream)
     2535{
     2536    RT_NOREF(pDevIns, pThis, pStream);
    21952537
    21962538    LogFlowFuncEnter();
    21972539
    2198     LogFlowFuncLeave();
    2199 }
    2200 
    2201 
    2202 /* -=-=-=-=-=- Saved state -=-=-=-=-=- */
     2540    /* Nothing to do in here right now. */
     2541}
     2542
     2543static void sb16StreamTransferScheduleNext(PSB16STATE pThis, PSB16STREAM pStream, uint32_t cbBytes)
     2544{
     2545    RT_NOREF(pStream);
     2546
     2547#if 0
     2548    uint32_t const freq     = pThis->freq > 0 ? pThis->freq : 11025;
     2549    uint32_t const bytes    = cSamples << pThis->fmt_stereo << (pThis->fmt_bits == 16 ? 1 : 0);
     2550    uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ);
     2551    uint64_t const cTicks   = (bytes * uTimerHz) / freq;
     2552#endif
     2553
     2554    uint64_t const uTimerHz = PDMDevHlpTimerGetFreq(pThis->pDevInsR3, pThis->hTimerIRQ);
     2555
     2556    const uint64_t usBytes        = PDMAudioPropsBytesToMicro(&pStream->Cfg.Props, cbBytes);
     2557    const uint64_t cTransferTicks = PDMDevHlpTimerFromMicro(pThis->pDevInsR3, pThis->hTimerIRQ, usBytes);
     2558
     2559    LogFlowFunc(("%RU32 bytes -> %RU64 ticks\n", cbBytes, cTransferTicks));
     2560
     2561    if (cTransferTicks < uTimerHz / 1024) /** @todo Explain this. */
     2562    {
     2563        LogFlowFunc(("IRQ\n"));
     2564        PDMDevHlpISASetIrq(pThis->pDevInsR3, pThis->irq, 1);
     2565    }
     2566    else
     2567    {
     2568        LogFlowFunc(("Scheduled\n"));
     2569        PDMDevHlpTimerSetRelative(pThis->pDevInsR3, pThis->hTimerIRQ, cTransferTicks, NULL);
     2570    }
     2571}
     2572
     2573/**
     2574 * Main function for off-DMA data processing by the audio backend(s).
     2575 *
     2576 * Might be called by a timer callback or by an async I/O worker thread, depending
     2577 * on the configuration.
     2578 *
     2579 * @param   pStream     The SB16 stream to open.
     2580 * @param   pSink       Mixer sink to use for updating.
     2581 */
     2582static void sb16StreamUpdate(PSB16STREAM pStream, PAUDMIXSINK pSink)
     2583{
     2584    if (sb16GetDirFromIndex(pStream->uIdx) == PDMAUDIODIR_OUT)
     2585    {
     2586        uint32_t const cbSinkWritable     = AudioMixerSinkGetWritable(pSink);
     2587        uint32_t const cbStreamReadable   = (uint32_t)RTCircBufUsed(pStream->State.pCircBuf);
     2588
     2589        uint32_t       cbToReadFromStream = RT_MIN(cbStreamReadable, cbSinkWritable);
     2590        /* Make sure that we always align the number of bytes when reading to the stream's PCM properties. */
     2591        cbToReadFromStream = PDMAudioPropsFloorBytesToFrame(&pStream->Cfg.Props, cbToReadFromStream);
     2592
     2593        Log3Func(("[SD%RU8] cbSinkWritable=%RU32, cbStreamReadable=%RU32\n", pStream->uIdx, cbSinkWritable, cbStreamReadable));
     2594
     2595        void /*const*/ *pvSrcBuf;
     2596        size_t          cbSrcBuf;
     2597
     2598        while (cbToReadFromStream > 0)
     2599        {
     2600            uint32_t cbWritten = 0;
     2601
     2602            RTCircBufAcquireReadBlock(pStream->State.pCircBuf, cbToReadFromStream, &pvSrcBuf, &cbSrcBuf);
     2603
     2604            int rc2 = AudioMixerSinkWrite(pSink, AUDMIXOP_COPY, pvSrcBuf, cbSrcBuf, &cbWritten);
     2605            AssertRC(rc2);
     2606            Assert(cbWritten <= cbSrcBuf);
     2607
     2608            RTCircBufReleaseReadBlock(pStream->State.pCircBuf, cbWritten);
     2609
     2610            Assert(cbToReadFromStream >= cbWritten);
     2611            cbToReadFromStream -= cbWritten;
     2612        }
     2613    }
     2614    else
     2615        AssertFailed(); /** @todo Recording not implemented yet. */
     2616
     2617    int rc2 = AudioMixerSinkUpdate(pSink);
     2618    AssertRC(rc2);
     2619}
     2620
     2621
     2622/*********************************************************************************************************************************
     2623*   Saved state handling                                                                                                         *
     2624*********************************************************************************************************************************/
    22032625
    22042626/**
     
    22292651    pHlp->pfnSSMPutS32(pSSM, pThis->port);
    22302652    pHlp->pfnSSMPutS32(pSSM, pThis->ver);
    2231     pHlp->pfnSSMPutS32(pSSM, pThis->in_index);
    2232     pHlp->pfnSSMPutS32(pSSM, pThis->out_data_len);
     2653    pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_idx);
     2654    pHlp->pfnSSMPutS32(pSSM, pThis->dsp_out_data_len);
    22332655    pHlp->pfnSSMPutS32(pSSM, pThis->fmt_stereo);
    22342656    pHlp->pfnSSMPutS32(pSSM, pThis->fmt_signed);
     
    22432665    pHlp->pfnSSMPutS32(pSSM, pThis->time_const);
    22442666    pHlp->pfnSSMPutS32(pSSM, pThis->speaker);
    2245     pHlp->pfnSSMPutS32(pSSM, pThis->needed_bytes);
     2667    pHlp->pfnSSMPutS32(pSSM, pThis->dsp_in_needed_bytes);
    22462668    pHlp->pfnSSMPutS32(pSSM, pThis->cmd);
    22472669    pHlp->pfnSSMPutS32(pSSM, pThis->use_hdma);
     
    22602682    pHlp->pfnSSMPutS32(pSSM, pThis->csp_reg83w);
    22612683
    2262     pHlp->pfnSSMPutMem(pSSM, pThis->in2_data, sizeof(pThis->in2_data));
    2263     pHlp->pfnSSMPutMem(pSSM, pThis->out_data, sizeof(pThis->out_data));
     2684    pHlp->pfnSSMPutMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
     2685    pHlp->pfnSSMPutMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
    22642686    pHlp->pfnSSMPutU8 (pSSM, pThis->test_reg);
    22652687    pHlp->pfnSSMPutU8 (pSSM, pThis->last_read_byte);
     
    22992721    pHlp->pfnSSMGetS32(pSSM, &pThis->port);
    23002722    pHlp->pfnSSMGetS32(pSSM, &pThis->ver);
    2301     pHlp->pfnSSMGetS32(pSSM, &pThis->in_index);
    2302     pHlp->pfnSSMGetS32(pSSM, &pThis->out_data_len);
     2723    pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_idx);
     2724    pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_out_data_len);
    23032725    pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_stereo);
    23042726    pHlp->pfnSSMGetS32(pSSM, &pThis->fmt_signed);
     
    23132735    pHlp->pfnSSMGetS32(pSSM, &pThis->time_const);
    23142736    pHlp->pfnSSMGetS32(pSSM, &pThis->speaker);
    2315     pHlp->pfnSSMGetS32(pSSM, &pThis->needed_bytes);
     2737    pHlp->pfnSSMGetS32(pSSM, &pThis->dsp_in_needed_bytes);
    23162738    pHlp->pfnSSMGetS32(pSSM, &pThis->cmd);
    23172739    pHlp->pfnSSMGetS32(pSSM, &pThis->use_hdma);
     
    23302752    pHlp->pfnSSMGetS32(pSSM, &pThis->csp_reg83w);
    23312753
    2332     pHlp->pfnSSMGetMem(pSSM, pThis->in2_data, sizeof(pThis->in2_data));
    2333     pHlp->pfnSSMGetMem(pSSM, pThis->out_data, sizeof(pThis->out_data));
     2754    pHlp->pfnSSMGetMem(pSSM, pThis->dsp_in_data, sizeof(pThis->dsp_in_data));
     2755    pHlp->pfnSSMGetMem(pSSM, pThis->dsp_out_data, sizeof(pThis->dsp_out_data));
    23342756    pHlp->pfnSSMGetU8 (pSSM, &pThis->test_reg);
    23352757    pHlp->pfnSSMGetU8 (pSSM, &pThis->last_read_byte);
     
    23502772    if (pThis->dma_running)
    23512773    {
    2352         sb16CheckAndReOpenOut(pDevIns, pThis);
    23532774        sb16Control(pDevIns, pThis, 1);
    23542775        sb16SpeakerControl(pThis, pThis->speaker);
     
    24102831
    24112832
    2412 /* -=-=-=-=-=- IBase -=-=-=-=-=- */
     2833/*********************************************************************************************************************************
     2834*   IBase implementation                                                                                                         *
     2835*********************************************************************************************************************************/
    24132836
    24142837/**
     
    24252848
    24262849
    2427 /* -=-=-=-=-=- Device -=-=-=-=-=- */
     2850/*********************************************************************************************************************************
     2851*   Device (PDM) handling                                                                                                        *
     2852*********************************************************************************************************************************/
    24282853
    24292854/**
     
    25903015    PSB16STATE pThis = PDMDEVINS_2_DATA(pDevIns, PSB16STATE);
    25913016
     3017    LogRel2(("SB16: Reset\n"));
     3018
    25923019    /* Bring back the device to initial state, and especially make
    25933020     * sure there's no interrupt or DMA activity.
     
    26003027
    26013028    pThis->dma_auto = 0;
    2602     pThis->in_index = 0;
    2603     pThis->out_data_len = 0;
     3029    pThis->dsp_in_idx = 0;
     3030    pThis->dsp_out_data_len = 0;
    26043031    pThis->left_till_irq = 0;
    2605     pThis->needed_bytes = 0;
     3032    pThis->dsp_in_needed_bytes = 0;
    26063033    pThis->block_size = -1;
    26073034    pThis->nzero = 0;
     
    26303057     * Destroy all streams.
    26313058     */
    2632     sb16StreamClose(pThis, &pThis->StreamOut);
    2633     sb16StreamDestroy(pThis, &pThis->StreamOut);
    2634     /** @todo Add removal + destruction of other streams here once we support them. */
     3059    for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
     3060        sb16StreamDestroy(pDevIns, pThis, &pThis->aStreams[i]);
    26353061
    26363062    /*
     
    27733199     * For now we have one stream only, namely the output (playback) stream.
    27743200     */
    2775     rc = sb16StreamCreate(pThis, &pThis->StreamOut, false /* fIn */);
    2776     AssertRCReturn(rc, rc);
     3201    AssertCompile(RT_ELEMENTS(pThis->aStreams) == SB16_MAX_STREAMS);
     3202    for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
     3203    {
     3204        rc = sb16StreamCreate(pThis, &pThis->aStreams[i], i /* uIdx */);
     3205        AssertRCReturn(rc, rc);
     3206    }
    27773207
    27783208    /*
     
    27913221                              TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IRQ", &pThis->hTimerIRQ);
    27923222    AssertRCReturn(rc, rc);
    2793     rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, pThis,
    2794                               TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "SB16 IO", &pThis->hTimerIO);
    2795     AssertRCReturn(rc, rc);
    2796     pThis->cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerIO) / uTimerHz;
    2797     pThis->tsTimerIO             = PDMDevHlpTimerGet(pDevIns, pThis->hTimerIO);
    2798     LogFunc(("Timer ticks=%RU64 (%RU16 Hz)\n", pThis->cTicksTimerIOInterval, uTimerHz));
     3223
     3224    static const char * const s_apszNames[] = { "SB16 OUT" };
     3225    AssertCompile(RT_ELEMENTS(s_apszNames) == SB16_MAX_STREAMS);
     3226    for (unsigned i = 0; i < SB16_MAX_STREAMS; i++)
     3227    {
     3228        rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, sb16TimerIO, &pThis->aStreams[i],
     3229                                  TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, s_apszNames[i], &pThis->aStreams[i].hTimerIO);
     3230        AssertRCReturn(rc, rc);
     3231
     3232        pThis->aStreams[i].cTicksTimerIOInterval = PDMDevHlpTimerGetFreq(pDevIns, pThis->aStreams[i].hTimerIO) / uTimerHz;
     3233        pThis->aStreams[i].tsTimerIO             = PDMDevHlpTimerGet(pDevIns, pThis->aStreams[i].hTimerIO);
     3234    }
    27993235
    28003236    /*
     
    28353271    AssertRCReturn(rc, rc);
    28363272
    2837     rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, &pThis->StreamOut /* pvUser */);
     3273    rc = PDMDevHlpDMARegister(pDevIns, pThis->hdma, sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
    28383274    AssertRCReturn(rc, rc);
    2839     rc = PDMDevHlpDMARegister(pDevIns, pThis->dma,  sb16DMARead, &pThis->StreamOut /* pvUser */);
     3275    rc = PDMDevHlpDMARegister(pDevIns, pThis->dma,  sb16DMARead, &pThis->aStreams[SB16_IDX_OUT] /* pvUser */);
    28403276    AssertRCReturn(rc, rc);
    28413277
     
    28473283    rc = PDMDevHlpSSMRegister3(pDevIns, SB16_SAVE_STATE_VERSION, sizeof(SB16STATE), sb16LiveExec, sb16SaveExec, sb16LoadExec);
    28483284    AssertRCReturn(rc, rc);
     3285
     3286# ifdef VBOX_WITH_AUDIO_SB16_ASYNC_IO
     3287    LogRel(("SB16: Asynchronous I/O enabled\n"));
     3288# endif
    28493289
    28503290    /*
Note: See TracChangeset for help on using the changeset viewer.

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