Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results


Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging

The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.

Check out The OSR Learning Library at:

MSVAD sending audio through IOCTL and getting silences each X ms

forlayoforlayo Member Posts: 2

I am sending audio frames from userland to kernel mode driver using IOCTL, it's working so far but I am having a lot of silences each X ms. I am struggling with timings to wait between sendings and with buffer size without luck at the minute.

The code I use to send data is the following:

using (var reader = new WaveFileReader("Bontempi-B3-C6.wav"))
    var outFormat = new WaveFormat(48000, 16, 2);
    var bufferedWaveProvider = new BufferedWaveProvider(outFormat)
        BufferDuration = TimeSpan.FromSeconds(10)

    byte[] fileBuffer = new byte[2048];
    int bytesRead;
        bytesRead = reader.Read(fileBuffer, 0, fileBuffer.Length);
        bufferedWaveProvider.AddSamples(fileBuffer, 0, bytesRead);
    } while (bytesRead > 0);

    byte[] buffer = new byte[1920];

    Stopwatch stopwatch = new Stopwatch();
    while (bufferedWaveProvider.BufferedBytes > 0)

        int bytesWritten = bufferedWaveProvider.Read(buffer, 0, buffer.Length);
        if (bytesWritten == 0) break;

        bool success = true;
        uint bytesReturned;

        success = DeviceIoControl(hDevice, IOCTL_CSMT_READ_METHOD_BUFFERED, IntPtr.Zero, 0, buffer, (uint)bytesWritten, out bytesReturned, IntPtr.Zero);

        if (!success)
            Console.WriteLine("Error sending IOCTL");
            Console.WriteLine("Sent: " + buffer.Length);

        // Waiting enough time to accomplish with required bitrate. If I try without this ioctl would fail all times as ring buffer get overflow.
        double bytesPerMillisecond = 192;
        double bytesSent = (double)bytesWritten;
        double millisecondsElapsed = (double)stopwatch.ElapsedMilliseconds;
        double timeToWait = (bytesSent / bytesPerMillisecond) - millisecondsElapsed;

        if (timeToWait > 0)


At the driver side I am pushing it to a ring buffer with this put method:

NTSTATUS RingBuffer::Put(BYTE* pBytes, SIZE_T count) 
    if (count > m_BufferLength) return STATUS_BUFFER_TOO_SMALL;
    if (count == 0) return STATUS_SUCCESS;
    if (m_Buffer == NULL) return STATUS_INVALID_DEVICE_STATE; // not initialized

    //buffer overrun
    if ((m_LinearBufferWritePosition + count) - m_LinearBufferReadPosition > m_BufferLength)
        status = STATUS_BUFFER_OVERFLOW;
        m_LinearBufferReadPosition = (m_LinearBufferWritePosition + count) - m_BufferLength + 1;

    SIZE_T bufferOffset = m_LinearBufferWritePosition % m_BufferLength;
    SIZE_T bytesWritten = 0;
    while (count > 0)
        SIZE_T runWrite = min(count, m_BufferLength - bufferOffset);
        RtlCopyMemory(m_Buffer + bufferOffset, pBytes, runWrite);
        bufferOffset = (bufferOffset + runWrite) % m_BufferLength;
        count -= runWrite;
        bytesWritten += runWrite;
    m_LinearBufferWritePosition += bytesWritten;

    if (m_IsFilling && (m_LinearBufferWritePosition - m_LinearBufferReadPosition) > (m_BufferLength / 2))
        DPF(D_TERSE, ("RingBuffer filled with %u bytes.", (m_LinearBufferWritePosition - m_LinearBufferReadPosition)));
        m_IsFilling = false;
    return status;

And then it's copied to DMA using this method:

NTSTATUS RingBuffer::Take(BYTE* pTarget, SIZE_T count, SIZE_T* readCount)
    KeAcquireSpinLock(m_BufferLock, &m_SpinLockIrql);

    if (m_IsFilling)
        *readCount = 0;
        KeReleaseSpinLock(m_BufferLock, m_SpinLockIrql);

    count = min(count, m_LinearBufferWritePosition - m_LinearBufferReadPosition);
    SIZE_T bufferOffset = m_LinearBufferReadPosition % m_BufferLength;
    SIZE_T bytesRead = 0;
    while (count > 0)
        SIZE_T runWrite = min(count, m_BufferLength - bufferOffset);
        RtlCopyMemory(pTarget + bytesRead, m_Buffer + bufferOffset, runWrite);
        bufferOffset = (bufferOffset + runWrite) % m_BufferLength;
        count -= runWrite;
        bytesRead += runWrite;
    *readCount = bytesRead;
    m_LinearBufferReadPosition += bytesRead;
    if (m_LinearBufferWritePosition - m_LinearBufferReadPosition == 0)
        DPF(D_TERSE, ("RingBuffer empty with %u bytes.", (m_LinearBufferWritePosition - m_LinearBufferReadPosition)));
        m_IsFilling = true;
        //m_nByteAlignBufferCount = 0;

    KeReleaseSpinLock(m_BufferLock, m_SpinLockIrql);
    return STATUS_SUCCESS;

The buffer is initialised in this way:

#pragma code_seg("PAGE")
NTSTATUS MiniportWaveRTStream::AllocateBufferWithNotification
    _In_    ULONG               NotificationCount_,
    _In_    ULONG               RequestedSize_,
    _Out_   PMDL                *AudioBufferMdl_,
    _Out_   ULONG               *ActualSize_,
    _Out_   ULONG               *OffsetFromFirstPage_,
    _Out_   MEMORY_CACHING_TYPE *CacheType_

    ULONG ulBufferDurationMs = 0;

    if ((0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign))

    if ((NotificationCount_ == 0) || (RequestedSize_ % NotificationCount_ != 0))

    RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign);

    PHYSICAL_ADDRESS highAddress;
    highAddress.HighPart = 0;
    highAddress.LowPart = MAXULONG;

    PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl(highAddress, RequestedSize_);

    if (NULL == pBufferMdl)

    // From MSDN: 
    // "Since the Windows audio stack does not support a mechanism to express memory access 
    //  alignment requirements for buffers, audio drivers must select a caching type for mapped
    //  memory buffers that does not impose platform-specific alignment requirements. In other 
    //  words, the caching type used by the audio driver for mapped memory buffers, must not make 
    //  assumptions about the memory alignment requirements for any specific platform.
    //  This method maps the physical memory pages in the MDL into kernel-mode virtual memory. 
    //  Typically, the miniport driver calls this method if it requires software access to the 
    //  scatter-gather list for an audio buffer. In this case, the storage for the scatter-gather 
    //  list must have been allocated by the IPortWaveRTStream::AllocatePagesForMdl or 
    //  IPortWaveRTStream::AllocateContiguousPagesForMdl method. 
    //  A WaveRT miniport driver should not require software access to the audio buffer itself."
    m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached);
    m_ulNotificationsPerBuffer = NotificationCount_;
    m_ulDmaBufferSize = RequestedSize_;
    ulBufferDurationMs = (RequestedSize_ * 1000) / m_ulDmaMovementRate;
    m_ulNotificationIntervalMs = ulBufferDurationMs / NotificationCount_;

    RingBuffer::GetInstance()->Init(m_ulDmaBufferSize * 4, m_pWfExt->Format.nBlockAlign);

    *AudioBufferMdl_ = pBufferMdl;
    *ActualSize_ = RequestedSize_;
    *OffsetFromFirstPage_ = 0;
    *CacheType_ = MmCached;

    return STATUS_SUCCESS;

As I said, it works so far and it produces sound, but it's having a silence of few ms each X ms making the sound not continuous..

What should I do to have this in sync ?


  • forlayoforlayo Member Posts: 2

    I finally found the issue that was based on the situation of using Thread.sleep() to defined the periods of sending buffer to the driver. That doesn't had the enough preccision and it is usually defaulting to windows clock at aroun 15-25ms in works cases, and these milliseconds matters a lot here.

    The implementation is even worst as hadn't in account the time taken by the call itself making next call being more late. Normally that call is soo fast, but when it's giving back an error it could take 4-5ms.

    The solution I added for this problem is to use Precission-Time.NET to have the cycle of audio sending based on it that uses Windows Multimedia Timers with a preccision up to 1ms.

    I hope this would help someone :D

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Internals & Software Drivers 19-23 June 2023 Live, Online
Writing WDF Drivers 10-14 July 2023 Live, Online
Kernel Debugging 16-20 October 2023 Live, Online
Developing Minifilters 13-17 November 2023 Live, Online