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 #include "precomp.h"
10
11 // #define NDEBUG
12 #include <debug.h>
13
14 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
15 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
17 const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
18 const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
19 const GUID KSNODETYPE_MUTE = {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
20 const GUID KSNODETYPE_TONE = {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
21 const GUID KSNODETYPE_VOLUME = {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
22 const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
23 const GUID KSNODETYPE_MUX = {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
24 const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
25 const GUID KSNODETYPE_CHORUS = {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
26 const GUID KSNODETYPE_REVERB = {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
27 const GUID KSNODETYPE_SUPERMIX = {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
28
29 const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
30 const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
31 const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
32 const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
33 const GUID KSEVENTSETID_AudioControlChange = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}};
34
35 const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
36 const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
37 const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
38
39 MIXER_STATUS
MMixerVerifyContext(IN PMIXER_CONTEXT MixerContext)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
MMixerGetMixerLineContainingNodeId(IN LPMIXER_INFO MixerInfo,IN ULONG NodeID)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
MMixerGetLowestLogicalTopologyPinOffsetFromArray(IN ULONG LogicalPinArrayCount,IN PULONG LogicalPinArray,OUT PULONG PinOffset)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
MMixerFreeMixerInfo(IN PMIXER_CONTEXT MixerContext,IN LPMIXER_INFO MixerInfo)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 LPMIXER_DATA
MMixerGetMixerDataByDeviceHandle(IN PMIXER_CONTEXT MixerContext,IN HANDLE hDevice)128 MMixerGetMixerDataByDeviceHandle(
129 IN PMIXER_CONTEXT MixerContext,
130 IN HANDLE hDevice)
131 {
132 LPMIXER_DATA MixerData;
133 PLIST_ENTRY Entry;
134 PMIXER_LIST MixerList;
135
136 /* get mixer list */
137 MixerList = (PMIXER_LIST)MixerContext->MixerContext;
138
139 if (!MixerList->MixerDataCount)
140 return NULL;
141
142 Entry = MixerList->MixerData.Flink;
143
144 while(Entry != &MixerList->MixerData)
145 {
146 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
147
148 if (MixerData->hDevice == hDevice)
149 return MixerData;
150
151 /* move to next mixer entry */
152 Entry = Entry->Flink;
153 }
154 return NULL;
155 }
156
157 LPMIXER_INFO
MMixerGetMixerInfoByIndex(IN PMIXER_CONTEXT MixerContext,IN ULONG MixerIndex)158 MMixerGetMixerInfoByIndex(
159 IN PMIXER_CONTEXT MixerContext,
160 IN ULONG MixerIndex)
161 {
162 LPMIXER_INFO MixerInfo;
163 PLIST_ENTRY Entry;
164 PMIXER_LIST MixerList;
165 ULONG Index = 0;
166
167 /* get mixer list */
168 MixerList = (PMIXER_LIST)MixerContext->MixerContext;
169
170 if (!MixerList->MixerListCount)
171 return NULL;
172
173 Entry = MixerList->MixerList.Flink;
174
175 while(Entry != &MixerList->MixerList)
176 {
177 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
178
179 if (Index == MixerIndex)
180 return MixerInfo;
181
182 /* move to next mixer entry */
183 Index++;
184 Entry = Entry->Flink;
185 }
186
187 return NULL;
188 }
189
190 MIXER_STATUS
MMixerGetMixerByName(IN PMIXER_LIST MixerList,IN LPWSTR MixerName,OUT LPMIXER_INFO * OutMixerInfo)191 MMixerGetMixerByName(
192 IN PMIXER_LIST MixerList,
193 IN LPWSTR MixerName,
194 OUT LPMIXER_INFO *OutMixerInfo)
195 {
196 LPMIXER_INFO MixerInfo;
197 PLIST_ENTRY Entry;
198
199 Entry = MixerList->MixerList.Flink;
200 while(Entry != &MixerList->MixerList)
201 {
202 MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
203
204 DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName);
205 if (_wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0)
206 {
207 *OutMixerInfo = MixerInfo;
208 return MM_STATUS_SUCCESS;
209 }
210 /* move to next mixer entry */
211 Entry = Entry->Flink;
212 }
213
214 return MM_STATUS_UNSUCCESSFUL;
215 }
216
217 LPMIXERLINE_EXT
MMixerGetSourceMixerLineByLineId(LPMIXER_INFO MixerInfo,DWORD dwLineID)218 MMixerGetSourceMixerLineByLineId(
219 LPMIXER_INFO MixerInfo,
220 DWORD dwLineID)
221 {
222 PLIST_ENTRY Entry;
223 LPMIXERLINE_EXT MixerLineSrc;
224
225 /* get first entry */
226 Entry = MixerInfo->LineList.Flink;
227
228 while(Entry != &MixerInfo->LineList)
229 {
230 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
231 DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc);
232 if (MixerLineSrc->Line.dwLineID == dwLineID)
233 return MixerLineSrc;
234
235 Entry = Entry->Flink;
236 }
237
238 return NULL;
239 }
240
241 LPGUID
MMixerGetNodeType(IN PKSMULTIPLE_ITEM MultipleItem,IN ULONG Index)242 MMixerGetNodeType(
243 IN PKSMULTIPLE_ITEM MultipleItem,
244 IN ULONG Index)
245 {
246 LPGUID NodeType;
247
248 ASSERT(Index < MultipleItem->Count);
249
250 NodeType = (LPGUID)(MultipleItem + 1);
251 return &NodeType[Index];
252 }
253
254 LPMIXERLINE_EXT
MMixerGetSourceMixerLineByComponentType(LPMIXER_INFO MixerInfo,DWORD dwComponentType)255 MMixerGetSourceMixerLineByComponentType(
256 LPMIXER_INFO MixerInfo,
257 DWORD dwComponentType)
258 {
259 PLIST_ENTRY Entry;
260 LPMIXERLINE_EXT MixerLineSrc;
261
262 /* get first entry */
263 Entry = MixerInfo->LineList.Flink;
264
265 while(Entry != &MixerInfo->LineList)
266 {
267 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
268 if (MixerLineSrc->Line.dwComponentType == dwComponentType)
269 return MixerLineSrc;
270
271 Entry = Entry->Flink;
272 }
273
274 return NULL;
275 }
276
277 MIXER_STATUS
MMixerGetMixerControlById(LPMIXER_INFO MixerInfo,DWORD dwControlID,LPMIXERLINE_EXT * OutMixerLine,LPMIXERCONTROL_EXT * OutMixerControl,PULONG NodeId)278 MMixerGetMixerControlById(
279 LPMIXER_INFO MixerInfo,
280 DWORD dwControlID,
281 LPMIXERLINE_EXT *OutMixerLine,
282 LPMIXERCONTROL_EXT *OutMixerControl,
283 PULONG NodeId)
284 {
285 PLIST_ENTRY Entry, ControlEntry;
286 LPMIXERLINE_EXT MixerLineSrc;
287 LPMIXERCONTROL_EXT MixerControl;
288
289 /* get first entry */
290 Entry = MixerInfo->LineList.Flink;
291
292 while(Entry != &MixerInfo->LineList)
293 {
294 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
295
296 ControlEntry = MixerLineSrc->ControlsList.Flink;
297 while(ControlEntry != &MixerLineSrc->ControlsList)
298 {
299 MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
300 if (MixerControl->Control.dwControlID == dwControlID)
301 {
302 if (OutMixerLine)
303 *OutMixerLine = MixerLineSrc;
304 if (OutMixerControl)
305 *OutMixerControl = MixerControl;
306 if (NodeId)
307 *NodeId = MixerControl->NodeID;
308 return MM_STATUS_SUCCESS;
309 }
310 ControlEntry = ControlEntry->Flink;
311 }
312 Entry = Entry->Flink;
313 }
314
315 return MM_STATUS_UNSUCCESSFUL;
316 }
317
318 ULONG
MMixerGetVolumeControlIndex(LPMIXERVOLUME_DATA VolumeData,LONG Value)319 MMixerGetVolumeControlIndex(
320 LPMIXERVOLUME_DATA VolumeData,
321 LONG Value)
322 {
323 ULONG Index;
324
325 for(Index = 0; Index < VolumeData->ValuesCount; Index++)
326 {
327 if (VolumeData->Values[Index] > Value)
328 {
329 return VolumeData->InputSteppingDelta * Index;
330 }
331 }
332 return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
333 }
334
335 VOID
MMixerNotifyControlChange(IN PMIXER_CONTEXT MixerContext,IN LPMIXER_INFO MixerInfo,IN ULONG NotificationType,IN ULONG Value)336 MMixerNotifyControlChange(
337 IN PMIXER_CONTEXT MixerContext,
338 IN LPMIXER_INFO MixerInfo,
339 IN ULONG NotificationType,
340 IN ULONG Value)
341 {
342 PLIST_ENTRY Entry;
343 PEVENT_NOTIFICATION_ENTRY NotificationEntry;
344
345 /* enumerate list and perform notification */
346 Entry = MixerInfo->EventList.Flink;
347 while(Entry != &MixerInfo->EventList)
348 {
349 /* get notification entry offset */
350 NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry);
351
352 if (NotificationEntry->MixerEventRoutine)
353 {
354 /* now perform the callback */
355 NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value);
356 }
357
358 /* move to next notification entry */
359 Entry = Entry->Flink;
360 }
361 }
362
363 MIXER_STATUS
MMixerSetGetMuteControlDetails(IN PMIXER_CONTEXT MixerContext,IN LPMIXER_INFO MixerInfo,IN LPMIXERCONTROL_EXT MixerControl,IN ULONG dwLineID,IN LPMIXERCONTROLDETAILS MixerControlDetails,IN ULONG bSet)364 MMixerSetGetMuteControlDetails(
365 IN PMIXER_CONTEXT MixerContext,
366 IN LPMIXER_INFO MixerInfo,
367 IN LPMIXERCONTROL_EXT MixerControl,
368 IN ULONG dwLineID,
369 IN LPMIXERCONTROLDETAILS MixerControlDetails,
370 IN ULONG bSet)
371 {
372 LPMIXERCONTROLDETAILS_BOOLEAN Input;
373 LONG Value;
374 MIXER_STATUS Status;
375
376 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
377 return MM_STATUS_INVALID_PARAMETER;
378
379 /* get input */
380 Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
381
382 /* FIXME SEH */
383 if (bSet)
384 Value = Input->fValue;
385
386 /* set control details */
387 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
388
389 if (Status != MM_STATUS_SUCCESS)
390 return Status;
391
392 /* FIXME SEH */
393 if (!bSet)
394 {
395 Input->fValue = Value;
396 return Status;
397 }
398 else
399 {
400 /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */
401 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID);
402 }
403
404 return Status;
405 }
406
407 MIXER_STATUS
MMixerSetGetMuxControlDetails(IN PMIXER_CONTEXT MixerContext,IN LPMIXER_INFO MixerInfo,IN ULONG NodeId,IN ULONG bSet,IN ULONG Flags,IN LPMIXERCONTROL_EXT MixerControl,IN LPMIXERCONTROLDETAILS MixerControlDetails,IN LPMIXERLINE_EXT MixerLine)408 MMixerSetGetMuxControlDetails(
409 IN PMIXER_CONTEXT MixerContext,
410 IN LPMIXER_INFO MixerInfo,
411 IN ULONG NodeId,
412 IN ULONG bSet,
413 IN ULONG Flags,
414 IN LPMIXERCONTROL_EXT MixerControl,
415 IN LPMIXERCONTROLDETAILS MixerControlDetails,
416 IN LPMIXERLINE_EXT MixerLine)
417 {
418 MIXER_STATUS Status;
419 PULONG LogicalNodes, ConnectedNodes;
420 ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset;
421 LPMIXER_DATA MixerData;
422 LPMIXERCONTROLDETAILS_LISTTEXTW ListText;
423 LPMIXERCONTROLDETAILS_BOOLEAN Values;
424 LPMIXERLINE_EXT SourceLine;
425 KSNODEPROPERTY Request;
426
427 DPRINT("MixerControlDetails %p\n", MixerControlDetails);
428 DPRINT("bSet %lx\n", bSet);
429 DPRINT("Flags %lx\n", Flags);
430 DPRINT("NodeId %lu\n", MixerControl->NodeID);
431 DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID);
432 DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels);
433 DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems);
434 DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails);
435 DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails);
436
437 if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
438 {
439 /* control acts uniform */
440 if (MixerControlDetails->cChannels != 1)
441 {
442 /* expected 1 channel */
443 DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels);
444 return MM_STATUS_UNSUCCESSFUL;
445 }
446 }
447
448 /* check if multiple items match */
449 if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems)
450 {
451 DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems);
452 return MM_STATUS_UNSUCCESSFUL;
453 }
454
455 if (bSet)
456 {
457 if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM)
458 {
459 /* tell me when this is hit */
460 ASSERT(FALSE);
461 }
462 else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE)
463 {
464 /* sanity check */
465 ASSERT(bSet == TRUE);
466 ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN));
467
468 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
469 CurLogicalPinOffset = MAXULONG;
470 for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++)
471 {
472 if (Values[Index].fValue)
473 {
474 /* mux can only activate one line at a time */
475 ASSERT(CurLogicalPinOffset == MAXULONG);
476 CurLogicalPinOffset = Index;
477 }
478 }
479
480 /* setup request */
481 Request.NodeId = NodeId;
482 Request.Reserved = 0;
483 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
484 Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
485 Request.Property.Set = KSPROPSETID_Audio;
486
487 /* perform getting source */
488 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
489 if (Status != MM_STATUS_SUCCESS)
490 {
491 /* failed to get source */
492 return Status;
493 }
494
495 DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset);
496
497 if (OldLogicalPinOffset == CurLogicalPinOffset)
498 {
499 /* cannot be unselected */
500 return MM_STATUS_UNSUCCESSFUL;
501 }
502
503 /* perform setting source */
504 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET;
505 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned);
506 if (Status != MM_STATUS_SUCCESS)
507 {
508 /* failed to set source */
509 return Status;
510 }
511
512 /* notify control change */
513 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID );
514
515 return Status;
516 }
517 }
518 else
519 {
520 if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE)
521 {
522 /* setup request */
523 Request.NodeId = NodeId;
524 Request.Reserved = 0;
525 Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
526 Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
527 Request.Property.Set = KSPROPSETID_Audio;
528
529 /* perform getting source */
530 Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
531 if (Status != MM_STATUS_SUCCESS)
532 {
533 /* failed to get source */
534 return Status;
535 }
536
537 /* gets the corresponding mixer data */
538 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
539
540 /* sanity check */
541 ASSERT(MixerData);
542 ASSERT(MixerData->Topology);
543 ASSERT(MixerData->MixerInfo == MixerInfo);
544
545 /* now allocate logical pin array */
546 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
547 if (Status != MM_STATUS_SUCCESS)
548 {
549 /* no memory */
550 return MM_STATUS_NO_MEMORY;
551 }
552
553 /* get logical pin nodes */
554 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
555
556 /* sanity check */
557 ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
558 ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
559
560 Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
561 for(Index = 0; Index < LogicalNodesCount; Index++)
562 {
563 /* getting logical pin offset */
564 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
565
566 if (CurLogicalPinOffset == OldLogicalPinOffset)
567 {
568 /* mark index as active */
569 Values[Index].fValue = TRUE;
570 }
571 else
572 {
573 /* index not active */
574 Values[Index].fValue = FALSE;
575 }
576
577 /* mark offset as consumed */
578 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
579 }
580
581 /* cleanup */
582 MixerContext->Free(LogicalNodes);
583
584 /* done */
585 return MM_STATUS_SUCCESS;
586 }
587 else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
588 {
589 /* sanity check */
590 ASSERT(bSet == FALSE);
591
592 /* gets the corresponding mixer data */
593 MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
594
595 /* sanity check */
596 ASSERT(MixerData);
597 ASSERT(MixerData->Topology);
598 ASSERT(MixerData->MixerInfo == MixerInfo);
599
600 /* now allocate logical pin array */
601 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
602 if (Status != MM_STATUS_SUCCESS)
603 {
604 /* no memory */
605 return MM_STATUS_NO_MEMORY;
606 }
607
608 /* allocate connected node array */
609 Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
610 if (Status != MM_STATUS_SUCCESS)
611 {
612 /* no memory */
613 MixerContext->Free(LogicalNodes);
614 return MM_STATUS_NO_MEMORY;
615 }
616
617 /* get logical pin nodes */
618 MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
619
620 /* get connected nodes */
621 MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
622
623 /* sanity check */
624 ASSERT(ConnectedNodesCount == LogicalNodesCount);
625 ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
626 ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
627
628 ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
629
630 for(Index = 0; Index < ConnectedNodesCount; Index++)
631 {
632 /* getting logical pin offset */
633 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
634
635 /* get mixer line with that node */
636 SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
637
638 /* sanity check */
639 ASSERT(SourceLine);
640
641 DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
642
643 /* copy details */
644 ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
645 ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
646 MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
647
648 /* mark offset as consumed */
649 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
650 }
651
652 /* cleanup */
653 MixerContext->Free(LogicalNodes);
654 MixerContext->Free(ConnectedNodes);
655
656 /* done */
657 return MM_STATUS_SUCCESS;
658 }
659 }
660
661 return MM_STATUS_NOT_IMPLEMENTED;
662 }
663
664 MIXER_STATUS
MMixerSetGetVolumeControlDetails(IN PMIXER_CONTEXT MixerContext,IN LPMIXER_INFO MixerInfo,IN ULONG NodeId,IN ULONG bSet,LPMIXERCONTROL_EXT MixerControl,IN LPMIXERCONTROLDETAILS MixerControlDetails,LPMIXERLINE_EXT MixerLine)665 MMixerSetGetVolumeControlDetails(
666 IN PMIXER_CONTEXT MixerContext,
667 IN LPMIXER_INFO MixerInfo,
668 IN ULONG NodeId,
669 IN ULONG bSet,
670 LPMIXERCONTROL_EXT MixerControl,
671 IN LPMIXERCONTROLDETAILS MixerControlDetails,
672 LPMIXERLINE_EXT MixerLine)
673 {
674 LPMIXERCONTROLDETAILS_UNSIGNED Input;
675 LONG Value;
676 ULONG Index, Channel = 0;
677 ULONG dwValue;
678 MIXER_STATUS Status;
679 LPMIXERVOLUME_DATA VolumeData;
680
681 if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
682 return MM_STATUS_INVALID_PARAMETER;
683
684 VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData;
685 if (!VolumeData)
686 return MM_STATUS_UNSUCCESSFUL;
687
688 /* get input */
689 Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
690 if (!Input)
691 return MM_STATUS_UNSUCCESSFUL; /* to prevent dereferencing NULL */
692
693 if (bSet)
694 {
695 /* FIXME SEH */
696 Value = Input->dwValue;
697 Index = Value / VolumeData->InputSteppingDelta;
698
699 if (Index >= VolumeData->ValuesCount)
700 {
701 DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
702 return MM_STATUS_INVALID_PARAMETER;
703 }
704
705 Value = VolumeData->Values[Index];
706 }
707
708 /* set control details */
709 if (bSet)
710 {
711 /* TODO */
712 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
713 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
714 }
715 else
716 {
717 Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
718 }
719
720 if (!bSet)
721 {
722 dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
723 /* FIXME SEH */
724 Input->dwValue = dwValue;
725 }
726 else
727 {
728 /* notify clients of a line change MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
729 MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
730 }
731 return Status;
732 }
733
734 LPMIXER_DATA
MMixerGetDataByDeviceId(IN PMIXER_LIST MixerList,IN ULONG DeviceId)735 MMixerGetDataByDeviceId(
736 IN PMIXER_LIST MixerList,
737 IN ULONG DeviceId)
738 {
739 PLIST_ENTRY Entry;
740 LPMIXER_DATA MixerData;
741
742 Entry = MixerList->MixerData.Flink;
743 while(Entry != &MixerList->MixerData)
744 {
745 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
746 if (MixerData->DeviceId == DeviceId)
747 {
748 return MixerData;
749 }
750 Entry = Entry->Flink;
751 }
752 return NULL;
753 }
754
755 LPMIXER_DATA
MMixerGetDataByDeviceName(IN PMIXER_LIST MixerList,IN LPWSTR DeviceName)756 MMixerGetDataByDeviceName(
757 IN PMIXER_LIST MixerList,
758 IN LPWSTR DeviceName)
759 {
760 PLIST_ENTRY Entry;
761 LPMIXER_DATA MixerData;
762
763 Entry = MixerList->MixerData.Flink;
764 while(Entry != &MixerList->MixerData)
765 {
766 MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
767 if (_wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
768 {
769 /* found entry */
770 return MixerData;
771 }
772 Entry = Entry->Flink;
773 }
774 return NULL;
775 }
776
777 MIXER_STATUS
MMixerCreateMixerData(IN PMIXER_CONTEXT MixerContext,IN PMIXER_LIST MixerList,IN ULONG DeviceId,IN LPWSTR DeviceName,IN HANDLE hDevice,IN HANDLE hKey)778 MMixerCreateMixerData(
779 IN PMIXER_CONTEXT MixerContext,
780 IN PMIXER_LIST MixerList,
781 IN ULONG DeviceId,
782 IN LPWSTR DeviceName,
783 IN HANDLE hDevice,
784 IN HANDLE hKey)
785 {
786 LPMIXER_DATA MixerData;
787
788 MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
789 if (!MixerData)
790 return MM_STATUS_NO_MEMORY;
791
792 MixerData->DeviceId = DeviceId;
793 MixerData->DeviceName = DeviceName;
794 MixerData->hDevice = hDevice;
795 MixerData->hDeviceInterfaceKey = hKey;
796 MixerData->Topology = NULL;
797
798 InsertTailList(&MixerList->MixerData, &MixerData->Entry);
799 MixerList->MixerDataCount++;
800 return MM_STATUS_SUCCESS;
801 }
802
803 MIXER_STATUS
MMixerGetDeviceNameWithComponentId(IN PMIXER_CONTEXT MixerContext,IN HANDLE hMixer,OUT LPWSTR OutDeviceName)804 MMixerGetDeviceNameWithComponentId(
805 IN PMIXER_CONTEXT MixerContext,
806 IN HANDLE hMixer,
807 OUT LPWSTR OutDeviceName)
808 {
809 MIXER_STATUS Status;
810 KSPROPERTY Property;
811 KSCOMPONENTID ComponentId;
812 ULONG Length;
813 UNICODE_STRING GuidString;
814 ULONG ResultLength, KeyType;
815 HANDLE hMediaKey, hGuidKey;
816 LPWSTR DeviceName;
817
818 /* prepare property */
819 Property.Flags = KSPROPERTY_TYPE_GET;
820 Property.Set = KSPROPSETID_General;
821 Property.Id = KSPROPERTY_GENERAL_COMPONENTID;
822
823 /* try get component id */
824 Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &ComponentId, sizeof(KSCOMPONENTID), &Length);
825
826 if (Status == MM_STATUS_SUCCESS)
827 {
828 Status = MixerContext->OpenKey(NULL, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories", KEY_READ, &hMediaKey);
829 if (Status == MM_STATUS_SUCCESS)
830 {
831 RtlStringFromGUID(&ComponentId.Name, &GuidString);
832 Status = MixerContext->OpenKey(hMediaKey, GuidString.Buffer, KEY_READ, &hGuidKey);
833 RtlFreeUnicodeString(&GuidString);
834 if (Status == MM_STATUS_SUCCESS)
835 {
836 Status = MixerContext->QueryKeyValue(hGuidKey, L"Name", (PVOID*)&DeviceName, &ResultLength, &KeyType);
837 if (Status == MM_STATUS_SUCCESS)
838 {
839 MixerContext->Copy(OutDeviceName, DeviceName, min(ResultLength, (MAXPNAMELEN-1)*2));
840 MixerContext->Free(DeviceName);
841 }
842
843 MixerContext->CloseKey(hGuidKey);
844 }
845 MixerContext->CloseKey(hMediaKey);
846 }
847 }
848 return Status;
849 }
850
851 MIXER_STATUS
MMixerGetDeviceName(IN PMIXER_CONTEXT MixerContext,OUT LPWSTR DeviceName,IN HANDLE hKey)852 MMixerGetDeviceName(
853 IN PMIXER_CONTEXT MixerContext,
854 OUT LPWSTR DeviceName,
855 IN HANDLE hKey)
856 {
857 LPWSTR Name;
858 HANDLE hTemp;
859 ULONG Length;
860 ULONG Type;
861 MIXER_STATUS Status;
862
863 Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
864 if (Status == MM_STATUS_SUCCESS)
865 {
866 /* copy device name */
867 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
868
869 /* make sure its null terminated */
870 DeviceName[MAXPNAMELEN-1] = L'\0';
871
872 /* free device name */
873 MixerContext->Free(Name);
874
875 /* done */
876 return Status;
877 }
878
879 Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
880 if (Status != MM_STATUS_SUCCESS)
881 return Status;
882
883 Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
884 if (Status == MM_STATUS_SUCCESS)
885 {
886 /* copy device name */
887 MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
888
889 /* make sure its null terminated */
890 DeviceName[MAXPNAMELEN-1] = L'\0';
891
892 /* free device name */
893 MixerContext->Free(Name);
894 }
895
896 MixerContext->CloseKey(hTemp);
897 return Status;
898 }
899
900 VOID
MMixerInitializePinConnect(IN OUT PKSPIN_CONNECT PinConnect,IN ULONG PinId)901 MMixerInitializePinConnect(
902 IN OUT PKSPIN_CONNECT PinConnect,
903 IN ULONG PinId)
904 {
905 PinConnect->Interface.Set = KSINTERFACESETID_Standard;
906 PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
907 PinConnect->Interface.Flags = 0;
908 PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
909 PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
910 PinConnect->Medium.Flags = 0;
911 PinConnect->PinToHandle = NULL;
912 PinConnect->PinId = PinId;
913 PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
914 PinConnect->Priority.PrioritySubClass = 1;
915 }
916