summaryrefslogtreecommitdiff
path: root/audio/simpleaudiosample/Source/Utilities
diff options
context:
space:
mode:
authorjudzmura <[email protected]>2021-03-01 13:12:34 -0800
committerGitHub <[email protected]>2021-03-01 13:12:34 -0800
commit9a2e9aecb9135e782c434e617f57bfbdf9d8f2fd (patch)
treec67dd7a72a15a8d72c7bb6c6e19c1086d5e15f08 /audio/simpleaudiosample/Source/Utilities
parent6c41d837b87a0b70fe5dcee78baeba4ef28c45ad (diff)
Add SimpleAudioSample driver. (#577)
* Add SimpleAudioSample driver. * Fixed outdated copyrights and updated readme. * Added Filters as a dependency for Main and defined FileDigestAlgorithm as SHA256.
Diffstat (limited to 'audio/simpleaudiosample/Source/Utilities')
-rw-r--r--audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp285
-rw-r--r--audio/simpleaudiosample/Source/Utilities/ToneGenerator.h74
-rw-r--r--audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj266
-rw-r--r--audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters31
-rw-r--r--audio/simpleaudiosample/Source/Utilities/hw.cpp446
-rw-r--r--audio/simpleaudiosample/Source/Utilities/hw.h105
-rw-r--r--audio/simpleaudiosample/Source/Utilities/kshelper.cpp958
-rw-r--r--audio/simpleaudiosample/Source/Utilities/savedata.cpp1033
-rw-r--r--audio/simpleaudiosample/Source/Utilities/savedata.h189
9 files changed, 3387 insertions, 0 deletions
diff --git a/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp
new file mode 100644
index 00000000..f50e81e1
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.cpp
@@ -0,0 +1,285 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ ToneGenerator
+
+Abstract:
+
+ Implementation of Simple Audio Sample sine wave generator
+
+--*/
+#include "definitions.h"
+#include "ToneGenerator.h"
+
+const double TWO_PI = M_PI * 2;
+
+extern DWORD g_DisableToneGenerator;
+
+//
+// Double to long conversion.
+//
+long ConvertToLong(double Value)
+{
+ return (long)(Value * _I32_MAX);
+};
+
+//
+// Double to short conversion.
+//
+short ConvertToShort(double Value)
+{
+ return (short)(Value * _I16_MAX);
+};
+
+//
+// Double to char conversion.
+//
+unsigned char ConvertToUChar(double Value)
+{
+ const double F_127_5 = 127.5;
+ return (unsigned char)(Value * F_127_5 + F_127_5);
+};
+
+//
+// Ctor: basic init.
+//
+ToneGenerator::ToneGenerator()
+: m_Frequency(0),
+ m_ChannelCount(0),
+ m_BitsPerSample(0),
+ m_SamplesPerSecond(0),
+ m_Mute(false),
+ m_PartialFrame(NULL),
+ m_PartialFrameBytes(0),
+ m_FrameSize(0)
+{
+ // Theta (double) and SampleIncrement (double) are init in the Init() method
+ // after saving the floating point state.
+}
+
+//
+// Dtor: free resources.
+//
+ToneGenerator::~ToneGenerator()
+{
+ if (m_PartialFrame)
+ {
+ ExFreePoolWithTag(m_PartialFrame, SIMPLEAUDIOSAMPLE_POOLTAG);
+ m_PartialFrame = NULL;
+ m_PartialFrameBytes = 0;
+ }
+}
+
+//
+// Init a new frame.
+// Note: caller will save and restore the floatingpoint state.
+//
+#pragma warning(push)
+// Caller wraps this routine between KeSaveFloatingPointState/KeRestoreFloatingPointState calls.
+#pragma warning(disable: 28110)
+
+VOID ToneGenerator::InitNewFrame
+(
+ _Out_writes_bytes_(FrameSize) BYTE* Frame,
+ _In_ DWORD FrameSize
+)
+{
+ double sinValue = m_ToneDCOffset + m_ToneAmplitude * sin( m_Theta );
+
+ if (FrameSize != (DWORD)m_ChannelCount * m_BitsPerSample/8)
+ {
+ ASSERT(FALSE);
+ RtlZeroMemory(Frame, FrameSize);
+ return;
+ }
+
+ for(ULONG i = 0; i < m_ChannelCount; ++i)
+ {
+ if (m_BitsPerSample == 8)
+ {
+ unsigned char *dataBuffer = reinterpret_cast<unsigned char *>(Frame);
+ dataBuffer[i] = ConvertToUChar(sinValue);
+ }
+ else if (m_BitsPerSample == 16)
+ {
+ short *dataBuffer = reinterpret_cast<short *>(Frame);
+ dataBuffer[i] = ConvertToShort(sinValue);
+ }
+ else if (m_BitsPerSample == 24)
+ {
+ BYTE *dataBuffer = Frame;
+ long val = ConvertToLong(sinValue);
+ val = val >> 8;
+ RtlCopyMemory(dataBuffer, &val, 3);
+ }
+ else if (m_BitsPerSample == 32)
+ {
+ long *dataBuffer = reinterpret_cast<long *>(Frame);
+ dataBuffer[i] = ConvertToLong(sinValue);
+ }
+ }
+
+ m_Theta += m_SampleIncrement;
+ if (m_Theta >= TWO_PI)
+ {
+ m_Theta -= TWO_PI;
+ }
+}
+#pragma warning(pop)
+
+//
+// GenerateSamples()
+//
+// Generate a sine wave that fits into the specified buffer.
+//
+// Buffer - Buffer to hold the samples
+// BufferLength - Length of the buffer.
+//
+//
+void ToneGenerator::GenerateSine
+(
+ _Out_writes_bytes_(BufferLength) BYTE *Buffer,
+ _In_ size_t BufferLength
+)
+{
+ NTSTATUS status;
+ KFLOATING_SAVE saveData;
+ BYTE * buffer;
+ size_t length;
+ size_t copyBytes;
+
+ // if muted, or tone generator disabled via registry,
+ // we deliver silence.
+ if (m_Mute || g_DisableToneGenerator)
+ {
+ goto ZeroBuffer;
+ }
+
+ status = KeSaveFloatingPointState(&saveData);
+ if (!NT_SUCCESS(status))
+ {
+ goto ZeroBuffer;
+ }
+
+ buffer = Buffer;
+ length = BufferLength;
+
+ //
+ // Check if we have any residual frame bytes from the last time.
+ //
+ if (m_PartialFrameBytes)
+ {
+ ASSERT(m_FrameSize > m_PartialFrameBytes);
+ DWORD offset = m_FrameSize - m_PartialFrameBytes;
+ copyBytes = MIN(m_PartialFrameBytes, length);
+ RtlCopyMemory(buffer, m_PartialFrame + offset, copyBytes);
+ RtlZeroMemory(m_PartialFrame + offset, copyBytes);
+ length -= copyBytes;
+ buffer += copyBytes;
+ m_PartialFrameBytes = 0;
+ }
+
+ IF_TRUE_JUMP(length == 0, Done);
+
+ //
+ // Copy all the aligned frames.
+ //
+
+ size_t frames = length/m_FrameSize;
+
+ for (size_t i = 0; i < frames; ++i)
+ {
+ InitNewFrame(buffer, m_FrameSize);
+ buffer += m_FrameSize;
+ length -= m_FrameSize;
+ }
+
+ IF_TRUE_JUMP(length == 0, Done);
+
+ //
+ // Copy any partial frame at the end.
+ //
+ ASSERT(m_FrameSize > length);
+ InitNewFrame(m_PartialFrame, m_FrameSize);
+ RtlCopyMemory(buffer, m_PartialFrame, length);
+ RtlZeroMemory(m_PartialFrame, length);
+ m_PartialFrameBytes = m_FrameSize - (DWORD)length;
+
+Done:
+ KeRestoreFloatingPointState(&saveData);
+ return;
+
+ZeroBuffer:
+ RtlZeroMemory(Buffer, BufferLength);
+ return;
+}
+
+NTSTATUS ToneGenerator::Init
+(
+ _In_ DWORD ToneFrequency,
+ _In_ double ToneAmplitude,
+ _In_ double ToneDCOffset,
+ _In_ double ToneInitialPhase,
+ _In_ PWAVEFORMATEXTENSIBLE WfExt
+)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ KFLOATING_SAVE saveData;
+
+ //
+ // This sample supports PCM formats only.
+ //
+ if ((WfExt->Format.wFormatTag != WAVE_FORMAT_PCM &&
+ !(WfExt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+ IsEqualGUIDAligned(WfExt->SubFormat, KSDATAFORMAT_SUBTYPE_PCM))))
+ {
+ status = STATUS_NOT_SUPPORTED;
+ }
+ IF_FAILED_JUMP(status, Done);
+
+ //
+ // Save floating state (just in case).
+ //
+ status = KeSaveFloatingPointState(&saveData);
+ IF_FAILED_JUMP(status, Done);
+
+ //
+ // Basic init.
+ //
+ m_Theta = ToneInitialPhase;
+ m_Frequency = ToneFrequency;
+ m_ToneAmplitude = ToneAmplitude;
+ m_ToneDCOffset = ToneDCOffset;
+
+ m_ChannelCount = WfExt->Format.nChannels; // # channels.
+ m_BitsPerSample = WfExt->Format.wBitsPerSample; // bits per sample.
+ m_SamplesPerSecond = WfExt->Format.nSamplesPerSec; // samples per sec.
+ m_Mute = false;
+ m_SampleIncrement = (m_Frequency * TWO_PI) / (double)m_SamplesPerSecond;
+ m_FrameSize = (DWORD)m_ChannelCount * m_BitsPerSample/8;
+ ASSERT(m_FrameSize == WfExt->Format.nBlockAlign);
+
+ //
+ // Restore floating state.
+ //
+ KeRestoreFloatingPointState(&saveData);
+
+ //
+ // Allocate a buffer to hold a partial frame.
+ //
+ m_PartialFrame = (BYTE*)ExAllocatePool2(
+ POOL_FLAG_NON_PAGED,
+ m_FrameSize,
+ SIMPLEAUDIOSAMPLE_POOLTAG);
+
+ IF_TRUE_ACTION_JUMP(m_PartialFrame == NULL, status = STATUS_INSUFFICIENT_RESOURCES, Done);
+
+ status = STATUS_SUCCESS;
+
+Done:
+ return status;
+}
+
diff --git a/audio/simpleaudiosample/Source/Utilities/ToneGenerator.h b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.h
new file mode 100644
index 00000000..85b1229a
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/ToneGenerator.h
@@ -0,0 +1,74 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ ToneGenerator.h
+
+Abstract:
+
+ Declaration of Simple Audio Sample sine wave generator.
+--*/
+#ifndef _SIMPLEAUDIOSAMPLE_TONEGENERATOR_H
+#define _SIMPLEAUDIOSAMPLE_TONEGENERATOR_H
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <limits.h>
+
+class ToneGenerator
+{
+public:
+ DWORD m_Frequency;
+ WORD m_ChannelCount;
+ WORD m_BitsPerSample;
+ DWORD m_SamplesPerSecond;
+ double m_Theta;
+ double m_SampleIncrement;
+ bool m_Mute;
+ BYTE* m_PartialFrame;
+ DWORD m_PartialFrameBytes;
+ DWORD m_FrameSize;
+ double m_ToneAmplitude;
+ double m_ToneDCOffset;
+
+public:
+ ToneGenerator();
+ ~ToneGenerator();
+
+ NTSTATUS
+ Init
+ (
+ _In_ DWORD ToneFrequency,
+ _In_ double ToneAmplitude,
+ _In_ double ToneDCOffset,
+ _In_ double ToneInitialPhase,
+ _In_ PWAVEFORMATEXTENSIBLE WfExt
+ );
+
+ VOID
+ GenerateSine
+ (
+ _Out_writes_bytes_(BufferLength) BYTE *Buffer,
+ _In_ size_t BufferLength
+ );
+
+ VOID
+ SetMute
+ (
+ _In_ bool Value
+ )
+ {
+ m_Mute = Value;
+ }
+
+private:
+ VOID InitNewFrame
+ (
+ _Out_writes_bytes_(FrameSize) BYTE* Frame,
+ _In_ DWORD FrameSize
+ );
+};
+
+#endif // _SIMPLEAUDIOSAMPLE_TONEGENERATOR_H
diff --git a/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj
new file mode 100644
index 00000000..9af7c493
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|ARM64">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM64">
+ <Configuration>Release</Configuration>
+ <Platform>ARM64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{33E61864-6F2C-4F9F-BE70-8F8985A4F283}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{F51739CE-5253-42B5-9191-57F28B5842C6}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>Utilities</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <TargetName>Utilities</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>Utilities</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <TargetName>Utilities</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>Utilities</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>Utilities</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..;.</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc;.</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_;_NEW_DELETE_OPERATORS_</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <DisableSpecificWarnings>4595;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);..</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\Inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);_USE_WAVERT_</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="hw.cpp" />
+ <ClCompile Include="kshelper.cpp" />
+ <ClCompile Include="savedata.cpp" />
+ <ClCompile Include="tonegenerator.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
diff --git a/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters
new file mode 100644
index 00000000..7a4ebde2
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{CDBBEDA3-98E9-4C7A-BDB1-25EC47D0B087}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{FA3DF57E-BA19-4783-831B-CF9D4D594F70}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{CB830E4F-01DA-4D35-9789-E7DB0D02A79C}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{49F92783-3B39-4D0C-A153-8C346F754749}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="*.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
diff --git a/audio/simpleaudiosample/Source/Utilities/hw.cpp b/audio/simpleaudiosample/Source/Utilities/hw.cpp
new file mode 100644
index 00000000..133bcc99
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/hw.cpp
@@ -0,0 +1,446 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ hw.cpp
+
+Abstract:
+
+ Implementation of Simple Audio Sample HW class.
+ Simple Audio Sample HW has an array for storing mixer and volume settings
+ for the topology.
+--*/
+#include "definitions.h"
+#include "hw.h"
+
+//=============================================================================
+// CSimpleAudioSampleHW
+//=============================================================================
+
+//=============================================================================
+#pragma code_seg("PAGE")
+CSimpleAudioSampleHW::CSimpleAudioSampleHW()
+: m_ulMux(0),
+ m_bDevSpecific(FALSE),
+ m_iDevSpecific(0),
+ m_uiDevSpecific(0)
+/*++
+
+Routine Description:
+
+ Constructor for SimpleAudioSampleHW.
+
+Arguments:
+
+Return Value:
+
+ void
+
+--*/
+{
+ PAGED_CODE();
+
+ MixerReset();
+} // SimpleAudioSampleHW
+#pragma code_seg()
+
+
+//=============================================================================
+BOOL
+CSimpleAudioSampleHW::bGetDevSpecific()
+/*++
+
+Routine Description:
+
+ Gets the HW (!) Device Specific info
+
+Arguments:
+
+ N/A
+
+Return Value:
+
+ True or False (in this example).
+
+--*/
+{
+ return m_bDevSpecific;
+} // bGetDevSpecific
+
+//=============================================================================
+void
+CSimpleAudioSampleHW::bSetDevSpecific
+(
+ _In_ BOOL bDevSpecific
+)
+/*++
+
+Routine Description:
+
+ Sets the HW (!) Device Specific info
+
+Arguments:
+
+ fDevSpecific - true or false for this example.
+
+Return Value:
+
+ void
+
+--*/
+{
+ m_bDevSpecific = bDevSpecific;
+} // bSetDevSpecific
+
+//=============================================================================
+INT
+CSimpleAudioSampleHW::iGetDevSpecific()
+/*++
+
+Routine Description:
+
+ Gets the HW (!) Device Specific info
+
+Arguments:
+
+ N/A
+
+Return Value:
+
+ int (in this example).
+
+--*/
+{
+ return m_iDevSpecific;
+} // iGetDevSpecific
+
+//=============================================================================
+void
+CSimpleAudioSampleHW::iSetDevSpecific
+(
+ _In_ INT iDevSpecific
+)
+/*++
+
+Routine Description:
+
+ Sets the HW (!) Device Specific info
+
+Arguments:
+
+ fDevSpecific - true or false for this example.
+
+Return Value:
+
+ void
+
+--*/
+{
+ m_iDevSpecific = iDevSpecific;
+} // iSetDevSpecific
+
+//=============================================================================
+UINT
+CSimpleAudioSampleHW::uiGetDevSpecific()
+/*++
+
+Routine Description:
+
+ Gets the HW (!) Device Specific info
+
+Arguments:
+
+ N/A
+
+Return Value:
+
+ UINT (in this example).
+
+--*/
+{
+ return m_uiDevSpecific;
+} // uiGetDevSpecific
+
+//=============================================================================
+void
+CSimpleAudioSampleHW::uiSetDevSpecific
+(
+ _In_ UINT uiDevSpecific
+)
+/*++
+
+Routine Description:
+
+ Sets the HW (!) Device Specific info
+
+Arguments:
+
+ uiDevSpecific - int for this example.
+
+Return Value:
+
+ void
+
+--*/
+{
+ m_uiDevSpecific = uiDevSpecific;
+} // uiSetDevSpecific
+
+
+//=============================================================================
+BOOL
+CSimpleAudioSampleHW::GetMixerMute
+(
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel
+)
+/*++
+
+Routine Description:
+
+ Gets the HW (!) mute levels for Simple Audio Sample
+
+Arguments:
+
+ ulNode - topology node id
+
+ ulChannel - which channel are we reading?
+
+Return Value:
+
+ mute setting
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ulChannel);
+
+ if (ulNode < MAX_TOPOLOGY_NODES)
+ {
+ return m_MuteControls[ulNode];
+ }
+
+ return 0;
+} // GetMixerMute
+
+//=============================================================================
+ULONG
+CSimpleAudioSampleHW::GetMixerMux()
+/*++
+
+Routine Description:
+
+ Return the current mux selection
+
+Arguments:
+
+Return Value:
+
+ ULONG
+
+--*/
+{
+ return m_ulMux;
+} // GetMixerMux
+
+//=============================================================================
+LONG
+CSimpleAudioSampleHW::GetMixerVolume
+(
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel
+)
+/*++
+
+Routine Description:
+
+ Gets the HW (!) volume for Simple Audio Sample.
+
+Arguments:
+
+ ulNode - topology node id
+
+ ulChannel - which channel are we reading?
+
+Return Value:
+
+ LONG - volume level
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ulChannel);
+
+ if (ulNode < MAX_TOPOLOGY_NODES)
+ {
+ return m_VolumeControls[ulNode];
+ }
+
+ return 0;
+} // GetMixerVolume
+
+//=============================================================================
+LONG
+CSimpleAudioSampleHW::GetMixerPeakMeter
+(
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel
+)
+/*++
+
+Routine Description:
+
+ Gets the HW (!) peak meter for Simple Audio Sample.
+
+Arguments:
+
+ ulNode - topology node id
+
+ ulChannel - which channel are we reading?
+
+Return Value:
+
+ LONG - sample peak meter level
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ulChannel);
+
+ if (ulNode < MAX_TOPOLOGY_NODES)
+ {
+ return m_PeakMeterControls[ulNode];
+ }
+
+ return 0;
+} // GetMixerVolume
+
+//=============================================================================
+#pragma code_seg("PAGE")
+void
+CSimpleAudioSampleHW::MixerReset()
+/*++
+
+Routine Description:
+
+ Resets the mixer registers.
+
+Arguments:
+
+Return Value:
+
+ void
+
+--*/
+{
+ PAGED_CODE();
+
+ RtlFillMemory(m_VolumeControls, sizeof(LONG) * MAX_TOPOLOGY_NODES, 0xFF);
+ // Endpoints are not muted by default.
+ RtlZeroMemory(m_MuteControls, sizeof(BOOL) * MAX_TOPOLOGY_NODES);
+
+ for (ULONG i=0; i<MAX_TOPOLOGY_NODES; ++i)
+ {
+ m_PeakMeterControls[i] = PEAKMETER_SIGNED_MAXIMUM/2;
+ }
+
+ // BUGBUG change this depending on the topology
+ m_ulMux = 2;
+} // MixerReset
+#pragma code_seg()
+
+//=============================================================================
+void
+CSimpleAudioSampleHW::SetMixerMute
+(
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel,
+ _In_ BOOL fMute
+)
+/*++
+
+Routine Description:
+
+ Sets the HW (!) mute levels for Simple Audio Sample
+
+Arguments:
+
+ ulNode - topology node id
+
+ ulChannel - which channel are we setting?
+
+ fMute - mute flag
+
+Return Value:
+
+ void
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ulChannel);
+
+ if (ulNode < MAX_TOPOLOGY_NODES)
+ {
+ m_MuteControls[ulNode] = fMute;
+ }
+} // SetMixerMute
+
+//=============================================================================
+void
+CSimpleAudioSampleHW::SetMixerMux
+(
+ _In_ ULONG ulNode
+)
+/*++
+
+Routine Description:
+
+ Sets the HW (!) mux selection
+
+Arguments:
+
+ ulNode - topology node id
+
+Return Value:
+
+ void
+
+--*/
+{
+ m_ulMux = ulNode;
+} // SetMixMux
+
+//=============================================================================
+void
+CSimpleAudioSampleHW::SetMixerVolume
+(
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel,
+ _In_ LONG lVolume
+)
+/*++
+
+Routine Description:
+
+ Sets the HW (!) volume for Simple Audio Sample.
+
+Arguments:
+
+ ulNode - topology node id
+
+ ulChannel - which channel are we setting?
+
+ lVolume - volume level
+
+Return Value:
+
+ void
+
+--*/
+{
+ UNREFERENCED_PARAMETER(ulChannel);
+
+ if (ulNode < MAX_TOPOLOGY_NODES)
+ {
+ m_VolumeControls[ulNode] = lVolume;
+ }
+} // SetMixerVolume
diff --git a/audio/simpleaudiosample/Source/Utilities/hw.h b/audio/simpleaudiosample/Source/Utilities/hw.h
new file mode 100644
index 00000000..3360c24a
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/hw.h
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ hw.h
+
+Abstract:
+
+ Declaration of Simple Audio Sample HW class.
+ Simple Audio Sample HW has an array for storing mixer and volume settings
+ for the topology.
+--*/
+
+#ifndef _SIMPLEAUDIOSAMPLE_HW_H_
+#define _SIMPLEAUDIOSAMPLE_HW_H_
+
+//=============================================================================
+// Defines
+//=============================================================================
+// BUGBUG we should dynamically allocate this...
+#define MAX_TOPOLOGY_NODES 20
+
+//=============================================================================
+// Classes
+//=============================================================================
+///////////////////////////////////////////////////////////////////////////////
+// CSimpleAudioSampleHW
+// This class represents virtual Simple Audio Sample HW. An array representing volume
+// registers and mute registers.
+
+class CSimpleAudioSampleHW
+{
+public:
+protected:
+ BOOL m_MuteControls[MAX_TOPOLOGY_NODES];
+ LONG m_VolumeControls[MAX_TOPOLOGY_NODES];
+ LONG m_PeakMeterControls[MAX_TOPOLOGY_NODES];
+ ULONG m_ulMux; // Mux selection
+ BOOL m_bDevSpecific;
+ INT m_iDevSpecific;
+ UINT m_uiDevSpecific;
+
+private:
+
+public:
+ CSimpleAudioSampleHW();
+
+ void MixerReset();
+ BOOL bGetDevSpecific();
+ void bSetDevSpecific
+ (
+ _In_ BOOL bDevSpecific
+ );
+ INT iGetDevSpecific();
+ void iSetDevSpecific
+ (
+ _In_ INT iDevSpecific
+ );
+ UINT uiGetDevSpecific();
+ void uiSetDevSpecific
+ (
+ _In_ UINT uiDevSpecific
+ );
+ BOOL GetMixerMute
+ (
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel
+ );
+ void SetMixerMute
+ (
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel,
+ _In_ BOOL fMute
+ );
+ ULONG GetMixerMux();
+ void SetMixerMux
+ (
+ _In_ ULONG ulNode
+ );
+ LONG GetMixerVolume
+ (
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel
+ );
+ void SetMixerVolume
+ (
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel,
+ _In_ LONG lVolume
+ );
+
+ LONG GetMixerPeakMeter
+ (
+ _In_ ULONG ulNode,
+ _In_ ULONG ulChannel
+ );
+
+protected:
+private:
+};
+typedef CSimpleAudioSampleHW *PCSimpleAudioSampleHW;
+
+#endif // _SIMPLEAUDIOSAMPLE_HW_H_
diff --git a/audio/simpleaudiosample/Source/Utilities/kshelper.cpp b/audio/simpleaudiosample/Source/Utilities/kshelper.cpp
new file mode 100644
index 00000000..e71c6021
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/kshelper.cpp
@@ -0,0 +1,958 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ kshelper.cpp
+
+Abstract:
+
+ Helper functions for simple audio sample
+--*/
+
+#include "definitions.h"
+
+//4127: conditional expression is constant
+#pragma warning (disable : 4127)
+
+//-----------------------------------------------------------------------------
+#pragma code_seg("PAGE")
+PWAVEFORMATEX
+GetWaveFormatEx
+(
+ _In_ PKSDATAFORMAT pDataFormat
+)
+/*++
+
+Routine Description:
+
+ Returns the waveformatex for known formats.
+
+Arguments:
+
+ pDataFormat - data format.
+
+Return Value:
+
+ waveformatex in DataFormat.
+ NULL for unknown data formats.
+
+--*/
+{
+ PAGED_CODE();
+
+ PWAVEFORMATEX pWfx = NULL;
+
+ // If this is a known dataformat extract the waveformat info.
+ //
+ if
+ (
+ pDataFormat &&
+ ( IsEqualGUIDAligned(pDataFormat->MajorFormat,
+ KSDATAFORMAT_TYPE_AUDIO) &&
+ ( IsEqualGUIDAligned(pDataFormat->Specifier,
+ KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) ||
+ IsEqualGUIDAligned(pDataFormat->Specifier,
+ KSDATAFORMAT_SPECIFIER_DSOUND) ) )
+ )
+ {
+ pWfx = PWAVEFORMATEX(pDataFormat + 1);
+
+ if (IsEqualGUIDAligned(pDataFormat->Specifier,
+ KSDATAFORMAT_SPECIFIER_DSOUND))
+ {
+ PKSDSOUND_BUFFERDESC pwfxds;
+
+ pwfxds = PKSDSOUND_BUFFERDESC(pDataFormat + 1);
+ pWfx = &pwfxds->WaveFormatEx;
+ }
+ }
+
+ return pWfx;
+} // GetWaveFormatEx
+
+//-----------------------------------------------------------------------------
+#pragma code_seg("PAGE")
+NTSTATUS
+ValidatePropertyParams
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG cbValueSize,
+ _In_ ULONG cbInstanceSize /* = 0 */
+)
+/*++
+
+Routine Description:
+
+ Validates property parameters.
+
+Arguments:
+
+ PropertyRequest -
+ cbValueSize -
+ cbInstanceSize -
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
+
+ if (PropertyRequest && cbValueSize)
+ {
+ // If the caller is asking for ValueSize.
+ //
+ if (0 == PropertyRequest->ValueSize)
+ {
+ PropertyRequest->ValueSize = cbValueSize;
+ ntStatus = STATUS_BUFFER_OVERFLOW;
+ }
+ // If the caller passed an invalid ValueSize.
+ //
+ else if (PropertyRequest->ValueSize < cbValueSize)
+ {
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+ else if (PropertyRequest->InstanceSize < cbInstanceSize)
+ {
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+ // If all parameters are OK.
+ //
+ else if (PropertyRequest->ValueSize >= cbValueSize)
+ {
+ if (PropertyRequest->Value)
+ {
+ ntStatus = STATUS_SUCCESS;
+ //
+ // Caller should set ValueSize, if the property
+ // call is successful.
+ //
+ }
+ }
+ }
+ else
+ {
+ ntStatus = STATUS_INVALID_PARAMETER;
+ }
+
+ // Clear the ValueSize if unsuccessful.
+ //
+ if (PropertyRequest &&
+ STATUS_SUCCESS != ntStatus &&
+ STATUS_BUFFER_OVERFLOW != ntStatus)
+ {
+ PropertyRequest->ValueSize = 0;
+ }
+
+ return ntStatus;
+} // ValidatePropertyParams
+
+//-----------------------------------------------------------------------------
+#pragma code_seg("PAGE")
+NTSTATUS
+SimpleAudioSamplePropertyDispatch
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest
+)
+/*++
+ Handles and dispatches a SIMPLEAUDIOSAMPLE_PROPERTY_ITEM.
+
+ Use this as the property handler only if the property item is a
+ SIMPLEAUDIOSAMPLE_PROPERTY_ITEM.
+--*/
+{
+ PAGED_CODE();
+
+ SIMPLEAUDIOSAMPLE_PROPERTY_ITEM* item = (SIMPLEAUDIOSAMPLE_PROPERTY_ITEM*)PropertyRequest->PropertyItem;
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
+ {
+ if (item->SupportHandler != nullptr)
+ {
+ return item->SupportHandler(PropertyRequest);
+ }
+ else
+ {
+ return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL);
+ }
+ }
+
+ // Verify instance data size
+ if (PropertyRequest->InstanceSize < item->MinProperty)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ ULONG cbMinSize = item->MinData;
+
+ // Verify value size
+ if (PropertyRequest->ValueSize == 0)
+ {
+ PropertyRequest->ValueSize = cbMinSize;
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ if (PropertyRequest->ValueSize < cbMinSize)
+ {
+ PropertyRequest->ValueSize = 0;
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
+ {
+ if (item->GetHandler != nullptr)
+ {
+ return item->GetHandler(PropertyRequest);
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+ }
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
+ {
+ if (item->SetHandler != nullptr)
+ {
+ return item->SetHandler(PropertyRequest);
+ }
+ else
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+ }
+
+ return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+//-----------------------------------------------------------------------------
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_BasicSupport
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG Flags,
+ _In_ DWORD PropTypeSetId
+)
+/*++
+
+Routine Description:
+
+ Default basic support handler. Basic processing depends on the size of data.
+ For ULONG it only returns Flags. For KSPROPERTY_DESCRIPTION, the structure
+ is filled.
+
+Arguments:
+
+ PropertyRequest -
+
+ Flags - Support flags.
+
+ PropTypeSetId - PropTypeSetId
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT(Flags & KSPROPERTY_TYPE_BASICSUPPORT);
+
+ NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
+
+ if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION))
+ {
+ // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it
+ //
+ PKSPROPERTY_DESCRIPTION PropDesc =
+ PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
+
+ PropDesc->AccessFlags = Flags;
+ PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION);
+ if (VT_ILLEGAL != PropTypeSetId)
+ {
+ PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
+ PropDesc->PropTypeSet.Id = PropTypeSetId;
+ }
+ else
+ {
+ PropDesc->PropTypeSet.Set = GUID_NULL;
+ PropDesc->PropTypeSet.Id = 0;
+ }
+ PropDesc->PropTypeSet.Flags = 0;
+ PropDesc->MembersListCount = 0;
+ PropDesc->Reserved = 0;
+
+ PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION);
+ ntStatus = STATUS_SUCCESS;
+ }
+ else if (PropertyRequest->ValueSize >= sizeof(ULONG))
+ {
+ // if return buffer can hold a ULONG, return the access flags
+ //
+ *(PULONG(PropertyRequest->Value)) = Flags;
+
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ ntStatus = STATUS_SUCCESS;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = 0;
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return ntStatus;
+} // PropertyHandler_BasicSupport
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_BasicSupportVolume
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG MaxChannels
+)
+/*++
+
+Routine Description:
+
+ Handles BasicSupport for Volume nodes.
+
+Arguments:
+
+ PropertyRequest - property request structure.
+
+ MaxChannels - # of supported channels.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ ULONG cbFullProperty =
+ sizeof(KSPROPERTY_DESCRIPTION) +
+ sizeof(KSPROPERTY_MEMBERSHEADER) +
+ sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels;
+
+ ASSERT(MaxChannels > 0);
+
+ if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION)))
+ {
+ PKSPROPERTY_DESCRIPTION PropDesc =
+ PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
+
+ PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL;
+ PropDesc->DescriptionSize = cbFullProperty;
+ PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
+ PropDesc->PropTypeSet.Id = VT_I4;
+ PropDesc->PropTypeSet.Flags = 0;
+ PropDesc->MembersListCount = 1;
+ PropDesc->Reserved = 0;
+
+ // if return buffer can also hold a range description, return it too
+ if(PropertyRequest->ValueSize >= cbFullProperty)
+ {
+ // fill in the members header
+ PKSPROPERTY_MEMBERSHEADER Members =
+ PKSPROPERTY_MEMBERSHEADER(PropDesc + 1);
+
+ Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
+ Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG);
+ Members->MembersCount = MaxChannels;
+ Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL;
+
+ // fill in the stepped range
+ PKSPROPERTY_STEPPING_LONG Range =
+ PKSPROPERTY_STEPPING_LONG(Members + 1);
+
+ for (ULONG i=0; i<MaxChannels; ++i)
+ {
+ Range[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; // 0 dB
+ Range[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; // -96 dB
+ Range[i].SteppingDelta = VOLUME_STEPPING_DELTA; // .5 dB
+ Range[i].Reserved = 0;
+ }
+
+ // set the return value size
+ PropertyRequest->ValueSize = cbFullProperty;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION);
+ }
+ }
+ else if(PropertyRequest->ValueSize >= sizeof(ULONG))
+ {
+ // if return buffer can hold a ULONG, return the access flags
+ PULONG AccessFlags = PULONG(PropertyRequest->Value);
+
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ *AccessFlags = KSPROPERTY_TYPE_ALL;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = 0;
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return ntStatus;
+} // PropertyHandlerBasicSupportVolume
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_BasicSupportMute
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG MaxChannels
+)
+/*++
+
+Routine Description:
+
+ Handles BasicSupport for Mute nodes.
+
+Arguments:
+
+ PropertyRequest - property request structure.
+
+ MaxChannels - # of supported channels.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ ULONG cbFullProperty =
+ sizeof(KSPROPERTY_DESCRIPTION) +
+ sizeof(KSPROPERTY_MEMBERSHEADER) +
+ sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels;
+
+ ASSERT(MaxChannels > 0);
+
+ if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION)))
+ {
+ PKSPROPERTY_DESCRIPTION PropDesc =
+ PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
+
+ PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL;
+ PropDesc->DescriptionSize = cbFullProperty;
+ PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
+ PropDesc->PropTypeSet.Id = VT_BOOL;
+ PropDesc->PropTypeSet.Flags = 0;
+ PropDesc->MembersListCount = 1;
+ PropDesc->Reserved = 0;
+
+ // if return buffer can also hold a range description, return it too
+ if(PropertyRequest->ValueSize >= cbFullProperty)
+ {
+ // fill in the members header
+ PKSPROPERTY_MEMBERSHEADER Members =
+ PKSPROPERTY_MEMBERSHEADER(PropDesc + 1);
+
+ Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
+ Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG);
+ Members->MembersCount = MaxChannels;
+ Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL;
+
+ // fill in the stepped range
+ PKSPROPERTY_STEPPING_LONG Range =
+ PKSPROPERTY_STEPPING_LONG(Members + 1);
+
+ for (ULONG i=0; i<MaxChannels; ++i)
+ {
+ Range[i].Bounds.SignedMaximum = 1; // true
+ Range[i].Bounds.SignedMinimum = 0; // false
+ Range[i].SteppingDelta = 1; // false <- -> true
+ Range[i].Reserved = 0;
+ }
+
+ // set the return value size
+ PropertyRequest->ValueSize = cbFullProperty;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION);
+ }
+ }
+ else if(PropertyRequest->ValueSize >= sizeof(ULONG))
+ {
+ // if return buffer can hold a ULONG, return the access flags
+ PULONG AccessFlags = PULONG(PropertyRequest->Value);
+
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ *AccessFlags = KSPROPERTY_TYPE_ALL;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = 0;
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return ntStatus;
+} // PropertyHandlerBasicSupportVolume
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_BasicSupportPeakMeter2
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG MaxChannels
+)
+/*++
+
+Routine Description:
+
+ Handles BasicSupport for peak meter nodes.
+
+Arguments:
+
+ PropertyRequest - property request structure.
+
+ MaxChannels - # of supported channels.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ ULONG cbFullProperty =
+ sizeof(KSPROPERTY_DESCRIPTION) +
+ sizeof(KSPROPERTY_MEMBERSHEADER) +
+ sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels;
+
+ ASSERT(MaxChannels > 0);
+
+ if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION)))
+ {
+ PKSPROPERTY_DESCRIPTION PropDesc =
+ PKSPROPERTY_DESCRIPTION(PropertyRequest->Value);
+
+ PropDesc->AccessFlags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT;
+ PropDesc->DescriptionSize = cbFullProperty;
+ PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
+ PropDesc->PropTypeSet.Id = VT_I4;
+ PropDesc->PropTypeSet.Flags = 0;
+ PropDesc->MembersListCount = 1;
+ PropDesc->Reserved = 0;
+
+ // if return buffer can also hold a range description, return it too
+ if(PropertyRequest->ValueSize >= cbFullProperty)
+ {
+ // fill in the members header
+ PKSPROPERTY_MEMBERSHEADER Members =
+ PKSPROPERTY_MEMBERSHEADER(PropDesc + 1);
+
+ Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
+ Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG);
+ Members->MembersCount = MaxChannels;
+ Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL;
+
+ // fill in the stepped range
+ PKSPROPERTY_STEPPING_LONG Range =
+ PKSPROPERTY_STEPPING_LONG(Members + 1);
+
+ for (ULONG i=0; i<MaxChannels; ++i)
+ {
+ Range[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM;
+ Range[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM;
+ Range[i].SteppingDelta = PEAKMETER_STEPPING_DELTA;
+ Range[i].Reserved = 0;
+ }
+
+ // set the return value size
+ PropertyRequest->ValueSize = cbFullProperty;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION);
+ }
+ }
+ else if(PropertyRequest->ValueSize >= sizeof(ULONG))
+ {
+ // if return buffer can hold a ULONG, return the access flags
+ PULONG AccessFlags = PULONG(PropertyRequest->Value);
+
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ *AccessFlags = KSPROPERTY_TYPE_ALL;
+ }
+ else
+ {
+ PropertyRequest->ValueSize = 0;
+ ntStatus = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return ntStatus;
+} // PropertyHandlerBasicSupportVolume
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_CpuResources
+(
+ _In_ PPCPROPERTY_REQUEST PropertyRequest
+)
+/*++
+
+Routine Description:
+
+ Processes KSPROPERTY_AUDIO_CPURESOURCES
+
+Arguments:
+
+ PropertyRequest - property request structure.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[%s]",__FUNCTION__));
+
+ NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
+ {
+ ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(ULONG));
+ if (NT_SUCCESS(ntStatus))
+ {
+ *(PULONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU;
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ }
+ }
+ else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
+ {
+ ntStatus =
+ PropertyHandler_BasicSupport
+ (
+ PropertyRequest,
+ KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
+ VT_UI4
+ );
+ }
+
+ return ntStatus;
+} // PropertyHandlerCpuResources
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_Volume
+(
+ _In_ PADAPTERCOMMON AdapterCommon,
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG MaxChannels
+)
+/*++
+
+Routine Description:
+
+ Property handler for KSPROPERTY_AUDIO_VOLUMELEVEL
+
+Arguments:
+
+ AdapterCommon - interface to the common adapter object.
+
+ PropertyRequest - property request structure.
+
+ MaxChannels - # of supported channels.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[%s]",__FUNCTION__));
+
+ NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
+ ULONG ulChannel;
+ PLONG plVolume;
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
+ {
+ ntStatus = PropertyHandler_BasicSupportVolume(
+ PropertyRequest,
+ MaxChannels);
+ }
+ else
+ {
+ ntStatus =
+ ValidatePropertyParams
+ (
+ PropertyRequest,
+ sizeof(LONG), // volume value is a LONG
+ sizeof(ULONG) // instance is the channel number
+ );
+ if (NT_SUCCESS(ntStatus))
+ {
+ ulChannel = * (PULONG (PropertyRequest->Instance));
+ plVolume = PLONG (PropertyRequest->Value);
+
+ if (ulChannel >= MaxChannels &&
+ ulChannel != ALL_CHANNELS_ID)
+ {
+ ntStatus = STATUS_INVALID_PARAMETER;
+ }
+ else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
+ {
+ *plVolume =
+ AdapterCommon->MixerVolumeRead
+ (
+ PropertyRequest->Node,
+ ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel
+ );
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ }
+ else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
+ {
+ if (ALL_CHANNELS_ID == ulChannel)
+ {
+ for (ULONG i=0; i<ulChannel; ++i)
+ {
+ AdapterCommon->MixerVolumeWrite
+ (
+ PropertyRequest->Node,
+ i,
+ VOLUME_NORMALIZE_IN_RANGE(*plVolume)
+ );
+ }
+ }
+ else
+ {
+ AdapterCommon->MixerVolumeWrite
+ (
+ PropertyRequest->Node,
+ ulChannel,
+ VOLUME_NORMALIZE_IN_RANGE(*plVolume)
+ );
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus));
+ }
+ }
+
+ return ntStatus;
+} // PropertyHandlerVolume
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_Mute
+(
+ _In_ PADAPTERCOMMON AdapterCommon,
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG MaxChannels
+)
+/*++
+
+Routine Description:
+
+ Property handler for KSPROPERTY_AUDIO_MUTE
+
+Arguments:
+
+ AdapterCommon - interface to the common adapter object.
+
+ PropertyRequest - property request structure.
+
+ MaxChannels - # of supported channels.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[%s]",__FUNCTION__));
+
+ NTSTATUS ntStatus;
+ ULONG ulChannel;
+ PBOOL pfMute;
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
+ {
+ ntStatus = PropertyHandler_BasicSupportMute(
+ PropertyRequest,
+ MaxChannels);
+ }
+ else
+ {
+ ntStatus =
+ ValidatePropertyParams
+ (
+ PropertyRequest,
+ sizeof(BOOL),
+ sizeof(ULONG)
+ );
+ if (NT_SUCCESS(ntStatus))
+ {
+ ulChannel = * (PULONG (PropertyRequest->Instance));
+ pfMute = PBOOL (PropertyRequest->Value);
+
+ if (ulChannel >= MaxChannels &&
+ ulChannel != ALL_CHANNELS_ID)
+ {
+ ntStatus = STATUS_INVALID_PARAMETER;
+ }
+ else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
+ {
+ *pfMute =
+ AdapterCommon->MixerMuteRead
+ (
+ PropertyRequest->Node,
+ ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel
+ );
+ PropertyRequest->ValueSize = sizeof(BOOL);
+ }
+ else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
+ {
+ if (ALL_CHANNELS_ID == ulChannel)
+ {
+ for (ULONG i=0; i<ulChannel; ++i)
+ {
+ AdapterCommon->MixerMuteWrite
+ (
+ PropertyRequest->Node,
+ i,
+ (*pfMute) ? TRUE : FALSE
+ );
+ }
+ }
+ else
+ {
+ AdapterCommon->MixerMuteWrite
+ (
+ PropertyRequest->Node,
+ ulChannel,
+ (*pfMute) ? TRUE : FALSE
+ );
+ }
+ }
+ }
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus));
+ }
+ }
+
+ return ntStatus;
+} // PropertyHandlerMute
+
+//=============================================================================
+#pragma code_seg("PAGE")
+NTSTATUS
+PropertyHandler_PeakMeter2
+(
+ _In_ PADAPTERCOMMON AdapterCommon,
+ _In_ PPCPROPERTY_REQUEST PropertyRequest,
+ _In_ ULONG MaxChannels
+)
+/*++
+
+Routine Description:
+
+ Property handler for KSPROPERTY_AUDIO_PEAKMETER2
+
+Arguments:
+
+ AdapterCommon - interface to the common adapter object.
+
+ PropertyRequest - property request structure.
+
+ MaxChannels - # of supported channels.
+
+Return Value:
+
+ NT status code.
+
+--*/
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[%s]",__FUNCTION__));
+
+ NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
+ ULONG ulChannel;
+ PLONG plSample;
+
+ if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
+ {
+ ntStatus = PropertyHandler_BasicSupportPeakMeter2(
+ PropertyRequest,
+ MaxChannels);
+ }
+ else
+ {
+ ntStatus =
+ ValidatePropertyParams
+ (
+ PropertyRequest,
+ sizeof(LONG), // sample value is a LONG
+ sizeof(ULONG) // instance is the channel number
+ );
+ if (NT_SUCCESS(ntStatus))
+ {
+ ulChannel = * (PULONG (PropertyRequest->Instance));
+ plSample = PLONG (PropertyRequest->Value);
+
+ if (ulChannel >= MaxChannels &&
+ ulChannel != ALL_CHANNELS_ID)
+ {
+ ntStatus = STATUS_INVALID_PARAMETER;
+ }
+ else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
+ {
+ *plSample =
+ PEAKMETER_NORMALIZE_IN_RANGE(
+ AdapterCommon->MixerPeakMeterRead
+ (
+ PropertyRequest->Node,
+ ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel
+ ));
+
+ PropertyRequest->ValueSize = sizeof(ULONG);
+ }
+ }
+
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus));
+ }
+ }
+
+ return ntStatus;
+} // PropertyHandlerVolume
+
diff --git a/audio/simpleaudiosample/Source/Utilities/savedata.cpp b/audio/simpleaudiosample/Source/Utilities/savedata.cpp
new file mode 100644
index 00000000..7b2a11e9
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/savedata.cpp
@@ -0,0 +1,1033 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ savedata.cpp
+
+Abstract:
+
+ Implementation of Simple Audio Sample data saving class.
+
+ To save the playback data to disk, this class maintains a circular data
+ buffer, associated frame structures and worker items to save frames to
+ disk.
+ Each frame structure represents a portion of buffer. When that portion
+ of frame is full, a workitem is scheduled to save it to disk.
+--*/
+#pragma warning (disable : 4127)
+#pragma warning (disable : 26165)
+
+#include "definitions.h"
+#include "savedata.h"
+#include <ntstrsafe.h> // This is for using RtlStringcbPrintf
+
+#define SAVEDATA_POOLTAG 'TDVS'
+#define SAVEDATA_POOLTAG1 '1DVS'
+#define SAVEDATA_POOLTAG2 '2DVS'
+#define SAVEDATA_POOLTAG3 '3DVS'
+#define SAVEDATA_POOLTAG4 '4DVS'
+#define SAVEDATA_POOLTAG5 '5DVS'
+#define SAVEDATA_POOLTAG6 '6DVS'
+#define SAVEDATA_POOLTAG7 '7DVS'
+
+//=============================================================================
+// Defines
+//=============================================================================
+#define RIFF_TAG 0x46464952;
+#define WAVE_TAG 0x45564157;
+#define FMT__TAG 0x20746D66;
+#define DATA_TAG 0x61746164;
+
+#define DEFAULT_FRAME_COUNT 4
+#define DEFAULT_FRAME_SIZE PAGE_SIZE * 4
+#define DEFAULT_BUFFER_SIZE DEFAULT_FRAME_SIZE * DEFAULT_FRAME_COUNT
+
+#define DEFAULT_FILE_NAME L"\\DosDevices\\C:\\STREAM"
+#define OSDATA_FILE_NAME L"\\DosDevices\\O:\\STREAM"
+#define OFFLOAD_FILE_NAME L"OFFLOAD"
+#define HOST_FILE_NAME L"HOST"
+
+#define MAX_WORKER_ITEM_COUNT 15
+
+//=============================================================================
+// Statics
+//=============================================================================
+ULONG CSaveData::m_ulStreamId = 0;
+
+#pragma code_seg("PAGE")
+//=============================================================================
+// CSaveData
+//=============================================================================
+
+//=============================================================================
+CSaveData::CSaveData()
+: m_pDataBuffer(NULL),
+ m_FileHandle(NULL),
+ m_ulFrameCount(DEFAULT_FRAME_COUNT),
+ m_ulBufferSize(DEFAULT_BUFFER_SIZE),
+ m_ulFrameSize(DEFAULT_FRAME_SIZE),
+ m_ulBufferOffset(0),
+ m_ulFrameIndex(0),
+ m_fFrameUsed(NULL),
+ m_waveFormat(NULL),
+ m_pFilePtr(NULL),
+ m_fWriteDisabled(FALSE),
+ m_bInitialized(FALSE)
+{
+ PAGED_CODE();
+
+ m_FileHeader.dwRiff = RIFF_TAG;
+ m_FileHeader.dwFileSize = 0;
+ m_FileHeader.dwWave = WAVE_TAG;
+ m_FileHeader.dwFormat = FMT__TAG;
+ m_FileHeader.dwFormatLength = sizeof(WAVEFORMATEX);
+
+ m_DataHeader.dwData = DATA_TAG;
+ m_DataHeader.dwDataLength = 0;
+
+ RtlZeroMemory(&m_objectAttributes, sizeof(m_objectAttributes));
+} // CSaveData
+
+//=============================================================================
+CSaveData::~CSaveData()
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[CSaveData::~CSaveData]"));
+
+ // Update the wave header in data file with real file size.
+ //
+ if(m_pFilePtr)
+ {
+ m_FileHeader.dwFileSize =
+ (DWORD) m_pFilePtr->QuadPart - 2 * sizeof(DWORD);
+ m_DataHeader.dwDataLength = (DWORD) m_pFilePtr->QuadPart -
+ sizeof(m_FileHeader) -
+ m_FileHeader.dwFormatLength -
+ sizeof(m_DataHeader);
+
+ if (STATUS_SUCCESS == KeWaitForSingleObject
+ (
+ &m_FileSync,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ ))
+ {
+ if (NT_SUCCESS(FileOpen(FALSE)))
+ {
+ FileWriteHeader();
+
+ FileClose();
+ }
+
+ KeReleaseMutex(&m_FileSync, FALSE);
+ }
+ }
+
+ if (m_waveFormat)
+ {
+ ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1);
+ m_waveFormat = NULL;
+ }
+
+ if (m_fFrameUsed)
+ {
+ ExFreePoolWithTag(m_fFrameUsed, SAVEDATA_POOLTAG2);
+ m_fFrameUsed = NULL;
+ // NOTE : Do not release m_pFilePtr.
+ }
+
+ if (m_FileName.Buffer)
+ {
+ ExFreePoolWithTag(m_FileName.Buffer, SAVEDATA_POOLTAG3);
+ m_FileName.Buffer = NULL;
+ }
+
+ if (m_pDataBuffer)
+ {
+ ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4);
+ m_pDataBuffer = NULL;
+ }
+} // CSaveData
+
+//=============================================================================
+void
+CSaveData::DestroyWorkItems
+(
+ void
+)
+{
+ PAGED_CODE();
+
+ if (m_pWorkItems)
+ {
+ for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
+ {
+ if (m_pWorkItems[i].WorkItem!=NULL)
+ {
+ IoFreeWorkItem(m_pWorkItems[i].WorkItem);
+ m_pWorkItems[i].WorkItem = NULL;
+ }
+ }
+ ExFreePoolWithTag(m_pWorkItems, SAVEDATA_POOLTAG);
+ m_pWorkItems = NULL;
+ }
+
+} // DestroyWorkItems
+
+//=============================================================================
+void
+CSaveData::Disable
+(
+ _In_ BOOL fDisable
+)
+{
+ PAGED_CODE();
+
+ m_fWriteDisabled = fDisable;
+} // Disable
+
+//=============================================================================
+NTSTATUS
+CSaveData::FileClose(void)
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+
+ if (m_FileHandle)
+ {
+ ntStatus = ZwClose(m_FileHandle);
+ m_FileHandle = NULL;
+ }
+
+ return ntStatus;
+} // FileClose
+
+//=============================================================================
+NTSTATUS
+CSaveData::FileOpen
+(
+ _In_ BOOL fOverWrite
+)
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ IO_STATUS_BLOCK ioStatusBlock;
+
+ if( FALSE == m_bInitialized )
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if(!m_FileHandle)
+ {
+ ntStatus =
+ ZwCreateFile
+ (
+ &m_FileHandle,
+ GENERIC_WRITE | SYNCHRONIZE,
+ &m_objectAttributes,
+ &ioStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ 0,
+ fOverWrite ? FILE_OVERWRITE_IF : FILE_OPEN_IF,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0
+ );
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[CSaveData::FileOpen : Error opening data file]"));
+ }
+ }
+
+ return ntStatus;
+} // FileOpen
+
+//=============================================================================
+NTSTATUS
+CSaveData::FileWrite
+(
+ _In_reads_bytes_(ulDataSize) PBYTE pData,
+ _In_ ULONG ulDataSize
+)
+{
+ PAGED_CODE();
+
+ ASSERT(pData);
+ ASSERT(m_pFilePtr);
+
+ NTSTATUS ntStatus;
+
+ if (m_FileHandle)
+ {
+ IO_STATUS_BLOCK ioStatusBlock;
+
+ ntStatus = ZwWriteFile( m_FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ pData,
+ ulDataSize,
+ m_pFilePtr,
+ NULL);
+
+ if (NT_SUCCESS(ntStatus))
+ {
+ ASSERT(ioStatusBlock.Information == ulDataSize);
+
+ m_pFilePtr->QuadPart += ulDataSize;
+ }
+ else
+ {
+ DPF(D_TERSE, ("[CSaveData::FileWrite : WriteFileError]"));
+ }
+ }
+ else
+ {
+ DPF(D_TERSE, ("[CSaveData::FileWrite : File not open]"));
+ ntStatus = STATUS_INVALID_HANDLE;
+ }
+
+ return ntStatus;
+} // FileWrite
+
+//=============================================================================
+NTSTATUS
+CSaveData::FileWriteHeader(void)
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus;
+
+ if (m_FileHandle && m_waveFormat)
+ {
+ IO_STATUS_BLOCK ioStatusBlock;
+
+ m_pFilePtr->QuadPart = 0;
+
+ m_FileHeader.dwFormatLength = (m_waveFormat->wFormatTag == WAVE_FORMAT_PCM) ?
+ sizeof( PCMWAVEFORMAT ) :
+ sizeof( WAVEFORMATEX ) + m_waveFormat->cbSize;
+
+ ntStatus = ZwWriteFile( m_FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ &m_FileHeader,
+ sizeof(m_FileHeader),
+ m_pFilePtr,
+ NULL);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write File Header Error]"));
+ }
+
+ m_pFilePtr->QuadPart += sizeof(m_FileHeader);
+
+ ntStatus = ZwWriteFile( m_FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ m_waveFormat,
+ m_FileHeader.dwFormatLength,
+ m_pFilePtr,
+ NULL);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Format Error]"));
+ }
+
+ m_pFilePtr->QuadPart += m_FileHeader.dwFormatLength;
+
+ ntStatus = ZwWriteFile( m_FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ &m_DataHeader,
+ sizeof(m_DataHeader),
+ m_pFilePtr,
+ NULL);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Data Header Error]"));
+ }
+
+ m_pFilePtr->QuadPart += sizeof(m_DataHeader);
+ }
+ else
+ {
+ DPF(D_TERSE, ("[CSaveData::FileWriteHeader : File not open]"));
+ ntStatus = STATUS_INVALID_HANDLE;
+ }
+
+ return ntStatus;
+} // FileWriteHeader
+NTSTATUS
+CSaveData::SetDeviceObject
+(
+ _In_ PDEVICE_OBJECT DeviceObject
+)
+{
+ PAGED_CODE();
+
+ ASSERT(DeviceObject);
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+
+ m_pDeviceObject = DeviceObject;
+ return ntStatus;
+}
+
+PDEVICE_OBJECT
+CSaveData::GetDeviceObject
+(
+ void
+)
+{
+ PAGED_CODE();
+
+ return m_pDeviceObject;
+}
+
+#pragma code_seg()
+//=============================================================================
+PSAVEWORKER_PARAM
+CSaveData::GetNewWorkItem
+(
+ void
+)
+{
+ LARGE_INTEGER timeOut = { 0 };
+ NTSTATUS ntStatus;
+
+ for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
+ {
+ ntStatus =
+ KeWaitForSingleObject
+ (
+ &m_pWorkItems[i].EventDone,
+ Executive,
+ KernelMode,
+ FALSE,
+ &timeOut
+ );
+ if (STATUS_SUCCESS == ntStatus)
+ {
+ if (m_pWorkItems[i].WorkItem)
+ return &(m_pWorkItems[i]);
+ else
+ return NULL;
+ }
+ }
+
+ return NULL;
+} // GetNewWorkItem
+#pragma code_seg("PAGE")
+
+//=============================================================================
+NTSTATUS
+CSaveData::Initialize
+(
+)
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ WCHAR szTemp[MAX_PATH];
+ size_t cLen;
+ OBJECT_ATTRIBUTES objectAttributes;
+ UNICODE_STRING osDataVolumeString;
+ HANDLE osDataFileHandle = NULL;
+ IO_STATUS_BLOCK ioStatusBlock;
+
+ DPF_ENTER(("[CSaveData::Initialize]"));
+
+ m_ulStreamId++;
+
+ // Probe if OSData volume exists.
+ //
+ RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_probe.txt", OSDATA_FILE_NAME);
+ RtlInitUnicodeString(&osDataVolumeString, szTemp);
+ InitializeObjectAttributes
+ (
+ &objectAttributes,
+ &osDataVolumeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL
+ );
+
+ ntStatus =
+ ZwCreateFile
+ (
+ &osDataFileHandle,
+ GENERIC_WRITE | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ 0,
+ FILE_OVERWRITE_IF,
+ FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0
+ );
+ if (NT_SUCCESS(ntStatus))
+ {
+ ZwClose(osDataFileHandle);
+ }
+
+ // Allocate data file name.
+ //
+ RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_%s_%d.wav", NT_SUCCESS(ntStatus) ? OSDATA_FILE_NAME : DEFAULT_FILE_NAME, HOST_FILE_NAME, m_ulStreamId);
+ m_FileName.Length = 0;
+ ntStatus = RtlStringCchLengthW (szTemp, sizeof(szTemp)/sizeof(szTemp[0]), &cLen);
+ if (NT_SUCCESS(ntStatus))
+ {
+ m_FileName.MaximumLength = (USHORT)((cLen * sizeof(WCHAR)) + sizeof(WCHAR));//convert to wchar and add room for NULL
+ m_FileName.Buffer = (PWSTR)
+ ExAllocatePool2
+ (
+ POOL_FLAG_PAGED,
+ m_FileName.MaximumLength,
+ SAVEDATA_POOLTAG3
+ );
+ if (!m_FileName.Buffer)
+ {
+ DPF(D_TERSE, ("[Could not allocate memory for FileName]"));
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ // Allocate memory for data buffer.
+ //
+ if (NT_SUCCESS(ntStatus))
+ {
+ RtlStringCbCopyW(m_FileName.Buffer, m_FileName.MaximumLength, szTemp);
+ m_FileName.Length = (USHORT)wcslen(m_FileName.Buffer) * sizeof(WCHAR);
+ DPF(D_BLAB, ("[New DataFile -- %S", m_FileName.Buffer));
+
+ m_pDataBuffer = (PBYTE)
+ ExAllocatePool2
+ (
+ POOL_FLAG_NON_PAGED,
+ m_ulBufferSize,
+ SAVEDATA_POOLTAG4
+ );
+ if (!m_pDataBuffer)
+ {
+ DPF(D_TERSE, ("[Could not allocate memory for Saving Data]"));
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ // Allocate memory for frame usage flags and m_pFilePtr.
+ //
+ if (NT_SUCCESS(ntStatus))
+ {
+ m_fFrameUsed = (PBOOL)
+ ExAllocatePool2
+ (
+ POOL_FLAG_NON_PAGED,
+ m_ulFrameCount * sizeof(BOOL) +
+ sizeof(LARGE_INTEGER),
+ SAVEDATA_POOLTAG2
+ );
+ if (!m_fFrameUsed)
+ {
+ DPF(D_TERSE, ("[Could not allocate memory for frame flags]"));
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ // Initialize the spinlock to synchronize access to the frames
+ //
+ KeInitializeSpinLock ( &m_FrameInUseSpinLock ) ;
+
+ // Initialize the file mutex
+ //
+ KeInitializeMutex( &m_FileSync, 1 ) ;
+
+ // Open the data file.
+ //
+ if (NT_SUCCESS(ntStatus))
+ {
+ // m_fFrameUsed has additional memory to hold m_pFilePtr
+ //
+ m_pFilePtr = (PLARGE_INTEGER)
+ (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL));
+
+ // Create data file.
+ InitializeObjectAttributes
+ (
+ &m_objectAttributes,
+ &m_FileName,
+ OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL
+ );
+
+ m_bInitialized = TRUE;
+
+ // Write wave header information to data file.
+ ntStatus = KeWaitForSingleObject
+ (
+ &m_FileSync,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if (STATUS_SUCCESS == ntStatus)
+ {
+ ntStatus = FileOpen(TRUE);
+ if (NT_SUCCESS(ntStatus))
+ {
+ ntStatus = FileWriteHeader();
+
+ FileClose();
+ }
+
+ KeReleaseMutex( &m_FileSync, FALSE );
+ }
+ }
+
+ return ntStatus;
+} // Initialize
+
+//=============================================================================
+NTSTATUS
+CSaveData::InitializeWorkItems
+(
+ _In_ PDEVICE_OBJECT DeviceObject
+)
+{
+ PAGED_CODE();
+
+ ASSERT(DeviceObject);
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+
+ DPF_ENTER(("[CSaveData::InitializeWorkItems]"));
+
+ if (m_pWorkItems != NULL)
+ {
+ return ntStatus;
+ }
+
+ m_pWorkItems = (PSAVEWORKER_PARAM)
+ ExAllocatePool2
+ (
+ POOL_FLAG_NON_PAGED,
+ sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT,
+ SAVEDATA_POOLTAG
+ );
+ if (m_pWorkItems)
+ {
+ for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
+ {
+
+ m_pWorkItems[i].WorkItem = IoAllocateWorkItem(DeviceObject);
+ if(m_pWorkItems[i].WorkItem == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ KeInitializeEvent
+ (
+ &m_pWorkItems[i].EventDone,
+ NotificationEvent,
+ TRUE
+ );
+ }
+ }
+ else
+ {
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return ntStatus;
+} // InitializeWorkItems
+
+//=============================================================================
+
+IO_WORKITEM_ROUTINE SaveFrameWorkerCallback;
+
+VOID
+SaveFrameWorkerCallback
+(
+ _In_ PDEVICE_OBJECT pDeviceObject,
+ _In_opt_ PVOID Context
+)
+{
+ UNREFERENCED_PARAMETER(pDeviceObject);
+
+ PAGED_CODE();
+
+ ASSERT(Context);
+
+ PSAVEWORKER_PARAM pParam = (PSAVEWORKER_PARAM) Context;
+ PCSaveData pSaveData;
+
+ if (NULL == pParam)
+ {
+ // This is completely unexpected, assert here.
+ //
+ ASSERT(pParam);
+ return;
+ }
+
+ DPF(D_VERBOSE, ("[SaveFrameWorkerCallback], %d", pParam->ulFrameNo));
+
+ ASSERT(pParam->pSaveData);
+ ASSERT(pParam->pSaveData->m_fFrameUsed);
+
+ if (pParam->WorkItem)
+ {
+ pSaveData = pParam->pSaveData;
+
+ if (STATUS_SUCCESS == KeWaitForSingleObject
+ (
+ &pSaveData->m_FileSync,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ ))
+ {
+ if (NT_SUCCESS(pSaveData->FileOpen(FALSE)))
+ {
+ pSaveData->FileWrite(pParam->pData, pParam->ulDataSize);
+ pSaveData->FileClose();
+ }
+ InterlockedExchange( (LONG *)&(pSaveData->m_fFrameUsed[pParam->ulFrameNo]), FALSE );
+
+ KeReleaseMutex( &pSaveData->m_FileSync, FALSE );
+ }
+ }
+
+ KeSetEvent(&pParam->EventDone, 0, FALSE);
+} // SaveFrameWorkerCallback
+
+//=============================================================================
+NTSTATUS
+CSaveData::SetDataFormat
+(
+ _In_ PKSDATAFORMAT pDataFormat
+)
+{
+ PAGED_CODE();
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+
+ DPF_ENTER(("[CSaveData::SetDataFormat]"));
+
+ ASSERT(pDataFormat);
+
+ PWAVEFORMATEX pwfx = NULL;
+
+ if (IsEqualGUIDAligned(pDataFormat->Specifier,
+ KSDATAFORMAT_SPECIFIER_DSOUND))
+ {
+ pwfx =
+ &(((PKSDATAFORMAT_DSOUND) pDataFormat)->BufferDesc.WaveFormatEx);
+ }
+ else if (IsEqualGUIDAligned(pDataFormat->Specifier,
+ KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
+ {
+ pwfx = &((PKSDATAFORMAT_WAVEFORMATEX) pDataFormat)->WaveFormatEx;
+ }
+
+ if (pwfx)
+ {
+ // Free the previously allocated waveformat
+ if (m_waveFormat)
+ {
+ ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1);
+ }
+
+ m_waveFormat = (PWAVEFORMATEX)
+ ExAllocatePool2
+ (
+ POOL_FLAG_NON_PAGED,
+ (pwfx->wFormatTag == WAVE_FORMAT_PCM) ?
+ sizeof( PCMWAVEFORMAT ) :
+ sizeof( WAVEFORMATEX ) + pwfx->cbSize,
+ SAVEDATA_POOLTAG1
+ );
+
+ if(m_waveFormat)
+ {
+ RtlCopyMemory( m_waveFormat,
+ pwfx,
+ (pwfx->wFormatTag == WAVE_FORMAT_PCM) ?
+ sizeof( PCMWAVEFORMAT ) :
+ sizeof( WAVEFORMATEX ) + pwfx->cbSize);
+ }
+ else
+ {
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ return ntStatus;
+} // SetDataFormat
+
+//=============================================================================
+NTSTATUS
+CSaveData::SetMaxWriteSize
+(
+ _In_ ULONG ulMaxWriteSize
+)
+{
+ PAGED_CODE();
+
+ NTSTATUS ntStatus = STATUS_SUCCESS;
+ ULONG bufferSize = 0;
+ PBYTE buffer = NULL;
+
+ DPF_ENTER(("[CSaveData::SetMaxWriteSize]"));
+
+ //
+ // Compute new buffer size.
+ //
+ ntStatus = RtlULongMult(ulMaxWriteSize, DEFAULT_FRAME_COUNT, &bufferSize);
+ if (!NT_SUCCESS(ntStatus))
+ {
+ DPF(D_TERSE, ("[Could not allocate memory for Saving Data, MaxWriteSize %u is too big]", ulMaxWriteSize));
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ goto Done;
+ }
+
+ //
+ // Alloc memory for buffer.
+ //
+ buffer = (PBYTE)
+ ExAllocatePool2
+ (
+ POOL_FLAG_NON_PAGED,
+ bufferSize,
+ SAVEDATA_POOLTAG4
+ );
+ if (!buffer)
+ {
+ DPF(D_TERSE, ("[Could not allocate memory for Saving Data]"));
+ ntStatus = STATUS_INSUFFICIENT_RESOURCES;
+ goto Done;
+ }
+
+ //
+ // Free old one.
+ //
+ if (m_pDataBuffer)
+ {
+ ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4);
+ m_pDataBuffer = NULL;
+ }
+
+ //
+ // Init new buffer settings.
+ //
+ m_pDataBuffer = buffer;
+ m_ulBufferSize = bufferSize;
+ m_ulFrameSize = ulMaxWriteSize;
+
+ ntStatus = STATUS_SUCCESS;
+
+Done:
+ return ntStatus;
+} // SetDataFormat
+
+//=============================================================================
+void
+CSaveData::ReadData
+(
+ _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer,
+ _In_ ULONG ulByteCount
+)
+{
+ UNREFERENCED_PARAMETER(pBuffer);
+ UNREFERENCED_PARAMETER(ulByteCount);
+
+ PAGED_CODE();
+
+ // Not implemented yet.
+} // ReadData
+
+//=============================================================================
+#pragma code_seg()
+void
+CSaveData::SaveFrame
+(
+ _In_ ULONG ulFrameNo,
+ _In_ ULONG ulDataSize
+)
+{
+ PSAVEWORKER_PARAM pParam = NULL;
+
+ DPF_ENTER(("[CSaveData::SaveFrame]"));
+
+ pParam = GetNewWorkItem();
+ if (pParam)
+ {
+ pParam->pSaveData = this;
+ pParam->ulFrameNo = ulFrameNo;
+ pParam->ulDataSize = ulDataSize;
+ pParam->pData = m_pDataBuffer + ulFrameNo * m_ulFrameSize;
+ KeResetEvent(&pParam->EventDone);
+ IoQueueWorkItem(pParam->WorkItem, SaveFrameWorkerCallback,
+ CriticalWorkQueue, (PVOID)pParam);
+ }
+} // SaveFrame
+#pragma code_seg("PAGE")
+//=============================================================================
+void
+CSaveData::WaitAllWorkItems
+(
+ void
+)
+{
+ PAGED_CODE();
+
+ DPF_ENTER(("[CSaveData::WaitAllWorkItems]"));
+
+ // Save the last partially-filled frame
+ if (m_ulBufferOffset > m_ulFrameIndex * m_ulFrameSize)
+ {
+ ULONG size;
+
+ size = m_ulBufferOffset - m_ulFrameIndex * m_ulFrameSize;
+ SaveFrame(m_ulFrameIndex, size);
+ }
+
+ for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++)
+ {
+ DPF(D_VERBOSE, ("[Waiting for WorkItem] %d", i));
+ KeWaitForSingleObject
+ (
+ &(m_pWorkItems[i].EventDone),
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+} // WaitAllWorkItems
+
+#pragma code_seg()
+//=============================================================================
+void
+CSaveData::WriteData
+(
+ _In_reads_bytes_(ulByteCount) PBYTE pBuffer,
+ _In_ ULONG ulByteCount
+)
+{
+ ASSERT(pBuffer);
+
+ BOOL fSaveFrame = FALSE;
+ ULONG ulSaveFrameIndex = 0;
+ KIRQL oldIrql;
+
+ // If stream writing is disabled, then exit.
+ //
+ if (m_fWriteDisabled)
+ {
+ return;
+ }
+
+ DPF_ENTER(("[CSaveData::WriteData ulByteCount=%lu]", ulByteCount));
+
+ if( 0 == ulByteCount )
+ {
+ return;
+ }
+
+ // The logic below assumes that write size is <= than frame size.
+ if (ulByteCount > m_ulFrameSize)
+ {
+ ulByteCount = m_ulFrameSize;
+ }
+
+ // Check to see if this frame is available.
+ KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql);
+ if (!m_fFrameUsed[m_ulFrameIndex])
+ {
+ KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql );
+
+ ULONG ulWriteBytes = ulByteCount;
+
+ if( (m_ulBufferSize - m_ulBufferOffset) < ulWriteBytes )
+ {
+ ulWriteBytes = m_ulBufferSize - m_ulBufferOffset;
+ }
+
+ RtlCopyMemory(m_pDataBuffer + m_ulBufferOffset, pBuffer, ulWriteBytes);
+ m_ulBufferOffset += ulWriteBytes;
+
+ // Check to see if we need to save this frame
+ if (m_ulBufferOffset >= ((m_ulFrameIndex + 1) * m_ulFrameSize))
+ {
+ fSaveFrame = TRUE;
+ }
+
+ // Loop the buffer, if we reached the end.
+ if (m_ulBufferOffset == m_ulBufferSize)
+ {
+ fSaveFrame = TRUE;
+ m_ulBufferOffset = 0;
+ }
+
+ if (fSaveFrame)
+ {
+ InterlockedExchange( (LONG *)&(m_fFrameUsed[m_ulFrameIndex]), TRUE );
+ ulSaveFrameIndex = m_ulFrameIndex;
+ m_ulFrameIndex = (m_ulFrameIndex + 1) % m_ulFrameCount;
+ }
+
+ // Write the left over if the next frame is available.
+ if (ulWriteBytes != ulByteCount)
+ {
+ KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql );
+ if (!m_fFrameUsed[m_ulFrameIndex])
+ {
+ KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql );
+ RtlCopyMemory
+ (
+ m_pDataBuffer + m_ulBufferOffset,
+ pBuffer + ulWriteBytes,
+ ulByteCount - ulWriteBytes
+ );
+
+ m_ulBufferOffset += ulByteCount - ulWriteBytes;
+ }
+ else
+ {
+ KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql);
+ DPF(D_BLAB, ("[Frame overflow, next frame is in use]"));
+ }
+ }
+
+ if (fSaveFrame)
+ {
+ SaveFrame(ulSaveFrameIndex, m_ulFrameSize);
+ }
+ }
+ else
+ {
+ KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql );
+ DPF(D_BLAB, ("[Frame %d is in use]", m_ulFrameIndex));
+ }
+
+} // WriteData
+
diff --git a/audio/simpleaudiosample/Source/Utilities/savedata.h b/audio/simpleaudiosample/Source/Utilities/savedata.h
new file mode 100644
index 00000000..c658ec71
--- /dev/null
+++ b/audio/simpleaudiosample/Source/Utilities/savedata.h
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) Microsoft Corporation All Rights Reserved
+
+Module Name:
+
+ savedata.h
+
+Abstract:
+
+ Declaration of Simple Audio Sample data saving class. This class supplies services
+to save data to disk.
+
+--*/
+
+#ifndef _SIMPLEAUDIOSAMPLE_SAVEDATA_H
+#define _SIMPLEAUDIOSAMPLE_SAVEDATA_H
+
+//-----------------------------------------------------------------------------
+// Forward declaration
+//-----------------------------------------------------------------------------
+class CSaveData;
+typedef CSaveData *PCSaveData;
+
+
+//-----------------------------------------------------------------------------
+// Structs
+//-----------------------------------------------------------------------------
+
+// Parameter to workitem.
+#include <pshpack1.h>
+typedef struct _SAVEWORKER_PARAM {
+ PIO_WORKITEM WorkItem;
+ ULONG ulFrameNo;
+ ULONG ulDataSize;
+ PBYTE pData;
+ PCSaveData pSaveData;
+ KEVENT EventDone;
+} SAVEWORKER_PARAM;
+typedef SAVEWORKER_PARAM *PSAVEWORKER_PARAM;
+#include <poppack.h>
+
+// wave file header.
+#include <pshpack1.h>
+typedef struct _OUTPUT_FILE_HEADER
+{
+ DWORD dwRiff;
+ DWORD dwFileSize;
+ DWORD dwWave;
+ DWORD dwFormat;
+ DWORD dwFormatLength;
+} OUTPUT_FILE_HEADER;
+typedef OUTPUT_FILE_HEADER *POUTPUT_FILE_HEADER;
+
+typedef struct _OUTPUT_DATA_HEADER
+{
+ DWORD dwData;
+ DWORD dwDataLength;
+} OUTPUT_DATA_HEADER;
+typedef OUTPUT_DATA_HEADER *POUTPUT_DATA_HEADER;
+
+#include <poppack.h>
+
+//-----------------------------------------------------------------------------
+// Classes
+//-----------------------------------------------------------------------------
+
+///////////////////////////////////////////////////////////////////////////////
+// CSaveData
+// Saves the wave data to disk.
+//
+IO_WORKITEM_ROUTINE SaveFrameWorkerCallback;
+
+class CSaveData
+{
+protected:
+ UNICODE_STRING m_FileName; // DataFile name.
+ HANDLE m_FileHandle; // DataFile handle.
+ PBYTE m_pDataBuffer; // Data buffer.
+ ULONG m_ulBufferSize; // Total buffer size.
+
+ ULONG m_ulFrameIndex; // Current Frame.
+ ULONG m_ulFrameCount; // Frame count.
+ ULONG m_ulFrameSize;
+ ULONG m_ulBufferOffset; // index in buffer.
+ PBOOL m_fFrameUsed; // Frame usage table.
+ KSPIN_LOCK m_FrameInUseSpinLock; // Spinlock for synch.
+ KMUTEX m_FileSync; // Synchronizes file access
+
+ OBJECT_ATTRIBUTES m_objectAttributes; // Used for opening file.
+
+ OUTPUT_FILE_HEADER m_FileHeader;
+ PWAVEFORMATEX m_waveFormat;
+ OUTPUT_DATA_HEADER m_DataHeader;
+ PLARGE_INTEGER m_pFilePtr;
+
+ static PDEVICE_OBJECT m_pDeviceObject;
+ static ULONG m_ulStreamId;
+ static PSAVEWORKER_PARAM m_pWorkItems;
+
+ BOOL m_fWriteDisabled;
+
+ BOOL m_bInitialized;
+
+public:
+ CSaveData();
+ ~CSaveData();
+
+ static NTSTATUS InitializeWorkItems
+ (
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+ static void DestroyWorkItems
+ (
+ void
+ );
+ void Disable
+ (
+ _In_ BOOL fDisable
+ );
+ static PSAVEWORKER_PARAM GetNewWorkItem
+ (
+ void
+ );
+ NTSTATUS Initialize
+ (
+ );
+ static NTSTATUS SetDeviceObject
+ (
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+ static PDEVICE_OBJECT GetDeviceObject
+ (
+ void
+ );
+ void ReadData
+ (
+ _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer,
+ _In_ ULONG ulByteCount
+ );
+ NTSTATUS SetDataFormat
+ (
+ _In_ PKSDATAFORMAT pDataFormat
+ );
+ NTSTATUS SetMaxWriteSize
+ (
+ _In_ ULONG ulMaxWriteSize
+ );
+ void WaitAllWorkItems
+ (
+ void
+ );
+ void WriteData
+ (
+ _In_reads_bytes_(ulByteCount) PBYTE pBuffer,
+ _In_ ULONG ulByteCount
+ );
+
+private:
+ NTSTATUS FileClose
+ (
+ void
+ );
+ NTSTATUS FileOpen
+ (
+ _In_ BOOL fOverWrite
+ );
+ NTSTATUS FileWrite
+ (
+ _In_reads_bytes_(ulDataSize) PBYTE pData,
+ _In_ ULONG ulDataSize
+ );
+ NTSTATUS FileWriteHeader
+ (
+ void
+ );
+
+ void SaveFrame
+ (
+ _In_ ULONG ulFrameNo,
+ _In_ ULONG ulDataSize
+ );
+
+ friend
+ IO_WORKITEM_ROUTINE SaveFrameWorkerCallback;
+};
+typedef CSaveData *PCSaveData;
+
+#endif