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