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