xref: /reactos/sdk/lib/drivers/sound/mmixer/wave.c (revision c2c66aff)
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 YDEBUG
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 
115 
116 
117 VOID
118 MMixerInitializeDataFormat(
119     IN PKSDATAFORMAT_WAVEFORMATEX DataFormat,
120     LPWAVEFORMATEX WaveFormatEx)
121 {
122 
123     DataFormat->WaveFormatEx.wFormatTag = WaveFormatEx->wFormatTag;
124     DataFormat->WaveFormatEx.nChannels = WaveFormatEx->nChannels;
125     DataFormat->WaveFormatEx.nSamplesPerSec = WaveFormatEx->nSamplesPerSec;
126     DataFormat->WaveFormatEx.nBlockAlign = WaveFormatEx->nBlockAlign;
127     DataFormat->WaveFormatEx.nAvgBytesPerSec = WaveFormatEx->nAvgBytesPerSec;
128     DataFormat->WaveFormatEx.wBitsPerSample = WaveFormatEx->wBitsPerSample;
129     DataFormat->WaveFormatEx.cbSize = 0;
130     DataFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX);
131     DataFormat->DataFormat.Flags = 0;
132     DataFormat->DataFormat.Reserved = 0;
133     DataFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
134 
135     DataFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
136     DataFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
137     DataFormat->DataFormat.SampleSize = 4;
138 }
139 
140 
141 MIXER_STATUS
142 MMixerGetAudioPinDataRanges(
143     IN PMIXER_CONTEXT MixerContext,
144     IN HANDLE hDevice,
145     IN ULONG PinId,
146     IN OUT PKSMULTIPLE_ITEM * OutMultipleItem)
147 {
148     KSP_PIN PinProperty;
149     ULONG BytesReturned = 0;
150     MIXER_STATUS Status;
151     PKSMULTIPLE_ITEM MultipleItem;
152 
153     /* retrieve size of data ranges buffer */
154     PinProperty.Reserved = 0;
155     PinProperty.PinId = PinId;
156     PinProperty.Property.Set = KSPROPSETID_Pin;
157     PinProperty.Property.Id = KSPROPERTY_PIN_DATARANGES;
158     PinProperty.Property.Flags = KSPROPERTY_TYPE_GET;
159 
160     Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)NULL, 0, &BytesReturned);
161     if (Status != MM_STATUS_MORE_ENTRIES)
162     {
163         return Status;
164     }
165 
166     MultipleItem = MixerContext->Alloc(BytesReturned);
167     if (!MultipleItem)
168     {
169         /* not enough memory */
170         return MM_STATUS_NO_MEMORY;
171     }
172 
173     Status = MixerContext->Control(hDevice, IOCTL_KS_PROPERTY, (PVOID)&PinProperty, sizeof(KSP_PIN), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
174     if (Status != MM_STATUS_SUCCESS)
175     {
176         /* failed */
177         MixerContext->Free(MultipleItem);
178         return Status;
179     }
180 
181     /* save result */
182     *OutMultipleItem = MultipleItem;
183     return Status;
184 }
185 
186 MIXER_STATUS
187 MMixerFindAudioDataRange(
188     PKSMULTIPLE_ITEM MultipleItem,
189     PKSDATARANGE_AUDIO * OutDataRangeAudio)
190 {
191     ULONG Index;
192     PKSDATARANGE_AUDIO DataRangeAudio;
193     PKSDATARANGE DataRange;
194 
195     DataRange = (PKSDATARANGE) (MultipleItem + 1);
196     for(Index = 0; Index < MultipleItem->Count; Index++)
197     {
198         if (DataRange->FormatSize == sizeof(KSDATARANGE_AUDIO))
199         {
200             DataRangeAudio = (PKSDATARANGE_AUDIO)DataRange;
201             if (IsEqualGUIDAligned(&DataRangeAudio->DataRange.MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) &&
202                 IsEqualGUIDAligned(&DataRangeAudio->DataRange.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) &&
203                 IsEqualGUIDAligned(&DataRangeAudio->DataRange.Specifier, &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
204             {
205                 DPRINT("Min Sample %u Max Sample %u Min Bits %u Max Bits %u Max Channel %u\n", DataRangeAudio->MinimumSampleFrequency, DataRangeAudio->MaximumSampleFrequency,
206                                                          DataRangeAudio->MinimumBitsPerSample, DataRangeAudio->MaximumBitsPerSample, DataRangeAudio->MaximumChannels);
207                 *OutDataRangeAudio = DataRangeAudio;
208                 return MM_STATUS_SUCCESS;
209             }
210         }
211         DataRange = (PKSDATARANGE)((ULONG_PTR)DataRange + DataRange->FormatSize);
212     }
213     return MM_STATUS_UNSUCCESSFUL;
214 }
215 
216 MIXER_STATUS
217 MMixerOpenWavePin(
218     IN PMIXER_CONTEXT MixerContext,
219     IN PMIXER_LIST MixerList,
220     IN ULONG DeviceId,
221     IN ULONG PinId,
222     IN LPWAVEFORMATEX WaveFormatEx,
223     IN ACCESS_MASK DesiredAccess,
224     IN PIN_CREATE_CALLBACK CreateCallback,
225     IN PVOID Context,
226     OUT PHANDLE PinHandle)
227 {
228     PKSPIN_CONNECT PinConnect;
229     PKSDATAFORMAT_WAVEFORMATEX DataFormat;
230     LPMIXER_DATA MixerData;
231     NTSTATUS Status;
232     MIXER_STATUS MixerStatus;
233 
234     MixerData = MMixerGetDataByDeviceId(MixerList, DeviceId);
235     if (!MixerData)
236         return MM_STATUS_INVALID_PARAMETER;
237 
238     /* allocate pin connect */
239     PinConnect = MMixerAllocatePinConnect(MixerContext, sizeof(KSDATAFORMAT_WAVEFORMATEX));
240     if (!PinConnect)
241     {
242         /* no memory */
243         return MM_STATUS_NO_MEMORY;
244     }
245 
246     /* initialize pin connect struct */
247     MMixerInitializePinConnect(PinConnect, PinId);
248 
249     /* get offset to dataformat */
250     DataFormat = (PKSDATAFORMAT_WAVEFORMATEX) (PinConnect + 1);
251     /* initialize with requested wave format */
252     MMixerInitializeDataFormat(DataFormat, WaveFormatEx);
253 
254     if (CreateCallback)
255     {
256         /* let the callback handle the creation */
257         MixerStatus = CreateCallback(Context, DeviceId, PinId, MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
258     }
259     else
260     {
261         /* now create the pin */
262         Status = KsCreatePin(MixerData->hDevice, PinConnect, DesiredAccess, PinHandle);
263 
264         /* normalize status */
265         if (Status == STATUS_SUCCESS)
266             MixerStatus = MM_STATUS_SUCCESS;
267         else
268             MixerStatus = MM_STATUS_UNSUCCESSFUL;
269     }
270 
271     /* free create info */
272     MixerContext->Free(PinConnect);
273 
274     /* done */
275     return MixerStatus;
276 }
277 
278 VOID
279 MMixerCheckFormat(
280     IN PKSDATARANGE_AUDIO DataRangeAudio,
281     IN LPWAVE_INFO WaveInfo,
282     IN ULONG bInput)
283 {
284     ULONG Index, SampleFrequency;
285     ULONG Result = 0;
286 
287     for(Index = 0; Index < AUDIO_TEST_RANGE; Index++)
288     {
289         SampleFrequency = TestRange[Index].SampleRate;
290 
291         if (DataRangeAudio->MinimumSampleFrequency <= SampleFrequency && DataRangeAudio->MaximumSampleFrequency >= SampleFrequency)
292         {
293             /* the audio adapter supports the sample frequency */
294             if (DataRangeAudio->MinimumBitsPerSample <= 8 && DataRangeAudio->MaximumBitsPerSample >= 8)
295             {
296                 Result |= TestRange[Index].Bit8Mono;
297 
298                 if (DataRangeAudio->MaximumChannels > 1)
299                 {
300                     /* check if pin supports the sample rate in 8-Bit Stereo */
301                     Result |= TestRange[Index].Bit8Stereo;
302                 }
303             }
304 
305             if (DataRangeAudio->MinimumBitsPerSample <= 16 && DataRangeAudio->MaximumBitsPerSample >= 16)
306             {
307                 /* check if pin supports the sample rate in 16-Bit Mono */
308                 Result |= TestRange[Index].Bit16Mono;
309 
310 
311                 if (DataRangeAudio->MaximumChannels > 1)
312                 {
313                     /* check if pin supports the sample rate in 16-Bit Stereo */
314                     Result |= TestRange[Index].Bit16Stereo;
315                 }
316             }
317         }
318     }
319 
320 
321     if (bInput)
322         WaveInfo->u.InCaps.dwFormats = Result;
323     else
324         WaveInfo->u.OutCaps.dwFormats = Result;
325 
326     DPRINT("Format %lx bInput %u\n", Result, bInput);
327 }
328 
329 MIXER_STATUS
330 MMixerInitializeWaveInfo(
331     IN PMIXER_CONTEXT MixerContext,
332     IN PMIXER_LIST MixerList,
333     IN LPMIXER_DATA MixerData,
334     IN LPWSTR DeviceName,
335     IN ULONG bWaveIn,
336     IN ULONG PinCount,
337     IN PULONG Pins)
338 {
339     MIXER_STATUS Status;
340     PKSMULTIPLE_ITEM MultipleItem;
341     PKSDATARANGE_AUDIO DataRangeAudio;
342     LPWAVE_INFO WaveInfo;
343 
344     WaveInfo = (LPWAVE_INFO)MixerContext->Alloc(sizeof(WAVE_INFO));
345     if (!WaveInfo)
346         return MM_STATUS_NO_MEMORY;
347 
348     if (PinCount > 1)
349     {
350         /* FIXME support multiple pins for wave device */
351         DPRINT1("Implement support for multiple pins\n");
352         //ASSERT(PinCount == 1);
353     }
354 
355     /* initialize wave info */
356     WaveInfo->DeviceId = MixerData->DeviceId;
357     WaveInfo->PinId = Pins[0];
358 
359     /* sanity check */
360     ASSERT(wcslen(DeviceName) + 1 < MAXPNAMELEN);
361 
362     /* copy device name */
363     if (bWaveIn)
364     {
365         wcscpy(WaveInfo->u.InCaps.szPname, DeviceName);
366     }
367     else
368     {
369         wcscpy(WaveInfo->u.OutCaps.szPname, DeviceName);
370     }
371 
372     /* FIXME determine manufacturer / product id */
373     if (bWaveIn)
374     {
375         WaveInfo->u.InCaps.wMid = MM_MICROSOFT;
376         WaveInfo->u.InCaps.wPid = MM_PID_UNMAPPED;
377         WaveInfo->u.InCaps.vDriverVersion = 1;
378     }
379     else
380     {
381         WaveInfo->u.OutCaps.wMid = MM_MICROSOFT;
382         WaveInfo->u.OutCaps.wPid = MM_PID_UNMAPPED;
383         WaveInfo->u.OutCaps.vDriverVersion = 1;
384     }
385 
386     /* get audio pin data ranges */
387     Status = MMixerGetAudioPinDataRanges(MixerContext, MixerData->hDevice, Pins[0], &MultipleItem);
388     if (Status != MM_STATUS_SUCCESS)
389     {
390         /* failed to get audio pin data ranges */
391         MixerContext->Free(WaveInfo);
392         return MM_STATUS_UNSUCCESSFUL;
393     }
394 
395     /* find an KSDATARANGE_AUDIO range */
396     Status = MMixerFindAudioDataRange(MultipleItem, &DataRangeAudio);
397     if (Status != MM_STATUS_SUCCESS)
398     {
399         /* failed to find audio pin data range */
400         MixerContext->Free(MultipleItem);
401         MixerContext->Free(WaveInfo);
402         return MM_STATUS_UNSUCCESSFUL;
403     }
404 
405     /* store channel count */
406     if (bWaveIn)
407     {
408         WaveInfo->u.InCaps.wChannels = DataRangeAudio->MaximumChannels;
409     }
410     else
411     {
412        WaveInfo->u.OutCaps.wChannels = DataRangeAudio->MaximumChannels;
413     }
414 
415     /* get all supported formats */
416     MMixerCheckFormat(DataRangeAudio, WaveInfo, bWaveIn);
417 
418     /* free dataranges buffer */
419     MixerContext->Free(MultipleItem);
420 
421     if (bWaveIn)
422     {
423         InsertTailList(&MixerList->WaveInList, &WaveInfo->Entry);
424         MixerList->WaveInListCount++;
425     }
426     else
427     {
428         InsertTailList(&MixerList->WaveOutList, &WaveInfo->Entry);
429         MixerList->WaveOutListCount++;
430     }
431 
432     return MM_STATUS_SUCCESS;
433 }
434 
435 MIXER_STATUS
436 MMixerOpenWave(
437     IN PMIXER_CONTEXT MixerContext,
438     IN ULONG DeviceIndex,
439     IN ULONG bWaveIn,
440     IN LPWAVEFORMATEX WaveFormat,
441     IN PIN_CREATE_CALLBACK CreateCallback,
442     IN PVOID Context,
443     OUT PHANDLE PinHandle)
444 {
445     PMIXER_LIST MixerList;
446     MIXER_STATUS Status;
447     LPWAVE_INFO WaveInfo;
448     ACCESS_MASK DesiredAccess = 0;
449 
450     /* verify mixer context */
451     Status = MMixerVerifyContext(MixerContext);
452 
453     if (Status != MM_STATUS_SUCCESS)
454     {
455         /* invalid context passed */
456         return Status;
457     }
458 
459     /* grab mixer list */
460     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
461 
462     if (WaveFormat->wFormatTag != WAVE_FORMAT_PCM)
463     {
464         /* not implemented */
465         return MM_STATUS_NOT_IMPLEMENTED;
466     }
467 
468     /* find destination wave */
469     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, bWaveIn, &WaveInfo);
470     if (Status != MM_STATUS_SUCCESS)
471     {
472         /* failed to find wave info */
473         return MM_STATUS_INVALID_PARAMETER;
474     }
475 
476     /* get desired access */
477     if (bWaveIn)
478     {
479         DesiredAccess |= GENERIC_READ;
480     }
481      else
482     {
483         DesiredAccess |= GENERIC_WRITE;
484     }
485 
486     /* now try open the pin */
487     return MMixerOpenWavePin(MixerContext, MixerList, WaveInfo->DeviceId, WaveInfo->PinId, WaveFormat, DesiredAccess, CreateCallback, Context, PinHandle);
488 }
489 
490 MIXER_STATUS
491 MMixerWaveInCapabilities(
492     IN PMIXER_CONTEXT MixerContext,
493     IN ULONG DeviceIndex,
494     OUT LPWAVEINCAPSW Caps)
495 {
496     PMIXER_LIST MixerList;
497     MIXER_STATUS Status;
498     LPWAVE_INFO WaveInfo;
499 
500     /* verify mixer context */
501     Status = MMixerVerifyContext(MixerContext);
502 
503     if (Status != MM_STATUS_SUCCESS)
504     {
505         /* invalid context passed */
506         return Status;
507     }
508 
509     /* grab mixer list */
510     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
511 
512     /* find destination wave */
513     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, TRUE, &WaveInfo);
514     if (Status != MM_STATUS_SUCCESS)
515     {
516         /* failed to find wave info */
517         return MM_STATUS_UNSUCCESSFUL;
518     }
519 
520     /* copy capabilities */
521     MixerContext->Copy(Caps, &WaveInfo->u.InCaps, sizeof(WAVEINCAPSW));
522 
523     return MM_STATUS_SUCCESS;
524 }
525 
526 MIXER_STATUS
527 MMixerWaveOutCapabilities(
528     IN PMIXER_CONTEXT MixerContext,
529     IN ULONG DeviceIndex,
530     OUT LPWAVEOUTCAPSW Caps)
531 {
532     PMIXER_LIST MixerList;
533     MIXER_STATUS Status;
534     LPWAVE_INFO WaveInfo;
535 
536     /* verify mixer context */
537     Status = MMixerVerifyContext(MixerContext);
538 
539     if (Status != MM_STATUS_SUCCESS)
540     {
541         /* invalid context passed */
542         return Status;
543     }
544 
545     /* grab mixer list */
546     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
547 
548     /* find destination wave */
549     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceIndex, FALSE, &WaveInfo);
550     if (Status != MM_STATUS_SUCCESS)
551     {
552         /* failed to find wave info */
553         return MM_STATUS_UNSUCCESSFUL;
554     }
555 
556     /* copy capabilities */
557     MixerContext->Copy(Caps, &WaveInfo->u.OutCaps, sizeof(WAVEOUTCAPSW));
558 
559     return MM_STATUS_SUCCESS;
560 }
561 
562 ULONG
563 MMixerGetWaveInCount(
564     IN PMIXER_CONTEXT MixerContext)
565 {
566     PMIXER_LIST MixerList;
567     MIXER_STATUS Status;
568 
569      /* verify mixer context */
570     Status = MMixerVerifyContext(MixerContext);
571 
572     if (Status != MM_STATUS_SUCCESS)
573     {
574         /* invalid context passed */
575         return Status;
576     }
577 
578     /* grab mixer list */
579     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
580 
581     return MixerList->WaveInListCount;
582 }
583 
584 ULONG
585 MMixerGetWaveOutCount(
586     IN PMIXER_CONTEXT MixerContext)
587 {
588     PMIXER_LIST MixerList;
589     MIXER_STATUS Status;
590 
591     /* verify mixer context */
592     Status = MMixerVerifyContext(MixerContext);
593 
594     if (Status != MM_STATUS_SUCCESS)
595     {
596         /* invalid context passed */
597         return Status;
598     }
599 
600     /* grab mixer list */
601     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
602 
603     return MixerList->WaveOutListCount;
604 }
605 
606 MIXER_STATUS
607 MMixerSetWaveStatus(
608     IN PMIXER_CONTEXT MixerContext,
609     IN HANDLE PinHandle,
610     IN KSSTATE State)
611 {
612     KSPROPERTY Property;
613     ULONG Length;
614     MIXER_STATUS Status;
615 
616     /* verify mixer context */
617     Status = MMixerVerifyContext(MixerContext);
618 
619     if (Status != MM_STATUS_SUCCESS)
620     {
621         /* invalid context passed */
622         return Status;
623     }
624 
625     /* setup property request */
626     Property.Set = KSPROPSETID_Connection;
627     Property.Id = KSPROPERTY_CONNECTION_STATE;
628     Property.Flags = KSPROPERTY_TYPE_SET;
629 
630     return MixerContext->Control(PinHandle, IOCTL_KS_PROPERTY, &Property, sizeof(KSPROPERTY), &State, sizeof(KSSTATE), &Length);
631 }
632 
633 MIXER_STATUS
634 MMixerSetWaveResetState(
635     IN PMIXER_CONTEXT MixerContext,
636     IN HANDLE PinHandle,
637     IN ULONG bBegin)
638 {
639     ULONG Length;
640     MIXER_STATUS Status;
641     KSRESET Reset;
642 
643     /* verify mixer context */
644     Status = MMixerVerifyContext(MixerContext);
645 
646     if (Status != MM_STATUS_SUCCESS)
647     {
648         /* invalid context passed */
649         return Status;
650     }
651 
652     /* begin / stop reset */
653     Reset = (bBegin ? KSRESET_BEGIN : KSRESET_END);
654 
655     return MixerContext->Control(PinHandle, IOCTL_KS_RESET_STATE, &Reset, sizeof(KSRESET), NULL, 0, &Length);
656 }
657 
658 MIXER_STATUS
659 MMixerGetWaveDevicePath(
660     IN PMIXER_CONTEXT MixerContext,
661     IN ULONG bWaveIn,
662     IN ULONG DeviceId,
663     OUT LPWSTR * DevicePath)
664 {
665     PMIXER_LIST MixerList;
666     LPMIXER_DATA MixerData;
667     LPWAVE_INFO WaveInfo;
668     ULONG Length;
669     MIXER_STATUS Status;
670 
671     /* verify mixer context */
672     Status = MMixerVerifyContext(MixerContext);
673 
674     if (Status != MM_STATUS_SUCCESS)
675     {
676         /* invalid context passed */
677         return Status;
678     }
679 
680     /* grab mixer list */
681     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
682 
683     /* find destination wave */
684     Status = MMixerGetWaveInfoByIndexAndType(MixerList, DeviceId, bWaveIn, &WaveInfo);
685     if (Status != MM_STATUS_SUCCESS)
686     {
687         /* failed to find wave info */
688         return MM_STATUS_INVALID_PARAMETER;
689     }
690 
691     /* get associated device id */
692     MixerData = MMixerGetDataByDeviceId(MixerList, WaveInfo->DeviceId);
693     if (!MixerData)
694         return MM_STATUS_INVALID_PARAMETER;
695 
696     /* calculate length */
697     Length = wcslen(MixerData->DeviceName)+1;
698 
699     /* allocate destination buffer */
700     *DevicePath = MixerContext->Alloc(Length * sizeof(WCHAR));
701 
702     if (!*DevicePath)
703     {
704         /* no memory */
705         return MM_STATUS_NO_MEMORY;
706     }
707 
708     /* copy device path */
709     MixerContext->Copy(*DevicePath, MixerData->DeviceName, Length * sizeof(WCHAR));
710 
711     /* done */
712     return MM_STATUS_SUCCESS;
713 }
714