sync with trunk r47227
[reactos.git] / lib / drivers / sound / mmixer / sup.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: lib/drivers/sound/mmixer/sup.c
5 * PURPOSE: Mixer Support Functions
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9
10
11 #include "priv.h"
12
13 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
14 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
15 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16 const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
17 const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
18 const GUID KSNODETYPE_MUTE = {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
19 const GUID KSNODETYPE_TONE = {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
20 const GUID KSNODETYPE_VOLUME = {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
21 const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
22 const GUID KSNODETYPE_MUX = {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
23 const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
24 const GUID KSNODETYPE_CHORUS = {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
25 const GUID KSNODETYPE_REVERB = {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
26 const GUID KSNODETYPE_SUPERMIX = {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
27
28 const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
29 const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
30 const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
31 const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
32 const GUID KSEVENTSETID_AudioControlChange = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}};
33
34 MIXER_STATUS
35 MMixerVerifyContext(
36 IN PMIXER_CONTEXT MixerContext)
37 {
38 if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT))
39 return MM_STATUS_INVALID_PARAMETER;
40
41 if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open ||
42 !MixerContext->AllocEventData || !MixerContext->FreeEventData ||
43 !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey)
44 return MM_STATUS_INVALID_PARAMETER;
45
46 if (!MixerContext->MixerContext)
47 return MM_STATUS_INVALID_PARAMETER;
48
49 return MM_STATUS_SUCCESS;
50 }
51
52 VOID
53 MMixerFreeMixerInfo(
54 IN PMIXER_CONTEXT MixerContext,
55 IN LPMIXER_INFO MixerInfo)
56 {
57 //UNIMPLEMENTED
58 // FIXME
59 // free all lines
60
61 MixerContext->Free((PVOID)MixerInfo);
62 }
63
64 LPMIXER_INFO
65 MMixerGetMixerInfoByIndex(
66 IN PMIXER_CONTEXT MixerContext,
67 IN ULONG MixerIndex)
68 {
69 LPMIXER_INFO MixerInfo;
70 PLIST_ENTRY Entry;
71 PMIXER_LIST MixerList;
72 ULONG Index = 0;
73
74 // get mixer list
75 MixerList = (PMIXER_LIST)MixerContext->MixerContext;
76
77 if (!MixerList->MixerListCount)
78 return NULL;
79
80 Entry = MixerList->MixerList.Flink;
81
82 while(Entry != &MixerList->MixerList)
83 {
84 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
85
86 if (Index == MixerIndex)
87 return MixerInfo;
88
89 // move to next mixer entry
90 Index++;
91 Entry = Entry->Flink;
92 }
93
94 return NULL;
95 }
96
97 LPMIXERCONTROL_DATA
98 MMixerGetMixerControlDataById(
99 PLIST_ENTRY ListHead,
100 DWORD dwControlId)
101 {
102 PLIST_ENTRY Entry;
103 LPMIXERCONTROL_DATA Control;
104
105 /* get first entry */
106 Entry = ListHead->Flink;
107
108 while(Entry != ListHead)
109 {
110 Control = (LPMIXERCONTROL_DATA)CONTAINING_RECORD(Entry, MIXERCONTROL_DATA, Entry);
111 DPRINT("dwSource %x dwSource %x\n", Control->dwControlID, dwControlId);
112 if (Control->dwControlID == dwControlId)
113 return Control;
114
115 Entry = Entry->Flink;
116 }
117 return NULL;
118 }
119
120 LPMIXERLINE_EXT
121 MMixerGetSourceMixerLineByLineId(
122 LPMIXER_INFO MixerInfo,
123 DWORD dwLineID)
124 {
125 PLIST_ENTRY Entry;
126 LPMIXERLINE_EXT MixerLineSrc;
127
128 /* get first entry */
129 Entry = MixerInfo->LineList.Flink;
130
131 while(Entry != &MixerInfo->LineList)
132 {
133 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
134 DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc);
135 if (MixerLineSrc->Line.dwLineID == dwLineID)
136 return MixerLineSrc;
137
138 Entry = Entry->Flink;
139 }
140
141 return NULL;
142 }
143
144 ULONG
145 MMixerGetIndexOfGuid(
146 PKSMULTIPLE_ITEM MultipleItem,
147 LPCGUID NodeType)
148 {
149 ULONG Index;
150 LPGUID Guid;
151
152 Guid = (LPGUID)(MultipleItem+1);
153
154 /* iterate through node type array */
155 for(Index = 0; Index < MultipleItem->Count; Index++)
156 {
157 if (IsEqualGUIDAligned(NodeType, Guid))
158 {
159 /* found matching guid */
160 return Index;
161 }
162 Guid++;
163 }
164 return MAXULONG;
165 }
166
167 PKSTOPOLOGY_CONNECTION
168 MMixerGetConnectionByIndex(
169 IN PKSMULTIPLE_ITEM MultipleItem,
170 IN ULONG Index)
171 {
172 PKSTOPOLOGY_CONNECTION Descriptor;
173
174 ASSERT(Index < MultipleItem->Count);
175
176 Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
177 return &Descriptor[Index];
178 }
179
180 LPGUID
181 MMixerGetNodeType(
182 IN PKSMULTIPLE_ITEM MultipleItem,
183 IN ULONG Index)
184 {
185 LPGUID NodeType;
186
187 ASSERT(Index < MultipleItem->Count);
188
189 NodeType = (LPGUID)(MultipleItem + 1);
190 return &NodeType[Index];
191 }
192
193 MIXER_STATUS
194 MMixerGetNodeIndexes(
195 IN PMIXER_CONTEXT MixerContext,
196 IN PKSMULTIPLE_ITEM MultipleItem,
197 IN ULONG NodeIndex,
198 IN ULONG bNode,
199 IN ULONG bFrom,
200 OUT PULONG NodeReferenceCount,
201 OUT PULONG *NodeReference)
202 {
203 ULONG Index, Count = 0;
204 PKSTOPOLOGY_CONNECTION Connection;
205 PULONG Refs;
206
207 // KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION
208 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
209
210 // first count all referenced nodes
211 for(Index = 0; Index < MultipleItem->Count; Index++)
212 {
213 if (bNode)
214 {
215 if (bFrom)
216 {
217 if (Connection->FromNode == NodeIndex)
218 {
219 // node id has a connection
220 Count++;
221 }
222 }
223 else
224 {
225 if (Connection->ToNode == NodeIndex)
226 {
227 // node id has a connection
228 Count++;
229 }
230 }
231 }
232 else
233 {
234 if (bFrom)
235 {
236 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
237 {
238 // node id has a connection
239 Count++;
240 }
241 }
242 else
243 {
244 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
245 {
246 // node id has a connection
247 Count++;
248 }
249 }
250 }
251
252
253 // move to next connection
254 Connection++;
255 }
256
257 if (!Count)
258 {
259 *NodeReferenceCount = 0;
260 *NodeReference = NULL;
261 return MM_STATUS_SUCCESS;
262 }
263
264 ASSERT(Count != 0);
265
266 /* now allocate node index array */
267 Refs = (PULONG)MixerContext->Alloc(sizeof(ULONG) * Count);
268 if (!Refs)
269 {
270 // not enough memory
271 return MM_STATUS_NO_MEMORY;
272 }
273
274 Count = 0;
275 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
276 for(Index = 0; Index < MultipleItem->Count; Index++)
277 {
278 if (bNode)
279 {
280 if (bFrom)
281 {
282 if (Connection->FromNode == NodeIndex)
283 {
284 /* node id has a connection */
285 Refs[Count] = Index;
286 Count++;
287 }
288 }
289 else
290 {
291 if (Connection->ToNode == NodeIndex)
292 {
293 /* node id has a connection */
294 Refs[Count] = Index;
295 Count++;
296 }
297 }
298 }
299 else
300 {
301 if (bFrom)
302 {
303 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
304 {
305 /* node id has a connection */
306 Refs[Count] = Index;
307 Count++;
308 }
309 }
310 else
311 {
312 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
313 {
314 /* node id has a connection */
315 Refs[Count] = Index;
316 Count++;
317 }
318 }
319 }
320
321 /* move to next connection */
322 Connection++;
323 }
324
325 /* store result */
326 *NodeReference = Refs;
327 *NodeReferenceCount = Count;
328
329 return MM_STATUS_SUCCESS;
330 }
331
332 MIXER_STATUS
333 MMixerGetTargetPins(
334 IN PMIXER_CONTEXT MixerContext,
335 IN PKSMULTIPLE_ITEM NodeTypes,
336 IN PKSMULTIPLE_ITEM NodeConnections,
337 IN ULONG NodeIndex,
338 IN ULONG bUpDirection,
339 OUT PULONG Pins,
340 IN ULONG PinCount)
341 {
342 ULONG NodeConnectionCount, Index;
343 MIXER_STATUS Status;
344 PULONG NodeConnection;
345
346 // sanity check */
347 ASSERT(NodeIndex != (ULONG)-1);
348
349 /* get all node indexes referenced by that pin */
350 if (bUpDirection)
351 Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
352 else
353 Status = MMixerGetNodeIndexes(MixerContext, NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
354
355 //DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
356
357 if (Status == MM_STATUS_SUCCESS)
358 {
359 for(Index = 0; Index < NodeConnectionCount; Index++)
360 {
361 Status = MMixerGetTargetPinsByNodeConnectionIndex(MixerContext, NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
362 ASSERT(Status == STATUS_SUCCESS);
363 }
364 MixerContext->Free((PVOID)NodeConnection);
365 }
366
367 return Status;
368 }
369
370 LPMIXERLINE_EXT
371 MMixerGetSourceMixerLineByComponentType(
372 LPMIXER_INFO MixerInfo,
373 DWORD dwComponentType)
374 {
375 PLIST_ENTRY Entry;
376 LPMIXERLINE_EXT MixerLineSrc;
377
378 /* get first entry */
379 Entry = MixerInfo->LineList.Flink;
380
381 while(Entry != &MixerInfo->LineList)
382 {
383 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
384 if (MixerLineSrc->Line.dwComponentType == dwComponentType)
385 return MixerLineSrc;
386
387 Entry = Entry->Flink;
388 }
389
390 return NULL;
391 }
392
393 MIXER_STATUS
394 MMixerGetMixerControlById(
395 LPMIXER_INFO MixerInfo,
396 DWORD dwControlID,
397 LPMIXERLINE_EXT *MixerLine,
398 LPMIXERCONTROLW *MixerControl,
399 PULONG NodeId)
400 {
401 PLIST_ENTRY Entry;
402 LPMIXERLINE_EXT MixerLineSrc;
403 ULONG Index;
404
405 /* get first entry */
406 Entry = MixerInfo->LineList.Flink;
407
408 while(Entry != &MixerInfo->LineList)
409 {
410 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
411
412 for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
413 {
414 if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
415 {
416 if (MixerLine)
417 *MixerLine = MixerLineSrc;
418 if (MixerControl)
419 *MixerControl = &MixerLineSrc->LineControls[Index];
420 if (NodeId)
421 *NodeId = MixerLineSrc->NodeIds[Index];
422 return MM_STATUS_SUCCESS;
423 }
424 }
425 Entry = Entry->Flink;
426 }
427
428 return MM_STATUS_UNSUCCESSFUL;
429 }
430
431 ULONG
432 MMixerGetVolumeControlIndex(
433 LPMIXERVOLUME_DATA VolumeData,
434 LONG Value)
435 {
436 ULONG Index;
437
438 for(Index = 0; Index < VolumeData->ValuesCount; Index++)
439 {
440 if (VolumeData->Values[Index] > Value)
441 {
442 return VolumeData->InputSteppingDelta * Index;
443 }
444 }
445 return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
446 }
447
448 MIXER_STATUS
449 MMixerSetGetMuteControlDetails(
450 IN PMIXER_CONTEXT MixerContext,
451 IN HANDLE hMixer,
452 IN ULONG NodeId,
453 IN ULONG dwLineID,
454 IN LPMIXERCONTROLDETAILS MixerControlDetails,
455 IN ULONG bSet)
456 {
457 LPMIXERCONTROLDETAILS_BOOLEAN Input;
458 LONG Value;
459 MIXER_STATUS Status;
460
461 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
462 return MM_STATUS_INVALID_PARAMETER;
463
464 /* get input */
465 Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
466
467 /* FIXME SEH */
468 if (bSet)
469 Value = Input->fValue;
470
471 /* set control details */
472 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
473
474 if (Status != MM_STATUS_SUCCESS)
475 return Status;
476
477 /* FIXME SEH */
478 if (!bSet)
479 {
480 Input->fValue = Value;
481 return Status;
482 }
483 else
484 {
485 // FIXME notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID
486 }
487
488 return Status;
489 }
490
491 MIXER_STATUS
492 MMixerSetGetVolumeControlDetails(
493 IN PMIXER_CONTEXT MixerContext,
494 IN HANDLE hMixer,
495 IN ULONG NodeId,
496 IN ULONG bSet,
497 LPMIXERCONTROLW MixerControl,
498 IN LPMIXERCONTROLDETAILS MixerControlDetails,
499 LPMIXERLINE_EXT MixerLine)
500 {
501 LPMIXERCONTROLDETAILS_UNSIGNED Input;
502 LONG Value, Index, Channel = 0;
503 ULONG dwValue;
504 MIXER_STATUS Status;
505 LPMIXERVOLUME_DATA VolumeData;
506
507 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
508 return MM_STATUS_INVALID_PARAMETER;
509
510 VolumeData = (LPMIXERVOLUME_DATA)MMixerGetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
511 if (!VolumeData)
512 return MM_STATUS_UNSUCCESSFUL;
513
514 /* get input */
515 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
516
517 if (bSet)
518 {
519 /* FIXME SEH */
520 Value = Input->dwValue;
521 Index = Value / VolumeData->InputSteppingDelta;
522
523 if (Index >= VolumeData->ValuesCount)
524 {
525 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
526 DbgBreakPoint();
527 return MM_STATUS_INVALID_PARAMETER;
528 }
529
530 Value = VolumeData->Values[Index];
531 }
532
533 /* set control details */
534 if (bSet)
535 {
536 /* TODO */
537 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
538 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
539 }
540 else
541 {
542 Status = MMixerSetGetControlDetails(MixerContext, hMixer, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
543 }
544
545 if (!bSet)
546 {
547 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
548 /* FIXME SEH */
549 Input->dwValue = dwValue;
550 }
551 else
552 {
553 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
554 }
555 return Status;
556 }
557
558 LPMIXER_DATA
559 MMixerGetDataByDeviceId(
560 IN PMIXER_LIST MixerList,
561 IN ULONG DeviceId)
562 {
563 PLIST_ENTRY Entry;
564 LPMIXER_DATA MixerData;
565
566 Entry = MixerList->MixerData.Flink;
567 while(Entry != &MixerList->MixerData)
568 {
569 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
570 if (MixerData->DeviceId == DeviceId)
571 {
572 return MixerData;
573 }
574 Entry = Entry->Flink;
575 }
576 return NULL;
577 }
578
579 LPMIXER_DATA
580 MMixerGetDataByDeviceName(
581 IN PMIXER_LIST MixerList,
582 IN LPWSTR DeviceName)
583 {
584 PLIST_ENTRY Entry;
585 LPMIXER_DATA MixerData;
586
587 Entry = MixerList->MixerData.Flink;
588 while(Entry != &MixerList->MixerData)
589 {
590 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
591 if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
592 {
593 // found entry
594 return MixerData;
595 }
596 Entry = Entry->Flink;
597 }
598 return NULL;
599 }
600
601 MIXER_STATUS
602 MMixerCreateMixerData(
603 IN PMIXER_CONTEXT MixerContext,
604 IN PMIXER_LIST MixerList,
605 IN ULONG DeviceId,
606 IN LPWSTR DeviceName,
607 IN HANDLE hDevice,
608 IN HANDLE hKey)
609 {
610 LPMIXER_DATA MixerData;
611
612 MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
613 if (!MixerData)
614 return MM_STATUS_NO_MEMORY;
615
616 MixerData->DeviceId = DeviceId;
617 MixerData->DeviceName = DeviceName;
618 MixerData->hDevice = hDevice;
619 MixerData->hDeviceInterfaceKey = hKey;
620
621 InsertTailList(&MixerList->MixerData, &MixerData->Entry);
622 MixerList->MixerDataCount++;
623 return MM_STATUS_SUCCESS;
624 }
625
626 MIXER_STATUS
627 MMixerGetDeviceName(
628 IN PMIXER_CONTEXT MixerContext,
629 IN LPMIXER_INFO MixerInfo,
630 IN HANDLE hKey)
631 {
632 LPWSTR Name;
633 HANDLE hTemp;
634 ULONG Length;
635 ULONG Type;
636 MIXER_STATUS Status;
637
638 Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
639 if (Status == MM_STATUS_SUCCESS)
640 {
641 wcscpy(MixerInfo->MixCaps.szPname, Name);
642 MixerContext->Free(Name);
643 return Status;
644 }
645
646 Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
647 if (Status != MM_STATUS_SUCCESS)
648 return Status;
649
650 Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
651 if (Status == MM_STATUS_SUCCESS)
652 {
653 wcscpy(MixerInfo->MixCaps.szPname, Name);
654 MixerContext->Free(Name);
655 }
656
657 MixerContext->CloseKey(hTemp);
658 return Status;
659 }