xref: /reactos/sdk/lib/drivers/sound/mmixer/wave.c (revision 56582812)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            lib/drivers/sound/mmixer/wave.c
5  * PURPOSE:         Wave Handling Functions
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "precomp.h"
10 
11 // #define NDEBUG
12 #include <debug.h>
13 
14 const GUID KSPROPSETID_Connection               = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
15 const GUID KSDATAFORMAT_SPECIFIER_WAVEFORMATEX  = {0x05589f81L, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}};
16 const GUID KSDATAFORMAT_SUBTYPE_PCM             = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
17 const GUID KSDATAFORMAT_TYPE_AUDIO              = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
18 const GUID KSINTERFACESETID_Standard            = {0x1A8766A0L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
19 const GUID KSMEDIUMSETID_Standard               = {0x4747B320L, 0x62CE, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
20 
21 typedef struct
22 {
23     ULONG SampleRate;
24     ULONG Bit8Mono;
25     ULONG Bit8Stereo;
26     ULONG Bit16Mono;
27     ULONG Bit16Stereo;
28 }AUDIO_RANGE;
29 
30 #define AUDIO_TEST_RANGE (5)
31 
32 static AUDIO_RANGE TestRange[AUDIO_TEST_RANGE] =
33 {
34     {
35         11025,
36         WAVE_FORMAT_1M08,
37         WAVE_FORMAT_1S08,
38         WAVE_FORMAT_1M16,
39         WAVE_FORMAT_1S16
40     },
41     {
42         22050,
43         WAVE_FORMAT_2M08,
44         WAVE_FORMAT_2S08,
45         WAVE_FORMAT_2M16,
46         WAVE_FORMAT_2S16
47     },
48     {
49         44100,
50         WAVE_FORMAT_4M08,
51         WAVE_FORMAT_4S08,
52         WAVE_FORMAT_4M16,
53         WAVE_FORMAT_4S16
54     },
55     {
56         48000,
57         WAVE_FORMAT_48M08,
58         WAVE_FORMAT_48S08,
59         WAVE_FORMAT_48M16,
60         WAVE_FORMAT_48S16
61     },
62     {
63         96000,
64         WAVE_FORMAT_96M08,
65         WAVE_FORMAT_96S08,
66         WAVE_FORMAT_96M16,
67         WAVE_FORMAT_96S16
68     }
69 };
70 
71 PKSPIN_CONNECT
72 MMixerAllocatePinConnect(
73     IN PMIXER_CONTEXT MixerContext,
74     ULONG DataFormatSize)
75 {
76     return MixerContext->Alloc(sizeof(KSPIN_CONNECT) + DataFormatSize);
77 }
78 
79 MIXER_STATUS
80 MMixerGetWaveInfoByIndexAndType(
81     IN  PMIXER_LIST MixerList,
82     IN  ULONG DeviceIndex,
83     IN  ULONG bWaveInType,
84     OUT LPWAVE_INFO *OutWaveInfo)
85 {
86     ULONG Index = 0;
87     PLIST_ENTRY Entry, ListHead;
88     LPWAVE_INFO WaveInfo;
89 
90     if (bWaveInType)
91         ListHead = &MixerList->WaveInList;
92     else
93         ListHead = &MixerList->WaveOutList;
94 
95     /* get first entry */
96     Entry = ListHead->Flink;
97 
98     while(Entry != ListHead)
99     {
100         WaveInfo = (LPWAVE_INFO)CONTAINING_RECORD(Entry, WAVE_INFO, Entry);
101 
102         if (Index == DeviceIndex)
103         {
104             *OutWaveInfo = WaveInfo;
105             return MM_STATUS_SUCCESS;
106         }
107         Index++;
108         Entry = Entry->Flink;
109     }
110 
111     return MM_STATUS_INVALID_PARAMETER;
112 }
113 
114 VOID
115 MMixerInitializeDataFormat(
116     _Inout_ PKSDATAFORMAT_WAVEFORMATEX DataFormat,
117     _In_ LPWAVEFORMATEX WaveFormatEx,
118     _In_ DWORD cbSize)
119 {
120     DataFormat->WaveFormatEx.wFormatTag = WaveFormatEx->wFormatTag;
121     DataFormat->WaveFormatEx.nChannels = WaveFormatEx->nChannels;
122     DataFormat->WaveFormatEx.nSamplesPerSec = WaveFormatEx->nSamplesPerSec;
123     DataFormat->WaveFormatEx.nBlockAlign = WaveFormatEx->nBlockAlign;
124     DataFormat->WaveFormatEx.nAvgBytesPerSec = WaveFormatEx->nAvgBytesPerSec;
125     DataFormat->WaveFormatEx.wBitsPerSample = WaveFormatEx->wBitsPerSample;
126     DataFormat->WaveFormatEx.cbSize = cbSize;
127     DataFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX) + cbSize;
128     DataFormat->DataFormat.Flags = 0;
129     DataFormat->DataFormat.Reserved = 0;
130     DataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
131     DataFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
132     DataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
133     DataFormat->DataFormat.SampleSize = 4;
134 
135     /* Write additional fields for Extensible audio format */
136     if (WaveFormatEx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
137     {
138         PWAVEFORMATEXTENSIBLE WaveFormatExt = (PWAVEFORMATEXTENSIBLE)&DataFormat->WaveFormatEx;
139         WaveFormatExt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
140         WaveFormatExt->Samples.wValidBitsPerSample = WaveFormatEx->wBitsPerSample;
141         if (WaveFormatEx->nChannels == 0)
142             WaveFormatExt->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
143         else if (WaveFormatEx->nChannels == 1)
144             WaveFormatExt->dwChannelMask = KSAUDIO_SPEAKER_MONO;
145         else if (WaveFormatEx->nChannels == 2)
146             WaveFormatExt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
147         else if (WaveFormatEx->nChannels == 4)
148             WaveFormatExt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
149         else if (WaveFormatEx->nChannels == 5)
150             WaveFormatExt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
151         else if (WaveFormatEx->nChannels == 7)
152             WaveFormatExt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
153     }
154 }
155 
156 MIXER_STATUS
157 MMixerGetAudioPinDataRanges(
158     IN PMIXER_CONTEXT MixerContext,
159     IN HANDLE hDevice,
160     IN ULONG PinId,
161     IN OUT PKSMULTIPLE_ITEM * OutMultipleItem)
162 {
163     KSP_PIN PinProperty;
164     ULONG BytesReturned = 0;
165     MIXER_STATUS Status;
166     PKSMULTIPLE_ITEM MultipleItem;
167 
168     /* retrieve size of data ranges buffer */
169     PinProperty.Reserved = 0;
170     PinProperty.PinId = PinId;
171     PinProperty.Property.Set = KSPROPSETID_Pin;
172     PinProperty.Property.Id = KSPROPERTY_PIN_DATARANGES;
173     PinProperty.Property.Flags = KSPROPERTY_TYPE_GET;
174 
175     Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)NULL, 0, &BytesReturned);
176     if (Status != MM_STATUS_MORE_ENTRIES)
177     {
178         return Status;
179     }
180 
181     MultipleItem = MixerContext->Alloc(BytesReturned);
182     if (!MultipleItem)
183     {
184         /* not enough memory */
185         return MM_STATUS_NO_MEMORY;
186     }
187 
188     Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
189     if (Status != MM_STATUS_SUCCESS)
190     {
191         /* failed */
192         MixerContext->Free(MultipleItem);
193         return Status;
194     }
195 
196     /* save result */
197     *OutMultipleItem = MultipleItem;
198     return Status;
199 }
200 
201 MIXER_STATUS
202 MMixerFindAudioDataRange(
203     PKSMULTIPLE_ITEM MultipleItem,
204     PKSDATARANGE_AUDIO * OutDataRangeAudio)
205 {
206     ULONG Index;
207     PKSDATARANGE_AUDIO DataRangeAudio;
208     PKSDATARANGE DataRange;
209 
210     DataRange = (PKSDATARANGE) (MultipleItem + 1);
211     for(Index = 0; Index < MultipleItem->Count; Index++)
212     {
213         if (DataRange->FormatSize == sizeof(KSDATARANGE_AUDIO))
214         {
215             DataRangeAudio = (PKSDATARANGE_AUDIO)DataRange;
216             if (IsEqualGUIDAligned(&DataRangeAudio->DataRange.MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) &&
217                 IsEqualGUIDAligned(&DataRangeAudio->DataRange.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) &&
218                 IsEqualGUIDAligned(&DataRangeAudio->DataRange.Specifier, &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
219             {
220                 DPRINT("Min Sample %u Max Sample %u Min Bits %u Max Bits %u Max Channel %u\n", DataRangeAudio->MinimumSampleFrequency, DataRangeAudio->MaximumSampleFrequency,
221                                                          DataRangeAudio->MinimumBitsPerSample, DataRangeAudio->MaximumBitsPerSample, DataRangeAudio->MaximumChannels);
222                 *OutDataRangeAudio = DataRangeAudio;
223                 return MM_STATUS_SUCCESS;
224             }
225         }
226         DataRange = (PKSDATARANGE)((ULONG_PTR)DataRange + DataRange->FormatSize);
227     }
228     return MM_STATUS_UNSUCCESSFUL;
229 }
230 
231 MIXER_STATUS
232 MMixerOpenWavePin(
233     IN PMIXER_CONTEXT MixerContext,
234     IN PMIXER_LIST MixerList,
235     IN ULONG DeviceId,
236     IN ULONG PinId,
237     IN LPWAVEFORMATEX WaveFormatEx,
238     IN ACCESS_MASK DesiredAccess,
239     IN PIN_CREATE_CALLBACK CreateCallback,
240     IN PVOID Context,
241     OUT PHANDLE PinHandle)
242 {
243     PKSPIN_CONNECT PinConnect;
244     PKSDATAFORMAT_WAVEFORMATEX DataFormat;
245     LPMIXER_DATA MixerData;
246     NTSTATUS Status;
247     MIXER_STATUS MixerStatus;
248     DWORD cbSize;
249 
250     MixerData = MMixerGetDataByDeviceId(MixerList, DeviceId);
251     if (!MixerData)
252         return MM_STATUS_INVALID_PARAMETER;
253 
254     /* Enforce 0 for WAVE_FORMAT_PCM, which ignores extra information size */
255     cbSize = WaveFormatEx->wFormatTag == WAVE_FORMAT_PCM ? 0 : WaveFormatEx->cbSize;
256 
257     /* allocate pin connect */
258     PinConnect = MMixerAllocatePinConnect(MixerContext, sizeof(KSDATAFORMAT_WAVEFORMATEX) + cbSize);
259     if (!PinConnect)
260     {
261         /* no memory */
262         return MM_STATUS_NO_MEMORY;
263     }
264 
265     /* initialize pin connect struct */
266     MMixerInitializePinConnect(PinConnect, PinId);
267 
268     /* get offset to dataformat */
269     DataFormat = (PKSDATAFORMAT_WAVEFORMATEX) (PinConnect + 1);
270     /* initialize with requested wave format */
271     MMixerInitializeDataFormat(DataFormat, WaveFormatEx, cbSize);
272 
273     if (CreateCallback)
274     {
275         /* let the callback handle the creation */
276         MixerStatus = CreateCallback(Context, DeviceId, PinId, MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
277     }
278     else
279     {
280         /* now create the pin */
281         Status = KsCreatePin(MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
282 
283         /* normalize status */
284         if (Status == STATUS_SUCCESS)
285             MixerStatus = MM_STATUS_SUCCESS;
286         else
287             MixerStatus = MM_STATUS_UNSUCCESSFUL;
288     }
289 
290     /* free create info */
291     MixerContext->Free(PinConnect);
292 
293     /* done */
294     return MixerStatus;
295 }
296 
297 VOID
298 MMixerCheckFormat(
299     IN PKSDATARANGE_AUDIO DataRangeAudio,
300     IN LPWAVE_INFO WaveInfo,
301     IN ULONG bInput)
302 {
303     ULONG Index, SampleFrequency;
304     ULONG Result = 0;
305 
306     for(Index = 0; Index < AUDIO_TEST_RANGE; Index++)
307     {
308         SampleFrequency = TestRange[Index].SampleRate;
309 
310         if (DataRangeAudio->MinimumSampleFrequency <= SampleFrequency && DataRangeAudio->MaximumSampleFrequency >= SampleFrequency)
311         {
312             /* the audio adapter supports the sample frequency */
313             if (DataRangeAudio->MinimumBitsPerSample <= 8 && DataRangeAudio->MaximumBitsPerSample >= 8)
314             {
315                 Result |= TestRange[Index].Bit8Mono;
316 
317                 if (DataRangeAudio->MaximumChannels > 1)
318                 {
319                     /* check if pin supports the sample rate in 8-Bit Stereo */
320                     Result |= TestRange[Index].Bit8Stereo;
321                 }
322             }
323 
324             if (DataRangeAudio->MinimumBitsPerSample <= 16 && DataRangeAudio->MaximumBitsPerSample >= 16)
325             {
326                 /* check if pin supports the sample rate in 16-Bit Mono */
327                 Result |= TestRange[Index].Bit16Mono;
328 
329                 if (DataRangeAudio->MaximumChannels > 1)
330                 {
331                     /* check if pin supports the sample rate in 16-Bit Stereo */
332                     Result |= TestRange[Index].Bit16Stereo;
333                 }
334             }
335         }
336     }
337 
338     if (bInput)
339         WaveInfo->u.InCaps.dwFormats = Result;
340     else
341         WaveInfo->u.OutCaps.dwFormats = Result;
342 
343     DPRINT("Format %lx bInput %u\n", Result, bInput);
344 }
345 
346 MIXER_STATUS
347 MMixerInitializeWaveInfo(
348     IN PMIXER_CONTEXT MixerContext,
349     IN PMIXER_LIST MixerList,
350     IN LPMIXER_DATA MixerData,
351     IN LPWSTR DeviceName,
352     IN ULONG bWaveIn,
353     IN ULONG PinCount,
354     IN PULONG Pins)
355 {
356     MIXER_STATUS Status;
357     PKSMULTIPLE_ITEM MultipleItem;
358     PKSDATARANGE_AUDIO DataRangeAudio;
359     LPWAVE_INFO WaveInfo;
360 
361     WaveInfo = (LPWAVE_INFO)MixerContext->Alloc(sizeof(WAVE_INFO));
362     if (!WaveInfo)
363         return MM_STATUS_NO_MEMORY;
364 
365     if (PinCount > 1)
366     {
367         /* FIXME support multiple pins for wave device */
368         DPRINT1("Implement support for multiple pins\n");
369         //ASSERT(PinCount == 1);
370     }
371 
372     /* initialize wave info */
373     WaveInfo->DeviceId = MixerData->DeviceId;
374     WaveInfo->PinId = Pins[0];
375 
376     /* sanity check */
377     ASSERT(wcslen(DeviceName) < MAXPNAMELEN);
378 
379     /* copy device name */
380     if (bWaveIn)
381     {
382         wcscpy(WaveInfo->u.InCaps.szPname, DeviceName);
383     }
384     else
385     {
386         wcscpy(WaveInfo->u.OutCaps.szPname, DeviceName);
387     }
388 
389     /* FIXME determine manufacturer / product id */
390     if (bWaveIn)
391     {
392         WaveInfo->u.InCaps.wMid = MM_MICROSOFT;
393         WaveInfo->u.InCaps.wPid = MM_PID_UNMAPPED;
394         WaveInfo->u.InCaps.vDriverVersion = 1;
395     }
396     else
397     {
398         WaveInfo->u.OutCaps.wMid = MM_MICROSOFT;
399         WaveInfo->u.OutCaps.wPid = MM_PID_UNMAPPED;
400         WaveInfo->u.OutCaps.vDriverVersion = 1;
401     }
402 
403     /* get audio pin data ranges */
404     Status = MMixerGetAudioPinDataRanges(MixerContext, MixerData->hDevice, Pins[0], &MultipleItem);
405     if (Status != MM_STATUS_SUCCESS)
406     {
407         /* failed to get audio pin data ranges */
408         MixerContext->Free(WaveInfo);
409         return MM_STATUS_UNSUCCESSFUL;
410     }
411 
412     /* find an KSDATARANGE_AUDIO range */
413     Status = MMixerFindAudioDataRange(MultipleItem, &DataRangeAudio);
414     if (Status != MM_STATUS_SUCCESS)
415     {
416         /* failed to find audio pin data range */
417         MixerContext->Free(MultipleItem);
418         MixerContext->Free(WaveInfo);
419         return MM_STATUS_UNSUCCESSFUL;
420     }
421 
422     /* store channel count */
423     if (bWaveIn)
424     {
425         WaveInfo->u.InCaps.wChannels = DataRangeAudio->MaximumChannels;
426     }
427     else
428     {
429        WaveInfo->u.OutCaps.wChannels = DataRangeAudio->MaximumChannels;
430     }
431 
432     /* get all supported formats */
433     MMixerCheckFormat(DataRangeAudio, WaveInfo, bWaveIn);
434 
435     /* free dataranges buffer */
436     MixerContext->Free(MultipleItem);
437 
438     if (bWaveIn)
439     {
440         InsertTailList(&MixerList->WaveInList, &WaveInfo->Entry);
441         MixerList->WaveInListCount++;
442     }
443     else
444     {
445         InsertTailList(&MixerList->WaveOutList, &WaveInfo->Entry);
446         MixerList->WaveOutListCount++;
447     }
448 
449     return MM_STATUS_SUCCESS;
450 }
451 
452 MIXER_STATUS
453 MMixerOpenWave(
454     IN PMIXER_CONTEXT MixerContext,
455     IN ULONG DeviceIndex,
456     IN ULONG bWaveIn,
457     IN LPWAVEFORMATEX WaveFormat,
458     IN PIN_CREATE_CALLBACK CreateCallback,
459     IN PVOID Context,
460     OUT PHANDLE PinHandle)
461 {
462     PMIXER_LIST MixerList;
463     MIXER_STATUS Status;
464     LPWAVE_INFO WaveInfo;
465     ACCESS_MASK DesiredAccess = 0;
466 
467     /* verify mixer context */
468     Status = MMixerVerifyContext(MixerContext);
469 
470     if (Status != MM_STATUS_SUCCESS)
471     {
472         /* invalid context passed */
473         return Status;
474     }
475 
476     /* grab mixer list */
477     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
478 
479     /* find destination wave */
480     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, bWaveIn, &WaveInfo);
481     if (Status != MM_STATUS_SUCCESS)
482     {
483         /* failed to find wave info */
484         return MM_STATUS_INVALID_PARAMETER;
485     }
486 
487     /* get desired access */
488     if (bWaveIn)
489     {
490         DesiredAccess |= GENERIC_READ;
491     }
492      else
493     {
494         DesiredAccess |= GENERIC_WRITE;
495     }
496 
497     /* now try open the pin */
498     return MMixerOpenWavePin(MixerContext, MixerList, WaveInfo->DeviceId, WaveInfo->PinId, WaveFormat, DesiredAccess, CreateCallback, Context, PinHandle);
499 }
500 
501 MIXER_STATUS
502 MMixerWaveInCapabilities(
503     IN PMIXER_CONTEXT MixerContext,
504     IN ULONG DeviceIndex,
505     OUT LPWAVEINCAPSW Caps)
506 {
507     PMIXER_LIST MixerList;
508     MIXER_STATUS Status;
509     LPWAVE_INFO WaveInfo;
510 
511     /* verify mixer context */
512     Status = MMixerVerifyContext(MixerContext);
513 
514     if (Status != MM_STATUS_SUCCESS)
515     {
516         /* invalid context passed */
517         return Status;
518     }
519 
520     /* grab mixer list */
521     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
522 
523     /* find destination wave */
524     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, TRUE, &WaveInfo);
525     if (Status != MM_STATUS_SUCCESS)
526     {
527         /* failed to find wave info */
528         return MM_STATUS_UNSUCCESSFUL;
529     }
530 
531     /* copy capabilities */
532     MixerContext->Copy(Caps, &WaveInfo->u.InCaps, sizeof(WAVEINCAPSW));
533 
534     return MM_STATUS_SUCCESS;
535 }
536 
537 MIXER_STATUS
538 MMixerWaveOutCapabilities(
539     IN PMIXER_CONTEXT MixerContext,
540     IN ULONG DeviceIndex,
541     OUT LPWAVEOUTCAPSW Caps)
542 {
543     PMIXER_LIST MixerList;
544     MIXER_STATUS Status;
545     LPWAVE_INFO WaveInfo;
546 
547     /* verify mixer context */
548     Status = MMixerVerifyContext(MixerContext);
549 
550     if (Status != MM_STATUS_SUCCESS)
551     {
552         /* invalid context passed */
553         return Status;
554     }
555 
556     /* grab mixer list */
557     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
558 
559     /* find destination wave */
560     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, FALSE, &WaveInfo);
561     if (Status != MM_STATUS_SUCCESS)
562     {
563         /* failed to find wave info */
564         return MM_STATUS_UNSUCCESSFUL;
565     }
566 
567     /* copy capabilities */
568     MixerContext->Copy(Caps, &WaveInfo->u.OutCaps, sizeof(WAVEOUTCAPSW));
569 
570     return MM_STATUS_SUCCESS;
571 }
572 
573 ULONG
574 MMixerGetWaveInCount(
575     IN PMIXER_CONTEXT MixerContext)
576 {
577     PMIXER_LIST MixerList;
578     MIXER_STATUS Status;
579 
580      /* verify mixer context */
581     Status = MMixerVerifyContext(MixerContext);
582 
583     if (Status != MM_STATUS_SUCCESS)
584     {
585         /* invalid context passed */
586         return Status;
587     }
588 
589     /* grab mixer list */
590     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
591 
592     return MixerList->WaveInListCount;
593 }
594 
595 ULONG
596 MMixerGetWaveOutCount(
597     IN PMIXER_CONTEXT MixerContext)
598 {
599     PMIXER_LIST MixerList;
600     MIXER_STATUS Status;
601 
602     /* verify mixer context */
603     Status = MMixerVerifyContext(MixerContext);
604 
605     if (Status != MM_STATUS_SUCCESS)
606     {
607         /* invalid context passed */
608         return Status;
609     }
610 
611     /* grab mixer list */
612     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
613 
614     return MixerList->WaveOutListCount;
615 }
616 
617 MIXER_STATUS
618 MMixerSetWaveStatus(
619     IN PMIXER_CONTEXT MixerContext,
620     IN HANDLE PinHandle,
621     IN KSSTATE State)
622 {
623     KSPROPERTY Property;
624     ULONG Length;
625     MIXER_STATUS Status;
626 
627     /* verify mixer context */
628     Status = MMixerVerifyContext(MixerContext);
629 
630     if (Status != MM_STATUS_SUCCESS)
631     {
632         /* invalid context passed */
633         return Status;
634     }
635 
636     /* setup property request */
637     Property.Set = KSPROPSETID_Connection;
638     Property.Id = KSPROPERTY_CONNECTION_STATE;
639     Property.Flags = KSPROPERTY_TYPE_SET;
640 
641     return MixerContext->Control(PinHandle, IOCTL_KS_PROPERTY, &Property, sizeof(KSPROPERTY), &State, sizeof(KSSTATE), &Length);
642 }
643 
644 MIXER_STATUS
645 MMixerSetWaveResetState(
646     IN PMIXER_CONTEXT MixerContext,
647     IN HANDLE PinHandle,
648     IN ULONG bBegin)
649 {
650     ULONG Length;
651     MIXER_STATUS Status;
652     KSRESET Reset;
653 
654     /* verify mixer context */
655     Status = MMixerVerifyContext(MixerContext);
656 
657     if (Status != MM_STATUS_SUCCESS)
658     {
659         /* invalid context passed */
660         return Status;
661     }
662 
663     /* begin / stop reset */
664     Reset = (bBegin ? KSRESET_BEGIN : KSRESET_END);
665 
666     return MixerContext->Control(PinHandle, IOCTL_KS_RESET_STATE, &Reset, sizeof(KSRESET), NULL, 0, &Length);
667 }
668 
669 MIXER_STATUS
670 MMixerGetWaveDevicePath(
671     IN PMIXER_CONTEXT MixerContext,
672     IN ULONG bWaveIn,
673     IN ULONG DeviceId,
674     OUT LPWSTR * DevicePath)
675 {
676     PMIXER_LIST MixerList;
677     LPMIXER_DATA MixerData;
678     LPWAVE_INFO WaveInfo;
679     SIZE_T Length;
680     MIXER_STATUS Status;
681 
682     /* verify mixer context */
683     Status = MMixerVerifyContext(MixerContext);
684 
685     if (Status != MM_STATUS_SUCCESS)
686     {
687         /* invalid context passed */
688         return Status;
689     }
690 
691     /* grab mixer list */
692     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
693 
694     /* find destination wave */
695     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceId, bWaveIn, &WaveInfo);
696     if (Status != MM_STATUS_SUCCESS)
697     {
698         /* failed to find wave info */
699         return MM_STATUS_INVALID_PARAMETER;
700     }
701 
702     /* get associated device id */
703     MixerData = MMixerGetDataByDeviceId(MixerList, WaveInfo->DeviceId);
704     if (!MixerData)
705         return MM_STATUS_INVALID_PARAMETER;
706 
707     /* calculate length */
708     Length = wcslen(MixerData->DeviceName)+1;
709 
710     /* allocate destination buffer */
711     *DevicePath = MixerContext->Alloc(Length * sizeof(WCHAR));
712 
713     if (!*DevicePath)
714     {
715         /* no memory */
716         return MM_STATUS_NO_MEMORY;
717     }
718 
719     /* copy device path */
720     MixerContext->Copy(*DevicePath, MixerData->DeviceName, Length * sizeof(WCHAR));
721 
722     /* done */
723     return MM_STATUS_SUCCESS;
724 }
725