Trying again as an inline attachment. Davin
On Sun, 30 Oct 2005 15:31:15 +0100 Stefan Dösinger <[EMAIL PROTECTED]> wrote: > Am Sonntag, 30. Oktober 2005 14:23 schrieb Davin McCall: > > Good news - I have got it working. It required work in both the mixer and > > the Wine ALSA sound driver. It works on my system if the ALSA driver is > > used and hw emulation is NOT used (in winecfg). > > > > If anyone would like to test patches, give me a hoy. In particular I'd like > > to make sure it doesn't break anything else - StarCraft is one of the few > > Windows apps I actually have :-) > Can you send me the patch / upload it somewhere? I can test it with a few > games and tell you if everything works as it should. > > stefan
--- wine-0.9-orig/dlls/dsound/mixer.c Mon Oct 17 19:24:50 2005 +++ wine-0.9/dlls/dsound/mixer.c Mon Oct 31 00:46:10 2005 @@ -98,6 +98,14 @@ dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign; } +/** + * Check for application callback requests for when the play position + * reaches certain points. + * + * The offsets that will be triggered will be those between the recorded + * "last played" position for the buffer (i.e. dsb->playpos) and "len" bytes + * beyond that position. + */ void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len) { int i; @@ -163,6 +171,10 @@ return (s >> 8) ^ (unsigned char)0x80; } +/** + * Copy a single frame from the given input buffer to the given output buffer. + * Translate 8 <-> 16 bits and mono <-> stereo + */ static inline void cp_fields(const IDirectSoundBufferImpl *dsb, BYTE *ibuf, BYTE *obuf ) { DirectSoundDevice * device = dsb->dsound->device; @@ -209,7 +221,23 @@ } } -/* Now with PerfectPitch (tm) technology */ +/** + * Mix the given amount of data into the given device buffer from the + * given secondary buffer, starting from the dsb's first currently unmixed + * frame (buf_mixpos), translating frequency (pitch), stereo/mono and + * bits-per-sample. The secondary buffer sample is looped if it is not + * long enough. + * (Doesn't perform any mixing - this is a straight copy operation). + * + * Now with PerfectPitch (tm) technology + * + * dsb = the secondary buffer + * buf = the device buffer + * len = number of bytes to store in the device buffer + * + * Returns: the number of bytes read from the secondary buffer + * (ie. len, adjusted for frequency, number of channels and sample size) + */ static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len) { INT i, size, ipos, ilen; @@ -252,10 +280,10 @@ return (ilen); } - /* Mix in different sample rates */ - /* */ - /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */ - /* Patent Pending :-] */ + /* Mix in different sample rates + * + * New PerfectPitch(tm) Technology (c) 1998 Rob Riggs + * Patent Pending :-] */ /* Patent enhancements (c) 2000 Ove Kåven, * TransGaming Technologies Inc. */ @@ -356,6 +384,10 @@ } } +/** + * Make sure the device's tmp_buffer is at least the given size. Return a + * pointer to it. + */ static LPBYTE DSOUND_tmpbuffer(DirectSoundDevice *device, DWORD len) { TRACE("(%p,%ld)\n", device, len); @@ -372,6 +404,19 @@ return device->tmp_buffer; } +/** + * Mix (at most) the given number of bytes from the secondary buffer "dsb" + * (starting at the current mix position) into the given position in the + * device buffer. If the dsb buffer is not long enough, pad it with silence. + * + * Returns the number of bytes actually mixed into the device buffer. This + * will match fraglen unless the end of the secondary buffer is reached + * (and it is not looping). + * + * dsb = the secondary buffer to mix from + * writepos = position (offset) in device buffer to write at + * fraglen = number of bytes to mix + */ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen) { INT i, len, ilen, field, todo; @@ -381,6 +426,8 @@ len = fraglen; if (!(dsb->playflags & DSBPLAY_LOOPING)) { + /* This buffer is not looping, so make sure the requested + * length will not take us past the end of the buffer */ int secondary_remainder = dsb->buflen - dsb->buf_mixpos; int adjusted_remainder = MulDiv(dsb->dsound->device->pwfx->nAvgBytesPerSec, secondary_remainder, dsb->nAvgBytesPerSec); assert(adjusted_remainder >= 0); @@ -404,12 +451,16 @@ TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos); + /* first, copy the data from the DirectSoundBuffer into the temporary + buffer, translating frequency/bits-per-sample/number-of-channels + to match the device settings */ ilen = DSOUND_MixerNorm(dsb, ibuf, len); if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D)) DSOUND_MixerVol(dsb, ibuf, len); + /* Now mix the temporary buffer into the devices main buffer */ if (dsb->dsound->device->pwfx->wBitsPerSample == 8) { BYTE *obuf = dsb->dsound->device->buffer + writepos; @@ -536,8 +587,8 @@ for (i = 0; i < todo; i++) { /* 8-bit WAV is unsigned */ - field = (*ibuf++ - 128); - field -= (*obuf - 128); + field = (*obuf - 128); + field -= (*ibuf++ - 128); if (field > 127) field = 127; else if (field < -128) field = -128; *obuf++ = field + 128; @@ -549,8 +600,8 @@ for (i = 0; i < todo; i++) { /* 8-bit WAV is unsigned */ - field = (*ibuf++ - 128); - field -= (*obuf - 128); + field = (*obuf - 128); + field -= (*ibuf++ - 128); if (field > 127) field = 127; else if (field < -128) field = -128; *obuf++ = field + 128; @@ -569,8 +620,8 @@ for (i = 0; i < todo; i++) { /* 16-bit WAV is signed */ - field = *ibufs++; - field -= *obufs; + field = *obufs; + field -= *ibufs++; if (field > 32767) field = 32767; else if (field < -32768) field = -32768; *obufs++ = field; @@ -582,8 +633,8 @@ for (i = 0; i < todo; i++) { /* 16-bit WAV is signed */ - field = *ibufs++; - field -= *obufs; + field = *obufs; + field -= *ibufs++; if (field > 32767) field = 32767; else if (field < -32768) field = -32768; *obufs++ = field; @@ -667,30 +718,52 @@ LeaveCriticalSection(&dsb->lock); } +/** + * Calculate the distance between two buffer offsets, taking wraparound + * into account. + */ +static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2) +{ + if (ptr1 >= ptr2) { + return ptr1 - ptr2; + } else { + return buflen + ptr1 - ptr2; + } +} + +/** + * Mix some frames from the given secondary buffer "dsb" into the device + * primary buffer. + * + * dsb = the secondary buffer + * playpos = the current play position in the device buffer (primary buffer) + * writepos = the current safe-to-write position in the device buffer + * mixlen = the maximum number of bytes in the primary buffer to mix, from the + * current writepos. + * + * Returns: the number of bytes beyond the writepos that were mixed. + */ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen) { + /* The buffer's primary_mixpos may be before or after the the device + * buffer's mixpos, but both must be ahead of writepos. */ + DWORD len, slen; /* determine this buffer's write position */ DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, writepos, writepos); - /* determine how much already-mixed data exists */ - DWORD buf_done = - ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) + - dsb->buf_mixpos - buf_writepos; - DWORD primary_done = - ((dsb->primary_mixpos < writepos) ? dsb->dsound->device->buflen : 0) + - dsb->primary_mixpos - writepos; - DWORD adv_done = - ((dsb->dsound->device->mixpos < writepos) ? dsb->dsound->device->buflen : 0) + - dsb->dsound->device->mixpos - writepos; + /* the amount between the buffers current playpos and writepos + * (ie between writepos value from last call and this call) */ DWORD played = - ((buf_writepos < dsb->playpos) ? dsb->buflen : 0) + - buf_writepos - dsb->playpos; + DSOUND_BufPtrDiff(dsb->buflen, buf_writepos, dsb->playpos); + /* the amound of the secondary buffer from the writepos to the end */ DWORD buf_left = dsb->buflen - buf_writepos; - int still_behind; + DWORD primary_done = + DSOUND_BufPtrDiff(dsb->dsound->device->buflen, + dsb->primary_mixpos, writepos); TRACE("(%p,%ld,%ld,%ld)\n",dsb,playpos,writepos,mixlen); TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos); - TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done); + TRACE("primary_done=%ld\n", primary_done); TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos, mixlen); TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin); @@ -704,18 +777,6 @@ /* save write position for non-GETCURRENTPOSITION2... */ dsb->playpos = buf_writepos; - /* check whether CalcPlayPosition detected a mixing underrun */ - if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) { - /* it did, but did we have more to play? */ - if ((dsb->playflags & DSBPLAY_LOOPING) || - (dsb->buf_mixpos < dsb->buflen)) { - /* yes, have to recover */ - ERR("underrun on sound buffer %p\n", dsb); - TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos); - } - dsb->primary_mixpos = writepos; - primary_done = 0; - } /* determine how far ahead we should mix */ if (((dsb->playflags & DSBPLAY_LOOPING) || (dsb->leadin && (dsb->probably_valid_to != 0))) && @@ -760,33 +821,34 @@ } /* cut mixlen with what's already been mixed */ if (mixlen < primary_done) { - /* huh? and still CalcPlayPosition didn't - * detect an underrun? */ - FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done); return 0; } len = mixlen - primary_done; + TRACE("remaining mixlen=%ld\n", len); + /* len is now the maximum amount we should mix from dsb's + * primary_mixpos onwards */ if (len < dsb->dsound->device->fraglen) { /* smaller than a fragment, wait until it gets larger * before we take the mixing overhead */ TRACE("mixlen not worth it, deferring mixing\n"); - still_behind = 1; + len = 0; goto post_mix; } /* ok, we know how much to mix, let's go */ - still_behind = (adv_done > primary_done); while (len) { slen = dsb->dsound->device->buflen - dsb->primary_mixpos; if (slen > len) slen = len; slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen); + /* if ((dsb->primary_mixpos < dsb->dsound->device->mixpos) && (dsb->primary_mixpos + slen >= dsb->dsound->device->mixpos)) still_behind = FALSE; - + */ + dsb->primary_mixpos += slen; len -= slen; dsb->primary_mixpos %= dsb->dsound->device->buflen; @@ -810,11 +872,8 @@ /* return how far we think the primary buffer can * advance its underrun detector...*/ - if (still_behind) return 0; - if ((mixlen - len) < primary_done) return 0; - slen = ((dsb->primary_mixpos < dsb->dsound->device->mixpos) ? - dsb->dsound->device->buflen : 0) + dsb->primary_mixpos - - dsb->dsound->device->mixpos; + slen = DSOUND_BufPtrDiff(dsb->dsound->device->buflen, + dsb->primary_mixpos, writepos); if (slen > mixlen) { /* the primary_done and still_behind checks above should have worked */ FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen); @@ -823,6 +882,19 @@ return slen; } +/** + * For a DirectSoundDevice, go through all the currently playing buffers and + * mix them in to the device buffer. + * + * playpos = the current play position in the primary buffer + * writepos = the current safe-to-write position in the primary buffer + * mixlen = the maximum amount to mix into the primary buffer + * (beyond the current writepos) + * recover = true if the sound device may have been reset and the write + * position in the device buffer changed + * + * Returns: the length beyond the writepos that was mixed to. + */ static DWORD DSOUND_MixToPrimary(DirectSoundDevice *device, DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover) { INT i, len, maxlen = 0; @@ -844,6 +916,8 @@ dsb->primary_mixpos = writepos; dsb->cvolpan = dsb->volpan; dsb->need_remix = FALSE; + if (dsb->state == STATE_STARTING) + dsb->state = STATE_PLAYING; } else if (dsb->need_remix) { DSOUND_MixCancel(dsb, writepos, TRUE); @@ -851,8 +925,6 @@ dsb->need_remix = FALSE; } len = DSOUND_MixOne(dsb, playpos, writepos, mixlen); - if (dsb->state == STATE_STARTING) - dsb->state = STATE_PLAYING; maxlen = (len > maxlen) ? len : maxlen; } LeaveCriticalSection(&(dsb->lock)); @@ -941,11 +1013,17 @@ /* #define SYNC_CALLBACK */ +/** + * Perform mixing for a Direct Sound device. That is, go through all the + * secondary buffers (the sound bites currently playing) and mix them in + * to the primary buffer (the device buffer). + */ void DSOUND_PerformMix(DirectSoundDevice *device) { int nfiller; BOOL forced; HRESULT hres; + BOOL underrun; TRACE("(%p)\n", device); @@ -958,31 +1036,39 @@ if (device->priolevel != DSSCL_WRITEPRIMARY) { BOOL paused = ((device->state == STATE_STOPPED) || (device->state == STATE_STARTING)); /* FIXME: document variables */ - DWORD playpos, writepos, inq, maxq, frag; + DWORD playpos, writepos, inq, playleft, maxq, frag; + DWORD playlead; if (device->hwbuf) { hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, &writepos); if (hres) { WARN("IDsDriverBuffer_GetPosition failed\n"); return; } + playlead = DSOUND_BufPtrDiff(device->buflen, + writepos, playpos); + } else { + playpos = device->pwplay * device->fraglen; + playlead = ds_hel_margin * device->fraglen; + /* Well, we *could* do Just-In-Time mixing using the writepos, * but that's a little bit ambitious and unnecessary... */ /* rather add our safety margin to the writepos, if we're playing */ + writepos = playpos; + if (!paused) { - writepos += device->writelead; - writepos %= device->buflen; - } else writepos = playpos; - } else { - playpos = device->pwplay * device->fraglen; - writepos = playpos; - if (!paused) { - writepos += ds_hel_margin * device->fraglen; - writepos %= device->buflen; - } + writepos += playlead; + if (writepos > device->buflen) { + writepos -= device->buflen; + } + } } + TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n", playpos,writepos,device->playpos,device->mixpos,device->buflen); assert(device->playpos < device->buflen); + + EnterCriticalSection(&(device->mixlock)); + /* wipe out just-played sound data */ if (playpos < device->playpos) { FillMemory(device->buffer + device->playpos, device->buflen - device->playpos, nfiller); @@ -992,36 +1078,64 @@ } device->playpos = playpos; - EnterCriticalSection(&(device->mixlock)); - /* reset mixing if necessary */ DSOUND_CheckReset(device, writepos); /* check how much prebuffering is left */ - inq = device->mixpos; - if (inq < writepos) - inq += device->buflen; - inq -= writepos; - + inq = DSOUND_BufPtrDiff(device->buflen, device->mixpos, writepos); + /* find the maximum we can prebuffer */ - if (!paused) { - maxq = playpos; - if (maxq < writepos) - maxq += device->buflen; - maxq -= writepos; - } else maxq = device->buflen; + maxq = DSOUND_BufPtrDiff(device->buflen, + playpos, writepos); /* clip maxq to device->prebuf */ frag = device->prebuf * device->fraglen; if (maxq > frag) maxq = frag; /* check for consistency */ - if (inq > maxq) { - /* the playback position must have passed our last + underrun = (inq > maxq) || paused; + + /* do the mixing */ + frag = DSOUND_MixToPrimary(device, playpos, writepos, maxq, underrun); + if (frag != 0 || forced) { + device->mixpos = writepos + frag; + device->mixpos %= device->buflen; + } + + playleft = DSOUND_BufPtrDiff(device->buflen, + device->mixpos, playpos); + + if (paused) { + if (frag > 0 || forced) { + /* buffers have been filled, restart playback */ + if (device->state == STATE_STARTING) { + device->state = STATE_PLAYING; + } + else if (device->state == STATE_STOPPED) { + /* stopping just means that play will stop once there's no more to + * play. */ + device->state = STATE_STOPPING; + } +#ifdef SYNC_CALLBACK + LeaveCriticalSection(&(device->mixlock)); +#endif + if (paused) { + if (DSOUND_PrimaryPlay(device) != DS_OK) + WARN("DSOUND_PrimaryPlay failed\n"); + else + TRACE("starting playback\n"); + } +#ifdef SYNC_CALLBACK + EnterCriticalSection(&(device->mixlock)); +#endif + } + } + else + if (playleft > maxq + playlead) { + /* the playback position has passed our last * mixed position, i.e. it's an underrun, or we have * nothing more to play */ TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq); - inq = 0; /* stop the playback now, to allow buffers to refill */ if (device->state == STATE_PLAYING) { device->state = STATE_STARTING; @@ -1037,6 +1151,8 @@ /* DSOUND_callback may need this lock */ LeaveCriticalSection(&(device->mixlock)); #endif + FillMemory(device->buffer, device->buflen, nfiller); + if (DSOUND_PrimaryStop(device) != DS_OK) WARN("DSOUND_PrimaryStop failed\n"); #ifdef SYNC_CALLBACK @@ -1057,39 +1173,9 @@ writepos = playpos; device->playpos = playpos; device->mixpos = writepos; - inq = 0; - maxq = device->buflen; - if (maxq > frag) maxq = frag; - FillMemory(device->buffer, device->buflen, nfiller); - paused = TRUE; } + LeaveCriticalSection(&(device->mixlock)); - /* do the mixing */ - frag = DSOUND_MixToPrimary(device, playpos, writepos, maxq, paused); - if (forced) frag = maxq - inq; - device->mixpos += frag; - device->mixpos %= device->buflen; - - if (frag) { - /* buffers have been filled, restart playback */ - if (device->state == STATE_STARTING) { - device->state = STATE_PLAYING; - } - else if (device->state == STATE_STOPPED) { - /* the dsound is supposed to play if there's something to play - * even if it is reported as stopped, so don't let this confuse you */ - device->state = STATE_STOPPING; - } - LeaveCriticalSection(&(device->mixlock)); - if (paused) { - if (DSOUND_PrimaryPlay(device) != DS_OK) - WARN("DSOUND_PrimaryPlay failed\n"); - else - TRACE("starting playback\n"); - } - } - else - LeaveCriticalSection(&(device->mixlock)); } else { /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */ if (device->state == STATE_STARTING) { --- wine-0.9-orig/dlls/winmm/winealsa/audio.c Tue Sep 13 00:12:46 2005 +++ wine-0.9/dlls/winmm/winealsa/audio.c Mon Oct 31 00:47:59 2005 @@ -3011,6 +3011,7 @@ snd_pcm_uframes_t mmap_buflen_frames; snd_pcm_channel_area_t * mmap_areas; snd_async_handler_t * mmap_async_handler; + snd_pcm_uframes_t mmap_ppos; /* play position */ }; static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi) @@ -3037,7 +3038,7 @@ } } -static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi) +static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi, int mul) { WINE_WAVEDEV * wwo = &(WOutDev[pdbi->drv->wDevID]); unsigned int channels; @@ -3060,30 +3061,43 @@ TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels ); + /* while (avail >= period_size) + */ { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t ofs; snd_pcm_uframes_t frames; int err; - frames = avail / period_size * period_size; /* round down to a multiple of period_size */ + /* frames = avail / period_size * period_size; */ + /* round down to a multiple of period_size */ + /* frames = period_size * mul + period_size; */ + frames = pdbi->mmap_buflen_frames; EnterCriticalSection(&pdbi->mmap_crst); snd_pcm_mmap_begin(wwo->pcm, &areas, &ofs, &frames); if (areas != pdbi->mmap_areas || areas->addr != pdbi->mmap_areas->addr) - FIXME("Can't access sound driver's buffer directly.\n"); - err = snd_pcm_mmap_commit(wwo->pcm, ofs, frames); - + FIXME("Can't access sound driver's buffer directly.\n"); + /* err = snd_pcm_mmap_commit(wwo->pcm, ofs, frames); */ + err = snd_pcm_mmap_commit(wwo->pcm, ofs, period_size * mul); + pdbi->mmap_ppos = ofs; + if (pdbi->mmap_ppos > pdbi->mmap_buflen_frames) { + pdbi->mmap_ppos -= pdbi->mmap_buflen_frames; + } + LeaveCriticalSection(&pdbi->mmap_crst); + /* if ( err != (snd_pcm_sframes_t) frames) ERR("mmap partially failed.\n"); - + */ + avail = snd_pcm_avail_update(wwo->pcm); } + /* if (avail > 0) { const snd_pcm_channel_area_t *areas; @@ -3107,6 +3121,7 @@ avail = snd_pcm_avail_update(wwo->pcm); } + */ } static void DSDB_PCMCallback(snd_async_handler_t *ahandler) @@ -3114,7 +3129,8 @@ /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */ IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler); TRACE("callback called\n"); - DSDB_MMAPCopy(pdbi); + /* Commit another block */ + DSDB_MMAPCopy(pdbi, 1); } static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi) @@ -3286,6 +3302,7 @@ WINE_WAVEDEV * wwo = &(WOutDev[This->drv->wDevID]); snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t period_size; + snd_pcm_state_t state; int dir; int err; @@ -3296,17 +3313,19 @@ if (wwo->pcm == NULL) return DSERR_GENERIC; /** we need to track down buffer underruns */ - DSDB_CheckXRUN(This); + DSDB_CheckXRUN(This); EnterCriticalSection(&This->mmap_crst); - /* FIXME: snd_pcm_mmap_hw_ptr() should not be accessed by a user app. */ - /* It will NOT return what why want anyway. */ - hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->pcm); - if (hw_ptr >= period_size) hw_ptr -= period_size; else hw_ptr = 0; + hw_ptr = This->mmap_ppos; + + state = snd_pcm_state(wwo->pcm); + if (state != SND_PCM_STATE_RUNNING) + hw_ptr = 0; + if (lpdwPlay) - *lpdwPlay = snd_pcm_frames_to_bytes(wwo->pcm, hw_ptr/ period_size * period_size) % This->mmap_buflen_bytes; + *lpdwPlay = snd_pcm_frames_to_bytes(wwo->pcm, hw_ptr) % This->mmap_buflen_bytes; if (lpdwWrite) - *lpdwWrite = snd_pcm_frames_to_bytes(wwo->pcm, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes; + *lpdwWrite = snd_pcm_frames_to_bytes(wwo->pcm, hw_ptr + period_size * 2) % This->mmap_buflen_bytes; LeaveCriticalSection(&This->mmap_crst); TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1); @@ -3331,10 +3350,11 @@ state = snd_pcm_state(wwo->pcm); } if ( state == SND_PCM_STATE_PREPARED ) - { - DSDB_MMAPCopy(This); + { + /* prime with two periods */ + DSDB_MMAPCopy(This, 2); err = snd_pcm_start(wwo->pcm); - } + } return DS_OK; }