<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>The WaveHdr Chain</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
</head>
<body>
<h1>Wine use of WaveHdr</h1>
<blockquote><i>This document describes the use of the windows wavehdr data
structure within the wine winmm/wineoss dll<br>
</i></blockquote>
<div align="Center"><img src="wavehdr.png" alt="The WaveHdr Chain">
<br>
</div>
WAVEHDR is a structure defined in the windows APIs. It contains:<br>
<ul>
<li>Sound information to be written to a DSP</li>
<li>A pointer to the next wavehdr, so that wavehdrs may be chained
together</li>
<li>Flags used to indicate (among other things) loops</li>
<li>A loop count indicating the number of times a wavehdr loop is to
be repeated.<br>
This count is only read if the WHDR_BEGINLOOP flag is set. Note that
a wavehdr can be both the beginning and the end of a loop.<br>
</li>
<li>8 bytes of space reserved for the wavehdr player that the application
is not supposed to read or change.</li>
</ul>
The wine wavehdr player modifies the next wavehdr pointer and the reserved
space within each wavehdr it processes. Any value the application sets
in these fields prior to passing them in for playing is ignored. It
is assumed that the application will not modify these fields once they have
been passed in for playing until the application is notified that they have
been played.<br>
<blockquote></blockquote>
<h2>The wodPlayer thread</h2>
Wine maintains a thread which does nothing but play wavehdrs and notify when
they are complete. This thread sits in an event wait which receives
commands from the other threads and times out when the DSP is due to have
more data written to it or a previously written wavehdr is due to be notified.<br>
<br>
Wine maintains 3 pointers into the WaveHdr chain: the queue pointer, the
play pointer and the loop pointer.<br>
<blockquote>
<ul>
<li>The queue pointer points to the next WaveHdr to be notified as
complete. WaveHdrs are notified as complete once they have been written
to the DSP and the DSP has cleared them from its buffer. WaveHdrs are
not notified until both the play pointer and the loop pointer have moved
off them. If there are no remaining unnotified wavehdrs which have
been written to the DSP, the queue pointer will be null.</li>
<li>The play pointer points to the WaveHdr which is next to be fed
into the DSP. Once the last byte of a WaveHdr has been written to the
DSP, the play pointer moves on to the next WaveHdr. If there are no
more wavehdrs to be written to the DSP, the play pointer will be null.</li>
<li>The loop pointer points to the first WaveHdr in the currently
executing WaveHdr loop if there is one. A WaveHdr loop consists of
all the WaveHdrs in a chain beginning with a WaveHdr having the WHDR_BEGINLOOP
flag set and ending with a WaveHdr having the WHDR_ENDLOOP flag set. When
the player is not in a loop, the loop pointer is set to null.</li>
</ul>
</blockquote>
<h3>Feeding the DSP</h3>
wodPlayer uses the OSS GETOSPACE ioctl to determine how much space is available
in the DSPs write buffer and attempts to keep that buffer at least half full.
The WINE_WAVEOUT structure (universally a variable named wwo in the
wodPlayer) contains lpPartialData and dwPartialBytes which are a pointer
to and the size of any leftover data from the current wavehdr which could
not be written out last time the DSP was fed. The pointer is null if
the whole wavehdr was written. If there's still space in the DSP buffer
after any leftovers are written, we advance the play pointer to the next
wavehdr and attempt to write that wavehdr to the DSP. To determine
how long we should wait before attempting to feed the DSP again, we determine
the average number of milliseconds taken to empty half the DSPs buffer.<br>
</body>
</html>