diff options
| author | judzmura <[email protected]> | 2021-03-01 13:12:34 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-03-01 13:12:34 -0800 |
| commit | 9a2e9aecb9135e782c434e617f57bfbdf9d8f2fd (patch) | |
| tree | c67dd7a72a15a8d72c7bb6c6e19c1086d5e15f08 /audio/simpleaudiosample/Source/Utilities | |
| parent | 6c41d837b87a0b70fe5dcee78baeba4ef28c45ad (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.cpp | 285 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/ToneGenerator.h | 74 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj | 266 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/Utilities.vcxproj.Filters | 31 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/hw.cpp | 446 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/hw.h | 105 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/kshelper.cpp | 958 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/savedata.cpp | 1033 | ||||
| -rw-r--r-- | audio/simpleaudiosample/Source/Utilities/savedata.h | 189 |
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 |
