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