1 #define _UNICODE 2 #define UNICODE 3 #define WIN32_NO_STATUS 4 #define _KSDDK_ 5 6 #include <windows.h> 7 #include <stdio.h> 8 #include <math.h> 9 #include <setupapi.h> 10 #include <ndk/umtypes.h> 11 #include <ks.h> 12 #include <ksmedia.h> 13 #include "interface.h" 14 15 #define _2pi 6.283185307179586476925286766559 16 17 GUID CategoryGuid = {STATIC_KSCATEGORY_AUDIO}; 18 19 const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}}; 20 const GUID KSPROPSETID_Connection = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; 21 const GUID KSPROPSETID_Sysaudio = {0xCBE3FAA0L, 0xCC75, 0x11D0, {0xB4, 0x65, 0x00, 0x00, 0x1A, 0x18, 0x18, 0xE6}}; 22 const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}}; 23 const GUID KSINTERFACESETID_Standard = {0x1A8766A0L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; 24 const GUID KSMEDIUMSETID_Standard = {0x4747B320L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; 25 const GUID KSDATAFORMAT_TYPE_AUDIO = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; 26 const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; 27 const GUID KSDATAFORMAT_SPECIFIER_WAVEFORMATEX = {0x05589f81L, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}}; 28 29 VOID 30 TestKs() 31 { 32 SP_DEVICE_INTERFACE_DATA InterfaceData; 33 SP_DEVINFO_DATA DeviceData; 34 PSP_DEVICE_INTERFACE_DETAIL_DATA DetailData; 35 HDEVINFO DeviceHandle; 36 PKSDATAFORMAT_WAVEFORMATEX DataFormat; 37 PKSPIN_CONNECT PinConnect; 38 PKSSTREAM_HEADER Packet; 39 PKSPROPERTY Property; 40 KSSTATE State; 41 DWORD Length; 42 HANDLE FilterHandle; 43 HANDLE PinHandle; 44 PSHORT SoundBuffer; 45 UINT i = 0; 46 BOOL Result; 47 NTSTATUS Status; 48 49 // 50 // Get a handle to KS Audio Interfaces 51 // 52 DeviceHandle = SetupDiGetClassDevs(&CategoryGuid, 53 NULL, 54 NULL, 55 DIGCF_DEVICEINTERFACE); //DIGCF_PRESENT 56 57 printf("DeviceHandle %p\n", DeviceHandle); 58 59 // 60 // Enumerate the first interface 61 // 62 InterfaceData.cbSize = sizeof(InterfaceData); 63 InterfaceData.Reserved = 0; 64 Result = SetupDiEnumDeviceInterfaces(DeviceHandle, 65 NULL, 66 &CategoryGuid, 67 1, 68 &InterfaceData); 69 70 printf("SetupDiEnumDeviceInterfaces %u Error %ld\n", Result, GetLastError()); 71 72 // 73 // Get the interface details (namely the device path) 74 // 75 Length = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + MAX_PATH * sizeof(WCHAR); 76 DetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), 77 0, 78 Length); 79 DetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 80 DeviceData.cbSize = sizeof(DeviceData); 81 DeviceData.Reserved = 0; 82 Result = SetupDiGetDeviceInterfaceDetail(DeviceHandle, 83 &InterfaceData, 84 DetailData, 85 Length, 86 NULL, 87 &DeviceData); 88 89 wprintf(L"SetupDiGetDeviceInterfaceDetail %u Path DetailData %s\n", Result, (LPWSTR)&DetailData->DevicePath[0]); 90 91 // 92 // Open a handle to the device 93 // 94 FilterHandle = CreateFile(DetailData->DevicePath, 95 GENERIC_READ | GENERIC_WRITE, 96 0, 97 NULL, 98 OPEN_EXISTING, 99 FILE_ATTRIBUTE_NORMAL, 100 NULL); 101 102 printf("Handle %p\n", FilterHandle); 103 104 // 105 // Close the interface handle and clean up 106 // 107 SetupDiDestroyDeviceInfoList(DeviceHandle); 108 109 // 110 // Allocate a KS Pin Connection Request Structure 111 // 112 Length = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX); 113 printf("Length %ld KSPIN %Iu DATAFORMAT %Iu\n", Length, sizeof(KSPIN_CONNECT), sizeof(KSDATAFORMAT_WAVEFORMATEX)); 114 PinConnect = (PKSPIN_CONNECT)HeapAlloc(GetProcessHeap(), 0, Length); 115 DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)(PinConnect + 1); 116 117 // 118 // Setup the KS Pin Data 119 // 120 PinConnect->Interface.Set = KSINTERFACESETID_Standard; 121 PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING; 122 PinConnect->Interface.Flags = 0; 123 PinConnect->Medium.Set = KSMEDIUMSETID_Standard; 124 PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE; 125 PinConnect->Medium.Flags = 0; 126 PinConnect->PinId = 0; 127 PinConnect->PinToHandle = NULL; 128 PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL; 129 PinConnect->Priority.PrioritySubClass = 1; 130 131 // 132 // Setup the KS Data Format Information 133 // 134 DataFormat->WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; 135 DataFormat->WaveFormatEx.nChannels = 2; 136 DataFormat->WaveFormatEx.nSamplesPerSec = 48000; 137 DataFormat->WaveFormatEx.nBlockAlign = 4; 138 DataFormat->WaveFormatEx.nAvgBytesPerSec = 48000 * 4; 139 DataFormat->WaveFormatEx.wBitsPerSample = 16; 140 DataFormat->WaveFormatEx.cbSize = 0; 141 DataFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + 142 sizeof(WAVEFORMATEX); 143 DataFormat->DataFormat.Flags = KSDATAFORMAT_ATTRIBUTES; 144 DataFormat->DataFormat.Reserved = 0; 145 DataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; 146 DataFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 147 DataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; 148 DataFormat->DataFormat.SampleSize = 4; 149 150 // 151 // Create the pin 152 // 153 Status = KsCreatePin(FilterHandle, PinConnect, GENERIC_WRITE, &PinHandle); 154 155 printf("PinHandle %p Status %lx\n", PinHandle, Status); 156 157 // 158 // Allocate a buffer for 1 second 159 // 160 Length = 48000 * 4; 161 SoundBuffer = (PSHORT)HeapAlloc(GetProcessHeap(), 0, Length); 162 163 // 164 // Fill the buffer with a 500 Hz sine tone 165 // 166 while (i < Length / 2) 167 { 168 // 169 // Generate the wave for each channel: 170 // Amplitude * sin( Sample * Frequency * 2PI / SamplesPerSecond ) 171 // 172 SoundBuffer[i] = 0x7FFF * sin(0.5 * (i - 1) * 500 * _2pi / 48000); 173 i++; 174 SoundBuffer[i] = 0x7FFF * sin((0.5 * i - 2) * 500 * _2pi / 48000); 175 i++; 176 } 177 178 // 179 // Create and fill out the KS Stream Packet 180 // 181 Packet = (PKSSTREAM_HEADER)HeapAlloc(GetProcessHeap(), 182 HEAP_ZERO_MEMORY, 183 sizeof(KSSTREAM_HEADER)); 184 Packet->Data = SoundBuffer; 185 Packet->FrameExtent = Length; 186 Packet->DataUsed = Length; 187 Packet->Size = sizeof(KSSTREAM_HEADER); 188 Packet->PresentationTime.Numerator = 1; 189 Packet->PresentationTime.Denominator = 1; 190 191 // 192 // Setup a KS Property to change the state 193 // 194 Property = (PKSPROPERTY)HeapAlloc(GetProcessHeap(), 0, sizeof(KSPROPERTY)); 195 Property->Set = KSPROPSETID_Connection; 196 Property->Id = KSPROPERTY_CONNECTION_STATE; 197 Property->Flags = KSPROPERTY_TYPE_SET; 198 199 // 200 // Change the state to run 201 // 202 State = KSSTATE_RUN; 203 DeviceIoControl(PinHandle, 204 IOCTL_KS_PROPERTY, 205 Property, 206 sizeof(KSPROPERTY), 207 &State, 208 sizeof(State), 209 &Length, 210 NULL); 211 212 // 213 // Play our 1-second buffer 214 // 215 DeviceIoControl(PinHandle, 216 IOCTL_KS_WRITE_STREAM, 217 NULL, 218 0, 219 Packet, 220 Packet->Size, 221 &Length, 222 NULL); 223 224 // 225 // Change the state to stop 226 // 227 State = KSSTATE_STOP; 228 DeviceIoControl(PinHandle, 229 IOCTL_KS_PROPERTY, 230 Property, 231 sizeof(KSPROPERTY), 232 &State, 233 sizeof(State), 234 &Length, 235 NULL); 236 237 CloseHandle(PinHandle); 238 CloseHandle(FilterHandle); 239 } 240 241 int 242 __cdecl 243 main(int argc, char* argv[]) 244 { 245 ULONG Length; 246 PSHORT SoundBuffer; 247 ULONG i = 0; 248 BOOL Status; 249 OVERLAPPED Overlapped; 250 DWORD BytesReturned; 251 HANDLE hWdmAud; 252 WDMAUD_DEVICE_INFO DeviceInfo; 253 254 TestKs(); 255 return 0; 256 257 hWdmAud = CreateFileW(L"\\\\.\\wdmaud", 258 GENERIC_READ | GENERIC_WRITE, 259 0, 260 NULL, 261 OPEN_EXISTING, 262 FILE_FLAG_OVERLAPPED, 263 NULL); 264 if (!hWdmAud) 265 { 266 printf("Failed to open wdmaud with %lx\n", GetLastError()); 267 return -1; 268 } 269 270 printf("WDMAUD: opened\n"); 271 272 /* clear device info */ 273 RtlZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO)); 274 275 ZeroMemory(&Overlapped, sizeof(OVERLAPPED)); 276 Overlapped.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 277 278 DeviceInfo.DeviceType = WAVE_OUT_DEVICE_TYPE; 279 280 281 Status = DeviceIoControl(hWdmAud, IOCTL_GETNUMDEVS_TYPE, (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &BytesReturned, &Overlapped); 282 283 if (!Status) 284 { 285 if (WaitForSingleObject(&Overlapped.hEvent, 5000) != WAIT_OBJECT_0) 286 { 287 printf("Failed to get num of wave out devices with %lx\n", GetLastError()); 288 CloseHandle(hWdmAud); 289 return -1; 290 } 291 } 292 293 printf("WDMAUD: Num Devices %lu\n", DeviceInfo.DeviceCount); 294 295 if (!DeviceInfo.DeviceCount) 296 { 297 CloseHandle(hWdmAud); 298 return 0; 299 } 300 301 Status = DeviceIoControl(hWdmAud, IOCTL_GETCAPABILITIES, (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &BytesReturned, &Overlapped); 302 303 if (!Status) 304 { 305 if (WaitForSingleObject(&Overlapped.hEvent, 5000) != WAIT_OBJECT_0) 306 { 307 printf("Failed to get iocaps %lx\n", GetLastError()); 308 } 309 } 310 printf("WDMAUD: Capabilities NumChannels %x dwFormats %lx\n", DeviceInfo.u.WaveOutCaps.wChannels, DeviceInfo.u.WaveOutCaps.dwFormats); 311 312 DeviceInfo.u.WaveFormatEx.cbSize = sizeof(WAVEFORMATEX); 313 DeviceInfo.u.WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; 314 DeviceInfo.u.WaveFormatEx.nChannels = 2; 315 DeviceInfo.u.WaveFormatEx.nSamplesPerSec = 48000; 316 DeviceInfo.u.WaveFormatEx.nBlockAlign = 4; 317 DeviceInfo.u.WaveFormatEx.nAvgBytesPerSec = 48000 * 4; 318 DeviceInfo.u.WaveFormatEx.wBitsPerSample = 16; 319 320 321 322 Status = DeviceIoControl(hWdmAud, IOCTL_OPEN_WDMAUD, (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &BytesReturned, &Overlapped); 323 if (!Status) 324 { 325 if (WaitForSingleObject(&Overlapped.hEvent, 5000) != WAIT_OBJECT_0) 326 { 327 printf("Failed to open device with %lx\n", GetLastError()); 328 CloseHandle(hWdmAud); 329 return -1; 330 } 331 } 332 333 printf("WDMAUD: opened device\n"); 334 335 // 336 // Allocate a buffer for 1 second 337 // 338 Length = 48000 * 4; 339 SoundBuffer = (PSHORT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Length); 340 341 // 342 // Fill the buffer with a 500 Hz sine tone 343 // 344 while (i < Length / 2) 345 { 346 // 347 // Generate the wave for each channel: 348 // Amplitude * sin( Sample * Frequency * 2PI / SamplesPerSecond ) 349 // 350 SoundBuffer[i] = 0x7FFF * sin(0.5 * (i - 1) * 500 * _2pi / 48000); 351 i++; 352 SoundBuffer[i] = 0x7FFF * sin((0.5 * i - 2) * 500 * _2pi / 48000); 353 i++; 354 } 355 356 DeviceInfo.u.State = KSSTATE_RUN; 357 Status = DeviceIoControl(hWdmAud, IOCTL_SETDEVICE_STATE, (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &BytesReturned, &Overlapped); 358 if (!Status) 359 { 360 if (WaitForSingleObject(&Overlapped.hEvent, 5000) != WAIT_OBJECT_0) 361 { 362 printf("Failed to set device into run state %lx\n", GetLastError()); 363 CloseHandle(hWdmAud); 364 return -1; 365 } 366 } 367 368 // 369 // Play our 1-second buffer 370 // 371 DeviceInfo.Header.Data = (PUCHAR)SoundBuffer; 372 DeviceInfo.Header.DataUsed = DeviceInfo.Header.FrameExtent = Length; 373 Status = DeviceIoControl(hWdmAud, IOCTL_WRITEDATA, (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &BytesReturned, &Overlapped); 374 if (!Status) 375 { 376 if (WaitForSingleObject(&Overlapped.hEvent, 5000) != WAIT_OBJECT_0) 377 { 378 printf("Failed to play buffer %lx\n", GetLastError()); 379 CloseHandle(hWdmAud); 380 return -1; 381 } 382 } 383 384 printf("WDMAUD: Played buffer\n"); 385 386 DeviceInfo.u.State = KSSTATE_STOP; 387 Status = DeviceIoControl(hWdmAud, IOCTL_SETDEVICE_STATE, (LPVOID)&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), &BytesReturned, &Overlapped); 388 if (!Status) 389 { 390 if (WaitForSingleObject(&Overlapped.hEvent, 5000) != WAIT_OBJECT_0) 391 { 392 printf("Failed to set device into stop state %lx\n", GetLastError()); 393 CloseHandle(hWdmAud); 394 return -1; 395 } 396 } 397 printf("WDMAUD: STOPPED\n"); 398 CloseHandle(&Overlapped.hEvent); 399 CloseHandle(hWdmAud); 400 printf("WDMAUD: COMPLETE\n"); 401 return 0; 402 } 403