Hello Matthew, Thanks for the code snippet. Gaurav On Fri, May 23, 2014 at 5:44 AM, Matthew van Eerde < Matthew.van.Eerde@xxxxxxxxxxxxx> wrote: > 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 >