OK, here's what I came up with. Features: 1) A TODO for the driver developer who is using this as a template, to remind them to hook up the clock to the layer below them 2) No artificially introduced jitter at all (not even 0.29 ms) 3) No random slop introduced by rounding 4) An arbitrary startup delay controlled by m_ullStartupDelayRemaining, which I set to 8 * HNS_PER_MILLISECOND in the transition to KSSTATE_RUN; this makes MSVad have a measurable delay between SetState(KSSTATE_RUN) and the first non-zero device position //============================================================================= STDMETHODIMP CMiniportWaveCyclicStreamMSVAD::GetPosition ( _Out_ PULONG Position ) /*++ Routine Description: The GetPosition function gets the current position of the DMA read or write pointer for the stream. Callers of GetPosition should run at IRQL <= DISPATCH_LEVEL. Arguments: Position - Position of the DMA pointer Return Value: NT status code. --*/ { if (m_fDmaActive) { // TODO: Query the physical layer below us and // report the byte position of the sample, // either emerging from the speaker (if this is render) // or coming in to the microphone (if this is capture) // // Alas, WaveCyclic does not allow us to report a correlated position + QPC, // just a position. // So if the layer below us gives us a correlated position + QPC, // we have to do our own extrapolation and hand the extrapolated position to portcls // // Since this is only a virtual driver, we use KeQueryInterruptTime as a clock // ULONGLONG CurrentTime = KeQueryInterruptTime(); ULONGLONG TimeElapsed = CurrentTime - m_ullDmaTimeStamp; // Simulate a startup delay // if (m_ullStartupDelayRemaining > 0) { // burn down some startup delay if (TimeElapsed > m_ullStartupDelayRemaining) { TimeElapsed -= m_ullStartupDelayRemaining; m_ullStartupDelayRemaining = 0; } else { m_ullStartupDelayRemaining -= TimeElapsed; TimeElapsed = 0; } } // We have an elapsed time in hundred-nanoseconds, which we'll call "ticks" // but we want to convert it into a DMA position, which is in bytes // // Let H = 10 * 1000 * 1000; // this is the number of ticks which pass in a second // // Let B = m_ulDmaMovementRate = pwfx->nAvgBytesPerSec; // this is the number of bytes processed in a second // // Let h = ullElapsed; // this is the number of ticks that have elapsed // // We want to know b = the number of bytes that been processed // // b / B = h / H = the number of seconds that have elapsed // Solving for b, we get: b = B h / H // // In particular, floor(B h / H) bytes have completely been processed, // and we are (B h mod H) / H of the way into the next byte (this is between 0 and 1) // // To avoid dealing with floating-point arithmetic: // Let br = the byte rollover (B h mod H) which is a number in [0, H) // // But wait! // We need to consider the possibility that we had nonzero rollover from before; // "b = B h / H" was too naive, it should have been "b = (B h + br_prev) / H" // // In summary: // H = 10 * 1000 * 1000 = the number of ticks in a second // B = the number of bytes processed in a second // h = the number of ticks that have passed since the last time // br_prev = how far into the current byte we already were the last time, in [0, H) // // So we calculate: // b = floor((B h + br_prev) / H) = how many bytes have no completely passed // br_next = (B h + br_prev) mod H // ULONG ulByteDisplacement = (ULONG)( (m_ulDmaMovementRate * TimeElapsed + m_ullDmaPartialByte) / HNS_PER_SECOND ); m_ullDmaPartialByte = (m_ulDmaMovementRate * TimeElapsed + m_ullDmaPartialByte) % HNS_PER_SECOND; // Now we have the number of bytes that have been newly processed // Advance the position by that much, relative to the circular buffer // m_ulDmaPosition = (m_ulDmaPosition + ulByteDisplacement) % m_ulDmaBufferSize; // Return the new DMA position // *Position = m_ulDmaPosition; // Update the DMA time stamp for the next call to GetPosition() // m_ullDmaTimeStamp = CurrentTime; } else { // DMA is inactive so just return the current DMA position. // *Position = m_ulDmaPosition; } return STATUS_SUCCESS; } // GetPosition