[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 /* gets the corresponding mixer data */
540 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
541
542 /* sanity check */
543 ASSERT(MixerData);
544 ASSERT(MixerData->Topology);
545 ASSERT(MixerData->MixerInfo == MixerInfo);
546
547 /* get logical pin nodes */
548 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
549
550 /* sanity check */
551 ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
552 ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
553
554 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
555 for(Index = 0; Index < ConnectedNodesCount; Index++)
556 {
557 /* getting logical pin offset */
558 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
559
560 if (CurLogicalPinOffset == OldLogicalPinOffset)
561 {
562 /* mark index as active */
563 Values[Index].fValue = TRUE;
564 }
565 else
566 {
567 /* index not active */
568 Values[Index].fValue = FALSE;
569 }
570
571 /* mark offset as consumed */
572 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
573 }
574
575 /* cleanup */
576 MixerContext->Free(LogicalNodes);
577
578 /* done */
579 return MM_STATUS_SUCCESS;
580 }
581 else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
582 {
583 /* sanity check */
584 ASSERT(bSet == FALSE);
585
586 /* gets the corresponding mixer data */
587 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
588
589 /* sanity check */
590 ASSERT(MixerData);
591 ASSERT(MixerData->Topology);
592 ASSERT(MixerData->MixerInfo == MixerInfo);
593
594 /* now allocate logical pin array */
595 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
596 if (Status != MM_STATUS_SUCCESS)
597 {
598 /* no memory */
599 return MM_STATUS_NO_MEMORY;
600 }
601
602 /* allocate connected node array */
603 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
604 if (Status != MM_STATUS_SUCCESS)
605 {
606 /* no memory */
607 MixerContext->Free(LogicalNodes);
608 return MM_STATUS_NO_MEMORY;
609 }
610
611 /* get logical pin nodes */
612 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
613
614 /* get connected nodes */
615 MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
616
617 /* sanity check */
618 ASSERT(ConnectedNodesCount == LogicalNodesCount);
619 ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
620 ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
621
622 ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
623
624 for(Index = 0; Index < ConnectedNodesCount; Index++)
625 {
626 /* getting logical pin offset */
627 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
628
629 /* get mixer line with that node */
630 SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
631
632 /* sanity check */
633 ASSERT(SourceLine);
634
635 DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
636
637 /* copy details */
638 ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
639 ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
640 MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
641
642 /* mark offset as consumed */
643 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
644 }
645
646 /* cleanup */
647 MixerContext->Free(LogicalNodes);
648 MixerContext->Free(ConnectedNodes);
649
650 /* done */
651 return MM_STATUS_SUCCESS;
652 }
653 }
654
655 return MM_STATUS_NOT_IMPLEMENTED;
656 }
657
658 MIXER_STATUS
659 MMixerSetGetVolumeControlDetails(
660 IN PMIXER_CONTEXT MixerContext,
661 IN LPMIXER_INFO MixerInfo,
662 IN ULONG NodeId,
663 IN ULONG bSet,
664 LPMIXERCONTROL_EXT MixerControl,
665 IN LPMIXERCONTROLDETAILS MixerControlDetails,
666 LPMIXERLINE_EXT MixerLine)
667 {
668 LPMIXERCONTROLDETAILS_UNSIGNED Input;
669 LONG Value, Index, Channel = 0;
670 ULONG dwValue;
671 MIXER_STATUS Status;
672 LPMIXERVOLUME_DATA VolumeData;
673
674 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
675 return MM_STATUS_INVALID_PARAMETER;
676
677 VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData;
678 if (!VolumeData)
679 return MM_STATUS_UNSUCCESSFUL;
680
681 /* get input */
682 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
683
684 if (bSet)
685 {
686 /* FIXME SEH */
687 Value = Input->dwValue;
688 Index = Value / VolumeData->InputSteppingDelta;
689
690 if (Index >= VolumeData->ValuesCount)
691 {
692 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
693 return MM_STATUS_INVALID_PARAMETER;
694 }
695
696 Value = VolumeData->Values[Index];
697 }
698
699 /* set control details */
700 if (bSet)
701 {
702 /* TODO */
703 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
704 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
705 }
706 else
707 {
708 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
709 }
710
711 if (!bSet)
712 {
713 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
714 /* FIXME SEH */
715 Input->dwValue = dwValue;
716 }
717 else
718 {
719 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
720 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
721 }
722 return Status;
723 }
724
725 LPMIXER_DATA
726 MMixerGetDataByDeviceId(
727 IN PMIXER_LIST MixerList,
728 IN ULONG DeviceId)
729 {
730 PLIST_ENTRY Entry;
731 LPMIXER_DATA MixerData;
732
733 Entry = MixerList->MixerData.Flink;
734 while(Entry != &MixerList->MixerData)
735 {
736 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
737 if (MixerData->DeviceId == DeviceId)
738 {
739 return MixerData;
740 }
741 Entry = Entry->Flink;
742 }
743 return NULL;
744 }
745
746 LPMIXER_DATA
747 MMixerGetDataByDeviceName(
748 IN PMIXER_LIST MixerList,
749 IN LPWSTR DeviceName)
750 {
751 PLIST_ENTRY Entry;
752 LPMIXER_DATA MixerData;
753
754 Entry = MixerList->MixerData.Flink;
755 while(Entry != &MixerList->MixerData)
756 {
757 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
758 if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
759 {
760 /* found entry */
761 return MixerData;
762 }
763 Entry = Entry->Flink;
764 }
765 return NULL;
766 }
767
768 MIXER_STATUS
769 MMixerCreateMixerData(
770 IN PMIXER_CONTEXT MixerContext,
771 IN PMIXER_LIST MixerList,
772 IN ULONG DeviceId,
773 IN LPWSTR DeviceName,
774 IN HANDLE hDevice,
775 IN HANDLE hKey)
776 {
777 LPMIXER_DATA MixerData;
778
779 MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
780 if (!MixerData)
781 return MM_STATUS_NO_MEMORY;
782
783 MixerData->DeviceId = DeviceId;
784 MixerData->DeviceName = DeviceName;
785 MixerData->hDevice = hDevice;
786 MixerData->hDeviceInterfaceKey = hKey;
787 MixerData->Topology = NULL;
788
789 InsertTailList(&MixerList->MixerData, &MixerData->Entry);
790 MixerList->MixerDataCount++;
791 return MM_STATUS_SUCCESS;
792 }
793
794 MIXER_STATUS
795 MMixerGetDeviceName(
796 IN PMIXER_CONTEXT MixerContext,
797 OUT LPWSTR DeviceName,
798 IN HANDLE hKey)
799 {
800 LPWSTR Name;
801 HANDLE hTemp;
802 ULONG Length;
803 ULONG Type;
804 MIXER_STATUS Status;
805
806 Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
807 if (Status == MM_STATUS_SUCCESS)
808 {
809 /* copy device name */
810 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
811
812 /* make sure its null terminated */
813 DeviceName[MAXPNAMELEN-1] = L'\0';
814
815 /* free device name */
816 MixerContext->Free(Name);
817
818 /* done */
819 return Status;
820 }
821
822 Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
823 if (Status != MM_STATUS_SUCCESS)
824 return Status;
825
826 Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
827 if (Status == MM_STATUS_SUCCESS)
828 {
829 /* copy device name */
830 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
831
832 /* make sure its null terminated */
833 DeviceName[MAXPNAMELEN-1] = L'\0';
834
835 /* free device name */
836 MixerContext->Free(Name);
837 }
838
839 MixerContext->CloseKey(hTemp);
840 return Status;
841 }
842
843 VOID
844 MMixerInitializePinConnect(
845 IN OUT PKSPIN_CONNECT PinConnect,
846 IN ULONG PinId)
847 {
848 PinConnect->Interface.Set = KSINTERFACESETID_Standard;
849 PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
850 PinConnect->Interface.Flags = 0;
851 PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
852 PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
853 PinConnect->Medium.Flags = 0;
854 PinConnect->PinToHandle = NULL;
855 PinConnect->PinId = PinId;
856 PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
857 PinConnect->Priority.PrioritySubClass = 1;
858 }
859