Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1783 lines
44 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Module: virtual.c
  4. //
  5. // Description:
  6. // Virtual Source Stuff
  7. //
  8. //
  9. //@@BEGIN_MSINTERNAL
  10. // Development Team:
  11. // Andy Nicholson
  12. //
  13. // History: Date Author Comment
  14. //
  15. // To Do: Date Author Comment
  16. //
  17. //@@END_MSINTERNAL
  18. //---------------------------------------------------------------------------
  19. //
  20. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  21. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  22. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  23. // PURPOSE.
  24. //
  25. // Copyright (c) 1996-1999 Microsoft Corporation. All Rights Reserved.
  26. //
  27. //---------------------------------------------------------------------------
  28. #include "common.h"
  29. //---------------------------------------------------------------------------
  30. // Virtual Source Data
  31. KSDATARANGE DataRangeWildCard =
  32. {
  33. sizeof(KSDATARANGE),
  34. 0,
  35. 0,
  36. 0,
  37. STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
  38. STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM),
  39. STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WILDCARD),
  40. };
  41. KSPIN_MEDIUM VirtualPinMedium =
  42. {
  43. STATICGUIDOF(KSMEDIUMSETID_Standard),
  44. KSMEDIUM_STANDARD_DEVIO
  45. };
  46. KSDATARANGE VirtualPinDataRange =
  47. {
  48. sizeof(KSDATARANGE),
  49. 0,
  50. 0,
  51. 0,
  52. STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO),
  53. STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG),
  54. STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE),
  55. };
  56. //---------------------------------------------------------------------------
  57. NTSTATUS
  58. CreateVirtualMixer(
  59. PDEVICE_NODE pDeviceNode
  60. )
  61. {
  62. PTOPOLOGY_CONNECTION pTopologyConnection;
  63. PVIRTUAL_SOURCE_LINE pVirtualSourceLine;
  64. PLOGICAL_FILTER_NODE pLogicalFilterNode;
  65. PTOPOLOGY_PIN pTopologyPinSumOutput;
  66. PTOPOLOGY_NODE pTopologyNodeSum;
  67. NTSTATUS Status = STATUS_SUCCESS;
  68. PFILTER_NODE pFilterNode;
  69. PPIN_INFO pPinInfo;
  70. PPIN_NODE pPinNode;
  71. //
  72. // Create a special "virtual source" filter node and related structures
  73. //
  74. pFilterNode = new FILTER_NODE(FILTER_TYPE_AUDIO | FILTER_TYPE_VIRTUAL);
  75. if(pFilterNode == NULL) {
  76. Status = STATUS_INSUFFICIENT_RESOURCES;
  77. Trap();
  78. goto exit;
  79. }
  80. pFilterNode->SetFriendlyName(L"Virtual Mixer");
  81. Status = pFilterNode->AddDeviceInterfaceMatch(
  82. pDeviceNode->GetDeviceInterface());
  83. if(!NT_SUCCESS(Status)) {
  84. Trap();
  85. goto exit;
  86. }
  87. //
  88. // Create the logical filter node for this "virtual" filter
  89. //
  90. Status = CLogicalFilterNode::Create(&pLogicalFilterNode, pFilterNode);
  91. if(!NT_SUCCESS(Status)) {
  92. Trap();
  93. goto exit;
  94. }
  95. Status = CTopologyNode::Create(
  96. &pTopologyNodeSum,
  97. pFilterNode,
  98. MAXULONG,
  99. (GUID *)&KSNODETYPE_SUM);
  100. if(!NT_SUCCESS(Status)) {
  101. Trap();
  102. goto exit;
  103. }
  104. pTopologyNodeSum->iVirtualSource = 0;
  105. Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNodeSum);
  106. if(!NT_SUCCESS(Status)) {
  107. Trap();
  108. goto exit;
  109. }
  110. Status = pLogicalFilterNode->AddList(
  111. &pTopologyNodeSum->lstLogicalFilterNode);
  112. if(!NT_SUCCESS(Status)) {
  113. Trap();
  114. goto exit;
  115. }
  116. Status = CTopologyPin::Create(
  117. &pTopologyPinSumOutput,
  118. 0, // 0 output
  119. pTopologyNodeSum);
  120. if(!NT_SUCCESS(Status)) {
  121. Trap();
  122. goto exit;
  123. }
  124. //
  125. // Create a virtual sysaudio source pin
  126. //
  127. pPinInfo = new PIN_INFO(pFilterNode, 0);
  128. if(pPinInfo == NULL) {
  129. Status = STATUS_INSUFFICIENT_RESOURCES;
  130. Trap();
  131. goto exit;
  132. }
  133. pPinInfo->DataFlow = KSPIN_DATAFLOW_OUT;
  134. pPinInfo->Communication = KSPIN_COMMUNICATION_SOURCE;
  135. pPinInfo->cPinInstances.PossibleCount = MAXULONG;
  136. pFilterNode->cPins++;
  137. pPinNode = new PIN_NODE(pPinInfo);
  138. if(pPinNode == NULL) {
  139. Status = STATUS_INSUFFICIENT_RESOURCES;
  140. Trap();
  141. goto exit;
  142. }
  143. pPinNode->pMedium = INTERNAL_WILDCARD;
  144. pPinNode->pInterface = INTERNAL_WILDCARD;
  145. pPinNode->pDataRange = &DataRangeWildCard;
  146. Status = pLogicalFilterNode->lstPinNode.AddList(pPinNode);
  147. if(!NT_SUCCESS(Status)) {
  148. Trap();
  149. goto exit;
  150. }
  151. pPinNode->pLogicalFilterNode = pLogicalFilterNode;
  152. //
  153. // Connect the out of sum node to the source pin
  154. //
  155. Status = CTopologyConnection::Create(
  156. &pTopologyConnection,
  157. pFilterNode, // pFilterNode
  158. NULL, // pGraphNode
  159. pTopologyPinSumOutput, // pTopologyPin From
  160. NULL, // pTopologyPin To
  161. NULL, // pPinInfo From
  162. pPinInfo); // pPinInfo To
  163. if(!NT_SUCCESS(Status)) {
  164. Trap();
  165. goto exit;
  166. }
  167. Status = pTopologyConnection->AddList(
  168. &pLogicalFilterNode->lstTopologyConnection);
  169. if(!NT_SUCCESS(Status)) {
  170. Trap();
  171. goto exit;
  172. }
  173. FOR_EACH_LIST_ITEM(gplstVirtualSourceLine, pVirtualSourceLine) {
  174. ASSERT(pVirtualSourceLine->iVirtualSource <
  175. pDeviceNode->cVirtualSourceData);
  176. if(pDeviceNode->papVirtualSourceData[
  177. pVirtualSourceLine->iVirtualSource]->pTopologyNode != NULL) {
  178. continue;
  179. }
  180. Status = CreateVirtualLine(
  181. pDeviceNode,
  182. pFilterNode,
  183. pTopologyNodeSum,
  184. pLogicalFilterNode,
  185. pVirtualSourceLine);
  186. if(!NT_SUCCESS(Status)) {
  187. Trap();
  188. goto exit;
  189. }
  190. } END_EACH_LIST_ITEM
  191. // if new virtual source lines were created
  192. if(pFilterNode->cPins > 1) {
  193. pDeviceNode->pFilterNodeVirtual = pFilterNode;
  194. }
  195. else {
  196. delete pFilterNode;
  197. }
  198. exit:
  199. if(!NT_SUCCESS(Status)) {
  200. Trap();
  201. delete pFilterNode;
  202. }
  203. return(Status);
  204. }
  205. NTSTATUS
  206. CreateVirtualLine(
  207. PDEVICE_NODE pDeviceNode,
  208. PFILTER_NODE pFilterNode,
  209. PTOPOLOGY_NODE pTopologyNodeSum,
  210. PLOGICAL_FILTER_NODE pLogicalFilterNode,
  211. PVIRTUAL_SOURCE_LINE pVirtualSourceLine
  212. )
  213. {
  214. PTOPOLOGY_CONNECTION pTopologyConnection;
  215. NTSTATUS Status = STATUS_SUCCESS;
  216. PTOPOLOGY_NODE pTopologyNode;
  217. PTOPOLOGY_PIN pTopologyPinSumInput;
  218. PTOPOLOGY_PIN pTopologyPinVolume;
  219. PTOPOLOGY_PIN pTopologyPinMute;
  220. PPIN_INFO pPinInfo;
  221. PPIN_NODE pPinNode;
  222. //
  223. // Create a virtual sysaudio pin
  224. //
  225. pPinInfo = new PIN_INFO(pFilterNode, pVirtualSourceLine->iVirtualSource+1);
  226. if(pPinInfo == NULL) {
  227. Status = STATUS_INSUFFICIENT_RESOURCES;
  228. Trap();
  229. goto exit;
  230. }
  231. pPinInfo->DataFlow = KSPIN_DATAFLOW_IN;
  232. pPinInfo->Communication = KSPIN_COMMUNICATION_NONE;
  233. pPinInfo->pguidCategory = &pVirtualSourceLine->guidCategory;
  234. pPinInfo->pguidName = &pVirtualSourceLine->guidName;
  235. pFilterNode->cPins++;
  236. pPinNode = new PIN_NODE(pPinInfo);
  237. if(pPinNode == NULL) {
  238. Status = STATUS_INSUFFICIENT_RESOURCES;
  239. Trap();
  240. goto exit;
  241. }
  242. pPinNode->pMedium = &VirtualPinMedium;
  243. pPinNode->pDataRange = &VirtualPinDataRange;
  244. Status = pLogicalFilterNode->lstPinNode.AddList(pPinNode);
  245. if(!NT_SUCCESS(Status)) {
  246. Trap();
  247. goto exit;
  248. }
  249. pPinNode->pLogicalFilterNode = pLogicalFilterNode;
  250. //
  251. // Create a virtual volume topology node and input topology pin
  252. //
  253. Status = CTopologyNode::Create(
  254. &pTopologyNode,
  255. pFilterNode,
  256. MAXULONG,
  257. (GUID *)&KSNODETYPE_VOLUME);
  258. if(!NT_SUCCESS(Status)) {
  259. Trap();
  260. goto exit;
  261. }
  262. pTopologyNode->iVirtualSource = pVirtualSourceLine->iVirtualSource;
  263. ASSERT(pVirtualSourceLine->iVirtualSource <
  264. pDeviceNode->cVirtualSourceData);
  265. pDeviceNode->papVirtualSourceData[
  266. pVirtualSourceLine->iVirtualSource]->pTopologyNode = pTopologyNode;
  267. Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNode);
  268. if(!NT_SUCCESS(Status)) {
  269. Trap();
  270. goto exit;
  271. }
  272. Status = pLogicalFilterNode->AddList(&pTopologyNode->lstLogicalFilterNode);
  273. if(!NT_SUCCESS(Status)) {
  274. Trap();
  275. goto exit;
  276. }
  277. Status = CTopologyPin::Create(
  278. &pTopologyPinVolume,
  279. KSNODEPIN_STANDARD_IN, // 1 = input
  280. pTopologyNode);
  281. if(!NT_SUCCESS(Status)) {
  282. Trap();
  283. goto exit;
  284. }
  285. //
  286. // Create a connection from virtual pin to volume node
  287. //
  288. Status = CTopologyConnection::Create(
  289. &pTopologyConnection,
  290. pFilterNode, // pFilterNode
  291. NULL, // pGraphNode
  292. NULL, // pTopologyPin From
  293. pTopologyPinVolume, // pTopologyPin To
  294. pPinInfo, // pPinInfo From
  295. NULL); // pPinInfo To
  296. if(!NT_SUCCESS(Status)) {
  297. Trap();
  298. goto exit;
  299. }
  300. Status = pTopologyConnection->AddList(
  301. &pLogicalFilterNode->lstTopologyConnection);
  302. if(!NT_SUCCESS(Status)) {
  303. Trap();
  304. goto exit;
  305. }
  306. Status = CTopologyPin::Create(
  307. &pTopologyPinVolume,
  308. KSNODEPIN_STANDARD_OUT, // 0 = output
  309. pTopologyNode);
  310. if(!NT_SUCCESS(Status)) {
  311. Trap();
  312. goto exit;
  313. }
  314. //
  315. // Create a virtual mute topology node and input topology pin
  316. //
  317. Status = CTopologyNode::Create(
  318. &pTopologyNode,
  319. pFilterNode,
  320. MAXULONG,
  321. (GUID *)&KSNODETYPE_MUTE);
  322. if(!NT_SUCCESS(Status)) {
  323. Trap();
  324. goto exit;
  325. }
  326. pTopologyNode->iVirtualSource = pVirtualSourceLine->iVirtualSource;
  327. Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNode);
  328. if(!NT_SUCCESS(Status)) {
  329. Trap();
  330. goto exit;
  331. }
  332. Status = pLogicalFilterNode->AddList(&pTopologyNode->lstLogicalFilterNode);
  333. if(!NT_SUCCESS(Status)) {
  334. Trap();
  335. goto exit;
  336. }
  337. Status = CTopologyPin::Create(
  338. &pTopologyPinMute,
  339. KSNODEPIN_STANDARD_IN, // 1 = input
  340. pTopologyNode);
  341. if(!NT_SUCCESS(Status)) {
  342. Trap();
  343. goto exit;
  344. }
  345. //
  346. // Create a connection from volume node to mute node pin
  347. //
  348. Status = CTopologyConnection::Create(
  349. &pTopologyConnection,
  350. pFilterNode, // pFilterNode
  351. NULL, // pGraphNode
  352. pTopologyPinVolume, // pTopologyPin From
  353. pTopologyPinMute, // pTopologyPin To
  354. NULL, // pPinInfo From
  355. NULL); // pPinInfo To
  356. if(!NT_SUCCESS(Status)) {
  357. Trap();
  358. goto exit;
  359. }
  360. Status = pTopologyConnection->AddList(
  361. &pLogicalFilterNode->lstTopologyConnection);
  362. if(!NT_SUCCESS(Status)) {
  363. Trap();
  364. goto exit;
  365. }
  366. Status = CTopologyPin::Create(
  367. &pTopologyPinMute,
  368. KSNODEPIN_STANDARD_OUT, // 1 = output
  369. pTopologyNode);
  370. if(!NT_SUCCESS(Status)) {
  371. Trap();
  372. goto exit;
  373. }
  374. Status = CTopologyPin::Create(
  375. &pTopologyPinSumInput,
  376. pVirtualSourceLine->iVirtualSource + 1, // >= 1 input
  377. pTopologyNodeSum);
  378. if(!NT_SUCCESS(Status)) {
  379. Trap();
  380. goto exit;
  381. }
  382. //
  383. // Create a connection from mute node to sum node pin
  384. //
  385. Status = CTopologyConnection::Create(
  386. &pTopologyConnection,
  387. pFilterNode, // pFilterNode
  388. NULL, // pGraphNode
  389. pTopologyPinMute, // pTopologyPin From
  390. pTopologyPinSumInput, // pTopologyPin To
  391. NULL, // pPinInfo From
  392. NULL); // pPinInfo To
  393. if(!NT_SUCCESS(Status)) {
  394. Trap();
  395. goto exit;
  396. }
  397. Status = pTopologyConnection->AddList(
  398. &pLogicalFilterNode->lstTopologyConnection);
  399. if(!NT_SUCCESS(Status)) {
  400. Trap();
  401. goto exit;
  402. }
  403. exit:
  404. return(Status);
  405. }
  406. //---------------------------------------------------------------------------
  407. ENUMFUNC
  408. VirtualizeFindPin(
  409. IN PTOPOLOGY_CONNECTION pTopologyConnection,
  410. IN BOOL fToDirection,
  411. IN PVOID pReference
  412. )
  413. {
  414. NTSTATUS Status = STATUS_CONTINUE;
  415. IN PPIN_INFO pPinInfo;
  416. Assert(pTopologyConnection);
  417. if(!IS_CONNECTION_TYPE(pTopologyConnection, FILTER)) {
  418. Status = STATUS_DEAD_END;
  419. goto exit;
  420. }
  421. if(fToDirection) {
  422. pPinInfo = pTopologyConnection->pPinInfoTo;
  423. }
  424. else {
  425. pPinInfo = pTopologyConnection->pPinInfoFrom;
  426. }
  427. if(pPinInfo == NULL) {
  428. ASSERT(Status == STATUS_CONTINUE);
  429. goto exit;
  430. }
  431. if(pPinInfo->pguidCategory == NULL) {
  432. ASSERT(Status == STATUS_CONTINUE);
  433. goto exit;
  434. }
  435. if(IsEqualGUID(pPinInfo->pguidCategory, &KSNODETYPE_SPEAKER)) {
  436. Status = STATUS_SUCCESS;
  437. }
  438. exit:
  439. return(Status);
  440. }
  441. ENUMFUNC
  442. EnumerateVirtualizeFindPin(
  443. IN PTOPOLOGY_CONNECTION pTopologyConnection,
  444. IN BOOL fToDirection
  445. )
  446. {
  447. ENUM_TOPOLOGY EnumTopology;
  448. NTSTATUS Status;
  449. Assert(pTopologyConnection);
  450. EnumTopology.cTopologyRecursion = 0;
  451. EnumTopology.Function = VirtualizeFindPin;
  452. EnumTopology.fToDirection = fToDirection;
  453. EnumTopology.pReference = NULL;
  454. Status = EnumerateTopologyConnection(pTopologyConnection, &EnumTopology);
  455. return(Status);
  456. }
  457. ENUMFUNC
  458. VirtualizeFindNode(
  459. IN PTOPOLOGY_CONNECTION pTopologyConnection,
  460. IN BOOL fToDirection,
  461. OUT PTOPOLOGY_NODE *ppTopologyNode,
  462. IN GUID const *pguidType
  463. )
  464. {
  465. NTSTATUS Status = STATUS_CONTINUE;
  466. PTOPOLOGY_PIN pTopologyPin;
  467. Assert(pTopologyConnection);
  468. if(!IS_CONNECTION_TYPE(pTopologyConnection, FILTER)) {
  469. Status = STATUS_DEAD_END;
  470. goto exit;
  471. }
  472. if(fToDirection) {
  473. pTopologyPin = pTopologyConnection->pTopologyPinTo;
  474. }
  475. else {
  476. pTopologyPin = pTopologyConnection->pTopologyPinFrom;
  477. }
  478. if(pTopologyPin == NULL) {
  479. ASSERT(Status == STATUS_CONTINUE);
  480. goto exit;
  481. }
  482. if(IsEqualGUID(pTopologyPin->pTopologyNode->pguidType, &KSNODETYPE_SUM)) {
  483. Status = STATUS_DEAD_END;
  484. goto exit;
  485. }
  486. if(IsEqualGUID(pTopologyPin->pTopologyNode->pguidType, &KSNODETYPE_MUX)) {
  487. Status = STATUS_DEAD_END;
  488. goto exit;
  489. }
  490. if(IsEqualGUID(pTopologyPin->pTopologyNode->pguidType, pguidType)) {
  491. Status = EnumerateVirtualizeFindPin(pTopologyConnection, fToDirection);
  492. if(NT_SUCCESS(Status)) {
  493. *ppTopologyNode = pTopologyPin->pTopologyNode;
  494. }
  495. ASSERT(Status != STATUS_DEAD_END);
  496. }
  497. exit:
  498. return(Status);
  499. }
  500. ENUMFUNC
  501. VirtualizeFindMute(
  502. IN PTOPOLOGY_CONNECTION pTopologyConnection,
  503. IN BOOL fToDirection,
  504. OUT PTOPOLOGY_NODE *ppTopologyNode
  505. )
  506. {
  507. return(VirtualizeFindNode(
  508. pTopologyConnection,
  509. fToDirection,
  510. ppTopologyNode,
  511. &KSNODETYPE_MUTE));
  512. }
  513. ENUMFUNC
  514. VirtualizeFindVolume(
  515. IN PTOPOLOGY_CONNECTION pTopologyConnection,
  516. IN BOOL fToDirection,
  517. OUT PTOPOLOGY_NODE *ppTopologyNode
  518. )
  519. {
  520. return(VirtualizeFindNode(
  521. pTopologyConnection,
  522. fToDirection,
  523. ppTopologyNode,
  524. &KSNODETYPE_VOLUME));
  525. }
  526. VOID
  527. VirtualizeTopologyNode(
  528. IN PDEVICE_NODE pDeviceNode,
  529. IN PTOPOLOGY_NODE pTopologyNode,
  530. IN PVIRTUAL_SOURCE_LINE pVirtualSourceLine
  531. )
  532. {
  533. DPF3(100, "VirtualizeTopologyNode: real node #%d index %d %s",
  534. pTopologyNode->ulRealNodeNumber,
  535. pVirtualSourceLine->iVirtualSource,
  536. DbgGuid2Sz(&pVirtualSourceLine->guidCategory));
  537. ASSERT(
  538. (pTopologyNode->iVirtualSource == MAXULONG) ||
  539. (pTopologyNode->iVirtualSource == pVirtualSourceLine->iVirtualSource));
  540. // The PinId of a virtual pininfo has VirtualSourceData index
  541. pTopologyNode->iVirtualSource = pVirtualSourceLine->iVirtualSource;
  542. if(pVirtualSourceLine->ulFlags & VSL_FLAGS_CREATE_ONLY) {
  543. pTopologyNode->ulFlags |= TN_FLAGS_DONT_FORWARD;
  544. }
  545. ASSERT(pTopologyNode->iVirtualSource < pDeviceNode->cVirtualSourceData);
  546. if(IsEqualGUID(pTopologyNode->pguidType, &KSNODETYPE_VOLUME)) {
  547. pDeviceNode->papVirtualSourceData[
  548. pTopologyNode->iVirtualSource]->pTopologyNode = pTopologyNode;
  549. }
  550. }
  551. NTSTATUS
  552. AddVirtualMute(
  553. IN PDEVICE_NODE pDeviceNode,
  554. IN PTOPOLOGY_NODE pTopologyNodeVolume,
  555. IN PVIRTUAL_SOURCE_LINE pVirtualSourceLine
  556. )
  557. {
  558. PTOPOLOGY_CONNECTION pTopologyConnectionNew = NULL;
  559. PTOPOLOGY_CONNECTION pTopologyConnection = NULL;
  560. PTOPOLOGY_NODE pTopologyNodeMute = NULL;
  561. PTOPOLOGY_PIN pTopologyPinMuteInput = NULL;
  562. PTOPOLOGY_PIN pTopologyPinMuteOutput = NULL;
  563. PTOPOLOGY_PIN pTopologyPinVolumeOutput = NULL;
  564. PLOGICAL_FILTER_NODE pLogicalFilterNode;
  565. NTSTATUS Status;
  566. ASSERT(pTopologyNodeVolume->iVirtualSource != MAXULONG);
  567. FOR_EACH_LIST_ITEM(
  568. &pTopologyNodeVolume->lstTopologyPin,
  569. pTopologyPinVolumeOutput) {
  570. if(pTopologyPinVolumeOutput->ulPinNumber == KSNODEPIN_STANDARD_OUT) {
  571. break;
  572. }
  573. } END_EACH_LIST_ITEM
  574. if(pTopologyPinVolumeOutput == NULL) {
  575. Trap();
  576. Status = STATUS_INVALID_DEVICE_REQUEST;
  577. goto exit;
  578. }
  579. ASSERT(pTopologyPinVolumeOutput->ulPinNumber == KSNODEPIN_STANDARD_OUT);
  580. FOR_EACH_LIST_ITEM(
  581. &pTopologyPinVolumeOutput->lstTopologyConnection,
  582. pTopologyConnection) {
  583. Assert(pTopologyConnection);
  584. if(EnumerateVirtualizeFindPin(
  585. pTopologyConnection,
  586. TRUE) == STATUS_SUCCESS) { // Assumes KSPIN_DATAFLOW_IN
  587. break;
  588. }
  589. } END_EACH_LIST_ITEM
  590. if(pTopologyConnection == NULL) {
  591. Trap();
  592. Status = STATUS_INVALID_DEVICE_REQUEST;
  593. goto exit;
  594. }
  595. ASSERT(pTopologyConnection->pTopologyPinFrom == pTopologyPinVolumeOutput);
  596. Status = CTopologyNode::Create(
  597. &pTopologyNodeMute,
  598. pTopologyNodeVolume->pFilterNode,
  599. MAXULONG,
  600. (GUID *)&KSNODETYPE_MUTE);
  601. if(!NT_SUCCESS(Status)) {
  602. Trap();
  603. goto exit;
  604. }
  605. VirtualizeTopologyNode(pDeviceNode, pTopologyNodeMute, pVirtualSourceLine);
  606. Status = CTopologyPin::Create(
  607. &pTopologyPinMuteInput,
  608. KSNODEPIN_STANDARD_IN, // 1 = input
  609. pTopologyNodeMute);
  610. if(!NT_SUCCESS(Status)) {
  611. Trap();
  612. goto exit;
  613. }
  614. Status = CTopologyPin::Create(
  615. &pTopologyPinMuteOutput,
  616. KSNODEPIN_STANDARD_OUT, // 0 = output
  617. pTopologyNodeMute);
  618. if(!NT_SUCCESS(Status)) {
  619. Trap();
  620. goto exit;
  621. }
  622. Status = CTopologyConnection::Create(
  623. &pTopologyConnectionNew,
  624. pTopologyNodeVolume->pFilterNode, // pFilterNode
  625. NULL, // pGraphNode
  626. pTopologyPinVolumeOutput, // pTopologyPin From
  627. pTopologyPinMuteInput, // pTopologyPin To
  628. NULL, // pPinInfo From
  629. NULL); // pPinInfo To
  630. if(!NT_SUCCESS(Status)) {
  631. Trap();
  632. goto exit;
  633. }
  634. Status = pTopologyConnection->AddList(
  635. &pTopologyPinMuteOutput->lstTopologyConnection);
  636. if(!NT_SUCCESS(Status)) {
  637. Trap();
  638. goto exit;
  639. }
  640. FOR_EACH_LIST_ITEM(
  641. &pTopologyNodeVolume->lstLogicalFilterNode,
  642. pLogicalFilterNode) {
  643. Status = pLogicalFilterNode->AddList(
  644. &pTopologyNodeMute->lstLogicalFilterNode);
  645. if(!NT_SUCCESS(Status)) {
  646. Trap();
  647. goto exit;
  648. }
  649. Status = pLogicalFilterNode->lstTopologyNode.AddList(pTopologyNodeMute);
  650. if(!NT_SUCCESS(Status)) {
  651. Trap();
  652. goto exit;
  653. }
  654. Status = pTopologyConnectionNew->AddList(
  655. &pLogicalFilterNode->lstTopologyConnection);
  656. if(!NT_SUCCESS(Status)) {
  657. Trap();
  658. goto exit;
  659. }
  660. } END_EACH_LIST_ITEM
  661. pTopologyConnection->pTopologyPinFrom = pTopologyPinMuteOutput;
  662. pTopologyConnection->RemoveList(
  663. &pTopologyPinVolumeOutput->lstTopologyConnection);
  664. exit:
  665. if(!NT_SUCCESS(Status)) {
  666. Trap();
  667. if(pTopologyConnectionNew != NULL) {
  668. Trap();
  669. pTopologyConnectionNew->Destroy();
  670. }
  671. if(pTopologyNodeMute != NULL) {
  672. if(pTopologyNodeVolume != NULL) {
  673. Trap();
  674. pTopologyNodeMute->RemoveList(
  675. &pTopologyNodeVolume->pFilterNode->lstTopologyNode);
  676. FOR_EACH_LIST_ITEM(
  677. &pTopologyNodeVolume->lstLogicalFilterNode,
  678. pLogicalFilterNode) {
  679. pLogicalFilterNode->lstTopologyNode.RemoveList(
  680. pTopologyNodeMute);
  681. } END_EACH_LIST_ITEM
  682. }
  683. pTopologyNodeMute->Destroy();
  684. }
  685. }
  686. return(Status);
  687. }
  688. NTSTATUS
  689. VirtualizeTopology(
  690. PDEVICE_NODE pDeviceNode,
  691. PFILTER_NODE pFilterNode
  692. )
  693. {
  694. PVIRTUAL_SOURCE_LINE pVirtualSourceLine;
  695. PTOPOLOGY_NODE pTopologyNodeVolume;
  696. PTOPOLOGY_NODE pTopologyNodeMute;
  697. NTSTATUS Status = STATUS_SUCCESS;
  698. PPIN_INFO pPinInfo;
  699. FOR_EACH_LIST_ITEM(&pFilterNode->lstPinInfo, pPinInfo) {
  700. if(pPinInfo->pguidCategory == NULL) {
  701. continue;
  702. }
  703. FOR_EACH_LIST_ITEM(gplstVirtualSourceLine, pVirtualSourceLine) {
  704. if(pPinInfo->DataFlow != KSPIN_DATAFLOW_IN) {
  705. continue;
  706. }
  707. if(!IsEqualGUID(
  708. pPinInfo->pguidCategory,
  709. &pVirtualSourceLine->guidCategory)) {
  710. continue;
  711. }
  712. if(EnumerateTopology(
  713. pPinInfo,
  714. (TOP_PFN)VirtualizeFindVolume,
  715. &pTopologyNodeVolume) == STATUS_SUCCESS) {
  716. VirtualizeTopologyNode(
  717. pDeviceNode,
  718. pTopologyNodeVolume,
  719. pVirtualSourceLine);
  720. if(EnumerateTopology(
  721. pPinInfo,
  722. (TOP_PFN)VirtualizeFindMute,
  723. &pTopologyNodeMute) == STATUS_SUCCESS) {
  724. VirtualizeTopologyNode(
  725. pDeviceNode,
  726. pTopologyNodeMute,
  727. pVirtualSourceLine);
  728. }
  729. else {
  730. Status = AddVirtualMute(
  731. pDeviceNode,
  732. pTopologyNodeVolume,
  733. pVirtualSourceLine);
  734. if(!NT_SUCCESS(Status)) {
  735. Trap();
  736. goto exit;
  737. }
  738. }
  739. }
  740. } END_EACH_LIST_ITEM
  741. } END_EACH_LIST_ITEM
  742. exit:
  743. return(Status);
  744. }
  745. //---------------------------------------------------------------------------
  746. NTSTATUS
  747. CreateVirtualSource(
  748. IN PIRP pIrp,
  749. PSYSAUDIO_CREATE_VIRTUAL_SOURCE pCreateVirtualSource,
  750. OUT PULONG pulMixerPinId
  751. )
  752. {
  753. PVIRTUAL_SOURCE_LINE pVirtualSourceLine = NULL;
  754. NTSTATUS Status = STATUS_SUCCESS;
  755. PFILTER_INSTANCE pFilterInstance;
  756. PIO_STACK_LOCATION pIrpStack;
  757. PDEVICE_NODE pDeviceNode;
  758. pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  759. pFilterInstance = (PFILTER_INSTANCE)pIrpStack->FileObject->FsContext;
  760. Assert(pFilterInstance);
  761. if(!pFilterInstance->IsChildInstance()) {
  762. Trap();
  763. DPF(5, "CreateVirtualSource: FAILED - open pin instances");
  764. Status = STATUS_INVALID_DEVICE_REQUEST;
  765. goto exit;
  766. }
  767. FOR_EACH_LIST_ITEM(gplstVirtualSourceLine, pVirtualSourceLine) {
  768. if(!IsEqualGUID(
  769. &pVirtualSourceLine->guidCategory,
  770. &pCreateVirtualSource->PinCategory)) {
  771. continue;
  772. }
  773. if(!IsEqualGUID(
  774. &pVirtualSourceLine->guidName,
  775. &pCreateVirtualSource->PinName)) {
  776. continue;
  777. }
  778. ASSERT(NT_SUCCESS(Status));
  779. goto dup;
  780. } END_EACH_LIST_ITEM
  781. pVirtualSourceLine = new VIRTUAL_SOURCE_LINE(pCreateVirtualSource);
  782. if(pVirtualSourceLine == NULL) {
  783. Status = STATUS_INSUFFICIENT_RESOURCES;
  784. Trap();
  785. goto exit;
  786. }
  787. FOR_EACH_LIST_ITEM(gplstDeviceNode, pDeviceNode) {
  788. DPF2(60, "CreateVirtualSource: DN %08x %s",
  789. pDeviceNode,
  790. pDeviceNode->DumpName());
  791. Status = pDeviceNode->Update();
  792. if(!NT_SUCCESS(Status)) {
  793. Trap();
  794. goto exit;
  795. }
  796. } END_EACH_LIST_ITEM
  797. dup:
  798. *pulMixerPinId = pVirtualSourceLine->iVirtualSource;
  799. pIrp->IoStatus.Information = sizeof(ULONG);
  800. exit:
  801. if(!NT_SUCCESS(Status)) {
  802. delete pVirtualSourceLine;
  803. }
  804. return(Status);
  805. }
  806. NTSTATUS
  807. AttachVirtualSource(
  808. IN PIRP pIrp,
  809. IN PSYSAUDIO_ATTACH_VIRTUAL_SOURCE pAttachVirtualSource,
  810. IN OUT PVOID pData
  811. )
  812. {
  813. PVIRTUAL_NODE_DATA pVirtualNodeData = NULL;
  814. PSTART_NODE_INSTANCE pStartNodeInstance;
  815. PVIRTUAL_SOURCE_DATA pVirtualSourceData;
  816. NTSTATUS Status = STATUS_SUCCESS;
  817. PPIN_INSTANCE pPinInstance;
  818. PDEVICE_NODE pDeviceNode;
  819. LONG Channel;
  820. Status = ::GetStartNodeInstance(pIrp, &pStartNodeInstance);
  821. if(!NT_SUCCESS(Status)) {
  822. goto exit;
  823. }
  824. pPinInstance = pStartNodeInstance->pPinInstance;
  825. Assert(pPinInstance);
  826. Assert(pPinInstance->pFilterInstance);
  827. Assert(pPinInstance->pFilterInstance->pGraphNodeInstance);
  828. pDeviceNode = pPinInstance->pFilterInstance->GetDeviceNode();
  829. Assert(pDeviceNode);
  830. if(pAttachVirtualSource->MixerPinId >= pDeviceNode->cVirtualSourceData) {
  831. DPF(5, "AttachVirtualSource: invalid MixerPinId");
  832. Status = STATUS_INVALID_DEVICE_REQUEST;
  833. goto exit;
  834. }
  835. ASSERT(pDeviceNode->papVirtualSourceData != NULL);
  836. pVirtualSourceData =
  837. pDeviceNode->papVirtualSourceData[pAttachVirtualSource->MixerPinId];
  838. Assert(pVirtualSourceData);
  839. Status = GetVolumeNodeNumber(pPinInstance, pVirtualSourceData);
  840. if(!NT_SUCCESS(Status)) {
  841. Trap();
  842. goto exit;
  843. }
  844. if(pPinInstance->ulVolumeNodeNumber == MAXULONG) {
  845. ASSERT(NT_SUCCESS(Status));
  846. goto exit;
  847. }
  848. pVirtualNodeData = new VIRTUAL_NODE_DATA(
  849. pStartNodeInstance,
  850. pVirtualSourceData);
  851. if(pVirtualNodeData == NULL) {
  852. Trap();
  853. Status = STATUS_INSUFFICIENT_RESOURCES;
  854. goto exit;
  855. }
  856. if(pVirtualSourceData->pTopologyNode->ulRealNodeNumber != MAXULONG) {
  857. ULONG ulNodeNumber;
  858. //
  859. // Get the volume control range for the physical node
  860. //
  861. ulNodeNumber = pPinInstance->pFilterInstance->pGraphNodeInstance->
  862. paulNodeNumber[pAttachVirtualSource->MixerPinId];
  863. if(ulNodeNumber == pPinInstance->ulVolumeNodeNumber) {
  864. ASSERT(NT_SUCCESS(Status));
  865. delete pVirtualNodeData;
  866. goto exit;
  867. }
  868. Status = pStartNodeInstance->GetTopologyNodeFileObject(
  869. &pVirtualNodeData->pFileObject,
  870. ulNodeNumber);
  871. if(!NT_SUCCESS(Status)) {
  872. Trap();
  873. goto exit;
  874. }
  875. pVirtualNodeData->NodeId =
  876. pVirtualSourceData->pTopologyNode->ulRealNodeNumber;
  877. Status = GetControlRange(pVirtualNodeData);
  878. if(!NT_SUCCESS(Status)) {
  879. goto exit;
  880. }
  881. pVirtualSourceData->MinimumValue = pVirtualNodeData->MinimumValue;
  882. pVirtualSourceData->MaximumValue = pVirtualNodeData->MaximumValue;
  883. pVirtualSourceData->Steps = pVirtualNodeData->Steps;
  884. }
  885. Status = pStartNodeInstance->GetTopologyNodeFileObject(
  886. &pVirtualNodeData->pFileObject,
  887. pPinInstance->ulVolumeNodeNumber);
  888. if(!NT_SUCCESS(Status)) {
  889. Trap();
  890. goto exit;
  891. }
  892. pVirtualNodeData->NodeId = pPinInstance->pFilterInstance->
  893. pGraphNodeInstance->papTopologyNode[pPinInstance->ulVolumeNodeNumber]->
  894. ulRealNodeNumber;
  895. Status = GetControlRange(pVirtualNodeData);
  896. if(!NT_SUCCESS(Status)) {
  897. goto exit;
  898. }
  899. for(Channel = 0; Channel < pVirtualSourceData->cChannels; Channel++) {
  900. Status = SetVirtualVolume(pVirtualNodeData, Channel);
  901. if(!NT_SUCCESS(Status)) {
  902. Trap();
  903. goto exit;
  904. }
  905. }
  906. exit:
  907. if(!NT_SUCCESS(Status)) {
  908. delete pVirtualNodeData;
  909. }
  910. return(Status);
  911. }
  912. NTSTATUS
  913. FilterVirtualPropertySupportHandler(
  914. IN PIRP pIrp,
  915. IN PKSNODEPROPERTY pNodeProperty,
  916. IN OUT PVOID pData
  917. )
  918. {
  919. PGRAPH_NODE_INSTANCE pGraphNodeInstance;
  920. PTOPOLOGY_NODE pTopologyNode;
  921. NTSTATUS Status;
  922. Status = GetGraphNodeInstance(pIrp, &pGraphNodeInstance);
  923. if(!NT_SUCCESS(Status)) {
  924. goto exit;
  925. }
  926. Assert(pGraphNodeInstance);
  927. Status = STATUS_NOT_FOUND;
  928. if((pNodeProperty->Property.Flags & KSPROPERTY_TYPE_TOPOLOGY) == 0) {
  929. DPF(5, "FilterVirtualPropertySupportHandler: no TOPOLOGY bit");
  930. ASSERT(Status == STATUS_NOT_FOUND);
  931. goto exit;
  932. }
  933. if(pNodeProperty->NodeId >=
  934. pGraphNodeInstance->Topology.TopologyNodesCount) {
  935. DPF(5, "FilterVirtualPropertySupportHandler: invalid node #");
  936. ASSERT(Status == STATUS_NOT_FOUND);
  937. goto exit;
  938. }
  939. pTopologyNode = pGraphNodeInstance->papTopologyNode[pNodeProperty->NodeId];
  940. Assert(pTopologyNode);
  941. if(pTopologyNode->ulRealNodeNumber == MAXULONG) {
  942. ASSERT(pTopologyNode->iVirtualSource != MAXULONG);
  943. Status = STATUS_SOME_NOT_MAPPED;
  944. goto exit;
  945. }
  946. ASSERT(Status == STATUS_NOT_FOUND);
  947. exit:
  948. return(Status);
  949. }
  950. NTSTATUS
  951. FilterVirtualPropertyHandler(
  952. IN PIRP pIrp,
  953. IN PKSNODEPROPERTY pNodeProperty,
  954. IN OUT PLONG plLevel
  955. )
  956. {
  957. PGRAPH_NODE_INSTANCE pGraphNodeInstance;
  958. PVIRTUAL_SOURCE_DATA pVirtualSourceData;
  959. PVIRTUAL_NODE_DATA pVirtualNodeData;
  960. PIO_STACK_LOCATION pIrpStack;
  961. PTOPOLOGY_NODE pTopologyNode;
  962. LONG StopChannel, Channel;
  963. NTSTATUS Status;
  964. pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  965. Status = GetGraphNodeInstance(pIrp, &pGraphNodeInstance);
  966. if(!NT_SUCCESS(Status)) {
  967. goto exit;
  968. }
  969. Assert(pGraphNodeInstance);
  970. if(((pNodeProperty->Property.Flags & KSPROPERTY_TYPE_TOPOLOGY) == 0) ||
  971. (pNodeProperty->NodeId >=
  972. pGraphNodeInstance->Topology.TopologyNodesCount)) {
  973. DPF(5, "FilterVirtualPropertyHandler: invalid property");
  974. Status = STATUS_NOT_FOUND;
  975. goto exit;
  976. }
  977. pTopologyNode = pGraphNodeInstance->papTopologyNode[pNodeProperty->NodeId];
  978. Assert(pTopologyNode);
  979. if(pTopologyNode->iVirtualSource == MAXULONG) {
  980. Status = STATUS_NOT_FOUND;
  981. goto exit;
  982. }
  983. ASSERT(pTopologyNode->iVirtualSource < gcVirtualSources);
  984. ASSERT(pGraphNodeInstance->pGraphNode->pDeviceNode->
  985. papVirtualSourceData != NULL);
  986. pVirtualSourceData = pGraphNodeInstance->pGraphNode->pDeviceNode->
  987. papVirtualSourceData[pTopologyNode->iVirtualSource];
  988. Assert(pVirtualSourceData);
  989. if(pIrpStack->Parameters.DeviceIoControl.InputBufferLength <
  990. sizeof(KSNODEPROPERTY_AUDIO_CHANNEL) ||
  991. (pNodeProperty->Property.Id != KSPROPERTY_AUDIO_VOLUMELEVEL &&
  992. pNodeProperty->Property.Id != KSPROPERTY_AUDIO_MUTE)) {
  993. Trap();
  994. Status = STATUS_INVALID_DEVICE_REQUEST;
  995. goto exit;
  996. }
  997. Channel = ((PKSNODEPROPERTY_AUDIO_CHANNEL)pNodeProperty)->Channel;
  998. StopChannel = Channel;
  999. if(Channel == MAXULONG) {
  1000. Channel = 0;
  1001. StopChannel = pVirtualSourceData->cChannels - 1;
  1002. }
  1003. if(Channel >= MAX_NUM_CHANNELS || Channel < 0) {
  1004. Status = STATUS_INVALID_DEVICE_REQUEST;
  1005. goto exit;
  1006. }
  1007. for(; Channel <= StopChannel; Channel++) {
  1008. if(IsEqualGUID(pTopologyNode->pguidType, &KSNODETYPE_MUTE)) {
  1009. if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
  1010. *plLevel = pVirtualSourceData->fMuted[Channel];
  1011. pIrp->IoStatus.Information = sizeof(LONG);
  1012. }
  1013. else {
  1014. ASSERT(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET);
  1015. pVirtualSourceData->fMuted[Channel] = *plLevel;
  1016. if(pTopologyNode->ulRealNodeNumber == MAXULONG) {
  1017. Status = SetPhysicalVolume(
  1018. pGraphNodeInstance,
  1019. pVirtualSourceData,
  1020. Channel);
  1021. if(!NT_SUCCESS(Status)) {
  1022. Trap();
  1023. goto exit;
  1024. }
  1025. }
  1026. }
  1027. }
  1028. else if(IsEqualGUID(pTopologyNode->pguidType, &KSNODETYPE_VOLUME)) {
  1029. if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
  1030. *plLevel = pVirtualSourceData->lLevel[Channel];
  1031. pIrp->IoStatus.Information = sizeof(LONG);
  1032. }
  1033. else {
  1034. ASSERT(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET);
  1035. pVirtualSourceData->lLevel[Channel] = *plLevel;
  1036. }
  1037. }
  1038. else {
  1039. DPF2(5, "Invalid TopologyNode Prop.Id %d Node.Id %d",
  1040. pNodeProperty->Property.Id,
  1041. pTopologyNode->ulRealNodeNumber);
  1042. Status = STATUS_INVALID_DEVICE_REQUEST;
  1043. goto exit;
  1044. }
  1045. ASSERT(NT_SUCCESS(Status));
  1046. if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET) {
  1047. FOR_EACH_LIST_ITEM(
  1048. &pVirtualSourceData->lstVirtualNodeData,
  1049. pVirtualNodeData) {
  1050. ASSERT(pVirtualSourceData ==
  1051. pVirtualNodeData->pVirtualSourceData);
  1052. Status = SetVirtualVolume(pVirtualNodeData, Channel);
  1053. if(!NT_SUCCESS(Status)) {
  1054. Trap();
  1055. goto exit;
  1056. }
  1057. } END_EACH_LIST_ITEM
  1058. }
  1059. }
  1060. if(pTopologyNode->ulRealNodeNumber == MAXULONG ||
  1061. pTopologyNode->ulFlags & TN_FLAGS_DONT_FORWARD ||
  1062. pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
  1063. Status = STATUS_SUCCESS;
  1064. }
  1065. else {
  1066. // If topology node has a real node number, forward the irp to it
  1067. Status = STATUS_NOT_FOUND;
  1068. }
  1069. exit:
  1070. return(Status);
  1071. }
  1072. NTSTATUS
  1073. PinVirtualPropertySupportHandler(
  1074. IN PIRP pIrp,
  1075. IN PKSNODEPROPERTY pNodeProperty,
  1076. IN OUT PVOID pData
  1077. )
  1078. {
  1079. return(STATUS_NOT_FOUND);
  1080. }
  1081. NTSTATUS
  1082. PinVirtualPropertyHandler(
  1083. IN PIRP pIrp,
  1084. IN PKSNODEPROPERTY_AUDIO_CHANNEL pNodePropertyAudioChannel,
  1085. IN OUT PLONG plLevel
  1086. )
  1087. {
  1088. PSTART_NODE_INSTANCE pStartNodeInstance;
  1089. PFILTER_INSTANCE pFilterInstance;
  1090. NTSTATUS Status = STATUS_SUCCESS;
  1091. PKSNODEPROPERTY pNodeProperty;
  1092. PPIN_INSTANCE pPinInstance;
  1093. LONG StopChannel, Channel;
  1094. Status = ::GetStartNodeInstance(pIrp, &pStartNodeInstance);
  1095. if(!NT_SUCCESS(Status)) {
  1096. goto exit;
  1097. }
  1098. pPinInstance = pStartNodeInstance->pPinInstance;
  1099. Assert(pPinInstance);
  1100. pFilterInstance = pPinInstance->pFilterInstance;
  1101. Assert(pFilterInstance);
  1102. Assert(pFilterInstance->pGraphNodeInstance);
  1103. pNodeProperty = &pNodePropertyAudioChannel->NodeProperty;
  1104. if(((pNodeProperty->Property.Flags & KSPROPERTY_TYPE_TOPOLOGY) == 0) ||
  1105. (pNodeProperty->NodeId >=
  1106. pFilterInstance->pGraphNodeInstance->Topology.TopologyNodesCount)) {
  1107. DPF(5, "PinVirtualPropertyHandler: invalid property");
  1108. Status = STATUS_INVALID_DEVICE_REQUEST;
  1109. goto exit;
  1110. }
  1111. if(pStartNodeInstance->pVirtualNodeData == NULL ||
  1112. pPinInstance->ulVolumeNodeNumber == MAXULONG ||
  1113. pPinInstance->ulVolumeNodeNumber != pNodeProperty->NodeId) {
  1114. Status = STATUS_NOT_FOUND;
  1115. goto exit;
  1116. }
  1117. Assert(pStartNodeInstance->pVirtualNodeData);
  1118. Assert(pStartNodeInstance->pVirtualNodeData->pVirtualSourceData);
  1119. StopChannel = Channel = pNodePropertyAudioChannel->Channel;
  1120. if(Channel == MAXULONG) {
  1121. Channel = 0;
  1122. StopChannel = pStartNodeInstance->pVirtualNodeData->
  1123. pVirtualSourceData->cChannels - 1;
  1124. }
  1125. if(Channel >= MAX_NUM_CHANNELS || Channel < 0) {
  1126. Status = STATUS_INVALID_DEVICE_REQUEST;
  1127. goto exit;
  1128. }
  1129. for(; Channel <= StopChannel; Channel++) {
  1130. if(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_GET) {
  1131. *plLevel = pStartNodeInstance->pVirtualNodeData->lLevel[Channel];
  1132. pIrp->IoStatus.Information = sizeof(LONG);
  1133. ASSERT(NT_SUCCESS(Status));
  1134. }
  1135. else {
  1136. ASSERT(pNodeProperty->Property.Flags & KSPROPERTY_TYPE_SET);
  1137. pStartNodeInstance->pVirtualNodeData->lLevel[Channel] = *plLevel;
  1138. Status = SetVirtualVolume(
  1139. pStartNodeInstance->pVirtualNodeData,
  1140. Channel);
  1141. if(!NT_SUCCESS(Status)) {
  1142. Trap();
  1143. goto exit;
  1144. }
  1145. }
  1146. }
  1147. exit:
  1148. return(Status);
  1149. }
  1150. NTSTATUS
  1151. GetControlRange(
  1152. PVIRTUAL_NODE_DATA pVirtualNodeData
  1153. )
  1154. {
  1155. PKSPROPERTY_DESCRIPTION pPropertyDescription = NULL;
  1156. PKSPROPERTY_MEMBERSHEADER pMemberHeader;
  1157. PKSPROPERTY_STEPPING_LONG pSteppingLong;
  1158. NTSTATUS Status = STATUS_SUCCESS;
  1159. // Setup the defaults
  1160. pVirtualNodeData->MinimumValue = (-96 * 65536);
  1161. pVirtualNodeData->MaximumValue = 0;
  1162. pVirtualNodeData->Steps = (65536/2); // 1/2 db steps
  1163. Status = QueryPropertyRange(
  1164. pVirtualNodeData->pFileObject,
  1165. &KSPROPSETID_Audio,
  1166. KSPROPERTY_AUDIO_VOLUMELEVEL,
  1167. pVirtualNodeData->NodeId,
  1168. &pPropertyDescription);
  1169. if(!NT_SUCCESS(Status)) {
  1170. goto exit;
  1171. }
  1172. if((pPropertyDescription->MembersListCount == 0) ||
  1173. (!IsEqualGUID(
  1174. &pPropertyDescription->PropTypeSet.Set,
  1175. &KSPROPTYPESETID_General)) ||
  1176. (pPropertyDescription->PropTypeSet.Id != VT_I4)) {
  1177. Status = STATUS_NOT_SUPPORTED;
  1178. goto exit;
  1179. }
  1180. pMemberHeader = (PKSPROPERTY_MEMBERSHEADER)(pPropertyDescription + 1);
  1181. if(pMemberHeader->MembersFlags & KSPROPERTY_MEMBER_STEPPEDRANGES) {
  1182. pSteppingLong = (PKSPROPERTY_STEPPING_LONG)(pMemberHeader + 1);
  1183. pVirtualNodeData->MinimumValue = pSteppingLong->Bounds.SignedMinimum;
  1184. pVirtualNodeData->MaximumValue = pSteppingLong->Bounds.SignedMaximum;
  1185. pVirtualNodeData->Steps = pSteppingLong->SteppingDelta;
  1186. }
  1187. else {
  1188. Trap();
  1189. Status = STATUS_NOT_SUPPORTED;
  1190. goto exit;
  1191. }
  1192. exit:
  1193. delete pPropertyDescription;
  1194. return(STATUS_SUCCESS);
  1195. }
  1196. NTSTATUS
  1197. QueryPropertyRange(
  1198. PFILE_OBJECT pFileObject,
  1199. CONST GUID *pguidPropertySet,
  1200. ULONG ulPropertyId,
  1201. ULONG ulNodeId,
  1202. PKSPROPERTY_DESCRIPTION *ppPropertyDescription
  1203. )
  1204. {
  1205. KSPROPERTY_DESCRIPTION PropertyDescription;
  1206. KSNODEPROPERTY NodeProperty;
  1207. ULONG BytesReturned;
  1208. NTSTATUS Status;
  1209. NodeProperty.Property.Set = *pguidPropertySet;
  1210. NodeProperty.Property.Id = ulPropertyId;
  1211. NodeProperty.Property.Flags =
  1212. KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
  1213. NodeProperty.NodeId = ulNodeId;
  1214. NodeProperty.Reserved = 0;
  1215. AssertFileObject(pFileObject);
  1216. Status = KsSynchronousIoControlDevice(
  1217. pFileObject,
  1218. KernelMode,
  1219. IOCTL_KS_PROPERTY,
  1220. &NodeProperty,
  1221. sizeof(NodeProperty),
  1222. &PropertyDescription,
  1223. sizeof(PropertyDescription),
  1224. &BytesReturned);
  1225. if(!NT_SUCCESS(Status)) {
  1226. goto exit;
  1227. }
  1228. ASSERT(BytesReturned == sizeof(PropertyDescription));
  1229. *ppPropertyDescription =
  1230. (PKSPROPERTY_DESCRIPTION)new BYTE[PropertyDescription.DescriptionSize];
  1231. if(*ppPropertyDescription == NULL) {
  1232. Status = STATUS_INSUFFICIENT_RESOURCES;
  1233. goto exit;
  1234. }
  1235. AssertFileObject(pFileObject);
  1236. Status = KsSynchronousIoControlDevice(
  1237. pFileObject,
  1238. KernelMode,
  1239. IOCTL_KS_PROPERTY,
  1240. &NodeProperty,
  1241. sizeof( NodeProperty ),
  1242. *ppPropertyDescription,
  1243. PropertyDescription.DescriptionSize,
  1244. &BytesReturned);
  1245. if(!NT_SUCCESS(Status)) {
  1246. delete *ppPropertyDescription;
  1247. *ppPropertyDescription = NULL;
  1248. goto exit;
  1249. }
  1250. exit:
  1251. return(Status);
  1252. }
  1253. NTSTATUS
  1254. SetVirtualVolume(
  1255. PVIRTUAL_NODE_DATA pVirtualNodeData,
  1256. LONG Channel
  1257. )
  1258. {
  1259. KSNODEPROPERTY_AUDIO_CHANNEL NodePropertyAudioChannel;
  1260. NTSTATUS Status = STATUS_SUCCESS;
  1261. ULONG BytesReturned;
  1262. LONG lLevel;
  1263. ASSERT(pVirtualNodeData->NodeId != MAXULONG);
  1264. Assert(pVirtualNodeData->pVirtualSourceData);
  1265. NodePropertyAudioChannel.NodeProperty.Property.Set =
  1266. KSPROPSETID_Audio;
  1267. NodePropertyAudioChannel.NodeProperty.Property.Id =
  1268. KSPROPERTY_AUDIO_VOLUMELEVEL;
  1269. NodePropertyAudioChannel.NodeProperty.Property.Flags =
  1270. KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
  1271. NodePropertyAudioChannel.NodeProperty.NodeId = pVirtualNodeData->NodeId;
  1272. NodePropertyAudioChannel.Channel = Channel;
  1273. NodePropertyAudioChannel.Reserved = 0;
  1274. if(pVirtualNodeData->pVirtualSourceData->fMuted[Channel]) {
  1275. lLevel = LONG_MIN;
  1276. }
  1277. else {
  1278. if(pVirtualNodeData->pVirtualSourceData->lLevel[Channel] == LONG_MIN) {
  1279. lLevel = LONG_MIN;
  1280. }
  1281. else {
  1282. lLevel = pVirtualNodeData->lLevel[Channel];
  1283. if(lLevel != LONG_MIN) {
  1284. lLevel += pVirtualNodeData->pVirtualSourceData->lLevel[Channel];
  1285. lLevel = MapVirtualLevel(pVirtualNodeData, lLevel);
  1286. }
  1287. }
  1288. }
  1289. AssertFileObject(pVirtualNodeData->pFileObject);
  1290. Status = KsSynchronousIoControlDevice(
  1291. pVirtualNodeData->pFileObject,
  1292. KernelMode,
  1293. IOCTL_KS_PROPERTY,
  1294. &NodePropertyAudioChannel,
  1295. sizeof(KSNODEPROPERTY_AUDIO_CHANNEL),
  1296. &lLevel,
  1297. sizeof(LONG),
  1298. &BytesReturned);
  1299. if(!NT_SUCCESS(Status)) {
  1300. DPF4(10, "SetVirtualVolume: [%d] SNI %08x N# %u FAILED %08x",
  1301. Channel,
  1302. pVirtualNodeData->pStartNodeInstance,
  1303. pVirtualNodeData->NodeId,
  1304. Status);
  1305. }
  1306. return(STATUS_SUCCESS);
  1307. }
  1308. NTSTATUS
  1309. SetPhysicalVolume(
  1310. PGRAPH_NODE_INSTANCE pGraphNodeInstance,
  1311. PVIRTUAL_SOURCE_DATA pVirtualSourceData,
  1312. LONG Channel
  1313. )
  1314. {
  1315. KSNODEPROPERTY_AUDIO_CHANNEL NodePropertyAudioChannel;
  1316. NTSTATUS Status = STATUS_SUCCESS;
  1317. PFILE_OBJECT pFileObject;
  1318. ULONG BytesReturned;
  1319. LONG lLevel;
  1320. Assert(pGraphNodeInstance);
  1321. Assert(pVirtualSourceData);
  1322. ASSERT(IsEqualGUID(
  1323. pVirtualSourceData->pTopologyNode->pguidType,
  1324. &KSNODETYPE_VOLUME));
  1325. if(pVirtualSourceData->pTopologyNode->ulRealNodeNumber == MAXULONG) {
  1326. ASSERT(NT_SUCCESS(Status));
  1327. goto exit;
  1328. }
  1329. ASSERT(pVirtualSourceData->pTopologyNode->iVirtualSource <
  1330. gcVirtualSources);
  1331. Status = pGraphNodeInstance->GetTopologyNodeFileObject(
  1332. &pFileObject,
  1333. pGraphNodeInstance->paulNodeNumber[
  1334. pVirtualSourceData->pTopologyNode->iVirtualSource]);
  1335. if(!NT_SUCCESS(Status)) {
  1336. Trap();
  1337. goto exit;
  1338. }
  1339. NodePropertyAudioChannel.NodeProperty.Property.Set =
  1340. KSPROPSETID_Audio;
  1341. NodePropertyAudioChannel.NodeProperty.Property.Id =
  1342. KSPROPERTY_AUDIO_VOLUMELEVEL;
  1343. NodePropertyAudioChannel.NodeProperty.Property.Flags =
  1344. KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
  1345. NodePropertyAudioChannel.NodeProperty.NodeId =
  1346. pVirtualSourceData->pTopologyNode->ulRealNodeNumber;
  1347. NodePropertyAudioChannel.Channel = Channel;
  1348. NodePropertyAudioChannel.Reserved = 0;
  1349. if(pVirtualSourceData->fMuted[Channel]) {
  1350. lLevel = LONG_MIN;
  1351. }
  1352. else {
  1353. lLevel = pVirtualSourceData->lLevel[Channel];
  1354. }
  1355. AssertFileObject(pFileObject);
  1356. Status = KsSynchronousIoControlDevice(
  1357. pFileObject,
  1358. KernelMode,
  1359. IOCTL_KS_PROPERTY,
  1360. &NodePropertyAudioChannel,
  1361. sizeof(KSNODEPROPERTY_AUDIO_CHANNEL),
  1362. &lLevel,
  1363. sizeof(LONG),
  1364. &BytesReturned);
  1365. if(!NT_SUCCESS(Status)) {
  1366. Trap();
  1367. goto exit;
  1368. }
  1369. exit:
  1370. if(!NT_SUCCESS(Status)) {
  1371. DPF2(10, "SetPhysicalVolume: [%d] FAILED %08x", Channel, Status);
  1372. }
  1373. return(Status);
  1374. }
  1375. LONG
  1376. MapVirtualLevel(
  1377. PVIRTUAL_NODE_DATA pVirtualNodeData,
  1378. LONG lLevel
  1379. )
  1380. {
  1381. DPF4(100, "MapVirtualLevel: from %d max %d min %d step %d",
  1382. lLevel / 65536,
  1383. pVirtualNodeData->pVirtualSourceData->MaximumValue / 65536,
  1384. pVirtualNodeData->pVirtualSourceData->MinimumValue / 65536,
  1385. pVirtualNodeData->pVirtualSourceData->Steps / 65536);
  1386. if(lLevel != LONG_MIN) {
  1387. lLevel += pVirtualNodeData->MaximumValue -
  1388. pVirtualNodeData->pVirtualSourceData->MaximumValue;
  1389. }
  1390. DPF4(100, "MapVirtualLevel: to %d max %d min %d step %d",
  1391. lLevel / 65536,
  1392. pVirtualNodeData->MaximumValue / 65536,
  1393. pVirtualNodeData->MinimumValue / 65536,
  1394. pVirtualNodeData->Steps / 65536);
  1395. return(lLevel);
  1396. }
  1397. NTSTATUS
  1398. GetVolumeNodeNumber(
  1399. PPIN_INSTANCE pPinInstance,
  1400. PVIRTUAL_SOURCE_DATA pVirtualSourceData OPTIONAL
  1401. )
  1402. {
  1403. PSTART_NODE pStartNode;
  1404. NTSTATUS Status = STATUS_SUCCESS;
  1405. KSAUDIO_MIXCAP_TABLE AudioMixCapTable;
  1406. // This indicates to GetSuperMixCaps to only get in/out channels
  1407. PKSAUDIO_MIXCAP_TABLE pAudioMixCapTable = &AudioMixCapTable;
  1408. Assert(pPinInstance);
  1409. Assert(pPinInstance->pFilterInstance);
  1410. Assert(pPinInstance->pFilterInstance->pGraphNodeInstance);
  1411. if(pPinInstance->ulVolumeNodeNumber == MAXULONG) {
  1412. Assert(pPinInstance->pStartNodeInstance);
  1413. pStartNode = pPinInstance->pStartNodeInstance->pStartNode;
  1414. Assert(pStartNode);
  1415. Assert(pStartNode->GetStartInfo());
  1416. pPinInstance->ulVolumeNodeNumber =
  1417. pStartNode->GetStartInfo()->ulVolumeNodeNumberPre;
  1418. if(pStartNode->GetStartInfo()->ulVolumeNodeNumberSuperMix != MAXULONG &&
  1419. pStartNode->GetStartInfo()->ulVolumeNodeNumberPost != MAXULONG) {
  1420. Status = GetSuperMixCaps(
  1421. &pAudioMixCapTable,
  1422. pPinInstance->pStartNodeInstance,
  1423. pStartNode->GetStartInfo()->ulVolumeNodeNumberSuperMix);
  1424. if(!NT_SUCCESS(Status)) {
  1425. Status = STATUS_SUCCESS;
  1426. goto exit;
  1427. }
  1428. if(pAudioMixCapTable->OutputChannels != 1) {
  1429. pPinInstance->ulVolumeNodeNumber =
  1430. pStartNode->GetStartInfo()->ulVolumeNodeNumberPost;
  1431. if(pVirtualSourceData != NULL) {
  1432. pVirtualSourceData->cChannels =
  1433. pAudioMixCapTable->OutputChannels;
  1434. }
  1435. }
  1436. }
  1437. DPF2(100, "GetVolumeNodeNumber: SN %08x %02x",
  1438. pStartNode,
  1439. pPinInstance->ulVolumeNodeNumber);
  1440. }
  1441. exit:
  1442. return(Status);
  1443. }
  1444. NTSTATUS
  1445. GetSuperMixCaps(
  1446. OUT PKSAUDIO_MIXCAP_TABLE *ppAudioMixCapTable,
  1447. IN PSTART_NODE_INSTANCE pStartNodeInstance,
  1448. IN ULONG NodeId
  1449. )
  1450. {
  1451. KSAUDIO_MIXCAP_TABLE AudioMixCapTable;
  1452. NTSTATUS Status = STATUS_SUCCESS;
  1453. KSNODEPROPERTY NodeProperty;
  1454. PFILE_OBJECT pFileObject;
  1455. ULONG BytesReturned;
  1456. ULONG cb;
  1457. Assert(pStartNodeInstance);
  1458. ASSERT(NodeId != MAXULONG);
  1459. Status = pStartNodeInstance->GetTopologyNodeFileObject(
  1460. &pFileObject,
  1461. NodeId);
  1462. if(!NT_SUCCESS(Status)) {
  1463. Trap();
  1464. goto exit;
  1465. }
  1466. NodeProperty.Property.Set = KSPROPSETID_Audio;
  1467. NodeProperty.Property.Id = KSPROPERTY_AUDIO_MIX_LEVEL_CAPS;
  1468. NodeProperty.Property.Flags =
  1469. KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
  1470. NodeProperty.NodeId = pStartNodeInstance->pPinInstance->pFilterInstance->
  1471. pGraphNodeInstance->papTopologyNode[NodeId]->ulRealNodeNumber;
  1472. NodeProperty.Reserved = 0;
  1473. ASSERT(NodeProperty.NodeId != MAXULONG);
  1474. AssertFileObject(pFileObject);
  1475. Status = KsSynchronousIoControlDevice(
  1476. pFileObject,
  1477. KernelMode,
  1478. IOCTL_KS_PROPERTY,
  1479. &NodeProperty,
  1480. sizeof(KSNODEPROPERTY),
  1481. &AudioMixCapTable,
  1482. sizeof(KSAUDIO_MIXCAP_TABLE) - sizeof(KSAUDIO_MIX_CAPS),
  1483. &BytesReturned);
  1484. if(!NT_SUCCESS(Status)) {
  1485. goto exit;
  1486. }
  1487. if(*ppAudioMixCapTable != NULL) {
  1488. **ppAudioMixCapTable = AudioMixCapTable;
  1489. ASSERT(NT_SUCCESS(Status));
  1490. goto exit;
  1491. }
  1492. cb = (AudioMixCapTable.InputChannels *
  1493. AudioMixCapTable.OutputChannels *
  1494. sizeof(KSAUDIO_MIX_CAPS)) +
  1495. sizeof(KSAUDIO_MIXCAP_TABLE) - sizeof(KSAUDIO_MIX_CAPS);
  1496. *ppAudioMixCapTable = (PKSAUDIO_MIXCAP_TABLE)new BYTE[cb];
  1497. if(*ppAudioMixCapTable == NULL) {
  1498. Status = STATUS_INSUFFICIENT_RESOURCES;
  1499. goto exit;
  1500. }
  1501. AssertFileObject(pFileObject);
  1502. Status = KsSynchronousIoControlDevice(
  1503. pFileObject,
  1504. KernelMode,
  1505. IOCTL_KS_PROPERTY,
  1506. &NodeProperty,
  1507. sizeof(KSNODEPROPERTY),
  1508. *ppAudioMixCapTable,
  1509. cb,
  1510. &BytesReturned);
  1511. if(!NT_SUCCESS(Status)) {
  1512. Trap();
  1513. goto exit;
  1514. }
  1515. exit:
  1516. return(Status);
  1517. }