[AUDIO-BRINGUP]
[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 const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
35 const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
36 const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
37
38
39 MIXER_STATUS
40 MMixerVerifyContext(
41 IN PMIXER_CONTEXT MixerContext)
42 {
43 if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT))
44 return MM_STATUS_INVALID_PARAMETER;
45
46 if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open ||
47 !MixerContext->AllocEventData || !MixerContext->FreeEventData ||
48 !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey)
49 return MM_STATUS_INVALID_PARAMETER;
50
51 if (!MixerContext->MixerContext)
52 return MM_STATUS_INVALID_PARAMETER;
53
54 return MM_STATUS_SUCCESS;
55 }
56
57 LPMIXERLINE_EXT
58 MMixerGetMixerLineContainingNodeId(
59 IN LPMIXER_INFO MixerInfo,
60 IN ULONG NodeID)
61 {
62 PLIST_ENTRY Entry, ControlEntry;
63 LPMIXERLINE_EXT MixerLineSrc;
64 LPMIXERCONTROL_EXT MixerControl;
65
66 /* get first entry */
67 Entry = MixerInfo->LineList.Flink;
68
69 while(Entry != &MixerInfo->LineList)
70 {
71 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
72
73 ControlEntry = MixerLineSrc->ControlsList.Flink;
74 while(ControlEntry != &MixerLineSrc->ControlsList)
75 {
76 MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
77 if (MixerControl->NodeID == NodeID)
78 {
79 return MixerLineSrc;
80 }
81 ControlEntry = ControlEntry->Flink;
82 }
83 Entry = Entry->Flink;
84 }
85
86 return NULL;
87 }
88
89 VOID
90 MMixerGetLowestLogicalTopologyPinOffsetFromArray(
91 IN ULONG LogicalPinArrayCount,
92 IN PULONG LogicalPinArray,
93 OUT PULONG PinOffset)
94 {
95 ULONG Index;
96 ULONG LowestId = 0;
97
98 for(Index = 1; Index < LogicalPinArrayCount; Index++)
99 {
100 if (LogicalPinArray[Index] != MAXULONG)
101 {
102 /* sanity check: logical pin id must be unique */
103 ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]);
104 }
105
106 if (LogicalPinArray[Index] < LogicalPinArray[LowestId])
107 LowestId = Index;
108 }
109
110 /* store result */
111 *PinOffset = LowestId;
112 }
113
114 VOID
115 MMixerFreeMixerInfo(
116 IN PMIXER_CONTEXT MixerContext,
117 IN LPMIXER_INFO MixerInfo)
118 {
119 /* UNIMPLEMENTED
120 * FIXME
121 * free all lines
122 */
123
124 MixerContext->Free((PVOID)MixerInfo);
125 }
126
127
128 LPMIXER_DATA
129 MMixerGetMixerDataByDeviceHandle(
130 IN PMIXER_CONTEXT MixerContext,
131 IN HANDLE hDevice)
132 {
133 LPMIXER_DATA MixerData;
134 PLIST_ENTRY Entry;
135 PMIXER_LIST MixerList;
136
137 /* get mixer list */
138 MixerList = (PMIXER_LIST)MixerContext->MixerContext;
139
140 if (!MixerList->MixerDataCount)
141 return NULL;
142
143 Entry = MixerList->MixerData.Flink;
144
145 while(Entry != &MixerList->MixerData)
146 {
147 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
148
149 if (MixerData->hDevice == hDevice)
150 return MixerData;
151
152 /* move to next mixer entry */
153 Entry = Entry->Flink;
154 }
155 return NULL;
156 }
157
158
159 LPMIXER_INFO
160 MMixerGetMixerInfoByIndex(
161 IN PMIXER_CONTEXT MixerContext,
162 IN ULONG MixerIndex)
163 {
164 LPMIXER_INFO MixerInfo;
165 PLIST_ENTRY Entry;
166 PMIXER_LIST MixerList;
167 ULONG Index = 0;
168
169 /* get mixer list */
170 MixerList = (PMIXER_LIST)MixerContext->MixerContext;
171
172 if (!MixerList->MixerListCount)
173 return NULL;
174
175 Entry = MixerList->MixerList.Flink;
176
177 while(Entry != &MixerList->MixerList)
178 {
179 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
180
181 if (Index == MixerIndex)
182 return MixerInfo;
183
184 /* move to next mixer entry */
185 Index++;
186 Entry = Entry->Flink;
187 }
188
189 return NULL;
190 }
191
192 MIXER_STATUS
193 MMixerGetMixerByName(
194 IN PMIXER_LIST MixerList,
195 IN LPWSTR MixerName,
196 OUT LPMIXER_INFO *OutMixerInfo)
197 {
198 LPMIXER_INFO MixerInfo;
199 PLIST_ENTRY Entry;
200
201 Entry = MixerList->MixerList.Flink;
202 while(Entry != &MixerList->MixerList)
203 {
204 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
205
206 DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName);
207 if (wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0)
208 {
209 *OutMixerInfo = MixerInfo;
210 return MM_STATUS_SUCCESS;
211 }
212 /* move to next mixer entry */
213 Entry = Entry->Flink;
214 }
215
216 return MM_STATUS_UNSUCCESSFUL;
217 }
218
219 LPMIXERLINE_EXT
220 MMixerGetSourceMixerLineByLineId(
221 LPMIXER_INFO MixerInfo,
222 DWORD dwLineID)
223 {
224 PLIST_ENTRY Entry;
225 LPMIXERLINE_EXT MixerLineSrc;
226
227 /* get first entry */
228 Entry = MixerInfo->LineList.Flink;
229
230 while(Entry != &MixerInfo->LineList)
231 {
232 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
233 DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc);
234 if (MixerLineSrc->Line.dwLineID == dwLineID)
235 return MixerLineSrc;
236
237 Entry = Entry->Flink;
238 }
239
240 return NULL;
241 }
242
243 LPGUID
244 MMixerGetNodeType(
245 IN PKSMULTIPLE_ITEM MultipleItem,
246 IN ULONG Index)
247 {
248 LPGUID NodeType;
249
250 ASSERT(Index < MultipleItem->Count);
251
252 NodeType = (LPGUID)(MultipleItem + 1);
253 return &NodeType[Index];
254 }
255
256 LPMIXERLINE_EXT
257 MMixerGetSourceMixerLineByComponentType(
258 LPMIXER_INFO MixerInfo,
259 DWORD dwComponentType)
260 {
261 PLIST_ENTRY Entry;
262 LPMIXERLINE_EXT MixerLineSrc;
263
264 /* get first entry */
265 Entry = MixerInfo->LineList.Flink;
266
267 while(Entry != &MixerInfo->LineList)
268 {
269 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
270 if (MixerLineSrc->Line.dwComponentType == dwComponentType)
271 return MixerLineSrc;
272
273 Entry = Entry->Flink;
274 }
275
276 return NULL;
277 }
278
279 MIXER_STATUS
280 MMixerGetMixerControlById(
281 LPMIXER_INFO MixerInfo,
282 DWORD dwControlID,
283 LPMIXERLINE_EXT *OutMixerLine,
284 LPMIXERCONTROL_EXT *OutMixerControl,
285 PULONG NodeId)
286 {
287 PLIST_ENTRY Entry, ControlEntry;
288 LPMIXERLINE_EXT MixerLineSrc;
289 LPMIXERCONTROL_EXT MixerControl;
290
291 /* get first entry */
292 Entry = MixerInfo->LineList.Flink;
293
294 while(Entry != &MixerInfo->LineList)
295 {
296 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
297
298 ControlEntry = MixerLineSrc->ControlsList.Flink;
299 while(ControlEntry != &MixerLineSrc->ControlsList)
300 {
301 MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
302 if (MixerControl->Control.dwControlID == dwControlID)
303 {
304 if (OutMixerLine)
305 *OutMixerLine = MixerLineSrc;
306 if (OutMixerControl)
307 *OutMixerControl = MixerControl;
308 if (NodeId)
309 *NodeId = MixerControl->NodeID;
310 return MM_STATUS_SUCCESS;
311 }
312 ControlEntry = ControlEntry->Flink;
313 }
314 Entry = Entry->Flink;
315 }
316
317 return MM_STATUS_UNSUCCESSFUL;
318 }
319
320 ULONG
321 MMixerGetVolumeControlIndex(
322 LPMIXERVOLUME_DATA VolumeData,
323 LONG Value)
324 {
325 ULONG Index;
326
327 for(Index = 0; Index < VolumeData->ValuesCount; Index++)
328 {
329 if (VolumeData->Values[Index] > Value)
330 {
331 return VolumeData->InputSteppingDelta * Index;
332 }
333 }
334 return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
335 }
336
337 VOID
338 MMixerNotifyControlChange(
339 IN PMIXER_CONTEXT MixerContext,
340 IN LPMIXER_INFO MixerInfo,
341 IN ULONG NotificationType,
342 IN ULONG Value)
343 {
344 PLIST_ENTRY Entry;
345 PEVENT_NOTIFICATION_ENTRY NotificationEntry;
346
347 /* enumerate list and perform notification */
348 Entry = MixerInfo->EventList.Flink;
349 while(Entry != &MixerInfo->EventList)
350 {
351 /* get notification entry offset */
352 NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry);
353
354 if (NotificationEntry->MixerEventRoutine)
355 {
356 /* now perform the callback */
357 NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value);
358 }
359
360 /* move to next notification entry */
361 Entry = Entry->Flink;
362 }
363 }
364
365 MIXER_STATUS
366 MMixerSetGetMuteControlDetails(
367 IN PMIXER_CONTEXT MixerContext,
368 IN LPMIXER_INFO MixerInfo,
369 IN LPMIXERCONTROL_EXT MixerControl,
370 IN ULONG dwLineID,
371 IN LPMIXERCONTROLDETAILS MixerControlDetails,
372 IN ULONG bSet)
373 {
374 LPMIXERCONTROLDETAILS_BOOLEAN Input;
375 LONG Value;
376 MIXER_STATUS Status;
377
378 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
379 return MM_STATUS_INVALID_PARAMETER;
380
381 /* get input */
382 Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
383
384 /* FIXME SEH */
385 if (bSet)
386 Value = Input->fValue;
387
388 /* set control details */
389 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
390
391 if (Status != MM_STATUS_SUCCESS)
392 return Status;
393
394 /* FIXME SEH */
395 if (!bSet)
396 {
397 Input->fValue = Value;
398 return Status;
399 }
400 else
401 {
402 /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */
403 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID);
404 }
405
406 return Status;
407 }
408
409 MIXER_STATUS
410 MMixerSetGetMuxControlDetails(
411 IN PMIXER_CONTEXT MixerContext,
412 IN LPMIXER_INFO MixerInfo,
413 IN ULONG NodeId,
414 IN ULONG bSet,
415 IN ULONG Flags,
416 IN LPMIXERCONTROL_EXT MixerControl,
417 IN LPMIXERCONTROLDETAILS MixerControlDetails,
418 IN LPMIXERLINE_EXT MixerLine)
419 {
420 MIXER_STATUS Status;
421 PULONG LogicalNodes, ConnectedNodes;
422 ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset;
423 LPMIXER_DATA MixerData;
424 LPMIXERCONTROLDETAILS_LISTTEXTW ListText;
425 LPMIXERCONTROLDETAILS_BOOLEAN Values;
426 LPMIXERLINE_EXT SourceLine;
427 KSNODEPROPERTY Request;
428
429 DPRINT("MixerControlDetails %p\n", MixerControlDetails);
430 DPRINT("bSet %lx\n", bSet);
431 DPRINT("Flags %lx\n", Flags);
432 DPRINT("NodeId %lu\n", MixerControl->NodeID);
433 DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID);
434 DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels);
435 DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems);
436 DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails);
437 DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails);
438
439 if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
440 {
441 /* control acts uniform */
442 if (MixerControlDetails->cChannels != 1)
443 {
444 /* expected 1 channel */
445 DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels);
446 return MM_STATUS_UNSUCCESSFUL;
447 }
448 }
449
450 /* check if multiple items match */
451 if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems)
452 {
453 DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems);
454 return MM_STATUS_UNSUCCESSFUL;
455 }
456
457 if (bSet)
458 {
459 if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM)
460 {
461 /* tell me when this is hit */
462 ASSERT(FALSE);
463 }
464 else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE)
465 {
466 /* sanity check */
467 ASSERT(bSet == TRUE);
468 ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN));
469
470 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
471 CurLogicalPinOffset = MAXULONG;
472 for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++)
473 {
474 if (Values[Index].fValue)
475 {
476 /* mux can only activate one line at a time */
477 ASSERT(CurLogicalPinOffset == MAXULONG);
478 CurLogicalPinOffset = Index;
479 }
480 }
481
482 /* setup request */
483 Request.NodeId = NodeId;
484 Request.Reserved = 0;
485 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
486 Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
487 Request.Property.Set = KSPROPSETID_Audio;
488
489 /* perform getting source */
490 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
491 if (Status != MM_STATUS_SUCCESS)
492 {
493 /* failed to get source */
494 return Status;
495 }
496
497 DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset);
498
499 if (OldLogicalPinOffset == CurLogicalPinOffset)
500 {
501 /* cannot be unselected */
502 return MM_STATUS_UNSUCCESSFUL;
503 }
504
505 /* perform setting source */
506 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET;
507 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned);
508 if (Status != MM_STATUS_SUCCESS)
509 {
510 /* failed to set source */
511 return Status;
512 }
513
514 /* notify control change */
515 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID );
516
517 return Status;
518 }
519 }
520 else
521 {
522 if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE)
523 {
524 /* setup request */
525 Request.NodeId = NodeId;
526 Request.Reserved = 0;
527 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
528 Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
529 Request.Property.Set = KSPROPSETID_Audio;
530
531 /* perform getting source */
532 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
533 if (Status != MM_STATUS_SUCCESS)
534 {
535 /* failed to get source */
536 return Status;
537 }
538
539 /* get logical pin nodes */
540 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
541
542 /* sanity check */
543 ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
544 ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
545
546 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
547 for(Index = 0; Index < ConnectedNodesCount; Index++)
548 {
549 /* getting logical pin offset */
550 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
551
552 if (CurLogicalPinOffset == OldLogicalPinOffset)
553 {
554 /* mark index as active */
555 Values[Index].fValue = TRUE;
556 }
557 else
558 {
559 /* index not active */
560 Values[Index].fValue = FALSE;
561 }
562
563 /* mark offset as consumed */
564 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
565 }
566
567 /* cleanup */
568 MixerContext->Free(LogicalNodes);
569
570 /* done */
571 return MM_STATUS_SUCCESS;
572 }
573 else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
574 {
575 /* sanity check */
576 ASSERT(bSet == FALSE);
577
578 /* gets the corresponding mixer data */
579 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
580
581 /* sanity check */
582 ASSERT(MixerData);
583 ASSERT(MixerData->Topology);
584 ASSERT(MixerData->MixerInfo == MixerInfo);
585
586 /* now allocate logical pin array */
587 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
588 if (Status != MM_STATUS_SUCCESS)
589 {
590 /* no memory */
591 return MM_STATUS_NO_MEMORY;
592 }
593
594 /* allocate connected node array */
595 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
596 if (Status != MM_STATUS_SUCCESS)
597 {
598 /* no memory */
599 MixerContext->Free(LogicalNodes);
600 return MM_STATUS_NO_MEMORY;
601 }
602
603 /* get logical pin nodes */
604 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
605
606 /* get connected nodes */
607 MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
608
609 /* sanity check */
610 ASSERT(ConnectedNodesCount == LogicalNodesCount);
611 ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
612 ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
613
614 ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
615
616 for(Index = 0; Index < ConnectedNodesCount; Index++)
617 {
618 /* getting logical pin offset */
619 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
620
621 /* get mixer line with that node */
622 SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
623
624 /* sanity check */
625 ASSERT(SourceLine);
626
627 DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
628
629 /* copy details */
630 ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
631 ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
632 MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
633
634 /* mark offset as consumed */
635 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
636 }
637
638 /* cleanup */
639 MixerContext->Free(LogicalNodes);
640 MixerContext->Free(ConnectedNodes);
641
642 /* done */
643 return MM_STATUS_SUCCESS;
644 }
645 }
646
647 return MM_STATUS_NOT_IMPLEMENTED;
648 }
649
650 MIXER_STATUS
651 MMixerSetGetVolumeControlDetails(
652 IN PMIXER_CONTEXT MixerContext,
653 IN LPMIXER_INFO MixerInfo,
654 IN ULONG NodeId,
655 IN ULONG bSet,
656 LPMIXERCONTROL_EXT MixerControl,
657 IN LPMIXERCONTROLDETAILS MixerControlDetails,
658 LPMIXERLINE_EXT MixerLine)
659 {
660 LPMIXERCONTROLDETAILS_UNSIGNED Input;
661 LONG Value, Index, Channel = 0;
662 ULONG dwValue;
663 MIXER_STATUS Status;
664 LPMIXERVOLUME_DATA VolumeData;
665
666 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
667 return MM_STATUS_INVALID_PARAMETER;
668
669 VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData;
670 if (!VolumeData)
671 return MM_STATUS_UNSUCCESSFUL;
672
673 /* get input */
674 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
675
676 if (bSet)
677 {
678 /* FIXME SEH */
679 Value = Input->dwValue;
680 Index = Value / VolumeData->InputSteppingDelta;
681
682 if (Index >= VolumeData->ValuesCount)
683 {
684 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
685 return MM_STATUS_INVALID_PARAMETER;
686 }
687
688 Value = VolumeData->Values[Index];
689 }
690
691 /* set control details */
692 if (bSet)
693 {
694 /* TODO */
695 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
696 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
697 }
698 else
699 {
700 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
701 }
702
703 if (!bSet)
704 {
705 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
706 /* FIXME SEH */
707 Input->dwValue = dwValue;
708 }
709 else
710 {
711 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
712 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
713 }
714 return Status;
715 }
716
717 LPMIXER_DATA
718 MMixerGetDataByDeviceId(
719 IN PMIXER_LIST MixerList,
720 IN ULONG DeviceId)
721 {
722 PLIST_ENTRY Entry;
723 LPMIXER_DATA MixerData;
724
725 Entry = MixerList->MixerData.Flink;
726 while(Entry != &MixerList->MixerData)
727 {
728 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
729 if (MixerData->DeviceId == DeviceId)
730 {
731 return MixerData;
732 }
733 Entry = Entry->Flink;
734 }
735 return NULL;
736 }
737
738 LPMIXER_DATA
739 MMixerGetDataByDeviceName(
740 IN PMIXER_LIST MixerList,
741 IN LPWSTR DeviceName)
742 {
743 PLIST_ENTRY Entry;
744 LPMIXER_DATA MixerData;
745
746 Entry = MixerList->MixerData.Flink;
747 while(Entry != &MixerList->MixerData)
748 {
749 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
750 if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
751 {
752 /* found entry */
753 return MixerData;
754 }
755 Entry = Entry->Flink;
756 }
757 return NULL;
758 }
759
760 MIXER_STATUS
761 MMixerCreateMixerData(
762 IN PMIXER_CONTEXT MixerContext,
763 IN PMIXER_LIST MixerList,
764 IN ULONG DeviceId,
765 IN LPWSTR DeviceName,
766 IN HANDLE hDevice,
767 IN HANDLE hKey)
768 {
769 LPMIXER_DATA MixerData;
770
771 MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
772 if (!MixerData)
773 return MM_STATUS_NO_MEMORY;
774
775 MixerData->DeviceId = DeviceId;
776 MixerData->DeviceName = DeviceName;
777 MixerData->hDevice = hDevice;
778 MixerData->hDeviceInterfaceKey = hKey;
779 MixerData->Topology = NULL;
780
781 InsertTailList(&MixerList->MixerData, &MixerData->Entry);
782 MixerList->MixerDataCount++;
783 return MM_STATUS_SUCCESS;
784 }
785
786 MIXER_STATUS
787 MMixerGetDeviceName(
788 IN PMIXER_CONTEXT MixerContext,
789 OUT LPWSTR DeviceName,
790 IN HANDLE hKey)
791 {
792 LPWSTR Name;
793 HANDLE hTemp;
794 ULONG Length;
795 ULONG Type;
796 MIXER_STATUS Status;
797
798 Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
799 if (Status == MM_STATUS_SUCCESS)
800 {
801 /* copy device name */
802 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
803
804 /* make sure its null terminated */
805 DeviceName[MAXPNAMELEN-1] = L'\0';
806
807 /* free device name */
808 MixerContext->Free(Name);
809
810 /* done */
811 return Status;
812 }
813
814 Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
815 if (Status != MM_STATUS_SUCCESS)
816 return Status;
817
818 Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
819 if (Status == MM_STATUS_SUCCESS)
820 {
821 /* copy device name */
822 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
823
824 /* make sure its null terminated */
825 DeviceName[MAXPNAMELEN-1] = L'\0';
826
827 /* free device name */
828 MixerContext->Free(Name);
829 }
830
831 MixerContext->CloseKey(hTemp);
832 return Status;
833 }
834
835 VOID
836 MMixerInitializePinConnect(
837 IN OUT PKSPIN_CONNECT PinConnect,
838 IN ULONG PinId)
839 {
840 PinConnect->Interface.Set = KSINTERFACESETID_Standard;
841 PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
842 PinConnect->Interface.Flags = 0;
843 PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
844 PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
845 PinConnect->Medium.Flags = 0;
846 PinConnect->PinToHandle = NULL;
847 PinConnect->PinId = PinId;
848 PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
849 PinConnect->Priority.PrioritySubClass = 1;
850 }
851