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 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 LPMIXER_DATA 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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