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