summaryrefslogtreecommitdiff
path: root/audio/simpleaudiosample/Source/Main/minwavertstream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'audio/simpleaudiosample/Source/Main/minwavertstream.cpp')
-rw-r--r--audio/simpleaudiosample/Source/Main/minwavertstream.cpp1644
1 files changed, 1644 insertions, 0 deletions
diff --git a/audio/simpleaudiosample/Source/Main/minwavertstream.cpp b/audio/simpleaudiosample/Source/Main/minwavertstream.cpp
new file mode 100644
index 00000000..27289a0e
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Main/minwavertstream.cpp
@@ -0,0 +1,1644 @@
+#include "definitions.h"
+#include <limits.h>
+#include <ks.h>
+#include "endpoints.h"
+#include "minwavert.h"
+#include "minwavertstream.h"
+#define MINWAVERTSTREAM_POOLTAG 'SRWM'
+
+#pragma warning (disable : 4127)
+
+//=============================================================================
+// CMiniportWaveRTStream
+//=============================================================================
+
+//=============================================================================
+#pragma code_seg("PAGE")
+CMiniportWaveRTStream::~CMiniportWaveRTStream
+(
+ void
+)
+/*++
+
+Routine Description:
+
+ Destructor for wavertstream
+
+Arguments:
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+ if (NULL != m_pMiniport)
+ {
+
+ if (m_bUnregisterStream)
+ {
+ m_pMiniport->StreamClosed(m_ulPin, this);
+ m_bUnregisterStream = FALSE;
+ }
+
+ m_pMiniport->Release();
+ m_pMiniport = NULL;
+ }
+
+ if (m_pDpc)
+ {
+ ExFreePoolWithTag( m_pDpc, MINWAVERTSTREAM_POOLTAG );
+ m_pDpc = NULL;
+ }
+
+ if (m_pTimer)
+ {
+ ExFreePoolWithTag( m_pTimer, MINWAVERTSTREAM_POOLTAG );
+ m_pTimer = NULL;
+ }
+
+ if (m_pbMuted)
+ {
+ ExFreePoolWithTag( m_pbMuted, MINWAVERTSTREAM_POOLTAG );
+ m_pbMuted = NULL;
+ }
+
+ if (m_plVolumeLevel)
+ {
+ ExFreePoolWithTag( m_plVolumeLevel, MINWAVERTSTREAM_POOLTAG );
+ m_plVolumeLevel = NULL;
+ }
+
+ if (m_plPeakMeter)
+ {
+ ExFreePoolWithTag( m_plPeakMeter, MINWAVERTSTREAM_POOLTAG );
+ m_plPeakMeter = NULL;
+ }
+
+ if (m_pWfExt)
+ {
+ ExFreePoolWithTag( m_pWfExt, MINWAVERTSTREAM_POOLTAG );
+ m_pWfExt = NULL;
+ }
+ if (m_pNotificationTimer)
+ {
+ ExDeleteTimer
+ (
+ m_pNotificationTimer,
+ TRUE, // Cancel the timer if it is currently set.
+ TRUE, // Wait for the timer to finish expiring and for any callback to a ExTimerCallback routine to finish.
+ NULL
+ );
+ }
+
+ // Since we just cancelled the notification timer, wait for all queued
+ // DPCs to complete before we free the notification DPC.
+ //
+ KeFlushQueuedDpcs();
+
+ DPF_ENTER(("[CMiniportWaveRTStream::~CMiniportWaveRTStream]"));
+} // ~CMiniportWaveRTStream
+
+//=============================================================================
+#pragma code_seg("PAGE")
+
+NTSTATUS CMiniportWaveRTStream::ReadRegistrySettings()
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus;
+ UNICODE_STRING parametersPath;
+
+ RTL_QUERY_REGISTRY_TABLE paramTable[] = {
+ // QueryRoutine Flags Name EntryContext DefaultType DefaultData DefaultLength
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneFrequency", &m_ulHostCaptureToneFrequency, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_ulHostCaptureToneFrequency, sizeof(DWORD) },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneAmplitude", &m_dwHostCaptureToneAmplitude, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_dwHostCaptureToneAmplitude, sizeof(DWORD) },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneDCOffset", &m_dwHostCaptureToneDCOffset, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_dwHostCaptureToneDCOffset, sizeof(DWORD) },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK, L"HostCaptureToneInitialPhase", &m_dwHostCaptureToneInitialPhase, (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD, &m_dwHostCaptureToneInitialPhase, sizeof(DWORD) },
+ { NULL, 0, NULL, NULL, 0, NULL, 0 }
+ };
+
+ RtlInitUnicodeString(&parametersPath, NULL);
+
+ // The sizeof(WCHAR) is added to the maximum length, for allowing a space for null termination of the string.
+ parametersPath.MaximumLength =
+ g_RegistryPath.Length + sizeof(L"\\Parameters") + sizeof(WCHAR);
+
+ parametersPath.Buffer = (PWCH)ExAllocatePool2(POOL_FLAG_PAGED, parametersPath.MaximumLength, MINWAVERT_POOLTAG);
+ if (parametersPath.Buffer == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlAppendUnicodeToString(&parametersPath, g_RegistryPath.Buffer);
+ RtlAppendUnicodeToString(&parametersPath, L"\\Parameters");
+
+ ntStatus = RtlQueryRegistryValues(
+ RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+ parametersPath.Buffer,
+ &paramTable[0],
+ NULL,
+ NULL
+ );
+
+ ExFreePool(parametersPath.Buffer);
+
+ return ntStatus;
+}
+
+NTSTATUS
+CMiniportWaveRTStream::Init
+(
+ _In_ PCMiniportWaveRT Miniport_,
+ _In_ PPORTWAVERTSTREAM PortStream_,
+ _In_ ULONG Pin_,
+ _In_ BOOLEAN Capture_,
+ _In_ PKSDATAFORMAT DataFormat_,
+ _In_ GUID SignalProcessingMode
+)
+/*++
+
+Routine Description:
+
+ Initializes the stream object.
+
+Arguments:
+
+ Miniport_ -
+
+ Pin_ -
+
+ DataFormat -
+
+ SignalProcessingMode - The driver uses the signalProcessingMode to configure
+ driver and/or hardware specific signal processing to be applied to this new
+ stream.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ PWAVEFORMATEX pWfEx = NULL;
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+
+ m_pMiniport = NULL;
+ m_ulPin = 0;
+ m_bUnregisterStream = FALSE;
+ m_bCapture = FALSE;
+ m_ulDmaBufferSize = 0;
+ m_pDmaBuffer = NULL;
+ m_ulNotificationsPerBuffer = 0;
+ m_KsState = KSSTATE_STOP;
+ m_pTimer = NULL;
+ m_pDpc = NULL;
+ m_llPacketCounter = 0;
+ m_ullPlayPosition = 0;
+ m_ullWritePosition = 0;
+ m_ullDmaTimeStamp = 0;
+ m_hnsElapsedTimeCarryForward = 0;
+ m_ullLastDPCTimeStamp = 0;
+ m_hnsDPCTimeCarryForward = 0;
+ m_ulDmaMovementRate = 0;
+ m_byteDisplacementCarryForward = 0;
+ m_bLfxEnabled = FALSE;
+ m_pbMuted = NULL;
+ m_plVolumeLevel = NULL;
+ m_plPeakMeter = NULL;
+ m_pWfExt = NULL;
+ m_ullLinearPosition = 0;
+ m_ullPresentationPosition = 0;
+ m_ulContentId = 0;
+ m_ulCurrentWritePosition = 0;
+ m_ulLastOsReadPacket = ULONG_MAX;
+ m_ulLastOsWritePacket = ULONG_MAX;
+ m_IsCurrentWritePositionUpdated = 0;
+ m_SignalProcessingMode = SignalProcessingMode;
+ m_bEoSReceived = FALSE;
+ m_bLastBufferRendered = FALSE;
+
+ m_ulHostCaptureToneFrequency = IsEqualGUID(SignalProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) ? 1000 : 2000;
+ m_dwHostCaptureToneAmplitude = 50;
+ m_dwHostCaptureToneDCOffset = 0;
+ m_dwHostCaptureToneInitialPhase = 0;
+
+ m_pPortStream = PortStream_;
+ InitializeListHead(&m_NotificationList);
+ m_ulNotificationIntervalMs = 0;
+
+ // Initialize the spinlock to synchronize position updates
+ KeInitializeSpinLock(&m_PositionSpinLock);
+
+ m_pNotificationTimer = ExAllocateTimer(
+ TimerNotifyRT,
+ this,
+ EX_TIMER_HIGH_RESOLUTION
+ );
+ if (!m_pNotificationTimer)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pWfEx = GetWaveFormatEx(DataFormat_);
+ if (NULL == pWfEx)
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ m_pMiniport = reinterpret_cast<CMiniportWaveRT*>(Miniport_);
+ if (m_pMiniport == NULL)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+ m_pMiniport->AddRef();
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+ m_ulPin = Pin_;
+ m_bCapture = Capture_;
+ m_ulDmaMovementRate = pWfEx->nAvgBytesPerSec;
+
+ m_pDpc = (PRKDPC)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KDPC), MINWAVERTSTREAM_POOLTAG);
+ if (!m_pDpc)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(WAVEFORMATEX) + pWfEx->cbSize, MINWAVERTSTREAM_POOLTAG);
+ if (m_pWfExt == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ RtlCopyMemory(m_pWfExt, pWfEx, sizeof(WAVEFORMATEX) + pWfEx->cbSize);
+
+ m_pbMuted = (PBOOL)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(BOOL), MINWAVERTSTREAM_POOLTAG);
+ if (m_pbMuted == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ m_plVolumeLevel = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG);
+ if (m_plVolumeLevel == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ m_plPeakMeter = (PLONG)ExAllocatePool2(POOL_FLAG_NON_PAGED, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG);
+ if (m_plPeakMeter == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (m_bCapture)
+ {
+ ReadRegistrySettings();
+ DWORD toneFrequency = 0;
+ DWORD toneAmplitude = 0;
+ DWORD toneDCOffset = 0;
+ DWORD toneInitialPhase = 0;
+
+ double toneAmplitudeDouble = 0;
+ double toneDCOffsetDouble = 0;
+ double toneInitialPhaseDouble = 0;
+
+ toneFrequency = m_ulHostCaptureToneFrequency;
+ toneAmplitude = m_dwHostCaptureToneAmplitude;
+ toneDCOffset = m_dwHostCaptureToneDCOffset;
+ toneInitialPhase = m_dwHostCaptureToneInitialPhase;
+
+ if (labs(toneAmplitude) > 100)
+ {
+ toneAmplitude = toneAmplitude > 0 ? 100 : -100;
+ }
+
+ if (labs(toneDCOffset) > 100)
+ {
+ toneDCOffset = toneDCOffset > 0 ? 100 : -100;
+ }
+
+ DWORD abssum = labs(toneAmplitude) + labs(toneDCOffset);
+
+ if (abssum > 100)
+ {
+ toneAmplitudeDouble = ((double)toneAmplitude) / abssum;
+ toneDCOffsetDouble = ((double)toneDCOffset) / abssum;
+ }
+ else
+ {
+ toneAmplitudeDouble = ((double)toneAmplitude) / 100.0;
+ toneDCOffsetDouble = ((double)toneDCOffset) / 100.0;
+ }
+
+ if (labs(toneInitialPhase) > 31416)
+ {
+ toneInitialPhase = toneInitialPhase > 0 ? 31416 : -31416;
+ }
+
+ toneInitialPhaseDouble = (double)toneInitialPhase / 10000;
+
+ ntStatus = m_ToneGenerator.Init(toneFrequency, toneAmplitudeDouble, toneDCOffsetDouble, toneInitialPhaseDouble, m_pWfExt);
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+ }
+ else if (!g_DoNotCreateDataFiles)
+ {
+ //
+ // Create an output file for the render data.
+ //
+ DPF(D_TERSE, ("SaveData %p", &m_SaveData));
+ ntStatus = m_SaveData.SetDataFormat(DataFormat_);
+ if (NT_SUCCESS(ntStatus))
+ {
+ ntStatus = m_SaveData.Initialize();
+ }
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+ }
+
+ //
+ // Register this stream.
+ //
+ ntStatus = m_pMiniport->StreamCreated(m_ulPin, this);
+ if (NT_SUCCESS(ntStatus))
+ {
+ m_bUnregisterStream = TRUE;
+ }
+
+ return ntStatus;
+} // Init
+
+//=============================================================================
+#pragma code_seg("PAGE")
+STDMETHODIMP_(NTSTATUS)
+CMiniportWaveRTStream::NonDelegatingQueryInterface
+(
+ _In_ REFIID Interface,
+ _COM_Outptr_ PVOID * Object
+)
+/*++
+
+Routine Description:
+
+ QueryInterface
+
+Arguments:
+
+ Interface - GUID
+
+ Object - interface pointer to be returned
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT(Object);
+
+ if (IsEqualGUIDAligned(Interface, IID_IUnknown))
+ {
+ *Object = PVOID(PUNKNOWN(PMINIPORTWAVERTSTREAM(this)));
+ }
+ else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStream))
+ {
+ *Object = PVOID(PMINIPORTWAVERTSTREAM(this));
+ }
+ else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStreamNotification))
+ {
+ *Object = PVOID(PMINIPORTWAVERTSTREAMNOTIFICATION(this));
+ }
+ else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTInputStream) && (this->m_bCapture))
+ {
+ // This interface is supported only on capture streams
+ *Object = PVOID(PMINIPORTWAVERTINPUTSTREAM(this));
+ }
+ else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTOutputStream))
+ {
+ // This interface is supported only on host render streams
+ *Object = PVOID(PMINIPORTWAVERTOUTPUTSTREAM(this));
+ }
+ else if (IsEqualGUIDAligned(Interface, IID_IDrmAudioStream))
+ {
+ *Object = (PVOID)(IDrmAudioStream*)this;
+ }
+ else
+ {
+ *Object = NULL;
+ }
+
+ if (*Object)
+ {
+ PUNKNOWN(*Object)->AddRef();
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_INVALID_PARAMETER;
+} // NonDelegatingQueryInterface
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::AllocateBufferWithNotification
+(
+ _In_ ULONG NotificationCount_,
+ _In_ ULONG RequestedSize_,
+ _Out_ PMDL *AudioBufferMdl_,
+ _Out_ ULONG *ActualSize_,
+ _Out_ ULONG *OffsetFromFirstPage_,
+ _Out_ MEMORY_CACHING_TYPE *CacheType_
+)
+{
+ PAGED_CODE();
+
+ ULONG ulBufferDurationMs = 0;
+
+ if ( (0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign) )
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if ((NotificationCount_ == 0) || (RequestedSize_ % NotificationCount_ != 0))
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign);
+
+ if (!m_bCapture && (!g_DoNotCreateDataFiles))
+ {
+ NTSTATUS ntStatus;
+
+ // Simple Audio Sample uses following buffer to hold data before writing to a file.
+ // Allocating larger buffer will reduce File I/O operations.
+ ntStatus = m_SaveData.SetMaxWriteSize(RequestedSize_ * 4);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ return ntStatus;
+ }
+ }
+
+ PHYSICAL_ADDRESS highAddress;
+ highAddress.HighPart = 0;
+ highAddress.LowPart = MAXULONG;
+
+ PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl (highAddress, RequestedSize_);
+
+ if (NULL == pBufferMdl)
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ // 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_;
+
+ *AudioBufferMdl_ = pBufferMdl;
+ *ActualSize_ = RequestedSize_;
+ *OffsetFromFirstPage_ = 0;
+ *CacheType_ = MmCached;
+
+ return STATUS_SUCCESS;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+VOID CMiniportWaveRTStream::FreeBufferWithNotification
+(
+ _In_ PMDL Mdl_,
+ _In_ ULONG Size_
+)
+{
+ UNREFERENCED_PARAMETER(Size_);
+
+ PAGED_CODE();
+
+ if (Mdl_ != NULL)
+ {
+ if (m_pDmaBuffer != NULL)
+ {
+ m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_);
+ m_pDmaBuffer = NULL;
+ }
+
+ m_pPortStream->FreePagesFromMdl(Mdl_);
+ }
+
+ m_ulDmaBufferSize = 0;
+ m_ulNotificationsPerBuffer = 0;
+
+ return;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::RegisterNotificationEvent
+(
+ _In_ PKEVENT NotificationEvent_
+)
+{
+ UNREFERENCED_PARAMETER(NotificationEvent_);
+
+ PAGED_CODE();
+
+ NotificationListEntry *nleNew = (NotificationListEntry*)ExAllocatePool2(
+ POOL_FLAG_NON_PAGED,
+ sizeof(NotificationListEntry),
+ MINWAVERTSTREAM_POOLTAG);
+ if (NULL == nleNew)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ nleNew->NotificationEvent = NotificationEvent_;
+
+ // Fail if the notification event already exists in our list.
+ if (!IsListEmpty(&m_NotificationList))
+ {
+ PLIST_ENTRY leCurrent = m_NotificationList.Flink;
+ while (leCurrent != &m_NotificationList)
+ {
+ NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry);
+ if (nleCurrent->NotificationEvent == NotificationEvent_)
+ {
+ ExFreePoolWithTag( nleNew, MINWAVERTSTREAM_POOLTAG );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ leCurrent = leCurrent->Flink;
+ }
+ }
+
+ InsertTailList(&m_NotificationList, &(nleNew->ListEntry));
+
+ return STATUS_SUCCESS;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::UnregisterNotificationEvent
+(
+ _In_ PKEVENT NotificationEvent_
+)
+{
+ UNREFERENCED_PARAMETER(NotificationEvent_);
+
+ PAGED_CODE();
+
+ if (!IsListEmpty(&m_NotificationList))
+ {
+ PLIST_ENTRY leCurrent = m_NotificationList.Flink;
+ while (leCurrent != &m_NotificationList)
+ {
+ NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry);
+ if (nleCurrent->NotificationEvent == NotificationEvent_)
+ {
+ RemoveEntryList( leCurrent );
+ ExFreePoolWithTag( nleCurrent, MINWAVERTSTREAM_POOLTAG );
+ return STATUS_SUCCESS;
+ }
+
+ leCurrent = leCurrent->Flink;
+ }
+ }
+
+ return STATUS_NOT_FOUND;
+}
+
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::GetClockRegister
+(
+ _Out_ PKSRTAUDIO_HWREGISTER Register_
+)
+{
+ UNREFERENCED_PARAMETER(Register_);
+
+ PAGED_CODE();
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::GetPositionRegister
+(
+ _Out_ PKSRTAUDIO_HWREGISTER Register_
+)
+{
+ UNREFERENCED_PARAMETER(Register_);
+
+ PAGED_CODE();
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+VOID CMiniportWaveRTStream::GetHWLatency
+(
+ _Out_ PKSRTAUDIO_HWLATENCY Latency_
+)
+{
+ PAGED_CODE();
+
+ ASSERT(Latency_);
+
+ Latency_->ChipsetDelay = 0;
+ Latency_->CodecDelay = 0;
+ Latency_->FifoSize = 0;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+VOID CMiniportWaveRTStream::FreeAudioBuffer
+(
+_In_opt_ PMDL Mdl_,
+_In_ ULONG Size_
+)
+{
+ UNREFERENCED_PARAMETER(Size_);
+
+ PAGED_CODE();
+
+ if (Mdl_ != NULL)
+ {
+ if (m_pDmaBuffer != NULL)
+ {
+ m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_);
+ m_pDmaBuffer = NULL;
+ }
+
+ m_pPortStream->FreePagesFromMdl(Mdl_);
+ }
+
+ m_ulDmaBufferSize = 0;
+ m_ulNotificationsPerBuffer = 0;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::AllocateAudioBuffer
+(
+_In_ ULONG RequestedSize_,
+_Out_ PMDL *AudioBufferMdl_,
+_Out_ ULONG *ActualSize_,
+_Out_ ULONG *OffsetFromFirstPage_,
+_Out_ MEMORY_CACHING_TYPE *CacheType_
+)
+{
+ PAGED_CODE();
+
+ if ((0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign))
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ 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)
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ // 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_ulDmaBufferSize = RequestedSize_;
+ m_ulNotificationsPerBuffer = 0;
+
+ *AudioBufferMdl_ = pBufferMdl;
+ *ActualSize_ = RequestedSize_;
+ *OffsetFromFirstPage_ = 0;
+ *CacheType_ = MmCached;
+
+ return STATUS_SUCCESS;
+}
+
+//=============================================================================
+#pragma code_seg()
+NTSTATUS CMiniportWaveRTStream::GetPosition
+(
+ _Out_ KSAUDIO_POSITION *Position_
+)
+{
+ NTSTATUS ntStatus;
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+
+ if (m_KsState == KSSTATE_RUN)
+ {
+ //
+ // Get the current time and update position.
+ //
+ LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL);
+ UpdatePosition(ilQPC);
+ }
+
+ Position_->PlayOffset = m_ullPlayPosition;
+ Position_->WriteOffset = m_ullWritePosition;
+
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+
+ ntStatus = STATUS_SUCCESS;
+
+ return ntStatus;
+}
+
+//=============================================================================
+// CMiniportWaveRTStream::GetReadPacket
+//
+// Returns information about the next packet for the OS to read.
+//
+// Return value
+//
+// Returns STATUS_DEVICE_NOT_READY if no new packets are available.
+//
+// IRQL - PASSIVE_LEVEL
+//
+// Remarks
+// Although called at passive level, this routine is non-paged code because
+// it is called in the streaming path where page faults should be avoided.
+//
+// ISSUE-2014/10/4 Will this work correctly across pause/play?
+#pragma code_seg()
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS CMiniportWaveRTStream::GetReadPacket
+(
+ _Out_ ULONG* PacketNumber,
+ _Out_ DWORD* Flags,
+ _Out_ ULONG64* PerformanceCounterValue,
+ _Out_ BOOL* MoreData
+)
+{
+ ULONG availablePacketNumber;
+ ULONG droppedPackets;
+
+ // The call must be from event driven mode
+ if (m_ulNotificationsPerBuffer == 0)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ *Flags = 0;
+
+ if (m_KsState < KSSTATE_PAUSE)
+ {
+ return STATUS_INVALID_DEVICE_STATE;
+ }
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+
+ LONGLONG packetCounter = m_llPacketCounter;
+ ULONGLONG ullLinearPosition = m_ullLinearPosition;
+ ULONGLONG hnsElapsedTimeCarryForward = m_hnsElapsedTimeCarryForward;
+ ULONGLONG ullDmaTimeStamp = m_ullDmaTimeStamp;
+
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+
+ // The 0-based number of the last completed packet
+ // FUTURE-2014/10/27 Update to allow different numbers of packets per WaveRT buffer
+ availablePacketNumber = LODWORD(packetCounter - 1); // Note this might be ULONG_MAX if called during the first packet
+
+ // If no new packets are available...
+ if (availablePacketNumber == m_ulLastOsReadPacket)
+ {
+ return STATUS_DEVICE_NOT_READY;
+ }
+
+ // If more than one packet has transferred since the last packet read by
+ // the OS, then those were dropped. That is, a glitch occurred.
+ droppedPackets = availablePacketNumber - m_ulLastOsReadPacket - 1;
+ if (droppedPackets > 0)
+ {
+ // Trace a glitch
+ }
+
+ // Return next packet number to be read
+ *PacketNumber = availablePacketNumber;
+
+ // Compute and return timestamp corresponding to the end of the available packet. In a real hardware
+ // driver, the timestamp would be computed in a driver and hardware specific manner. In this sample
+ // driver, it is extrapolated from the sample driver's internal simulated position correlation
+ // [m_ullLinearPosition @ m_ullDmaTimeStamp] and the sample's internal 64-bit packet counter, subtracting
+ // 1 from the packet counter to compute the time at the start of that last completed packet.
+ ULONGLONG linearPositionOfAvailablePacket = packetCounter * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer);
+ // Need to divide by (1000 * 10000 because m_ulDmaMovementRate is average bytes per sec
+ ULONGLONG carryForwardBytes = (hnsElapsedTimeCarryForward * m_ulDmaMovementRate) / 10000000;
+ ULONGLONG deltaLinearPosition = ullLinearPosition + carryForwardBytes - linearPositionOfAvailablePacket;
+ ULONGLONG deltaTimeInHns = deltaLinearPosition * 10000000 / m_ulDmaMovementRate;
+ ULONGLONG timeOfAvailablePacketInHns = ullDmaTimeStamp - deltaTimeInHns;
+ ULONGLONG timeOfAvailablePacketInQpc = timeOfAvailablePacketInHns * m_ullPerformanceCounterFrequency.QuadPart / 10000000;
+
+ *PerformanceCounterValue = timeOfAvailablePacketInQpc;
+
+ // No flags are defined yet
+ *Flags = 0;
+
+ // This sample does not internally buffer data so there is never more data
+ // than revealed by the results from this routine.
+ *MoreData = FALSE;
+
+ // Update the last packet read by the OS
+ m_ulLastOsReadPacket = availablePacketNumber;
+
+ return STATUS_SUCCESS;
+}
+
+#pragma code_seg()
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS CMiniportWaveRTStream::SetWritePacket
+(
+ _In_ ULONG PacketNumber,
+ _In_ DWORD Flags,
+ _In_ ULONG EosPacketLength
+)
+{
+ UNREFERENCED_PARAMETER(EosPacketLength);
+ NTSTATUS ntStatus;
+
+ // The call must be from event driven mode
+ if (m_ulNotificationsPerBuffer == 0)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ ULONG oldLastOsWritePacket = m_ulLastOsWritePacket;
+
+ // This function should not be called once EoS has been set.
+ if (m_bEoSReceived)
+ {
+ return STATUS_INVALID_DEVICE_STATE;
+ }
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+ // 1-based count of completed packets, 0-based packet number of current packet
+ LONGLONG currentPacket = m_llPacketCounter;
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+
+ // If not running, the current packet hasn't actually started transfering so OS should be writing
+ // to the current packet. If running, then the current packing is already transfering to hardware
+ // so the OS should write the packet after the current packet.
+ ULONG expectedPacket = LODWORD(currentPacket);
+ if (m_KsState == KSSTATE_RUN)
+ {
+ expectedPacket++;
+ }
+
+ // Check if OS PacketNumber is behind or too far ahead of current packet
+ LONG deltaFromExpectedPacket = PacketNumber - expectedPacket; // Modulo arithemetic
+ if (deltaFromExpectedPacket < 0)
+ {
+ return STATUS_DATA_LATE_ERROR;
+ }
+ else if (deltaFromExpectedPacket > 0)
+ {
+ return STATUS_DATA_OVERRUN;
+ }
+
+ ULONG packetSize = (m_ulDmaBufferSize / m_ulNotificationsPerBuffer);
+ ULONG packetIndex = PacketNumber % m_ulNotificationsPerBuffer;
+ ULONG ulCurrentWritePosition = packetIndex * packetSize;
+
+ // Check if EOS flag was passed
+ if (Flags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ m_ulLastOsWritePacket = PacketNumber;
+
+ // This function sets the current write position to the specified byte in the DMA buffer.
+ // Will check if the write position is smaller than the DMA buffer size.
+ // Will not return an error when the passed in parameter is 0.
+ // Will also check if this function was called with the same write position(in event mode only)
+ // Underruning will also be checked via timer mechanism
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+ ntStatus = SetCurrentWritePositionInternal(ulCurrentWritePosition);
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+ }
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ m_ulLastOsWritePacket = oldLastOsWritePacket;
+ }
+
+ return ntStatus;
+}
+
+//=============================================================================
+#pragma code_seg()
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS CMiniportWaveRTStream::GetOutputStreamPresentationPosition
+(
+ _Out_ KSAUDIO_PRESENTATION_POSITION *pPresentationPosition
+)
+{
+ ASSERT (pPresentationPosition);
+
+ // The call must be from event driven mode
+ if(m_ulNotificationsPerBuffer == 0)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ return GetPresentationPosition(pPresentationPosition);
+}
+
+//=============================================================================
+#pragma code_seg()
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS CMiniportWaveRTStream::GetPacketCount
+(
+ _Out_ ULONG *pPacketCount
+)
+{
+ ASSERT(pPacketCount);
+
+ // The call must be from event driven mode
+ if(m_ulNotificationsPerBuffer == 0)
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+
+ if (m_KsState == KSSTATE_RUN)
+ {
+ // Get the current time and update simulated position.
+ LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL);
+ UpdatePosition(ilQPC);
+ }
+
+ *pPacketCount = LODWORD(m_llPacketCounter);
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+
+ return STATUS_SUCCESS;
+}
+
+//linear and presentation positions
+#pragma code_seg()
+NTSTATUS CMiniportWaveRTStream::GetPositions(
+ _Out_opt_ ULONGLONG* _pullLinearBufferPosition,
+ _Out_opt_ ULONGLONG* _pullPresentationPosition,
+ _Out_opt_ LARGE_INTEGER* _pliQPCTime
+)
+{
+ DPF_ENTER(("[CMiniportWaveRTStream::GetPositions]"));
+
+ NTSTATUS ntStatus;
+ LARGE_INTEGER ilQPC;
+ KIRQL oldIrql;
+
+ // Update *_pullLinearBufferPosition with the the number of bytes fetched from waveRT ever since a stream got set into RUN
+ // state.
+ // Once the stream is set to STOP state, any further read on this call would return zero.
+
+ //
+ // Get the current time and update position.
+ //
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+ ilQPC = KeQueryPerformanceCounter(NULL);
+ if (m_KsState == KSSTATE_RUN)
+ {
+ UpdatePosition(ilQPC);
+ }
+ if (_pullLinearBufferPosition)
+ {
+ *_pullLinearBufferPosition = m_ullLinearPosition;
+ }
+ if (_pullPresentationPosition)
+ {
+ *_pullPresentationPosition = m_ullPresentationPosition;
+ }
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+ if (_pliQPCTime)
+ {
+ *_pliQPCTime = ilQPC;
+ }
+
+ ntStatus = STATUS_SUCCESS;
+
+ return ntStatus;
+}
+
+NTSTATUS CMiniportWaveRTStream::GetPresentationPosition(_Out_ KSAUDIO_PRESENTATION_POSITION* _pPresentationPosition)
+{
+ ASSERT(_pPresentationPosition);
+ LARGE_INTEGER timeStamp;
+
+ DPF_ENTER(("[CMiniportWaveRTStream::GetPresentationPosition]"));
+
+ ULONGLONG ullLinearPosition = { 0 };
+ ULONGLONG ullPresentationPosition = { 0 };
+ NTSTATUS status = STATUS_SUCCESS;
+
+ status = GetPositions(&ullLinearPosition, &ullPresentationPosition, &timeStamp);
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ _pPresentationPosition->u64PositionInBlocks = ullPresentationPosition * m_pWfExt->Format.nSamplesPerSec / m_pWfExt->Format.nAvgBytesPerSec;
+ _pPresentationPosition->u64QPCPosition = (UINT64)timeStamp.QuadPart;
+
+ return STATUS_SUCCESS;
+}
+
+#pragma code_seg()
+NTSTATUS CMiniportWaveRTStream::SetCurrentWritePositionInternal(_In_ ULONG _ulCurrentWritePosition)
+{
+ DPF_ENTER(("[CMiniportWaveRTStream::SetCurrentWritePositionInternal]"));
+
+ ASSERT(m_bEoSReceived == FALSE);
+
+ if (m_bEoSReceived)
+ {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (_ulCurrentWritePosition > m_ulDmaBufferSize)
+ {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj();
+
+ //Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION
+ //Parameter 1: Current linear buffer position
+ //Parameter 2: Previous WaveRtBufferWritePosition that the driver received
+ //Parameter 3: Target WaveRtBufferWritePosition received from portcls
+ //Parameter 4: 0
+ pAdapterComm->WriteEtwEvent(eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION,
+ m_ullLinearPosition, // replace with the correct "Current linear buffer position"
+ m_ulCurrentWritePosition,
+ _ulCurrentWritePosition, // this is new write position
+ 0); // always zero
+
+//
+// Check for eMINIPORT_GLITCH_REPORT - Same WaveRT buffer write during event driven mode.
+//
+ if (m_ulNotificationIntervalMs > 0)
+ {
+ if (m_ulCurrentWritePosition == _ulCurrentWritePosition)
+ {
+ //Event type: eMINIPORT_GLITCH_REPORT
+ //Parameter 1: Current linear buffer position
+ //Parameter 2: Previous WaveRtBufferWritePosition that the driver received
+ //Parameter 3: Major glitch code: 3: Received same WaveRT buffer twice in a row during event driven mode
+ //Parameter 4: Minor code for the glitch cause
+ pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT,
+ m_ullLinearPosition, // replace with the correct "Current linear buffer position"
+ m_ulCurrentWritePosition,
+ 3, // received same WaveRT buffer twice in a row during event driven mode
+ _ulCurrentWritePosition);
+ }
+ }
+
+ m_ulCurrentWritePosition = _ulCurrentWritePosition;
+ InterlockedExchange(&m_IsCurrentWritePositionUpdated, 1);
+
+ return STATUS_SUCCESS;
+}
+
+//=============================================================================
+#pragma code_seg()
+NTSTATUS CMiniportWaveRTStream::SetState
+(
+ _In_ KSSTATE State_
+)
+{
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ KIRQL oldIrql;
+
+ // Spew an event for a pin state change request from portcls
+ //Event type: eMINIPORT_PIN_STATE
+ switch (State_)
+ {
+ case KSSTATE_STOP:
+ if (m_KsState == KSSTATE_ACQUIRE)
+ {
+ // Acquire stream resources
+ }
+ KeAcquireSpinLock(&m_PositionSpinLock, &oldIrql);
+ // Reset DMA
+ m_llPacketCounter = 0;
+ m_ullPlayPosition = 0;
+ m_ullWritePosition = 0;
+ m_ullLinearPosition = 0;
+ m_ullPresentationPosition = 0;
+
+ // Reset OS read/write positions
+ m_ulLastOsReadPacket = ULONG_MAX;
+ m_ulCurrentWritePosition = 0;
+ m_ulLastOsWritePacket = ULONG_MAX;
+ m_bEoSReceived = FALSE;
+ m_bLastBufferRendered = FALSE;
+
+ KeReleaseSpinLock(&m_PositionSpinLock, oldIrql);
+
+ // Wait until all work items are completed.
+ if (!m_bCapture && !g_DoNotCreateDataFiles)
+ {
+ m_SaveData.WaitAllWorkItems();
+ }
+ break;
+
+ case KSSTATE_ACQUIRE:
+ if (m_KsState == KSSTATE_STOP)
+ {
+ // Acquire stream resources
+ }
+ break;
+
+ case KSSTATE_PAUSE:
+
+ if (m_KsState > KSSTATE_PAUSE)
+ {
+ //
+ // Run -> Pause
+ //
+
+ // Pause DMA
+ if (m_ulNotificationIntervalMs > 0)
+ {
+ ExCancelTimer(m_pNotificationTimer, NULL);
+ KeFlushQueuedDpcs();
+
+ // If pin is transitioning from RUN, save the time since last buffer completion event was sent
+ // so if the pin goes to RUN state again we can send the buffer completion event at correct time.
+ if (m_ullLastDPCTimeStamp > 0)
+ {
+ LARGE_INTEGER qpc;
+ LARGE_INTEGER qpcFrequency;
+ LONGLONG hnsCurrentTime;
+
+ qpc = KeQueryPerformanceCounter(&qpcFrequency);
+
+ // Convert ticks to 100ns units.
+ hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, qpc);
+ m_hnsDPCTimeCarryForward = hnsCurrentTime - m_ullLastDPCTimeStamp + m_hnsDPCTimeCarryForward;
+ }
+ }
+ }
+ // This call updates the linear buffer and presentation positions.
+ GetPositions(NULL, NULL, NULL);
+ break;
+
+ case KSSTATE_RUN:
+ // Start DMA
+ LARGE_INTEGER ullPerfCounterTemp;
+ ullPerfCounterTemp = KeQueryPerformanceCounter(&m_ullPerformanceCounterFrequency);
+ m_ullLastDPCTimeStamp = m_ullDmaTimeStamp = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ullPerfCounterTemp);
+
+ if (m_ulNotificationIntervalMs > 0)
+ {
+ // Set timer for 1 ms. This will cause DPC to run every 1 ms but driver will send out
+ // notification events only after notification interval. This timer is used by Simple Audio Sample to
+ // emulate hardware and send out notification event. Real hardware should not use this
+ // timer to fire notification event as it will drain power if the timer is running at 1 msec.
+ ExSetTimer
+ (
+ m_pNotificationTimer,
+ (-1) * HNSTIME_PER_MILLISECOND,
+ HNSTIME_PER_MILLISECOND, // 1 ms
+ NULL
+ );
+
+ }
+
+ break;
+ }
+
+ m_KsState = State_;
+
+ return ntStatus;
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS CMiniportWaveRTStream::SetFormat
+(
+ _In_ KSDATAFORMAT *DataFormat_
+)
+{
+ UNREFERENCED_PARAMETER(DataFormat_);
+
+ PAGED_CODE();
+
+ //if (!m_fCapture && !g_DoNotCreateDataFiles)
+ //{
+ // ntStatus = m_SaveData.SetDataFormat(Format);
+ //}
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+#pragma code_seg()
+
+//=============================================================================
+#pragma code_seg()
+VOID CMiniportWaveRTStream::UpdatePosition
+(
+ _In_ LARGE_INTEGER ilQPC
+)
+{
+ // Convert ticks to 100ns units.
+ LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ilQPC);
+
+ // Calculate the time elapsed since the last call to GetPosition() or since the
+ // DMA engine started. Note that the division by 10000 to convert to milliseconds
+ // may cause us to lose some of the time, so we will carry the remainder forward
+ // to the next GetPosition() call.
+ //
+ ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward)/10000;
+
+ // Carry forward the remainder of this division so we don't fall behind with our position too much.
+ //
+ m_hnsElapsedTimeCarryForward = (hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward) % 10000;
+
+ // Calculate how many bytes in the DMA buffer would have been processed in the elapsed
+ // time. Note that the division by 1000 to convert to milliseconds may cause us to
+ // lose some bytes, so we will carry the remainder forward to the next GetPosition() call.
+ //
+ // need to divide by 1000 because m_ulDmaMovementRate is average bytes per sec.
+
+ ULONG ByteDisplacement = ((m_ulDmaMovementRate * TimeElapsedInMS) + m_byteDisplacementCarryForward) / 1000 ;
+ m_byteDisplacementCarryForward = ((m_ulDmaMovementRate * TimeElapsedInMS) + m_byteDisplacementCarryForward) % 1000;
+
+ // Increment presentation position even after last buffer is rendered.
+ m_ullPresentationPosition += ByteDisplacement;
+
+ if (m_bCapture)
+ {
+ // Write sine wave to buffer.
+ WriteBytes(ByteDisplacement);
+ }
+ else
+ {
+
+ if (m_bEoSReceived)
+ {
+ // since EoS flag is set, we'll need to make sure not to read data beyond EOS position.
+ // If driver's current position is less than EoS position, then make sure not to read data beyond EoS.
+ if (m_ullWritePosition <= m_ulCurrentWritePosition)
+ {
+ ByteDisplacement = min(ByteDisplacement, m_ulCurrentWritePosition - (ULONG)m_ullWritePosition);
+ }
+ // If our current position is ahead of EoS position and we'll wrap around after new position then adjust
+ // new position if it crosses EoS.
+ else if ((m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize < m_ullWritePosition)
+ {
+ if ((m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize > m_ulCurrentWritePosition)
+ {
+ ByteDisplacement = ByteDisplacement - (((ULONG)m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize - m_ulCurrentWritePosition);
+ }
+ }
+ }
+
+ // If the last packet was rendered(read in the sample driver's case), send out an etw event.
+ if (m_bEoSReceived && !m_bLastBufferRendered
+ && (m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize == m_ulCurrentWritePosition)
+ {
+ m_bLastBufferRendered = TRUE;
+ }
+
+ if (!g_DoNotCreateDataFiles)
+ {
+ // Read from buffer and write to a file.
+ ReadBytes(ByteDisplacement);
+ }
+ }
+
+ // Increment the DMA position by the number of bytes displaced since the last
+ // call to UpdatePosition() and ensure we properly wrap at buffer length.
+ //
+ m_ullPlayPosition = m_ullWritePosition =
+ (m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize;
+
+ // m_ullDmaTimeStamp is updated in both GetPostion and GetLinearPosition calls
+ // so m_ullLinearPosition needs to be updated accordingly here
+ //
+ m_ullLinearPosition += ByteDisplacement;
+
+ // Update the DMA time stamp for the next call to GetPosition()
+ //
+ m_ullDmaTimeStamp = hnsCurrentTime;
+}
+
+//=============================================================================
+#pragma code_seg()
+VOID CMiniportWaveRTStream::WriteBytes
+(
+ _In_ ULONG ByteDisplacement
+)
+/*++
+
+Routine Description:
+
+This function writes the audio buffer using a sine wave generator
+
+Arguments:
+
+ByteDisplacement - # of bytes to process.
+
+--*/
+{
+ ULONG bufferOffset = m_ullLinearPosition % m_ulDmaBufferSize;
+
+ // Normally this will loop no more than once for a single wrap, but if
+ // many bytes have been displaced then this may loops many times.
+ while (ByteDisplacement > 0)
+ {
+ ULONG runWrite = min(ByteDisplacement, m_ulDmaBufferSize - bufferOffset);
+
+ m_ToneGenerator.GenerateSine(m_pDmaBuffer + bufferOffset, runWrite);
+
+ bufferOffset = (bufferOffset + runWrite) % m_ulDmaBufferSize;
+ ByteDisplacement -= runWrite;
+ }
+}
+
+//=============================================================================
+#pragma code_seg()
+VOID CMiniportWaveRTStream::ReadBytes
+(
+ _In_ ULONG ByteDisplacement
+)
+/*++
+
+Routine Description:
+
+This function reads the audio buffer and saves the data in a file.
+
+Arguments:
+
+ByteDisplacement - # of bytes to process.
+
+--*/
+{
+ ULONG bufferOffset = m_ullLinearPosition % m_ulDmaBufferSize;
+
+ // Normally this will loop no more than once for a single wrap, but if
+ // many bytes have been displaced then this may loops many times.
+ while (ByteDisplacement > 0)
+ {
+ ULONG runWrite = min(ByteDisplacement, m_ulDmaBufferSize - bufferOffset);
+ m_SaveData.WriteData(m_pDmaBuffer + bufferOffset, runWrite);
+ bufferOffset = (bufferOffset + runWrite) % m_ulDmaBufferSize;
+ ByteDisplacement -= runWrite;
+ }
+}
+
+//=============================================================================
+#pragma code_seg("PAGE")
+STDMETHODIMP_(NTSTATUS)
+CMiniportWaveRTStream::SetContentId
+(
+ _In_ ULONG contentId,
+ _In_ PCDRMRIGHTS drmRights
+)
+/*++
+
+Routine Description:
+
+ Sets DRM content Id for this stream. Also updates the Mixed content Id.
+
+Arguments:
+
+ contentId - new content id
+
+ drmRights - rights for this stream.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[CMiniportWaveRT::SetContentId]"));
+
+ NTSTATUS ntStatus;
+ ULONG ulOldContentId = contentId;
+
+ m_ulContentId = contentId;
+
+ //
+ // Miniport should create a mixed DrmRights.
+ //
+ ntStatus = m_pMiniport->UpdateDrmRights();
+
+ //
+ // Restore the passed-in content Id.
+ //
+ if (!NT_SUCCESS(ntStatus))
+ {
+ m_ulContentId = ulOldContentId;
+ }
+
+ //
+ // Simple Audio Sample writes each stream seperately to disk. If the rights for this
+ // stream indicates that the stream is CopyProtected, stop writing to disk.
+ //
+ m_SaveData.Disable(drmRights->CopyProtect);
+
+ //
+ // From MSDN:
+ //
+ // This sample doesn't forward protected content, but if your driver uses
+ // lower layer drivers or a different stack to properly work, please see the
+ // following info from MSDN:
+ //
+ // "Before allowing protected content to flow through a data path, the system
+ // verifies that the data path is secure. To do so, the system authenticates
+ // each module in the data path beginning at the upstream end of the data path
+ // and moving downstream. As each module is authenticated, that module gives
+ // the system information about the next module in the data path so that it
+ // can also be authenticated. To be successfully authenticated, a module's
+ // binary file must be signed as DRM-compliant.
+ //
+ // Two adjacent modules in the data path can communicate with each other in
+ // one of several ways. If the upstream module calls the downstream module
+ // through IoCallDriver, the downstream module is part of a WDM driver. In
+ // this case, the upstream module calls the DrmForwardContentToDeviceObject
+ // function to provide the system with the device object representing the
+ // downstream module. (If the two modules communicate through the downstream
+ // module's COM interface or content handlers, the upstream module calls
+ // DrmForwardContentToInterface or DrmAddContentHandlers instead.)
+ //
+ // DrmForwardContentToDeviceObject performs the same function as
+ // PcForwardContentToDeviceObject and IDrmPort2::ForwardContentToDeviceObject."
+ //
+ // Other supported DRM DDIs for down-level module validation are:
+ // DrmForwardContentToInterfaces and DrmAddContentHandlers.
+ //
+ // For more information, see MSDN's DRM Functions and Interfaces.
+ //
+
+ return ntStatus;
+} // SetContentId
+
+//=============================================================================
+#pragma code_seg()
+void
+TimerNotifyRT
+(
+ _In_ PEX_TIMER Timer,
+ _In_opt_ PVOID DeferredContext
+)
+{
+ LARGE_INTEGER qpc;
+ LARGE_INTEGER qpcFrequency;
+ BOOL bufferCompleted = FALSE;
+
+ UNREFERENCED_PARAMETER(Timer);
+
+ _IRQL_limited_to_(DISPATCH_LEVEL);
+
+ CMiniportWaveRTStream* _this = (CMiniportWaveRTStream*)DeferredContext;
+
+ if (NULL == _this)
+ {
+ return;
+ }
+
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&_this->m_PositionSpinLock, &oldIrql);
+
+ qpc = KeQueryPerformanceCounter(&qpcFrequency);
+
+ // Convert ticks to 100ns units.
+ LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(_this->m_ullPerformanceCounterFrequency.QuadPart, qpc);
+
+ // Calculate the time elapsed since the last we ran DPC that matched Notification interval. Note that the division by 10000
+ // to convert to milliseconds may cause us to lose some of the time, so we will carry the remainder forward.
+
+ ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - _this->m_ullLastDPCTimeStamp + _this->m_hnsDPCTimeCarryForward)/10000;
+
+ if (TimeElapsedInMS >= _this->m_ulNotificationIntervalMs)
+ {
+ // Carry forward the time greater than notification interval to adjust time to signal next buffer completion event accordingly.
+ _this->m_hnsDPCTimeCarryForward = hnsCurrentTime - _this->m_ullLastDPCTimeStamp + _this->m_hnsDPCTimeCarryForward - (_this->m_ulNotificationIntervalMs * 10000);
+ // Save the last time DPC ran at notification interval
+ _this->m_ullLastDPCTimeStamp = hnsCurrentTime;
+ bufferCompleted = TRUE;
+ }
+
+ if (!bufferCompleted && !_this->m_bEoSReceived)
+ {
+ goto End;
+ }
+
+ _this->UpdatePosition(qpc);
+
+ if (!_this->m_bEoSReceived)
+ {
+ _this->m_llPacketCounter++;
+ }
+
+ if (_this->m_KsState != KSSTATE_RUN)
+ {
+ goto End;
+ }
+
+ PADAPTERCOMMON pAdapterComm = _this->m_pMiniport->GetAdapterCommObj();
+
+ // Simple buffer underrun detection.
+ if (!_this->IsCurrentWaveRTWritePositionUpdated() && !_this->m_bEoSReceived)
+ {
+ //Event type: eMINIPORT_GLITCH_REPORT
+ //Parameter 1: Current linear buffer position
+ //Parameter 2: Previous WaveRtBufferWritePosition that the driver received
+ //Parameter 3: Major glitch code: 1:WaveRT buffer is underrun
+ //Parameter 4: Minor code for the glitch cause
+ pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT,
+ _this->m_ullLinearPosition,
+ _this->GetCurrentWaveRTWritePosition(),
+ 1, // WaveRT buffer is underrun
+ 0);
+ }
+
+ // Send buffer completion event if either of the following is true
+ // 1. Driver consumed a complete buffer for this stream
+ // 2. Driver consumed a partial buffer containing EoS for this stream
+
+ if (!IsListEmpty(&_this->m_NotificationList) &&
+ (bufferCompleted || _this->m_bLastBufferRendered))
+ {
+ PLIST_ENTRY leCurrent = _this->m_NotificationList.Flink;
+ while (leCurrent != &_this->m_NotificationList)
+ {
+ NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry);
+ KeSetEvent(nleCurrent->NotificationEvent, 0, 0);
+
+ leCurrent = leCurrent->Flink;
+ }
+ }
+
+ if (_this->m_bLastBufferRendered)
+ {
+ ExCancelTimer(_this->m_pNotificationTimer, NULL);
+ }
+
+End:
+ KeReleaseSpinLock(&_this->m_PositionSpinLock, oldIrql);
+ return;
+}
+//=============================================================================
+