1 /* 2 * ReactOS Sound Volume Control 3 * Copyright (C) 2004-2005 Thomas Weidenmueller 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS Sound Volume Control 22 * FILE: base/applications/sndvol32/mixer.c 23 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com> 24 */ 25 26 #include "sndvol32.h" 27 28 #define NO_MIXER_SELECTED ((UINT)(~0)) 29 30 static VOID 31 ClearMixerCache(PSND_MIXER Mixer) 32 { 33 PSND_MIXER_DESTINATION Line, NextLine; 34 PSND_MIXER_CONNECTION Con, NextCon; 35 36 for (Line = Mixer->Lines; Line != NULL; Line = NextLine) 37 { 38 if (Line->Controls != NULL) 39 { 40 HeapFree(GetProcessHeap(), 41 0, 42 Line->Controls); 43 } 44 45 for (Con = Line->Connections; Con != NULL; Con = NextCon) 46 { 47 if (Con->Controls != NULL) 48 { 49 HeapFree(GetProcessHeap(), 50 0, 51 Con->Controls); 52 } 53 54 NextCon = Con->Next; 55 HeapFree(GetProcessHeap(), 56 0, 57 Con); 58 } 59 60 NextLine = Line->Next; 61 HeapFree(GetProcessHeap(), 62 0, 63 Line); 64 } 65 Mixer->Lines = NULL; 66 } 67 68 PSND_MIXER 69 SndMixerCreate(HWND hWndNotification, UINT MixerId) 70 { 71 PSND_MIXER Mixer = (PSND_MIXER) HeapAlloc(GetProcessHeap(), 72 HEAP_ZERO_MEMORY, 73 sizeof(SND_MIXER)); 74 if (Mixer != NULL) 75 { 76 Mixer->hWndNotification = hWndNotification; 77 Mixer->MixersCount = mixerGetNumDevs(); 78 Mixer->MixerId = NO_MIXER_SELECTED; 79 80 if (Mixer->MixersCount > 0) 81 { 82 /* select the first mixer by default */ 83 SndMixerSelect(Mixer, MixerId); 84 } 85 } 86 87 return Mixer; 88 } 89 90 VOID 91 SndMixerDestroy(PSND_MIXER Mixer) 92 { 93 ClearMixerCache(Mixer); 94 SndMixerClose(Mixer); 95 HeapFree(GetProcessHeap(), 96 0, 97 Mixer); 98 } 99 100 VOID 101 SndMixerClose(PSND_MIXER Mixer) 102 { 103 if (Mixer->hmx != NULL) 104 { 105 mixerClose(Mixer->hmx); 106 Mixer->hmx = NULL; 107 Mixer->MixerId = NO_MIXER_SELECTED; 108 } 109 } 110 111 BOOL 112 SndMixerQueryControls(PSND_MIXER Mixer, 113 PUINT DisplayControls, 114 LPMIXERLINE LineInfo, 115 LPMIXERCONTROL *Controls) 116 { 117 if (LineInfo->cControls > 0) 118 { 119 *Controls = (MIXERCONTROL*) HeapAlloc(GetProcessHeap(), 120 HEAP_ZERO_MEMORY, 121 LineInfo->cControls * sizeof(MIXERCONTROL)); 122 if (*Controls != NULL) 123 { 124 MIXERLINECONTROLS LineControls; 125 MMRESULT Result; 126 UINT j; 127 128 LineControls.cbStruct = sizeof(LineControls); 129 LineControls.dwLineID = LineInfo->dwLineID; 130 LineControls.cControls = LineInfo->cControls; 131 LineControls.cbmxctrl = sizeof(MIXERCONTROL); 132 LineControls.pamxctrl = (MIXERCONTROL*)(*Controls); 133 134 Result = mixerGetLineControls((HMIXEROBJ)Mixer->hmx, 135 &LineControls, 136 MIXER_GETLINECONTROLSF_ALL); 137 if (Result == MMSYSERR_NOERROR) 138 { 139 for (j = 0; j < LineControls.cControls; j++) 140 { 141 if (SndMixerIsDisplayControl(Mixer, 142 &(*Controls)[j])) 143 { 144 (*DisplayControls)++; 145 } 146 147 DPRINT("Line control: %ws (0x%x, 0x%x)\n", (*Controls)[j].szName, (*Controls)[j].fdwControl, (*Controls)[j].dwControlType); 148 } 149 150 return TRUE; 151 } 152 else 153 { 154 HeapFree(GetProcessHeap(), 155 0, 156 *Controls); 157 *Controls = NULL; 158 DPRINT("Failed to get line (ID: 0x%x) controls: %d\n", LineInfo->dwLineID, Result); 159 } 160 } 161 else 162 { 163 DPRINT("Failed to allocate memory for %d line (ID: 0x%x) controls!\n", LineInfo->dwLineID, LineInfo->cControls); 164 } 165 166 return FALSE; 167 } 168 else 169 { 170 return TRUE; 171 } 172 } 173 174 static BOOL 175 SndMixerQueryConnections(PSND_MIXER Mixer, 176 PSND_MIXER_DESTINATION Line) 177 { 178 UINT i, DispControls; 179 MIXERLINE LineInfo; 180 MMRESULT Result; 181 BOOL Ret = TRUE; 182 183 LineInfo.cbStruct = sizeof(LineInfo); 184 for (i = Line->Info.cConnections; i > 0; i--) 185 { 186 LineInfo.dwDestination = Line->Info.dwDestination; 187 LineInfo.dwSource = i - 1; 188 Result = mixerGetLineInfo((HMIXEROBJ)Mixer->hmx, 189 &LineInfo, 190 MIXER_GETLINEINFOF_SOURCE); 191 if (Result == MMSYSERR_NOERROR) 192 { 193 LPMIXERCONTROL Controls = NULL; 194 PSND_MIXER_CONNECTION Con; 195 196 DPRINT("++ Source: %ws\n", LineInfo.szName); 197 198 DispControls = 0; 199 200 if (!SndMixerQueryControls(Mixer, 201 &DispControls, 202 &LineInfo, 203 &Controls)) 204 { 205 DPRINT("Failed to query connection controls\n"); 206 Ret = FALSE; 207 break; 208 } 209 210 Con = (SND_MIXER_CONNECTION*) HeapAlloc(GetProcessHeap(), 211 HEAP_ZERO_MEMORY, 212 sizeof(SND_MIXER_CONNECTION)); 213 if (Con != NULL) 214 { 215 Con->Info = LineInfo; 216 Con->Controls = Controls; 217 Con->DisplayControls = DispControls; 218 Con->Next = Line->Connections; 219 Line->Connections = Con; 220 } 221 else 222 { 223 HeapFree(GetProcessHeap(), 224 0, 225 Controls); 226 } 227 } 228 else 229 { 230 DPRINT("Failed to get connection information: %d\n", Result); 231 Ret = FALSE; 232 break; 233 } 234 } 235 236 return Ret; 237 } 238 239 static BOOL 240 SndMixerQueryDestinations(PSND_MIXER Mixer) 241 { 242 UINT i; 243 BOOL Ret = TRUE; 244 245 for (i = Mixer->Caps.cDestinations; i > 0; i--) 246 { 247 PSND_MIXER_DESTINATION Line; 248 249 Line = (SND_MIXER_DESTINATION*) HeapAlloc(GetProcessHeap(), 250 HEAP_ZERO_MEMORY, 251 sizeof(SND_MIXER_DESTINATION)); 252 if (Line != NULL) 253 { 254 Line->Info.cbStruct = sizeof(Line->Info); 255 Line->Info.dwDestination = i - 1; 256 if (mixerGetLineInfo((HMIXEROBJ)Mixer->hmx, 257 &Line->Info, 258 MIXER_GETLINEINFOF_DESTINATION) == MMSYSERR_NOERROR) 259 { 260 DPRINT("+ Destination: %ws (0x%x, %d)\n", Line->Info.szName, Line->Info.dwLineID, Line->Info.dwComponentType); 261 262 if (!SndMixerQueryControls(Mixer, 263 &Line->DisplayControls, 264 &Line->Info, 265 &Line->Controls)) 266 { 267 DPRINT("Failed to query mixer controls!\n"); 268 Ret = FALSE; 269 break; 270 } 271 272 if (!SndMixerQueryConnections(Mixer, Line)) 273 { 274 DPRINT("Failed to query mixer connections!\n"); 275 Ret = FALSE; 276 break; 277 } 278 279 Line->Next = Mixer->Lines; 280 Mixer->Lines = Line; 281 } 282 else 283 { 284 DPRINT("Failed to get line information for id %d!\n", i); 285 HeapFree(GetProcessHeap(), 286 0, 287 Line); 288 Ret = FALSE; 289 break; 290 } 291 } 292 else 293 { 294 DPRINT("Allocation of SND_MIXER_DEST structure for id %d failed!\n", i); 295 Ret = FALSE; 296 break; 297 } 298 } 299 300 return Ret; 301 } 302 303 BOOL 304 SndMixerSelect(PSND_MIXER Mixer, 305 UINT MixerId) 306 { 307 if (MixerId >= Mixer->MixersCount) 308 { 309 return FALSE; 310 } 311 312 SndMixerClose(Mixer); 313 314 if (mixerOpen(&Mixer->hmx, 315 MixerId, 316 (DWORD_PTR)Mixer->hWndNotification, 317 0, 318 CALLBACK_WINDOW | MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR || 319 mixerOpen(&Mixer->hmx, 320 MixerId, 321 (DWORD_PTR)Mixer->hWndNotification, 322 0, 323 CALLBACK_WINDOW) == MMSYSERR_NOERROR || 324 mixerOpen(&Mixer->hmx, 325 MixerId, 326 0, 327 0, 328 0) == MMSYSERR_NOERROR) 329 { 330 if (mixerGetDevCaps(MixerId, 331 &Mixer->Caps, 332 sizeof(Mixer->Caps)) == MMSYSERR_NOERROR) 333 { 334 BOOL Ret = FALSE; 335 336 Mixer->MixerId = MixerId; 337 338 ClearMixerCache(Mixer); 339 340 Ret = SndMixerQueryDestinations(Mixer); 341 342 if (!Ret) 343 { 344 ClearMixerCache(Mixer); 345 } 346 347 return Ret; 348 } 349 else 350 { 351 mixerClose(Mixer->hmx); 352 } 353 } 354 355 Mixer->hmx = NULL; 356 Mixer->MixerId = NO_MIXER_SELECTED; 357 return FALSE; 358 } 359 360 UINT 361 SndMixerGetSelection(PSND_MIXER Mixer) 362 { 363 return Mixer->MixerId; 364 } 365 366 INT 367 SndMixerGetProductName(PSND_MIXER Mixer, 368 LPTSTR lpBuffer, 369 UINT uSize) 370 { 371 if (Mixer->hmx) 372 { 373 UINT lnsz = (UINT) lstrlen(Mixer->Caps.szPname); 374 if(lnsz + 1 > uSize) 375 { 376 return lnsz + 1; 377 } 378 else 379 { 380 memcpy(lpBuffer, Mixer->Caps.szPname, lnsz * sizeof(TCHAR)); 381 lpBuffer[lnsz] = _T('\0'); 382 return lnsz; 383 } 384 } 385 386 return -1; 387 } 388 389 INT 390 SndMixerGetLineName(PSND_MIXER Mixer, 391 DWORD LineID, 392 LPTSTR lpBuffer, 393 UINT uSize, 394 BOOL LongName) 395 { 396 if (Mixer->hmx) 397 { 398 UINT lnsz; 399 PSND_MIXER_DESTINATION Line; 400 LPMIXERLINE lpl = NULL; 401 402 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next) 403 { 404 if (Line->Info.dwLineID == LineID) 405 { 406 lpl = &Line->Info; 407 break; 408 } 409 } 410 411 if (lpl != NULL) 412 { 413 lnsz = (UINT) lstrlen(LongName ? lpl->szName : lpl->szShortName); 414 if(lnsz + 1 > uSize) 415 { 416 return lnsz + 1; 417 } 418 else 419 { 420 memcpy(lpBuffer, LongName ? lpl->szName : lpl->szShortName, lnsz * sizeof(TCHAR)); 421 lpBuffer[lnsz] = _T('\0'); 422 return lnsz; 423 } 424 } 425 } 426 427 return -1; 428 } 429 430 BOOL 431 SndMixerEnumProducts(PSND_MIXER Mixer, 432 PFNSNDMIXENUMPRODUCTS EnumProc, 433 PVOID Context) 434 { 435 MIXERCAPS Caps; 436 HMIXER hMixer; 437 UINT i; 438 BOOL Ret = TRUE; 439 440 for (i = 0; i < Mixer->MixersCount; i++) 441 { 442 if (mixerOpen(&hMixer, 443 i, 444 0, 445 0, 446 0) == MMSYSERR_NOERROR) 447 { 448 if (mixerGetDevCaps(i, 449 &Caps, 450 sizeof(Caps)) == MMSYSERR_NOERROR) 451 { 452 if (!EnumProc(Mixer, 453 i, 454 Caps.szPname, 455 Context)) 456 { 457 mixerClose(hMixer); 458 Ret = FALSE; 459 break; 460 } 461 } 462 else 463 { 464 DPRINT("Failed to get device capabilities for mixer id %d!\n", i); 465 } 466 mixerClose(hMixer); 467 } 468 } 469 470 return Ret; 471 } 472 473 INT 474 SndMixerSetVolumeControlDetails(PSND_MIXER Mixer, DWORD dwControlID, DWORD cChannels, DWORD cbDetails, LPVOID paDetails) 475 { 476 MIXERCONTROLDETAILS MixerDetails; 477 478 if (Mixer->hmx) 479 { 480 MixerDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); 481 MixerDetails.dwControlID = dwControlID; 482 MixerDetails.cChannels = cChannels; 483 MixerDetails.cMultipleItems = 0; 484 MixerDetails.cbDetails = cbDetails; 485 MixerDetails.paDetails = paDetails; 486 487 if (mixerSetControlDetails((HMIXEROBJ)Mixer->hmx, &MixerDetails, MIXER_SETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR) 488 { 489 return 1; 490 } 491 } 492 493 return -1; 494 } 495 496 497 INT 498 SndMixerGetVolumeControlDetails(PSND_MIXER Mixer, DWORD dwControlID, DWORD cChannels, DWORD cbDetails, LPVOID paDetails) 499 { 500 MIXERCONTROLDETAILS MixerDetails; 501 502 if (Mixer->hmx) 503 { 504 MixerDetails.cbStruct = sizeof(MIXERCONTROLDETAILS); 505 MixerDetails.dwControlID = dwControlID; 506 MixerDetails.cChannels = cChannels; 507 MixerDetails.cMultipleItems = 0; 508 MixerDetails.cbDetails = cbDetails; 509 MixerDetails.paDetails = paDetails; 510 511 if (mixerGetControlDetails((HMIXEROBJ)Mixer->hmx, &MixerDetails, MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HMIXER) == MMSYSERR_NOERROR) 512 { 513 return 1; 514 } 515 } 516 return -1; 517 } 518 519 INT 520 SndMixerGetDestinationCount(PSND_MIXER Mixer) 521 { 522 return (Mixer->hmx ? (INT)Mixer->Caps.cDestinations : -1); 523 } 524 525 BOOL 526 SndMixerEnumLines(PSND_MIXER Mixer, 527 PFNSNDMIXENUMLINES EnumProc, 528 PVOID Context) 529 { 530 if (Mixer->hmx) 531 { 532 PSND_MIXER_DESTINATION Line; 533 534 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next) 535 { 536 if (!EnumProc(Mixer, 537 &Line->Info, 538 Line->DisplayControls, 539 Context)) 540 { 541 return FALSE; 542 } 543 } 544 545 return TRUE; 546 } 547 548 return FALSE; 549 } 550 551 BOOL 552 SndMixerEnumConnections(PSND_MIXER Mixer, 553 DWORD LineID, 554 PFNSNDMIXENUMCONNECTIONS EnumProc, 555 PVOID Context) 556 { 557 if (Mixer->hmx) 558 { 559 PSND_MIXER_DESTINATION Line; 560 561 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next) 562 { 563 if (Line->Info.dwLineID == LineID) 564 { 565 PSND_MIXER_CONNECTION Connection; 566 567 if (Line->DisplayControls != 0) 568 { 569 if (!EnumProc(Mixer, 570 LineID, 571 &Line->Info, 572 Context)) 573 { 574 return FALSE; 575 } 576 } 577 578 for (Connection = Line->Connections; Connection != NULL; Connection = Connection->Next) 579 { 580 if (!EnumProc(Mixer, 581 LineID, 582 &Connection->Info, 583 Context)) 584 { 585 return FALSE; 586 } 587 } 588 589 return TRUE; 590 } 591 } 592 } 593 594 return FALSE; 595 } 596 597 BOOL 598 SndMixerIsDisplayControl(PSND_MIXER Mixer, 599 LPMIXERCONTROL Control) 600 { 601 if (Mixer->hmx && !(Control->fdwControl & MIXERCONTROL_CONTROLF_DISABLED)) 602 { 603 switch (Control->dwControlType & MIXERCONTROL_CT_CLASS_MASK) 604 { 605 case MIXERCONTROL_CT_CLASS_FADER: 606 case MIXERCONTROL_CT_CLASS_SWITCH: 607 return TRUE; 608 } 609 } 610 611 return FALSE; 612 } 613 614 LPMIXERLINE 615 SndMixerGetLineByName(PSND_MIXER Mixer, 616 DWORD LineID, 617 LPWSTR LineName) 618 { 619 PSND_MIXER_DESTINATION Line; 620 PSND_MIXER_CONNECTION Connection; 621 622 if (Mixer->hmx == 0) 623 return NULL; 624 625 for (Line = Mixer->Lines; Line != NULL; Line = Line->Next) 626 { 627 if (Line->Info.dwLineID == LineID) 628 { 629 if (Line->DisplayControls != 0) 630 { 631 if (wcsicmp(Line->Info.szName, LineName) == 0) 632 { 633 return &Line->Info; 634 } 635 } 636 637 for (Connection = Line->Connections; Connection != NULL; Connection = Connection->Next) 638 { 639 if (wcsicmp(Connection->Info.szName, LineName) == 0) 640 { 641 return &Connection->Info; 642 } 643 } 644 645 return NULL; 646 } 647 } 648 649 return NULL; 650 }