diff options
| -rw-r--r-- | Emu8000.cpp | 30 | ||||
| -rw-r--r-- | emu8k.c | 59 | ||||
| -rw-r--r-- | emu8k.h | 5 | ||||
| -rw-r--r-- | emu8k_internal.h | 3 | 
4 files changed, 67 insertions, 30 deletions
| diff --git a/Emu8000.cpp b/Emu8000.cpp index 86dcb11..4e3fd01 100644 --- a/Emu8000.cpp +++ b/Emu8000.cpp @@ -132,9 +132,12 @@ typedef struct {      bool volatile          fShutdown;      /** Flag from render thread indicated it has shutdown (e.g. due to error or timeout). */      bool volatile          fStopped; -    /** (System clock) timestamp of last OPL chip access. */ +    /** (System clock) timestamp of last port write. */      uint64_t               tmLastWrite; +    /** (Virtual clock) timestamp of last frame rendered. */ +    uint64_t               tmLastRender; +      /** To protect access to opl3_chip from the render thread and main thread. */      RTCRITSECT             critSect;      /** Handle to emu8k. */ @@ -148,18 +151,26 @@ typedef EMUSTATE *PEMUSTATE;  #ifndef VBOX_DEVICE_STRUCT_TESTCASE -static inline uint64_t emuCalculateFramesFromMilli(PEMUSTATE pThis, uint64_t milli) +DECLINLINE(uint64_t) emuCalculateFramesFromMilli(PEMUSTATE pThis, uint64_t milli)  {      uint64_t rate = pThis->uSampleRate;      return (rate * milli) / 1000;  } -static inline size_t emuCalculateBytesFromFrames(PEMUSTATE pThis, uint64_t frames) +DECLINLINE(uint64_t) emuCalculateFramesFromNano(PEMUSTATE pThis, uint64_t nano) +{ +    uint64_t rate = pThis->uSampleRate; +    return (rate * nano) / 1000000000; +} + +DECLINLINE(size_t) emuCalculateBytesFromFrames(PEMUSTATE pThis, uint64_t frames)  {      NOREF(pThis);      return frames * sizeof(uint16_t) * EMU_NUM_CHANNELS;  } + +  /**   * The render thread calls into the emulator to render audio frames, and then pushes them   * on the PCM output device. @@ -172,7 +183,8 @@ static inline size_t emuCalculateBytesFromFrames(PEMUSTATE pThis, uint64_t frame  static DECLCALLBACK(int) emuRenderThread(RTTHREAD ThreadSelf, void *pvUser)  {      RT_NOREF(ThreadSelf); -    PEMUSTATE pThis = (PEMUSTATE)pvUser; +    PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser; +    PEMUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PEMUSTATE);      PCMOutBackend *pPcmOut = &pThis->pcmOut;      // Compute the max number of frames we can store on our temporary buffer. @@ -190,6 +202,7 @@ static DECLCALLBACK(int) emuRenderThread(RTTHREAD ThreadSelf, void *pvUser)          RTCritSectEnter(&pThis->critSect);          emu8k_render(pThis->emu, buf, buf_frames); +        pThis->tmLastRender = PDMDevHlpTMTimeVirtGetNano(pDevIns);          RTCritSectLeave(&pThis->critSect);          Log9(("writing %lld frames\n", buf_frames)); @@ -275,7 +288,7 @@ static void emuWakeRenderThread(PPDMDEVINS pDevIns)          Log3(("Creating render thread\n")); -        int rc = RTThreadCreateF(&pThis->hRenderThread, emuRenderThread, pThis, 0, +        int rc = RTThreadCreateF(&pThis->hRenderThread, emuRenderThread, pDevIns, 0,                                   RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,                                   "emu%u_render", pDevIns->iInstance);          AssertLogRelRCReturnVoid(rc); @@ -291,6 +304,10 @@ static DECLCALLBACK(VBOXSTRICTRC) emuIoPortRead(PPDMDEVINS pDevIns, void *pvUser      PEMUSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PEMUSTATE); +    RTCritSectEnter(&pThis->critSect); +    uint64_t frames_since_last_render = emuCalculateFramesFromNano(pThis, PDMDevHlpTMTimeVirtGetNano(pDevIns) - pThis->tmLastRender); +    emu8k_update_virtual_sample_count(pThis->emu, frames_since_last_render); +      switch (cb) {          case sizeof(uint8_t):              *pu32 = emu8k_inb(pThis->emu, port); @@ -307,6 +324,8 @@ static DECLCALLBACK(VBOXSTRICTRC) emuIoPortRead(PPDMDEVINS pDevIns, void *pvUser              break;      } +    RTCritSectLeave(&pThis->critSect); +      Log9Func(("read port 0x%X (%u): %#04x\n", port, cb, *pu32));      return VINF_SUCCESS; @@ -397,6 +416,7 @@ static DECLCALLBACK(void) emuR3Reset(PPDMDEVINS pDevIns)      RTCritSectEnter(&pThis->critSect);      emu8k_reset(pThis->emu); +    pThis->tmLastRender = PDMDevHlpTMTimeVirtGetNano(pDevIns);      RTCritSectLeave(&pThis->critSect);  } @@ -685,7 +685,7 @@ uint16_t emu8k_inw(emu8k_t *emu8k, uint16_t addr)                                   it is used by programs to wait. Not critical, but should help reduce
                                   the amount of calls and wait time */
                          case 27: /*Sample Counter ( 44Khz clock) */
 -                                return emu8k->wc;
 +                                return emu8k->sample_count + emu8k->sample_count_virtual;
                          }
                          break;
 @@ -753,9 +753,11 @@ uint16_t emu8k_inw(emu8k_t *emu8k, uint16_t addr)  void emu8k_outw(emu8k_t *emu8k, uint16_t addr, uint16_t val)
  {
 +#if 0
          /*TODO: I would like to not call this here, but i found it was needed or else cubic player would not finish opening (take a looot more of time than usual).
           * Basically, being here means that the audio is generated in the emulation thread, instead of the audio thread.*/
 -        // TODO emu8k_update(emu8k);
 +        emu8k_update(emu8k);
 +#endif
  #ifdef EMU8K_DEBUG_REGISTERS
          if (addr == 0xE22)
 @@ -2150,7 +2152,8 @@ I've recopilated these sentences to get an idea of how to loop          }
          /* Update EMU clock. */
 -        emu8k->wc += (new_pos - emu8k->pos);
 +        emu8k->sample_count += (new_pos - emu8k->pos);
 +        emu8k->sample_count_virtual = 0;
          emu8k->pos = new_pos;
  }
 @@ -2388,29 +2391,32 @@ void emu8k_free(emu8k_t* emu8k)  void emu8k_reset(emu8k_t* emu8k)
  {
 -        /* NOTE! read_pos and buffer content is implicitly initialized to zero by the sb_t structure memset on sb_awe32_init() */
 -        emu8k->reverb_engine.reflections[0].bufsize = 2 * REV_BUFSIZE_STEP;
 -        emu8k->reverb_engine.reflections[1].bufsize = 4 * REV_BUFSIZE_STEP;
 -        emu8k->reverb_engine.reflections[2].bufsize = 8 * REV_BUFSIZE_STEP;
 -        emu8k->reverb_engine.reflections[3].bufsize = 13 * REV_BUFSIZE_STEP;
 -        emu8k->reverb_engine.reflections[4].bufsize = 19 * REV_BUFSIZE_STEP;
 -        emu8k->reverb_engine.reflections[5].bufsize = 26 * REV_BUFSIZE_STEP;
 -
 -        /*This is a bit random.*/
 -        for (int c = 0; c < 4; c++)
 -        {
 -                emu8k->reverb_engine.allpass[3 - c].feedback = 0.5;
 -                emu8k->reverb_engine.allpass[3 - c].bufsize = (4 * c) * REV_BUFSIZE_STEP + 55;
 -                emu8k->reverb_engine.allpass[7 - c].feedback = 0.5;
 -                emu8k->reverb_engine.allpass[7 - c].bufsize = (4 * c) * REV_BUFSIZE_STEP + 55;
 -        }
 +    /* NOTE! read_pos and buffer content is implicitly initialized to zero by the sb_t structure memset on sb_awe32_init() */
 +    emu8k->reverb_engine.reflections[0].bufsize = 2 * REV_BUFSIZE_STEP;
 +    emu8k->reverb_engine.reflections[1].bufsize = 4 * REV_BUFSIZE_STEP;
 +    emu8k->reverb_engine.reflections[2].bufsize = 8 * REV_BUFSIZE_STEP;
 +    emu8k->reverb_engine.reflections[3].bufsize = 13 * REV_BUFSIZE_STEP;
 +    emu8k->reverb_engine.reflections[4].bufsize = 19 * REV_BUFSIZE_STEP;
 +    emu8k->reverb_engine.reflections[5].bufsize = 26 * REV_BUFSIZE_STEP;
 +
 +    /*This is a bit random.*/
 +    for (int c = 0; c < 4; c++)
 +    {
 +        emu8k->reverb_engine.allpass[3 - c].feedback = 0.5;
 +        emu8k->reverb_engine.allpass[3 - c].bufsize = (4 * c) * REV_BUFSIZE_STEP + 55;
 +        emu8k->reverb_engine.allpass[7 - c].feedback = 0.5;
 +        emu8k->reverb_engine.allpass[7 - c].bufsize = (4 * c) * REV_BUFSIZE_STEP + 55;
 +    }
 -        /* Even when the documentation says that this has to be written by applications to initialize the card, 
 +    /* Even when the documentation says that this has to be written by applications to initialize the card,
           * several applications and drivers ( aweman on windows, linux oss driver..) read it to detect an AWE card. */
 -        emu8k->hwcf1 = 0x59;
 -        emu8k->hwcf2 = 0x20;
 -        /* Initial state is muted. 0x04 is unmuted. */
 -        emu8k->hwcf3 = 0x00;
 +    emu8k->hwcf1 = 0x59;
 +    emu8k->hwcf2 = 0x20;
 +    /* Initial state is muted. 0x04 is unmuted. */
 +    emu8k->hwcf3 = 0x00;
 +
 +    emu8k->sample_count = 0;
 +    emu8k->sample_count_virtual = 0;
  }
  void emu8k_render(emu8k_t *emu8k, int16_t *buf, size_t frames)
 @@ -2425,3 +2431,8 @@ void emu8k_render(emu8k_t *emu8k, int16_t *buf, size_t frames)      emu8k->pos = 0;
  }
 +
 +void emu8k_update_virtual_sample_count(emu8k_t *emu8k, uint8_t sample_count)
 +{
 +    emu8k->sample_count_virtual = sample_count;
 +}
 @@ -61,6 +61,11 @@ void emu8k_outb(emu8k_t *emu8k, uint16_t addr, uint8_t val);  void emu8k_render(emu8k_t *emu8k, int16_t *buf, size_t frames); +/** Between calls to emu8k_render, the virtual sample count is used to keep the "sample count" register ticking at a reasonable pace. */ +void emu8k_update_virtual_sample_count(emu8k_t *emu8k, uint8_t sample_count); +/*  Many programs seem to rely in this counter incrementing frequently, and may hang/error out if it doesn't. + *  It is reset to 0 whenever we render and therefore increment the real sample count. + *  This means that effectively the sample count register may readjust itself (go back or jump ahead) on _render :(. */  #ifdef __cplusplus  } /* extern "C" */ diff --git a/emu8k_internal.h b/emu8k_internal.h index a8e3ea8..19132fa 100644 --- a/emu8k_internal.h +++ b/emu8k_internal.h @@ -401,7 +401,8 @@ typedef struct emu8k_t          uint32_t smalr, smarr, smalw, smarw;          uint16_t smld_buffer, smrd_buffer; -        uint16_t wc; +        uint16_t sample_count; +        uint16_t sample_count_virtual;          uint16_t id; | 
