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.

5683 lines
165 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Module: mixer.c
  4. //
  5. // Description:
  6. // Contains the kernel mode portion of the mixer line driver.
  7. //
  8. //
  9. //@@BEGIN_MSINTERNAL
  10. // Development Team:
  11. // D. Baumberger
  12. //
  13. // History: Date Author Comment
  14. //
  15. //@@END_MSINTERNAL
  16. //
  17. //---------------------------------------------------------------------------
  18. //
  19. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  20. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  21. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  22. // PURPOSE.
  23. //
  24. // Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
  25. //
  26. //---------------------------------------------------------------------------
  27. ///////////////////////////////////////////////////////////////////////
  28. ///////////////////////////////////////////////////////////////////////
  29. // //
  30. // I N C L U D E S //
  31. // //
  32. ///////////////////////////////////////////////////////////////////////
  33. ///////////////////////////////////////////////////////////////////////
  34. #include "WDMSYS.H"
  35. typedef struct {
  36. VOID *pNext;
  37. PMXLCONTROL pControl;
  38. #ifdef DEBUG
  39. DWORD dwSig; // CONTROLLINK_SIGNATURE
  40. #endif
  41. } CONTROLLINK, *PCONTROLLINK;
  42. //
  43. // Data structure for Notification list.
  44. //
  45. typedef struct {
  46. PVOID pNext; // Pointer to next node in linked list.
  47. DWORD NodeId; // This is the control's Id as seen by SysAudio
  48. DWORD LineID; // Line that this control lives on
  49. DWORD ControlID; // ControlID from pControl->Control.dwControlID
  50. DWORD ControlType; //
  51. // context specific stuff...
  52. PWDMACONTEXT pContext; // Pointer to Global Context structure
  53. PMIXERDEVICE pmxd; // The mixer device for this control
  54. // PFILE_OBJECT pfo; // File Object to SysAudio for this Context
  55. PCONTROLLINK pcLink; // points to structure that contains valid
  56. // pControl addresses in this context.
  57. KDPC NodeEventDPC; // For handling the DPCs
  58. KSEVENTDATA NodeEventData;
  59. #ifdef DEBUG
  60. DWORD dwSig; // NOTIFICATION_SIGNATURE
  61. #endif
  62. } NOTENODE, *PNOTENODE;
  63. #ifdef DEBUG
  64. BOOL
  65. IsValidNoteNode(
  66. IN PNOTENODE pnnode
  67. );
  68. BOOL
  69. IsValidControlLink(
  70. IN PCONTROLLINK pcLink
  71. );
  72. /////////////////////////////////////////////////////////////////////////////
  73. //
  74. // IsValidNoteNode
  75. //
  76. // Validates that the pointer is a PNOTENODE type.
  77. //
  78. BOOL
  79. IsValidNoteNode(
  80. IN PNOTENODE pnnode)
  81. {
  82. NTSTATUS Status=STATUS_SUCCESS;
  83. try
  84. {
  85. if(pnnode->dwSig != NOTIFICATION_SIGNATURE)
  86. {
  87. DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->dwSig(%08X)",pnnode->dwSig) );
  88. Status=STATUS_UNSUCCESSFUL;
  89. }
  90. /*
  91. if(pnnode->pfo == NULL)
  92. {
  93. DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pfo(%08X)",pnnode->pfo) );
  94. Status=STATUS_UNSUCCESSFUL;
  95. }
  96. */
  97. if( !IsValidWdmaContext(pnnode->pContext) )
  98. {
  99. DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pContext(%08X)",pnnode->pContext) );
  100. Status=STATUS_UNSUCCESSFUL;
  101. }
  102. if( !IsValidMixerDevice(pnnode->pmxd) )
  103. {
  104. DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pmxd(%08X)",pnnode->pmxd) );
  105. Status=STATUS_UNSUCCESSFUL;
  106. }
  107. if( !IsValidControlLink(pnnode->pcLink) )
  108. {
  109. DPF(DL_ERROR|FA_ASSERT,("Invalid pnnode->pcLink(%08X)",pnnode->pcLink) );
  110. Status=STATUS_UNSUCCESSFUL;
  111. }
  112. }
  113. except (EXCEPTION_EXECUTE_HANDLER)
  114. {
  115. Status = GetExceptionCode();
  116. }
  117. if( NT_SUCCESS(Status) )
  118. {
  119. return TRUE;
  120. } else {
  121. return FALSE;
  122. }
  123. }
  124. /////////////////////////////////////////////////////////////////////////////
  125. //
  126. // IsValidControlLink
  127. //
  128. // Validates that the pointer is a PNOTENODE type.
  129. //
  130. BOOL
  131. IsValidControlLink(
  132. IN PCONTROLLINK pcLink)
  133. {
  134. NTSTATUS Status=STATUS_SUCCESS;
  135. try
  136. {
  137. if(pcLink->dwSig != CONTROLLINK_SIGNATURE)
  138. {
  139. DPF(DL_ERROR|FA_ASSERT,("Invalid pcLink->dwSig(%08X)",pcLink->dwSig) );
  140. Status=STATUS_UNSUCCESSFUL;
  141. }
  142. if( !IsValidControl(pcLink->pControl) )
  143. {
  144. Status=STATUS_UNSUCCESSFUL;
  145. }
  146. //
  147. // pcLink->pNext is a pointer to another CONTROLLINK structure. Thus,
  148. // if it's not NULL, the structure that it points to should also be valid.
  149. //
  150. if( pcLink->pNext )
  151. {
  152. PCONTROLLINK pTmp=pcLink->pNext;
  153. if( pTmp->dwSig != CONTROLLINK_SIGNATURE )
  154. {
  155. DPF(DL_ERROR|FA_ASSERT,("Invalid pcLink->pNext->dwSig(%08X)",pTmp->dwSig) );
  156. Status=STATUS_UNSUCCESSFUL;
  157. }
  158. }
  159. }
  160. except (EXCEPTION_EXECUTE_HANDLER)
  161. {
  162. Status = GetExceptionCode();
  163. }
  164. if( NT_SUCCESS(Status) )
  165. {
  166. return TRUE;
  167. } else {
  168. return FALSE;
  169. }
  170. }
  171. #endif
  172. PNOTENODE
  173. kmxlNewNoteNode(
  174. );
  175. PCONTROLLINK
  176. kmxlNewControlLink(
  177. IN PMXLCONTROL pControl
  178. );
  179. VOID
  180. kmxlFreeControlLink(
  181. IN OUT PCONTROLLINK pcLink
  182. );
  183. VOID
  184. kmxlFreeNoteNode(
  185. IN OUT PNOTENODE pnnode
  186. );
  187. VOID
  188. kmxlAddNoteNodeToList(
  189. IN OUT PNOTENODE pnnode
  190. );
  191. VOID
  192. kmxlRemoveNoteNodeFromList(
  193. IN OUT PNOTENODE pnnode
  194. );
  195. PNOTENODE
  196. kmxlFindControlInNoteList(
  197. IN PMXLCONTROL pControl
  198. );
  199. NTSTATUS
  200. kmxlFindNodeInNoteList(
  201. IN PNOTENODE pnlookupnode
  202. );
  203. PNOTENODE
  204. kmxlFindIdInContextInNoteList(
  205. IN PWDMACONTEXT pWdmaContext,
  206. IN PMIXERDEVICE pmxd,
  207. IN DWORD Id
  208. );
  209. PNOTENODE
  210. kmxlFindContextInNoteList(
  211. IN PWDMACONTEXT pWdmaContext
  212. );
  213. NTSTATUS
  214. kmxlAddControlToNoteList(
  215. IN OUT PNOTENODE pnnode,
  216. IN PMXLCONTROL pControl
  217. );
  218. PCONTROLLINK
  219. kmxlRemoveControlFromNoteList(
  220. IN OUT PNOTENODE pnnode,
  221. IN PMXLCONTROL pControl
  222. );
  223. NTSTATUS
  224. kmxlQueryControlChange(
  225. IN PFILE_OBJECT pfo,
  226. IN ULONG NodeId
  227. );
  228. NTSTATUS
  229. kmxlEnableControlChange(
  230. IN PFILE_OBJECT pfo,
  231. IN ULONG NodeId,
  232. IN OUT PKSEVENTDATA pksed
  233. );
  234. NTSTATUS
  235. kmxlDisableControlChange(
  236. IN PFILE_OBJECT pfo,
  237. IN ULONG NodeId,
  238. IN OUT PKSEVENTDATA pksed
  239. );
  240. NTSTATUS
  241. kmxlEnableControlChangeNotifications(
  242. IN PMIXEROBJECT pmxobj,
  243. IN PMXLLINE pLine,
  244. IN PMXLCONTROL pControl
  245. );
  246. VOID
  247. kmxlRemoveContextFromNoteList(
  248. IN PWDMACONTEXT pWdmaContext
  249. );
  250. NTSTATUS
  251. kmxlEnableAllControls(
  252. IN PMIXEROBJECT pmxobj
  253. );
  254. //
  255. // Used in persist
  256. //
  257. extern NTSTATUS
  258. kmxlPersistSingleControl(
  259. IN PFILE_OBJECT pfo,
  260. IN PMIXERDEVICE pmxd,
  261. IN PMXLCONTROL pControl,
  262. IN PVOID paDetails
  263. );
  264. VOID
  265. PersistHWControlWorker(
  266. IN LPVOID pData
  267. );
  268. VOID
  269. kmxlGrabNoteMutex(
  270. );
  271. VOID
  272. kmxlReleaseNoteMutex(
  273. );
  274. VOID
  275. kmxlCloseMixerDevice(
  276. IN OUT PMIXERDEVICE pmxd
  277. );
  278. #pragma LOCKED_CODE
  279. #pragma LOCKED_DATA
  280. ///////////////////////////////////////////////////////////////////////
  281. ///////////////////////////////////////////////////////////////////////
  282. // //
  283. // F U N C T I O N S //
  284. // //
  285. ///////////////////////////////////////////////////////////////////////
  286. ///////////////////////////////////////////////////////////////////////
  287. //
  288. // This is the head of the notification list. mtxNotification is used to
  289. // handle the list manipulcation. The allocated memory in the firstnotenode
  290. // list will be touched at DPC level.
  291. //
  292. PNOTENODE firstnotenode=NULL;
  293. extern KMUTEX mtxNote;
  294. #ifdef DEBUG
  295. LONG totalnotificationcount=0;
  296. #endif
  297. #define CALLBACKARRAYSIZE 128
  298. typedef struct {
  299. DWORD dwControlID;
  300. DWORD dwLineID;
  301. DWORD dwCallbackType;
  302. } CBINFO, *PCBINFO;
  303. ULONG emptyindex=0;
  304. volatile ULONG loadindex=0;
  305. CBINFO callbacks[CALLBACKARRAYSIZE]={0};
  306. KSPIN_LOCK HardwareCallbackSpinLock;
  307. LIST_ENTRY HardwareCallbackListHead;
  308. PKEVENT pHardwareCallbackEvent=NULL;
  309. PKSWORKER HardwareCallbackWorkerObject=NULL;
  310. WORK_QUEUE_ITEM HardwareCallbackWorkItem;
  311. ULONG HardwareCallbackScheduled=0;
  312. typedef struct tagHWLink {
  313. LIST_ENTRY Next;
  314. PNOTENODE pnnode;
  315. #ifdef DEBUG
  316. DWORD dwSig;
  317. #endif
  318. } HWLINK, *PHWLINK;
  319. /////////////////////////////////////////////////////////////////////////////
  320. //
  321. // NodeEventDPCHandler
  322. //
  323. // This routine is called at DISPATCH_LEVEL, thus you can not touch anything
  324. // but pnnode ellements. pnnode is fixed in memory thus it is safe.
  325. //
  326. VOID NodeEventDPCHandler(
  327. IN PKDPC Dpc,
  328. IN PVOID DeferredContext,
  329. IN PVOID SystemArgument1,
  330. IN PVOID SystemArgument2
  331. )
  332. {
  333. PHWLINK phwlink=NULL;
  334. PNOTENODE pnnode=(PNOTENODE)DeferredContext;
  335. PCBINFO pcbinfo;
  336. //
  337. // WorkItem: Use proper synchronization so that we never go away if there is
  338. // a work item scheduled!
  339. //
  340. DPFASSERT( pnnode->dwSig == NOTIFICATION_SIGNATURE );
  341. DPF(DL_TRACE|FA_HARDWAREEVENT, ("** ++ Event Signaled ++ **") );
  342. DPF(DL_TRACE|FA_HARDWAREEVENT, ("NodeId = %d LineID = %X ControlID = %X ControlType = %X",
  343. pnnode->NodeId,pnnode->LineID,
  344. pnnode->ControlID,pnnode->ControlType) );
  345. callbacks[loadindex%CALLBACKARRAYSIZE].dwControlID=pnnode->ControlID;
  346. callbacks[loadindex%CALLBACKARRAYSIZE].dwLineID=pnnode->LineID;
  347. callbacks[loadindex%CALLBACKARRAYSIZE].dwCallbackType=MIXER_CONTROL_CALLBACK;
  348. //
  349. // Now we want to setup a work item to persist the hardware event. The idea
  350. // is that we'll put all the events in a list and then remove them from the list
  351. // in the handler. If we already have a workitem outstanding to service the
  352. // list, we don't schedule another.
  353. if( HardwareCallbackWorkerObject )
  354. {
  355. //
  356. // Always allocate an entry in our list to service this DPC. We'll free this
  357. // event in the worker routine.
  358. //
  359. if( NT_SUCCESS(AudioAllocateMemory_Fixed(sizeof(HWLINK),
  360. TAG_AuDF_HARDWAREEVENT,
  361. ZERO_FILL_MEMORY,
  362. &phwlink) ) )
  363. {
  364. phwlink->pnnode=pnnode;
  365. #ifdef DEBUG
  366. phwlink->dwSig=HWLINK_SIGNATURE;
  367. #endif
  368. ExInterlockedInsertTailList(&HardwareCallbackListHead,
  369. &phwlink->Next,
  370. &HardwareCallbackSpinLock);
  371. //
  372. // Now, if we haven't scheduled a work item yet, do so to service this
  373. // info in the list. HardwareCallbackSchedule will be 0 at
  374. // initialization time. This variable will be set to 0 in the
  375. // work item handler. If we increment and it's any other value other
  376. // then 1, we've already scheduled a work item.
  377. //
  378. if( InterlockedIncrement(&HardwareCallbackScheduled) == 1 )
  379. {
  380. KsQueueWorkItem(HardwareCallbackWorkerObject, &HardwareCallbackWorkItem);
  381. }
  382. }
  383. }
  384. // Now if the control was a mute, then send line change as well.
  385. if (pnnode->ControlType==MIXERCONTROL_CONTROLTYPE_MUTE) {
  386. callbacks[loadindex%CALLBACKARRAYSIZE].dwCallbackType|=MIXER_LINE_CALLBACK;
  387. }
  388. loadindex++;
  389. if (pHardwareCallbackEvent!=NULL) {
  390. KeSetEvent(pHardwareCallbackEvent, 0 , FALSE);
  391. }
  392. }
  393. #pragma PAGEABLE_CODE
  394. #pragma PAGEABLE_DATA
  395. /////////////////////////////////////////////////////////////////////////////
  396. //
  397. // kmxlNewNoteNode
  398. //
  399. // This routine allocates another Notification node. Note that AudioAllocateMemory zeros
  400. // all successful memory allocations.
  401. //
  402. // The return value is a pointer to a Notification Node or NULL.
  403. //
  404. PNOTENODE
  405. kmxlNewNoteNode(
  406. )
  407. {
  408. PNOTENODE pnnode = NULL;
  409. NTSTATUS Status;
  410. PAGED_CODE();
  411. Status = AudioAllocateMemory_Fixed(sizeof( NOTENODE ),
  412. TAG_AuDN_NOTIFICATION,
  413. ZERO_FILL_MEMORY,
  414. &pnnode );
  415. if( !NT_SUCCESS( Status ) ) {
  416. return( NULL );
  417. }
  418. DPF(DL_MAX|FA_NOTE, ("New notification node allocated %08X",pnnode) );
  419. #ifdef DEBUG
  420. pnnode->dwSig=NOTIFICATION_SIGNATURE;
  421. #endif
  422. return pnnode;
  423. }
  424. /////////////////////////////////////////////////////////////////////////////
  425. //
  426. // kmxlNewControlLink
  427. //
  428. // This routine allocates another CONTROLLINK node. Note that AudioAllocateMemory zeros
  429. // all successful memory allocations.
  430. //
  431. // The return value is a pointer to a Notification Node or NULL.
  432. //
  433. PCONTROLLINK
  434. kmxlNewControlLink(
  435. IN PMXLCONTROL pControl
  436. )
  437. {
  438. PCONTROLLINK pcLink = NULL;
  439. NTSTATUS Status;
  440. PAGED_CODE();
  441. Status = AudioAllocateMemory_Fixed(sizeof( CONTROLLINK ),
  442. TAG_AuDL_LINK,
  443. ZERO_FILL_MEMORY,
  444. &pcLink );
  445. if( !NT_SUCCESS( Status ) ) {
  446. return( NULL );
  447. }
  448. DPF(DL_MAX|FA_NOTE, ("New controllink node allocated %08X",pcLink) );
  449. #ifdef DEBUG
  450. pcLink->dwSig=CONTROLLINK_SIGNATURE;
  451. #endif
  452. //
  453. // Setup the pcontrol first and then link it in.
  454. //
  455. pcLink->pControl=pControl;
  456. return pcLink;
  457. }
  458. VOID
  459. kmxlFreeControlLink(
  460. IN OUT PCONTROLLINK pcLink
  461. )
  462. {
  463. DPFASSERT(IsValidControlLink(pcLink));
  464. PAGED_CODE();
  465. #ifdef DEBUG
  466. pcLink->dwSig=(DWORD)0xDEADBEEF;
  467. #endif
  468. AudioFreeMemory(sizeof( CONTROLLINK ),&pcLink);
  469. }
  470. /////////////////////////////////////////////////////////////////////////////
  471. //
  472. // kmxlFreeNoteNode
  473. //
  474. // This routine frees a Notification Node.
  475. //
  476. VOID
  477. kmxlFreeNoteNode(
  478. IN OUT PNOTENODE pnnode
  479. )
  480. {
  481. PCONTROLLINK pcLink,pcTmp;
  482. PAGED_CODE();
  483. DPFASSERT( pnnode->dwSig == NOTIFICATION_SIGNATURE );
  484. DPFASSERT( pnnode->pNext == NULL );
  485. DPFASSERT( pnnode->pcLink == NULL );
  486. DPF(DL_MAX|FA_NOTE,("NotificationNode freed %08X",pnnode) );
  487. AudioFreeMemory( sizeof( NOTENODE ),&pnnode );
  488. }
  489. /////////////////////////////////////////////////////////////////////////////
  490. //
  491. // kmxlAddNoteNodeToList
  492. //
  493. // This routine adds the NotificationNode to the list. It places the node
  494. // at the head of the list.
  495. //
  496. VOID
  497. kmxlAddNoteNodeToList(
  498. IN OUT PNOTENODE pnnode
  499. )
  500. {
  501. PAGED_CODE();
  502. pnnode->pNext=firstnotenode;
  503. firstnotenode=pnnode;
  504. #ifdef DEBUG
  505. totalnotificationcount++;
  506. #endif
  507. DPF(DL_TRACE|FA_INSTANCE,("New NoteNode head %08X, Total=%d",pnnode,totalnotificationcount) );
  508. }
  509. /////////////////////////////////////////////////////////////////////////////
  510. //
  511. // kmxlRemoveNoteNodeFromList
  512. //
  513. // This routine removes a node from the list.
  514. //
  515. VOID
  516. kmxlRemoveNoteNodeFromList(
  517. IN OUT PNOTENODE pnnode
  518. )
  519. {
  520. PNOTENODE pTmp;
  521. PAGED_CODE();
  522. DPFASSERT(pnnode->dwSig == NOTIFICATION_SIGNATURE);
  523. // are we the head of the list? If so, move the next to the head.
  524. if( pnnode == firstnotenode )
  525. {
  526. firstnotenode=firstnotenode->pNext;
  527. DPF(DL_MAX|FA_NOTE,("removed notenode head") );
  528. } else {
  529. // we are somewhere in the list we need to walk the list until we find
  530. // our node and clip it out.
  531. for (pTmp=firstnotenode;pTmp!=NULL;pTmp=pTmp->pNext)
  532. {
  533. DPFASSERT(pTmp->dwSig==NOTIFICATION_SIGNATURE);
  534. // Did we find our node in the next position? If so, we
  535. // need to clip it out. Thus, pTmp.next needs to point to
  536. // (our node)'s next.
  537. if(pTmp->pNext == pnnode)
  538. {
  539. pTmp->pNext = pnnode->pNext;
  540. DPF(DL_MAX|FA_NOTE,("removed middle") );
  541. break;
  542. }
  543. }
  544. }
  545. #ifdef DEBUG
  546. totalnotificationcount--;
  547. #endif
  548. //
  549. // to indicate that this node has been removed, pNext gets set to NULL!
  550. //
  551. pnnode->pNext=NULL;
  552. }
  553. /////////////////////////////////////////////////////////////////////////////
  554. //
  555. // kmxlFindControlInNoteList
  556. //
  557. // This routine walks the linked list looking for this control. Because all controls
  558. // are globally allocated, all pControl addresses will be unique. Thus, all
  559. // we really need to do is find the control. If an exact match is found,
  560. // pnnode is returned.
  561. //
  562. PNOTENODE
  563. kmxlFindControlInNoteList(
  564. IN PMXLCONTROL pControl )
  565. {
  566. PNOTENODE pnnode;
  567. PCONTROLLINK pcLink;
  568. PAGED_CODE();
  569. for (pnnode=firstnotenode;pnnode!=NULL;pnnode=pnnode->pNext)
  570. {
  571. //
  572. // Can't check entire structure because pmxd may have been cleaned out.
  573. //
  574. DPFASSERT(pnnode->dwSig == NOTIFICATION_SIGNATURE);
  575. for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
  576. {
  577. DPFASSERT(IsValidControlLink(pcLink));
  578. if( pcLink->pControl == pControl )
  579. {
  580. //
  581. // We found this control in the list!
  582. //
  583. return pnnode;
  584. }
  585. }
  586. }
  587. return NULL;
  588. }
  589. /////////////////////////////////////////////////////////////////////////////
  590. //
  591. // kmxlFindControlTypeInList
  592. //
  593. // This routine walks
  594. //
  595. PMXLCONTROL
  596. kmxlFindControlTypeInList(
  597. IN PNOTENODE pnnode,
  598. IN DWORD dwControlType )
  599. {
  600. PCONTROLLINK pcLink;
  601. PAGED_CODE();
  602. for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
  603. {
  604. DPFASSERT(IsValidControlLink(pcLink));
  605. if( pcLink->pControl->Control.dwControlType == dwControlType )
  606. {
  607. //
  608. // We found this control in the list! Types match.
  609. //
  610. DPF(DL_TRACE|FA_NOTE,("Found Correct pControl %08X",pcLink->pControl) );
  611. return pcLink->pControl;
  612. }
  613. }
  614. return NULL;
  615. }
  616. #ifdef DEBUG
  617. /////////////////////////////////////////////////////////////////////////////
  618. //
  619. // kmxlFindAddressInNoteList
  620. //
  621. // This routine walks the linked list looking for this control within this
  622. // context. If an exact match is found, pnnode is returned.
  623. //
  624. VOID
  625. kmxlFindAddressInNoteList(
  626. IN PMXLCONTROL pControl
  627. )
  628. {
  629. PNOTENODE pnnode;
  630. PCONTROLLINK pcLink;
  631. PAGED_CODE();
  632. for (pnnode=firstnotenode;pnnode!=NULL;pnnode=pnnode->pNext)
  633. {
  634. DPFASSERT(pnnode->dwSig == NOTIFICATION_SIGNATURE);
  635. //
  636. // Let's look to see if this address is one of our pControls!
  637. //
  638. for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
  639. {
  640. DPFASSERT(pcLink->dwSig == CONTROLLINK_SIGNATURE);
  641. if( pcLink->pControl == pControl )
  642. {
  643. //
  644. // We found this control in the list - in the right context!
  645. //
  646. DPF(DL_ERROR|FA_NOTE,("Found pControl(%08X) in our list!",pControl) );
  647. return ;
  648. }
  649. }
  650. }
  651. return ;
  652. }
  653. #endif
  654. /////////////////////////////////////////////////////////////////////////////
  655. //
  656. // kmxlFindNodeInNoteList
  657. //
  658. // Walks the node list looking for this node. This must be called within the
  659. // mtxMutex.
  660. //
  661. NTSTATUS
  662. kmxlFindNodeInNoteList(
  663. IN PNOTENODE pnlookupnode )
  664. {
  665. PNOTENODE pnnode;
  666. PAGED_CODE();
  667. for (pnnode=firstnotenode;pnnode!=NULL;pnnode=pnnode->pNext)
  668. {
  669. if( pnnode == pnlookupnode )
  670. {
  671. //
  672. // Only if we find what we're looking for do we actually verify
  673. // that it's still good.
  674. //
  675. DPFASSERT(IsValidNoteNode(pnnode));
  676. //
  677. // we found this control in the list
  678. //
  679. return STATUS_SUCCESS;
  680. }
  681. }
  682. return STATUS_UNSUCCESSFUL;
  683. }
  684. /////////////////////////////////////////////////////////////////////////////
  685. //
  686. // kmxlFindIdInContextInNoteList
  687. //
  688. // Walks the notification list looking for the node that contains this id in
  689. // this context.
  690. //
  691. PNOTENODE
  692. kmxlFindIdInContextInNoteList(
  693. IN PWDMACONTEXT pWdmaContext,
  694. IN PMIXERDEVICE pmxd,
  695. IN DWORD NodeId
  696. )
  697. {
  698. PNOTENODE pnTmp;
  699. PAGED_CODE();
  700. for (pnTmp=firstnotenode;pnTmp!=NULL;pnTmp=pnTmp->pNext)
  701. {
  702. DPFASSERT(IsValidNoteNode(pnTmp));
  703. if(( pnTmp->NodeId == NodeId ) &&
  704. ( pnTmp->pmxd == pmxd ) &&
  705. ( pnTmp->pContext == pWdmaContext ) )
  706. {
  707. //
  708. // we found this control in the list in this context.
  709. //
  710. return pnTmp;
  711. }
  712. }
  713. //
  714. // We have walked the list. There is no control with this ID in this Context.
  715. //
  716. return NULL;
  717. }
  718. /////////////////////////////////////////////////////////////////////////////
  719. //
  720. // kmxlFindContextInNoteList
  721. //
  722. PNOTENODE
  723. kmxlFindContextInNoteList(
  724. IN PWDMACONTEXT pWdmaContext
  725. )
  726. {
  727. PNOTENODE pnTmp;
  728. PAGED_CODE();
  729. for (pnTmp=firstnotenode;pnTmp!=NULL;pnTmp=pnTmp->pNext)
  730. {
  731. DPFASSERT(IsValidNoteNode(pnTmp));
  732. if( pnTmp->pContext == pWdmaContext )
  733. {
  734. //
  735. // Found this context in our list.
  736. //
  737. DPFBTRAP();
  738. return pnTmp;
  739. }
  740. }
  741. //
  742. // We have walked the list. There is no control with this ID in this Context.
  743. //
  744. return NULL;
  745. }
  746. /////////////////////////////////////////////////////////////////////////////
  747. //
  748. // kmxlAddControlToNoteList
  749. //
  750. // Adds this pControl to pcLink list.
  751. //
  752. NTSTATUS
  753. kmxlAddControlToNoteList(
  754. IN OUT PNOTENODE pnnode,
  755. IN PMXLCONTROL pControl
  756. )
  757. {
  758. NTSTATUS Status=STATUS_SUCCESS;
  759. PCONTROLLINK pcLink = NULL;
  760. PAGED_CODE();
  761. #ifdef DEBUG
  762. //
  763. // Let's walk the list and make double sure that this node is not already in
  764. // the list.
  765. //
  766. for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
  767. {
  768. if( pcLink->pControl == pControl )
  769. {
  770. DPF(DL_ERROR|FA_NOTE,("pControl(%08X) already in list!") );
  771. return STATUS_UNSUCCESSFUL;
  772. }
  773. }
  774. #endif
  775. Status = AudioAllocateMemory_Fixed(sizeof( CONTROLLINK ),
  776. TAG_AuDL_LINK,
  777. ZERO_FILL_MEMORY,
  778. &pcLink );
  779. if( !NT_SUCCESS( Status ) ) {
  780. DPF(DL_ERROR|FA_NOTE,("Wasn't able to add control to list! Status=%08X",Status) );
  781. return Status;
  782. }
  783. #ifdef DEBUG
  784. pcLink->dwSig=CONTROLLINK_SIGNATURE;
  785. #endif
  786. pcLink->pControl=pControl;
  787. //
  788. // Make new head.
  789. //
  790. // if( pnnode->pcLink != NULL )
  791. // _asm int 3
  792. pcLink->pNext=pnnode->pcLink;
  793. pnnode->pcLink=pcLink;
  794. DPF(DL_TRACE|FA_NOTE,("Added pControl %d to pnnode(%08X)",pControl->Control.dwControlID,pnnode) );
  795. return Status;
  796. }
  797. PCONTROLLINK
  798. kmxlRemoveControlFromNoteList(
  799. IN OUT PNOTENODE pnnode,
  800. IN PMXLCONTROL pControl
  801. )
  802. {
  803. PCONTROLLINK pcLink,pcTmp;
  804. PAGED_CODE();
  805. //
  806. // Does the first node contain our pControl?
  807. //
  808. DPFASSERT(IsValidControlLink(pnnode->pcLink));
  809. for(pcLink=pnnode->pcLink;pcLink!=NULL;pcLink=pcLink->pNext)
  810. {
  811. if( pcLink->pControl == pControl )
  812. break;
  813. }
  814. if( pcLink == pnnode->pcLink )
  815. {
  816. pnnode->pcLink = pcLink->pNext;
  817. DPF(DL_TRACE|FA_NOTE,("removed head pControlLink") );
  818. } else {
  819. for( pcTmp=pnnode->pcLink;pcTmp!=NULL;pcTmp=pcTmp->pNext)
  820. {
  821. if( pcTmp->pNext == pcLink )
  822. {
  823. pcTmp->pNext = pcLink->pNext;
  824. DPF(DL_TRACE|FA_NOTE,("Removed Middle pControlLink") );
  825. break;
  826. }
  827. }
  828. }
  829. // DPFASSERT(IsValidNoteNode(pnnode));
  830. return pcLink;
  831. }
  832. /////////////////////////////////////////////////////////////////////////////
  833. //
  834. // kmxlQueryControlChange
  835. //
  836. // Query's to see if control change notifications are supported on this
  837. // control.
  838. //
  839. NTSTATUS
  840. kmxlQueryControlChange(
  841. IN PFILE_OBJECT pfo, // Handle of the topology driver instance
  842. IN ULONG NodeId //PMXLCONTROL pControl
  843. )
  844. {
  845. NTSTATUS Status;
  846. KSE_NODE KsNodeEvent;
  847. ULONG BytesReturned;
  848. PAGED_CODE();
  849. // initialize event for basic support query
  850. RtlZeroMemory(&KsNodeEvent,sizeof(KSE_NODE));
  851. KsNodeEvent.Event.Set = KSEVENTSETID_AudioControlChange;
  852. KsNodeEvent.Event.Id = KSEVENT_CONTROL_CHANGE;
  853. KsNodeEvent.Event.Flags = KSEVENT_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
  854. KsNodeEvent.NodeId = NodeId;
  855. DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_ENABLE_EVENT") );
  856. Status = KsSynchronousIoControlDevice(
  857. pfo, // The FILE_OBJECT for SysAudio
  858. KernelMode, // Call originates in Kernel mode
  859. IOCTL_KS_ENABLE_EVENT, // KS PROPERTY IOCTL
  860. &KsNodeEvent, // Pointer to the KSNODEPROPERTY struct
  861. sizeof(KSE_NODE), // Number or bytes input
  862. NULL, // Pointer to the buffer to store output
  863. 0, // Size of the output buffer
  864. &BytesReturned // Number of bytes returned from the call
  865. );
  866. if (!NT_SUCCESS(Status)) {
  867. DPF( DL_MAX|FA_HARDWAREEVENT, ("NODE #%d: KSEVENT_CONTROL_CHANGE Not Supported Error %08X",NodeId,Status) );
  868. RETURN( Status );
  869. }
  870. DPF(DL_TRACE|FA_HARDWAREEVENT ,("NodeId #%d: KSEVENT_CONTROL_CHANGE Supported",NodeId) );
  871. return Status;
  872. }
  873. /////////////////////////////////////////////////////////////////////////////
  874. //
  875. // kmxlEnableControlChange
  876. //
  877. // Turns on Control chnage notifications on this control.
  878. //
  879. NTSTATUS
  880. kmxlEnableControlChange(
  881. IN PFILE_OBJECT pfo, // Handle of the topology driver instance
  882. IN ULONG NodeId, //PMXLCONTROL pControl,
  883. IN OUT PKSEVENTDATA pksed
  884. )
  885. {
  886. NTSTATUS Status;
  887. KSE_NODE KsNodeEvent;
  888. ULONG BytesReturned;
  889. PAGED_CODE();
  890. // try to add an event
  891. RtlZeroMemory(&KsNodeEvent,sizeof(KSE_NODE));
  892. KsNodeEvent.Event.Set = KSEVENTSETID_AudioControlChange;
  893. KsNodeEvent.Event.Id = KSEVENT_CONTROL_CHANGE;
  894. KsNodeEvent.Event.Flags = KSEVENT_TYPE_ENABLE | KSPROPERTY_TYPE_TOPOLOGY;
  895. KsNodeEvent.NodeId = NodeId;
  896. DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_ENABLE_EVENT") );
  897. Status = KsSynchronousIoControlDevice(
  898. pfo, // The FILE_OBJECT for SysAudio
  899. KernelMode, // Call originates in Kernel mode
  900. IOCTL_KS_ENABLE_EVENT, // KS PROPERTY IOCTL
  901. &KsNodeEvent, // Pointer to the KSNODEPROPERTY struct
  902. sizeof(KSE_NODE), // Number or bytes input
  903. pksed, // Pointer to the buffer to store output
  904. sizeof(KSEVENTDATA), // Size of the output buffer
  905. &BytesReturned // Number of bytes returned from the call
  906. );
  907. if (!NT_SUCCESS(Status)) {
  908. DPF(DL_WARNING|FA_HARDWAREEVENT ,("KSEVENT_CONTROL_CHANGE Enable FAILED Error %08X",Status) );
  909. RETURN( Status );
  910. }
  911. DPF(DL_TRACE|FA_HARDWAREEVENT ,
  912. ("KSEVENT_CONTROL_CHANGE Enabled on NodeId #%d",NodeId) );
  913. return Status;
  914. }
  915. /////////////////////////////////////////////////////////////////////////////
  916. //
  917. // kmxlDisableControlChange
  918. //
  919. // Turns off Control chnage notifications on this control.
  920. //
  921. NTSTATUS
  922. kmxlDisableControlChange(
  923. IN PFILE_OBJECT pfo, // Handle of the topology driver instance
  924. IN ULONG NodeId, //PMXLCONTROL pControl,
  925. IN OUT PKSEVENTDATA pksed
  926. )
  927. {
  928. NTSTATUS Status;
  929. KSE_NODE KsNodeEvent;
  930. ULONG BytesReturned;
  931. PAGED_CODE();
  932. // initialize event for basic support query
  933. RtlZeroMemory(&KsNodeEvent,sizeof(KSE_NODE));
  934. KsNodeEvent.Event.Set = KSEVENTSETID_AudioControlChange;
  935. KsNodeEvent.Event.Id = KSEVENT_CONTROL_CHANGE;
  936. KsNodeEvent.Event.Flags = KSEVENT_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
  937. KsNodeEvent.NodeId = NodeId;
  938. DPF(DL_TRACE|FA_SYSAUDIO,("IOCTL_KS_DISABLE_EVENT") );
  939. Status = KsSynchronousIoControlDevice(
  940. pfo, // The FILE_OBJECT for SysAudio
  941. KernelMode, // Call originates in Kernel mode
  942. IOCTL_KS_DISABLE_EVENT, // KS PROPERTY IOCTL
  943. pksed, // Pointer to the buffer to store output
  944. sizeof(KSEVENTDATA), // Size of the output buffer
  945. NULL, // Pointer to the KSNODEPROPERTY struct
  946. 0, // Number or bytes input
  947. &BytesReturned // Number of bytes returned from the call
  948. );
  949. if (!NT_SUCCESS(Status)) {
  950. DPF(DL_WARNING|FA_HARDWAREEVENT,("KSEVENT_CONTROL_CHANGE Disable FAILED") );
  951. RETURN( Status );
  952. }
  953. DPF(DL_TRACE|FA_HARDWAREEVENT,
  954. ("KSEVENT_CONTROL_CHANGE disabled on NodeId #%d",NodeId) );
  955. return Status;
  956. }
  957. VOID
  958. kmxlGrabNoteMutex(
  959. )
  960. {
  961. //
  962. // Turn off the APCDisable flag in the thread structure before going for our
  963. // mutex. This will prevent us from getting suspeneded while holding this
  964. // mutex.
  965. //
  966. KeEnterCriticalRegion();
  967. KeWaitForMutexObject(&mtxNote, Executive, KernelMode, FALSE, NULL);
  968. }
  969. VOID
  970. kmxlReleaseNoteMutex(
  971. )
  972. {
  973. KeReleaseMutex(&mtxNote, FALSE);
  974. KeLeaveCriticalRegion();
  975. }
  976. /////////////////////////////////////////////////////////////////////////////
  977. //
  978. // kmxlEnableControlChangeNotifications
  979. //
  980. // This routine gets called every time a new control is created. At that point
  981. // we're going to query the node to see if it supports change notifications
  982. // and enable them in this context.
  983. //
  984. NTSTATUS
  985. kmxlEnableControlChangeNotifications(
  986. IN PMIXEROBJECT pmxobj,
  987. IN PMXLLINE pLine, // Line that owns control
  988. IN PMXLCONTROL pControl // Control to Enable
  989. )
  990. {
  991. PNOTENODE pnnode;
  992. NTSTATUS Status;
  993. LONG i;
  994. PMIXERDEVICE pmxd;
  995. PWDMACONTEXT pWdmaContext;
  996. kmxlGrabNoteMutex();
  997. DPFASSERT(IsValidMixerObject(pmxobj));
  998. pmxd=pmxobj->pMixerDevice;
  999. DPFASSERT(IsValidMixerDevice(pmxd));
  1000. pWdmaContext=pmxd->pWdmaContext;
  1001. PAGED_CODE();
  1002. //
  1003. // Never allow anything in the list if it's not valid!
  1004. //
  1005. DPFASSERT(IsValidControl(pControl));
  1006. //
  1007. // The first thing we do is look to see if a control of this ID is enabled in
  1008. // this context. If so, we'll get a PNOTENODE structure returned to us that
  1009. // contains that ID. All we should have to do is add our new pControl.
  1010. //
  1011. if( pnnode=kmxlFindIdInContextInNoteList(pWdmaContext,pmxd,pControl->Id) ) // Not pControl->Control.dwControlID
  1012. {
  1013. //
  1014. // yes there is. Add this pControl to this pnnode and we're done.
  1015. //
  1016. Status=kmxlAddControlToNoteList(pnnode,pControl);
  1017. } else {
  1018. //
  1019. // There is no Control with this ID in this Context. Let's try to
  1020. // Add one.
  1021. //
  1022. //
  1023. // This node is not in our list, we need to add it if and only if it
  1024. // supports change notifications
  1025. //
  1026. Status=kmxlQueryControlChange(pmxd->pfo,pControl->Id); //pLocfo
  1027. if( NT_SUCCESS(Status) )
  1028. {
  1029. //
  1030. // This control supports notifications, thus add info to our
  1031. // global list - if it's not already there...
  1032. //
  1033. if( (pnnode=kmxlNewNoteNode()) != NULL )
  1034. {
  1035. if( (pnnode->pcLink=kmxlNewControlLink(pControl)) != NULL )
  1036. {
  1037. pnnode->NodeId=pControl->Id;
  1038. //
  1039. // We have a new notification node to fill out.
  1040. //
  1041. pnnode->ControlID=pControl->Control.dwControlID;
  1042. pnnode->ControlType=pControl->Control.dwControlType;
  1043. pnnode->LineID=pLine->Line.dwLineID;
  1044. pnnode->pContext=pWdmaContext; //NOTENODE
  1045. pnnode->pmxd=pmxd;
  1046. DPF(DL_TRACE|FA_NOTE ,
  1047. ("Init pnnode, NodeId=#%d: CtrlID=%X CtrlType=%X Context=%08X",
  1048. pnnode->NodeId, //pControl->Id
  1049. pnnode->ControlID, //pControl->Control.dwControlID,
  1050. pnnode->ControlType,
  1051. pnnode->pContext) ); //pControl->Control.dwControlType) );
  1052. //
  1053. // Now setup the DPC handling
  1054. //
  1055. KeInitializeDpc(&pnnode->NodeEventDPC,NodeEventDPCHandler,pnnode);
  1056. pnnode->NodeEventData.NotificationType=KSEVENTF_DPC;
  1057. pnnode->NodeEventData.Dpc.Dpc=&pnnode->NodeEventDPC;
  1058. //
  1059. // At this point, we've got a little window. We call and enable
  1060. // events on this control. From that time until the time we actually
  1061. // add it to the list, if an event fires, we will not find this node
  1062. // in the list - thus we will not process it.
  1063. //
  1064. Status=kmxlEnableControlChange(pnnode->pmxd->pfo,
  1065. pnnode->NodeId, //pControl,
  1066. &pnnode->NodeEventData);
  1067. //&NodeEvent[NumEventDPCs].NodeEventData);
  1068. if( NT_SUCCESS(Status) )
  1069. {
  1070. DPF(DL_TRACE|FA_HARDWAREEVENT,("Enabled Control #%d in context(%08X)!",pControl->Id,pWdmaContext) );
  1071. //
  1072. // Now let's add it to our global list
  1073. //
  1074. //
  1075. kmxlAddNoteNodeToList(pnnode);
  1076. DPFASSERT(IsValidNoteNode(pnnode));
  1077. } else {
  1078. DPF(DL_WARNING|FA_HARDWAREEVENT,("Failed to Enable Control #%d!",pControl->Id) );
  1079. DPFBTRAP();
  1080. //
  1081. // For some reason, this node failed to enable.
  1082. //
  1083. kmxlFreeControlLink(pnnode->pcLink);
  1084. kmxlFreeNoteNode(pnnode);
  1085. }
  1086. } else {
  1087. DPF(DL_ERROR|FA_NOTE,("kmxlNewControlLink failed") );
  1088. kmxlFreeNoteNode(pnnode);
  1089. }
  1090. } else {
  1091. DPF(DL_WARNING|FA_NOTE,("kmxlNewNoteNode failed") );
  1092. }
  1093. } else {
  1094. DPF(DL_MAX|FA_HARDWAREEVENT,("This control #%d doesn't support change notifications",pControl->Id) );
  1095. }
  1096. }
  1097. kmxlReleaseNoteMutex();
  1098. return Status;
  1099. }
  1100. /////////////////////////////////////////////////////////////////////////////
  1101. //
  1102. // kmxlDisableControlChangeNotifications
  1103. //
  1104. // This routine gets called every time a control is freed. We walk the list
  1105. // of enable controls and see if it's there. If so, we disable and clean up.
  1106. // if it's not in the list, it didn't support change notifications.
  1107. //
  1108. NTSTATUS
  1109. kmxlDisableControlChangeNotifications(
  1110. IN PMXLCONTROL pControl
  1111. )
  1112. {
  1113. NTSTATUS Status=STATUS_SUCCESS;
  1114. PNOTENODE pnnode;
  1115. PAGED_CODE();
  1116. kmxlGrabNoteMutex();
  1117. DPFASSERT(IsValidControl(pControl));
  1118. //
  1119. // Find this control in our list.
  1120. //
  1121. if(pnnode=kmxlFindControlInNoteList(pControl)) //pWdmaContext,
  1122. {
  1123. PCONTROLLINK pcLink;
  1124. #ifdef DEBUG
  1125. if( pControl->Id != pnnode->NodeId )
  1126. {
  1127. DPF(DL_ERROR|FA_NOTE,("Control NodeId Changed! CtrlId=%08X,pnnodeID=%08X",
  1128. pControl->Id,pnnode->NodeId) );
  1129. }
  1130. #endif
  1131. //
  1132. // This call removes pControl from this node. note that after this
  1133. // control is removed, pnnode->pcLink will be NULL if there are no
  1134. // more controls hanging on this Notification node. Thus, we need
  1135. // to disable it.
  1136. //
  1137. pcLink=kmxlRemoveControlFromNoteList(pnnode,pControl);
  1138. if( pnnode->pcLink == NULL )
  1139. {
  1140. //
  1141. // During cleanup, the mixer device structure will have already been
  1142. // cleaned up. This can be seen because the pmxd->Device entry will
  1143. // be UNUSED_DEVICE. Thus, we can't do anything on that mixerdevice.
  1144. //
  1145. if( ( pnnode->pmxd->Device != UNUSED_DEVICE ) &&
  1146. ( pnnode->pmxd->pfo != NULL ) )
  1147. {
  1148. //
  1149. // There are no references to this node, we can free it. But, if
  1150. // this disable call fails, then the node was already distroyed.
  1151. //
  1152. Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
  1153. if( !NT_SUCCESS(Status) )
  1154. {
  1155. DPF(DL_WARNING|FA_NOTE,("Not able to disable! pnnode %08X",pnnode) );
  1156. }
  1157. } else {
  1158. DPF(DL_WARNING|FA_NOTE,("pmxd is invalid %08X",pnnode->pmxd) );
  1159. }
  1160. kmxlRemoveNoteNodeFromList(pnnode);
  1161. kmxlFreeNoteNode(pnnode);
  1162. }
  1163. DPF(DL_TRACE|FA_NOTE,("Removed PCONTROLLINK(%08X) for pControl(%08X)",pcLink,pcLink->pControl) );
  1164. kmxlFreeControlLink(pcLink);
  1165. } else {
  1166. //
  1167. // We get called on every control free. Thus, many will not be in our list.
  1168. //
  1169. DPF(DL_MAX|FA_NOTE,("Control=%d not in List!",pControl->Id) );
  1170. }
  1171. kmxlReleaseNoteMutex();
  1172. return Status;
  1173. }
  1174. /////////////////////////////////////////////////////////////////////////////
  1175. //
  1176. // kmxlRemoveContextFromNoteList
  1177. //
  1178. // This routine gets called when this context is going away. Thus, we need to
  1179. // remove it from our list.
  1180. //
  1181. VOID
  1182. kmxlRemoveContextFromNoteList(
  1183. IN PWDMACONTEXT pContext
  1184. )
  1185. {
  1186. NTSTATUS Status;
  1187. PNOTENODE pnnode;
  1188. PCONTROLLINK pcLink;
  1189. PAGED_CODE();
  1190. kmxlGrabNoteMutex();
  1191. //
  1192. // If we find this context is still alive in our list, someone leaked a control.
  1193. // Things should have been cleaned up when the controls went away! But, for
  1194. // some reasons they didn't.
  1195. //
  1196. // There could be multiple pContext nodes in list.
  1197. //
  1198. while( (pnnode=kmxlFindContextInNoteList(pContext)) != NULL )
  1199. {
  1200. DPFASSERT(IsValidNoteNode(pnnode));
  1201. DPF(DL_ERROR|FA_NOTE,("pContext(%08X) found in Notification List!",pContext) );
  1202. kmxlRemoveNoteNodeFromList(pnnode);
  1203. //
  1204. // There can be multiple Controls on this Notification node.
  1205. //
  1206. while( (pnnode->pcLink != NULL) &&
  1207. ( (pcLink=kmxlRemoveControlFromNoteList(pnnode,pnnode->pcLink->pControl)) != NULL) )
  1208. {
  1209. //
  1210. // To get here, pcLink is Valid. If it was the last pControl, then
  1211. // we want to turn off change notifications on it.
  1212. //
  1213. if( pnnode->pcLink == NULL )
  1214. {
  1215. //
  1216. // There are no references to this node, we can free it.
  1217. //
  1218. Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
  1219. DPFASSERT( Status == STATUS_SUCCESS );
  1220. DPFBTRAP();
  1221. }
  1222. kmxlFreeControlLink(pcLink);
  1223. DPFBTRAP();
  1224. }
  1225. kmxlFreeNoteNode(pnnode);
  1226. DPFBTRAP();
  1227. }
  1228. DPF(DL_TRACE|FA_NOTE,("pWdmaContext %08X going away",pContext) );
  1229. kmxlReleaseNoteMutex();
  1230. }
  1231. /////////////////////////////////////////////////////////////////////////////
  1232. //
  1233. // kmxlCleanupNotelist
  1234. //
  1235. // Driver is unloading, turn everything off and free the memory!
  1236. //
  1237. VOID
  1238. kmxlCleanupNoteList(
  1239. )
  1240. {
  1241. NTSTATUS Status;
  1242. PNOTENODE pnnode,pnnodeFree;
  1243. PCONTROLLINK pcLink;
  1244. PAGED_CODE();
  1245. kmxlGrabNoteMutex();
  1246. //
  1247. // If we find this context is still alive in our list, someone leaked a control.
  1248. // Things should have been cleaned up when the controls went away! But, for
  1249. // some reasons they didn't.
  1250. //
  1251. // There could be multiple pContext nodes in list.
  1252. //
  1253. pnnode=firstnotenode;
  1254. while( pnnode )
  1255. {
  1256. DPFASSERT(IsValidNoteNode(pnnode));
  1257. DPF(DL_ERROR|FA_NOTE,("pnnode(%08X) found in Notification List!",pnnode) );
  1258. kmxlRemoveNoteNodeFromList(pnnode);
  1259. //
  1260. // There can be multiple Controls on this Notification node.
  1261. //
  1262. while( (pnnode->pcLink != NULL) &&
  1263. ( (pcLink=kmxlRemoveControlFromNoteList(pnnode,pnnode->pcLink->pControl)) != NULL) )
  1264. {
  1265. //
  1266. // To get here, pcLink is Valid. If it was the last pControl, then
  1267. // we want to turn off change notifications on it.
  1268. //
  1269. if( pnnode->pcLink == NULL )
  1270. {
  1271. //
  1272. // There are no references to this node, we can free it.
  1273. //
  1274. Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
  1275. DPFASSERT( Status == STATUS_SUCCESS );
  1276. DPFBTRAP();
  1277. }
  1278. kmxlFreeControlLink(pcLink);
  1279. DPFBTRAP();
  1280. }
  1281. pnnodeFree=pnnode;
  1282. pnnode=pnnode->pNext;
  1283. kmxlFreeNoteNode(pnnodeFree);
  1284. DPFBTRAP();
  1285. }
  1286. DPF(DL_TRACE|FA_NOTE,("Done with cleanup") );
  1287. kmxlReleaseNoteMutex();
  1288. }
  1289. /////////////////////////////////////////////////////////////////////////////
  1290. //
  1291. // kmxlPersistHWControlWorker
  1292. //
  1293. // When kmxlPersistHWControlWorker gets called, the pData value is a pointer
  1294. // to a globally allocated NOTENODE structure. Thus, we have all the context
  1295. // we need with regard to persisting the control. We just need to make sure
  1296. // that our node didn't go away between the time the evern was scheduled and
  1297. // when we got called.
  1298. //
  1299. VOID
  1300. kmxlPersistHWControlWorker(
  1301. PVOID pReference
  1302. )
  1303. {
  1304. NTSTATUS Status;
  1305. PMXLCONTROL pControl;
  1306. PVOID paDetails = NULL; // kmxlPersistSingleControl() allocates paDetails
  1307. // via kmxlGetCurrentControlValue()
  1308. PNOTENODE pnnode;
  1309. PHWLINK phwlink;
  1310. PLIST_ENTRY ple;
  1311. PAGED_CODE();
  1312. //
  1313. // We set HardwareCallbackSchedule to 0 here so that we can start adding new
  1314. // events for handling hardware notifications. Note: we do that here at the
  1315. // start of the routine so that there will not be a window where we have
  1316. // something in the list that we never get a event scheduled for. In other
  1317. // words, this routine handles empty lists.
  1318. //
  1319. HardwareCallbackScheduled=0;
  1320. //
  1321. // while we have events in our queue, get one and service it.
  1322. //
  1323. while((ple = ExInterlockedRemoveHeadList(&HardwareCallbackListHead,
  1324. &HardwareCallbackSpinLock)) != NULL)
  1325. {
  1326. phwlink = CONTAINING_RECORD(ple, HWLINK, Next);
  1327. DPFASSERT(phwlink->dwSig == HWLINK_SIGNATURE);
  1328. //
  1329. // Get our data for this event and then free the link that was allocated in
  1330. // the DPC handler.
  1331. //
  1332. pnnode=phwlink->pnnode;
  1333. AudioFreeMemory(sizeof(HWLINK),&phwlink);
  1334. //
  1335. // We are going to be working in this context for a while. Thus, we're going
  1336. // to enter our mtxNote mutex to make sure that our node doesn't go away
  1337. // while we're persisting the values!
  1338. //
  1339. kmxlGrabNoteMutex();
  1340. //
  1341. // Now our list can't chnage! Is this node still valid? If we don't find
  1342. // it in the list, it was removed before this event fired. Thus, there is
  1343. // nothing that we can do. --- Free mutex and leave.
  1344. //
  1345. Status=kmxlFindNodeInNoteList(pnnode);
  1346. if( NT_SUCCESS(Status) )
  1347. {
  1348. DPF( DL_TRACE|FA_HARDWAREEVENT ,
  1349. ("Entering NodeId %d LineID %X ControlID %d ControlType = %X",
  1350. pnnode->NodeId, pnnode->LineID, pnnode->ControlID, pnnode->ControlType) );
  1351. //
  1352. // Yes. It's still valid. Persist the control.
  1353. //
  1354. pControl=kmxlFindControlTypeInList(pnnode,pnnode->ControlType);
  1355. if( pControl )
  1356. {
  1357. Status = kmxlPersistSingleControl(
  1358. pnnode->pmxd->pfo,
  1359. pnnode->pmxd,
  1360. pControl, // pControl here...
  1361. paDetails
  1362. );
  1363. }
  1364. if( !NT_SUCCESS(Status) )
  1365. {
  1366. //
  1367. // On shutdown, we might get an event that fires after things have
  1368. // been cleaned up.
  1369. //
  1370. if( Status != STATUS_TOO_LATE )
  1371. {
  1372. DPF(DL_WARNING|FA_NOTE, ("Failure from kmxlPersistSingleControl Status=%X",Status) );
  1373. }
  1374. }
  1375. else {
  1376. DPF(DL_TRACE|FA_HARDWAREEVENT ,("Done - success") );
  1377. }
  1378. } else {
  1379. DPF(DL_WARNING|FA_NOTE,("pnnode=%08X has been removed!",pnnode) );
  1380. }
  1381. //
  1382. // Persist this control!
  1383. //
  1384. kmxlReleaseNoteMutex();
  1385. }
  1386. DPF(DL_TRACE|FA_HARDWAREEVENT ,("exit") );
  1387. }
  1388. /////////////////////////////////////////////////////////////////////////////
  1389. //
  1390. // kmxlGetLineForControl
  1391. //
  1392. // For every line on this mixer device, look at every control to see if we
  1393. // can find this control. If found, return this line pointer.
  1394. //
  1395. NTSTATUS
  1396. kmxlEnableAllControls(
  1397. IN PMIXEROBJECT pmxobj
  1398. )
  1399. {
  1400. NTSTATUS Status=STATUS_SUCCESS;
  1401. PMIXERDEVICE pmxd;
  1402. PMXLLINE pLine;
  1403. PMXLCONTROL pControl;
  1404. PAGED_CODE();
  1405. //
  1406. // The first time through we will most likily not have a pfo in the MIXERDEVICE
  1407. // structure, thus fill it in!
  1408. //
  1409. DPFASSERT(pmxobj->dwSig == MIXEROBJECT_SIGNATURE );
  1410. DPFASSERT(pmxobj->pMixerDevice != NULL );
  1411. DPFASSERT(pmxobj->pMixerDevice->dwSig == MIXERDEVICE_SIGNATURE );
  1412. pmxd=pmxobj->pMixerDevice;
  1413. if( pmxd->pfo == NULL )
  1414. {
  1415. DPF(DL_WARNING|FA_NOTE,("fo is NULL, it should have been set!") );
  1416. //
  1417. // We need to assign a SysAudio FILE_OBJECT to this mixer device so that
  1418. // we can talk to it.
  1419. //
  1420. if( NULL==(pmxd->pfo=kmxlOpenSysAudio())) {
  1421. DPF(DL_WARNING|FA_NOTE,("OpenSysAudio failed") );
  1422. return STATUS_UNSUCCESSFUL;
  1423. }
  1424. Status = SetSysAudioProperty(
  1425. pmxd->pfo,
  1426. KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE,
  1427. sizeof( pmxd->Device ),
  1428. &pmxd->Device
  1429. );
  1430. if( !NT_SUCCESS( Status ) ) {
  1431. kmxlCloseSysAudio( pmxd->pfo );
  1432. pmxd->pfo=NULL;
  1433. DPF(DL_WARNING|FA_NOTE,("SetSysAudioProperty failed %X",Status) );
  1434. return Status;
  1435. }
  1436. }
  1437. DPFASSERT(IsValidMixerObject(pmxobj));
  1438. for(pLine = kmxlFirstInList( pmxd->listLines );
  1439. pLine != NULL;
  1440. pLine = kmxlNextLine( pLine )
  1441. )
  1442. {
  1443. DPFASSERT(IsValidLine(pLine));
  1444. for(pControl = kmxlFirstInList( pLine->Controls );
  1445. pControl != NULL;
  1446. pControl = kmxlNextControl( pControl )
  1447. )
  1448. {
  1449. DPFASSERT(IsValidControl(pControl));
  1450. //
  1451. // Enable Notifications if it supports it here.
  1452. //
  1453. DPF(DL_TRACE|FA_NOTE,("pControl->Id=%d, pControl->Control.dwControlID=%d",
  1454. pControl->Id,pControl->Control.dwControlID) );
  1455. Status = kmxlEnableControlChangeNotifications(pmxobj,pLine,pControl);
  1456. }
  1457. }
  1458. return Status;
  1459. }
  1460. VOID
  1461. kmxlCloseMixerDevice(
  1462. IN OUT PMIXERDEVICE pmxd
  1463. )
  1464. {
  1465. if(pmxd->pfo)
  1466. {
  1467. kmxlCloseSysAudio( pmxd->pfo );
  1468. pmxd->pfo = NULL;
  1469. }
  1470. }
  1471. /////////////////////////////////////////////////////////////////////////////
  1472. //
  1473. // GetHardwareEventData
  1474. //
  1475. // Called by user mode driver to get the notification information.
  1476. //
  1477. VOID GetHardwareEventData(LPDEVICEINFO pDeviceInfo)
  1478. {
  1479. PAGED_CODE();
  1480. if (emptyindex!=loadindex) {
  1481. (pDeviceInfo->dwID)[0]=callbacks[emptyindex%CALLBACKARRAYSIZE].dwControlID;
  1482. pDeviceInfo->dwLineID=callbacks[emptyindex%CALLBACKARRAYSIZE].dwLineID;
  1483. pDeviceInfo->dwCallbackType=callbacks[emptyindex%CALLBACKARRAYSIZE].dwCallbackType;
  1484. pDeviceInfo->ControlCallbackCount=1;
  1485. emptyindex++;
  1486. }
  1487. pDeviceInfo->mmr=MMSYSERR_NOERROR;
  1488. }
  1489. ///////////////////////////////////////////////////////////////////////
  1490. //
  1491. // kmxdInit
  1492. //
  1493. // Checks to see if the mixer lines have been built for the given
  1494. // index. If not, it calls kmxlBuildLines() to build up the lines.
  1495. //
  1496. // The topology information is kept around so that it can be dumped
  1497. // via a debugger command.
  1498. //
  1499. //
  1500. NTSTATUS
  1501. kmxlInit(
  1502. IN PFILE_OBJECT pfo, // Handle of the topology driver instance
  1503. IN PMIXERDEVICE pMixer
  1504. )
  1505. {
  1506. NTSTATUS Status = STATUS_SUCCESS;
  1507. HANDLE hKey;
  1508. ULONG ResultLength;
  1509. PAGED_CODE();
  1510. //
  1511. // Check to see if the lines have already been built for this device.
  1512. // If so, return success.
  1513. //
  1514. if( pMixer->listLines ) {
  1515. RETURN( STATUS_SUCCESS );
  1516. }
  1517. //
  1518. // Build the lines for this device.
  1519. //
  1520. Status = kmxlBuildLines(
  1521. pMixer,
  1522. pfo,
  1523. &pMixer->listLines,
  1524. &pMixer->cDestinations,
  1525. &pMixer->Topology
  1526. );
  1527. if( NT_SUCCESS( Status ) ) {
  1528. Status = kmxlOpenInterfaceKey( pfo, pMixer->Device, &hKey );
  1529. if( !NT_SUCCESS( Status ) ) {
  1530. pMixer->Mapping = MIXER_MAPPING_LOGRITHMIC;
  1531. Status = STATUS_SUCCESS;
  1532. goto exit;
  1533. }
  1534. Status = kmxlRegQueryValue( hKey,
  1535. L"CurveType",
  1536. &pMixer->Mapping,
  1537. sizeof( pMixer->Mapping ),
  1538. &ResultLength
  1539. );
  1540. if( !NT_SUCCESS( Status ) ) {
  1541. kmxlRegCloseKey( hKey );
  1542. pMixer->Mapping = MIXER_MAPPING_LOGRITHMIC;
  1543. Status = STATUS_SUCCESS;
  1544. goto exit;
  1545. }
  1546. kmxlRegCloseKey( hKey );
  1547. }
  1548. exit:
  1549. //
  1550. // Free up the topology allocated when the lines are built (RETAIL only).
  1551. //
  1552. #ifndef DEBUG
  1553. if(pMixer->Topology.Categories) {
  1554. ExFreePool(
  1555. ( (( PKSMULTIPLE_ITEM )
  1556. pMixer->Topology.Categories )) - 1 );
  1557. pMixer->Topology.Categories = NULL;
  1558. }
  1559. if(pMixer->Topology.TopologyNodes) {
  1560. ExFreePool(
  1561. ( (( PKSMULTIPLE_ITEM )
  1562. pMixer->Topology.TopologyNodes )) - 1 );
  1563. pMixer->Topology.TopologyNodes = NULL;
  1564. }
  1565. if(pMixer->Topology.TopologyConnections) {
  1566. ExFreePool(
  1567. ( (( PKSMULTIPLE_ITEM )
  1568. pMixer->Topology.TopologyConnections )) - 1 );
  1569. pMixer->Topology.TopologyConnections = NULL;
  1570. }
  1571. #endif // !DEBUG
  1572. RETURN( Status );
  1573. }
  1574. ////////////////////////////////////////////////////////////////////////
  1575. //
  1576. // kmxdDeInit
  1577. //
  1578. // Loops through each of the lines freeing the control structures and
  1579. // then the line structures.
  1580. //
  1581. //
  1582. NTSTATUS
  1583. kmxlDeInit(
  1584. PMIXERDEVICE pMixer
  1585. )
  1586. {
  1587. PMXLLINE pLine = NULL;
  1588. PMXLCONTROL pControl = NULL;
  1589. PAGED_CODE();
  1590. if( pMixer->Device != UNUSED_DEVICE ) {
  1591. while( pMixer->listLines ) {
  1592. pLine = kmxlRemoveFirstLine( pMixer->listLines );
  1593. while( pLine && pLine->Controls ) {
  1594. pControl = kmxlRemoveFirstControl( pLine->Controls );
  1595. kmxlFreeControl( pControl );
  1596. }
  1597. AudioFreeMemory( sizeof(MXLLINE),&pLine );
  1598. }
  1599. //
  1600. // Here we need to close sysaudio as used in this mixer device.
  1601. //
  1602. kmxlCloseMixerDevice(pMixer);
  1603. ASSERT( pMixer->listLines == NULL );
  1604. //
  1605. // Free up the topology (DEBUG only)
  1606. //
  1607. #ifdef DEBUG
  1608. if(pMixer->Topology.Categories) {
  1609. ExFreePool(( (( PKSMULTIPLE_ITEM )
  1610. pMixer->Topology.Categories )) - 1 );
  1611. pMixer->Topology.Categories = NULL;
  1612. }
  1613. if(pMixer->Topology.TopologyNodes) {
  1614. ExFreePool(( (( PKSMULTIPLE_ITEM )
  1615. pMixer->Topology.TopologyNodes )) - 1 );
  1616. pMixer->Topology.TopologyNodes = NULL;
  1617. }
  1618. if(pMixer->Topology.TopologyConnections) {
  1619. ExFreePool(( (( PKSMULTIPLE_ITEM )
  1620. pMixer->Topology.TopologyConnections )) - 1 );
  1621. pMixer->Topology.TopologyConnections = NULL;
  1622. }
  1623. #endif // !DEBUG
  1624. } // if
  1625. RETURN( STATUS_SUCCESS );
  1626. }
  1627. ///////////////////////////////////////////////////////////////////////
  1628. //
  1629. // kmxlBuildLines
  1630. //
  1631. // Builds up the line structures and count of destinations for the
  1632. // given instance.
  1633. //
  1634. //
  1635. NTSTATUS
  1636. kmxlBuildLines(
  1637. IN PMIXERDEVICE pMixer, // The mixer
  1638. IN PFILE_OBJECT pfoInstance, // The FILE_OBJECT of a filter instance
  1639. IN OUT LINELIST* plistLines, // Pointer to the list of all lines
  1640. IN OUT PULONG pcDestinations, // Pointer to the number of dests
  1641. IN OUT PKSTOPOLOGY pTopology // Pointer to a topology structure
  1642. )
  1643. {
  1644. NTSTATUS Status = STATUS_SUCCESS;
  1645. MIXEROBJECT mxobj;
  1646. LINELIST listSourceLines = NULL;
  1647. NODELIST listSources = NULL;
  1648. NODELIST listDests = NULL;
  1649. PMXLNODE pTemp = NULL;
  1650. ULONG i;
  1651. PAGED_CODE();
  1652. ASSERT( pfoInstance );
  1653. ASSERT( plistLines );
  1654. ASSERT( pcDestinations );
  1655. ASSERT( pTopology );
  1656. RtlZeroMemory( &mxobj, sizeof( MIXEROBJECT ) );
  1657. // Set up the MIXEROBJECT. Note that this structure is used only within
  1658. // the scope of this function, so it is okay to simply copy the
  1659. // DeviceInterface pointer from the MIXERDEV structure.
  1660. mxobj.pfo = pfoInstance;
  1661. mxobj.pTopology = pTopology;
  1662. mxobj.pMixerDevice = pMixer;
  1663. mxobj.DeviceInterface = pMixer->DeviceInterface;
  1664. #ifdef DEBUG
  1665. mxobj.dwSig = MIXEROBJECT_SIGNATURE;
  1666. #endif
  1667. //
  1668. // Read the topology from the device
  1669. //
  1670. DPF(DL_TRACE|FA_MIXER,("Querying Topology") );
  1671. Status = kmxlQueryTopology( mxobj.pfo, mxobj.pTopology );
  1672. if( !NT_SUCCESS( Status ) ) {
  1673. goto exit;
  1674. }
  1675. //
  1676. // Build up the node table. The node table is the mixer line's internal
  1677. // representation of the topology for easier processing.
  1678. //
  1679. DPF(DL_TRACE|FA_MIXER,("Building Node Table") );
  1680. mxobj.pNodeTable = kmxlBuildNodeTable( mxobj.pTopology );
  1681. if( !mxobj.pNodeTable ) {
  1682. Status = STATUS_INSUFFICIENT_RESOURCES;
  1683. DPF(DL_WARNING|FA_MIXER,("kmxlBuildNodeTable failed") );
  1684. goto exit;
  1685. }
  1686. //
  1687. // Parse the topology and build the necessary data structures
  1688. // to walk the topology.
  1689. //
  1690. DPF(DL_TRACE|FA_MIXER,("Parsing Topology") );
  1691. Status = kmxlParseTopology(
  1692. &mxobj,
  1693. &listSources,
  1694. &listDests );
  1695. if( !NT_SUCCESS( Status ) ) {
  1696. DPF(DL_WARNING|FA_MIXER,("kmxlParseTopoloty failed Status=%X",Status) );
  1697. goto exit;
  1698. }
  1699. //
  1700. // Build up a list of destination lines.
  1701. //
  1702. DPF(DL_TRACE|FA_MIXER,("Building Destination lines") );
  1703. *plistLines = kmxlBuildDestinationLines(
  1704. &mxobj,
  1705. listDests
  1706. );
  1707. if( !(*plistLines) ) {
  1708. Status = STATUS_INSUFFICIENT_RESOURCES;
  1709. DPF(DL_WARNING|FA_MIXER,("kmxlBuildDestinationLines failed") );
  1710. goto exit;
  1711. }
  1712. //
  1713. // Assign the line Ids and the Control Ids for the destinations. Also,
  1714. // fill in the number of destinations.
  1715. //
  1716. kmxlAssignLineAndControlIds( &mxobj, (*plistLines), DESTINATION_LIST );
  1717. *pcDestinations = kmxlListLength( (*plistLines) );
  1718. //
  1719. // Build up a list of source lines
  1720. //
  1721. DPF(DL_TRACE|FA_MIXER,("Building Source lines") );
  1722. listSourceLines = kmxlBuildSourceLines(
  1723. &mxobj,
  1724. listSources,
  1725. listDests
  1726. );
  1727. if( !listSourceLines ) {
  1728. Status = STATUS_INSUFFICIENT_RESOURCES;
  1729. DPF(DL_WARNING|FA_MIXER,("kmxlBuildSourceLines failed") );
  1730. goto exit;
  1731. }
  1732. //
  1733. // Polish off the lines. First sort them by destination so that
  1734. // the source Ids will be assigned correctly.
  1735. //
  1736. DPF(DL_TRACE|FA_MIXER,("Sort By Destination") );
  1737. kmxlSortByDestination( &listSourceLines );
  1738. DPF(DL_TRACE|FA_MIXER,("Assign Line and Control Ids") );
  1739. kmxlAssignLineAndControlIds( &mxobj, listSourceLines, SOURCE_LIST );
  1740. //
  1741. // Now assign destinations to sources and construct the line Ids for
  1742. // the source lines.
  1743. //
  1744. DPF(DL_TRACE|FA_MIXER,("Assign Destinations to Sources") );
  1745. kmxlAssignDestinationsToSources( listSourceLines, (*plistLines) );
  1746. //
  1747. // Update the number of sources mapping to each of the destinations.
  1748. //
  1749. DPF(DL_TRACE|FA_MIXER,("Update Destination Connection Count") );
  1750. kmxlUpdateDestintationConnectionCount( listSourceLines, (*plistLines) );
  1751. //
  1752. // Assign the dwComponentIds for the source and destination lines.
  1753. //
  1754. DPF(DL_TRACE|FA_MIXER,("Assign Conponent IDs") );
  1755. kmxlAssignComponentIds( &mxobj, listSourceLines, (*plistLines) );
  1756. //
  1757. // Construct a single list of lines. Destinations will be first in
  1758. // increasing numerical order by line id, folowed by sources in
  1759. // increasing numerical order.
  1760. //
  1761. kmxlAppendListToEndOfList( (PSLIST*) plistLines, (PSLIST) listSourceLines );
  1762. //
  1763. // Eliminate any lines that are invalid.
  1764. //
  1765. DPF(DL_TRACE|FA_MIXER,("Eliminate Invalid Lines") );
  1766. kmxlEliminateInvalidLines( plistLines );
  1767. //
  1768. // Update the mux line IDs to match real line IDs
  1769. //
  1770. DPF(DL_TRACE|FA_MIXER,("Assign Mux IDs") );
  1771. kmxlAssignMuxIds( &mxobj, *plistLines );
  1772. //
  1773. // Here is where we want to Enable Change Notifications on all controls
  1774. // that support notifications.
  1775. //
  1776. DPF(DL_TRACE|FA_MIXER,("Enable All Controls") );
  1777. kmxlEnableAllControls(&mxobj);
  1778. exit:
  1779. //
  1780. // If we got here because of an error, clean up all the mixer lines
  1781. //
  1782. if( !NT_SUCCESS( Status ) ) {
  1783. PMXLLINE pLine;
  1784. PMXLCONTROL pControl;
  1785. while( (*plistLines) ) {
  1786. pLine = kmxlRemoveFirstLine( (*plistLines) );
  1787. while( pLine && pLine->Controls ) {
  1788. pControl = kmxlRemoveFirstControl( pLine->Controls );
  1789. kmxlFreeControl( pControl );
  1790. }
  1791. AudioFreeMemory( sizeof(MXLLINE),&pLine );
  1792. }
  1793. }
  1794. //
  1795. // Free up the mux control list. Note that we don't want to free
  1796. // the controls using kmxlFreeControl() because we need the special
  1797. // mux instance data to persist.
  1798. //
  1799. {
  1800. PMXLCONTROL pControl;
  1801. while( mxobj.listMuxControls ) {
  1802. pControl = kmxlRemoveFirstControl( mxobj.listMuxControls );
  1803. ASSERT( pControl->pChannelStepping == NULL);
  1804. AudioFreeMemory( sizeof(MXLCONTROL),&pControl );
  1805. }
  1806. }
  1807. //
  1808. // Free up the source and destination lists. Both types of these lists
  1809. // are allocated list nodes and allocated nodes. Both need to be freed.
  1810. // The Children and Parent lists, though, are only allocated list nodes.
  1811. // The actual nodes are contained in the node table and will be deallocated
  1812. // in one chunk in the next block of code.
  1813. //
  1814. while( listSources ) {
  1815. pTemp = kmxlRemoveFirstNode( listSources );
  1816. kmxlFreePeerList( pTemp->Children );
  1817. AudioFreeMemory( sizeof(MXLNODE),&pTemp );
  1818. }
  1819. while( listDests ) {
  1820. pTemp = kmxlRemoveFirstNode( listDests );
  1821. kmxlFreePeerList( pTemp->Parents );
  1822. AudioFreeMemory( sizeof(MXLNODE),&pTemp );
  1823. }
  1824. //
  1825. // Free up the peer lists for the children and parents inside the
  1826. // nodes of the node table. Finally, deallocate the array of nodes.
  1827. //
  1828. if( mxobj.pNodeTable ) {
  1829. for( i = 0; i < mxobj.pTopology->TopologyNodesCount; i++ ) {
  1830. kmxlFreePeerList( mxobj.pNodeTable[ i ].Children );
  1831. kmxlFreePeerList( mxobj.pNodeTable[ i ].Parents );
  1832. }
  1833. AudioFreeMemory_Unknown( &mxobj.pNodeTable );
  1834. }
  1835. RETURN( Status );
  1836. }
  1837. ///////////////////////////////////////////////////////////////////////
  1838. ///////////////////////////////////////////////////////////////////////
  1839. // //
  1840. // M I X E R L I N E F U N C T I O N S //
  1841. // //
  1842. ///////////////////////////////////////////////////////////////////////
  1843. ///////////////////////////////////////////////////////////////////////
  1844. ///////////////////////////////////////////////////////////////////////
  1845. //
  1846. // kmxlBuildDestinationLines
  1847. //
  1848. // Loops through each of the destination nodes, allocates a line
  1849. // structure for it, and calls kmxlBuildDestinationControls to
  1850. // build the controls for that line.
  1851. //
  1852. //
  1853. LINELIST
  1854. kmxlBuildDestinationLines(
  1855. IN PMIXEROBJECT pmxobj,
  1856. IN NODELIST listDests // The list of destination nodes
  1857. )
  1858. {
  1859. LINELIST listDestLines = NULL;
  1860. PMXLNODE pDest = NULL;
  1861. PMXLLINE pLine = NULL;
  1862. PMXLCONTROL pControl = NULL;
  1863. ULONG MaxChannelsForLine;
  1864. ASSERT( pmxobj );
  1865. ASSERT( listDests );
  1866. PAGED_CODE();
  1867. //
  1868. // Loop over all the destination node allocating a line structure
  1869. // for each.
  1870. //
  1871. pDest = kmxlFirstInList( listDests );
  1872. while( pDest ) {
  1873. //
  1874. // Allocate a new line structure and add it to the list of
  1875. // destination lines.
  1876. //
  1877. pLine = kmxlAllocateLine( TAG_AudL_LINE );
  1878. if( !pLine ) {
  1879. goto exit;
  1880. }
  1881. kmxlAddToList( listDestLines, pLine );
  1882. //
  1883. // Fill in the details of the line structure. Some fields will
  1884. // be filled in later.
  1885. //
  1886. pLine->DestId = pDest->Id;
  1887. pLine->Type = pDest->NodeType;
  1888. pLine->Communication = pDest->Communication;
  1889. pLine->Line.cbStruct = sizeof( MIXERLINE );
  1890. pLine->Line.dwSource = (DWORD) -1;
  1891. pLine->Line.dwDestination = (DWORD) -1;
  1892. kmxlGetPinName( pmxobj->pfo, pDest->Id, pLine );
  1893. //
  1894. // HACK! The ACTIVE flag should only be set when the line is active
  1895. // but then no lines show up in SNDVOL32. It works if the flag is
  1896. // always set to ACTIVE for destinations. Also, the number of channels
  1897. // should be queried not hard coded. WDM Audio does not provide a
  1898. // way to easily query this.
  1899. //
  1900. pLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
  1901. pLine->Line.cChannels = 1; // should this default to 1 or 2?
  1902. //
  1903. // Build up a list of the controls on this destination
  1904. //
  1905. if( !NT_SUCCESS( kmxlBuildDestinationControls(
  1906. pmxobj,
  1907. pDest,
  1908. pLine
  1909. ) ) )
  1910. {
  1911. DPF(DL_WARNING|FA_MIXER,("kmxlBuildDestinationControls failed") );
  1912. goto exit;
  1913. }
  1914. pDest = kmxlNextNode( pDest );
  1915. }
  1916. pLine = kmxlFirstInList( listDestLines );
  1917. while( pLine ) {
  1918. MaxChannelsForLine = 1;
  1919. pControl = kmxlFirstInList( pLine->Controls );
  1920. while( pControl ) {
  1921. ASSERT( IsValidControl( pControl ) );
  1922. if ( pControl->NumChannels > MaxChannelsForLine) {
  1923. MaxChannelsForLine = pControl->NumChannels;
  1924. }
  1925. pControl = kmxlNextControl( pControl );
  1926. }
  1927. if( pLine->Controls == NULL ) {
  1928. pLine->Line.cChannels = 1; // should this default to 1 or 2?
  1929. } else {
  1930. pLine->Line.cChannels = MaxChannelsForLine;
  1931. }
  1932. pLine = kmxlNextLine( pLine );
  1933. }
  1934. return( listDestLines );
  1935. exit:
  1936. //
  1937. // A memory allocation failed. Clean up the destination lines and
  1938. // return failure.
  1939. //
  1940. while( listDestLines ) {
  1941. pLine = kmxlRemoveFirstLine( listDestLines );
  1942. while( pLine && pLine->Controls ) {
  1943. pControl = kmxlRemoveFirstControl( pLine->Controls );
  1944. kmxlFreeControl( pControl );
  1945. }
  1946. AudioFreeMemory_Unknown( &pLine );
  1947. }
  1948. return( NULL );
  1949. }
  1950. ///////////////////////////////////////////////////////////////////////
  1951. //
  1952. // BuildDestinationControls
  1953. //
  1954. // Starts at the destination node and translates all the parent nodes
  1955. // to mixer line controls. This process stops when the first SUM node
  1956. // is encountered, indicating the end of a destination line.
  1957. //
  1958. //
  1959. NTSTATUS
  1960. kmxlBuildDestinationControls(
  1961. IN PMIXEROBJECT pmxobj,
  1962. IN PMXLNODE pDest, // The destination to built controls for
  1963. IN PMXLLINE pLine // The line to add the controls to
  1964. )
  1965. {
  1966. PPEERNODE pTemp = NULL;
  1967. PMXLCONTROL pControl;
  1968. PAGED_CODE();
  1969. ASSERT( pmxobj );
  1970. ASSERT( pLine );
  1971. //
  1972. // Start at the immediate parent of the node passed.
  1973. //
  1974. pTemp = kmxlFirstParentNode( pDest );
  1975. while( pTemp ) {
  1976. if( IsEqualGUID( &pTemp->pNode->NodeType, &KSNODETYPE_SUM ) ||
  1977. ( pTemp->pNode->Type == SOURCE ) ||
  1978. ( pTemp->pNode->Type == DESTINATION ) ) {
  1979. //
  1980. // We've found a SUM node. Discontinue the loop... we've
  1981. // found all the controls.
  1982. //
  1983. break;
  1984. }
  1985. if( IsEqualGUID( &pTemp->pNode->NodeType, &KSNODETYPE_MUX ) ) {
  1986. if (kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl )) {
  1987. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  1988. }
  1989. break;
  1990. }
  1991. if( ( kmxlParentListLength( pTemp->pNode ) > 1 ) ) {
  1992. //
  1993. // Found a node with multiple parents that is not a SUM node.
  1994. // Can't handle that here so add any controls for this node
  1995. // and discontinue the loop.
  1996. //
  1997. if( kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl ) ) {
  1998. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  1999. }
  2000. break;
  2001. }
  2002. //
  2003. // By going up through the parents and inserting nodes at
  2004. // the front of the list, the list will contain the controls
  2005. // in the right order.
  2006. //
  2007. if( kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl ) ) {
  2008. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2009. }
  2010. pTemp = kmxlFirstParentNode( pTemp->pNode );
  2011. }
  2012. DPF(DL_TRACE|FA_MIXER,(
  2013. "Found %d controls on destination %d:",
  2014. kmxlListLength( pLine->Controls ),
  2015. pDest->Id
  2016. ) );
  2017. RETURN( STATUS_SUCCESS );
  2018. }
  2019. ///////////////////////////////////////////////////////////////////////
  2020. //
  2021. // kmxlBuildSourceLines
  2022. //
  2023. // Loops through each of the source nodes, allocating a new line
  2024. // structure, and calling kmxlBuildPath() to build the controls
  2025. // for the line (and possibly creating new lines if there are splits
  2026. // in the topology).
  2027. //
  2028. //
  2029. LINELIST
  2030. kmxlBuildSourceLines(
  2031. IN PMIXEROBJECT pmxobj,
  2032. IN NODELIST listSources, // The list of source nodes
  2033. IN NODELIST listDests // The list of dest. nodes
  2034. )
  2035. {
  2036. NTSTATUS Status;
  2037. LINELIST listSourceLines = NULL;
  2038. PMXLNODE pSource = NULL;
  2039. PMXLLINE pTemp = NULL;
  2040. PMXLCONTROL pControl;
  2041. ULONG MaxChannelsForLine;
  2042. ASSERT( pmxobj );
  2043. ASSERT( pmxobj->pfo );
  2044. ASSERT( pmxobj->pNodeTable );
  2045. ASSERT( listSources );
  2046. ASSERT( listDests );
  2047. PAGED_CODE();
  2048. pSource = kmxlFirstInList( listSources );
  2049. while( pSource ) {
  2050. //
  2051. // Allocate a new line structure and insert it into the list of
  2052. // source lines.
  2053. //
  2054. pTemp = kmxlAllocateLine( TAG_AudL_LINE );
  2055. if( !pTemp ) {
  2056. goto exit;
  2057. }
  2058. kmxlAddToEndOfList( listSourceLines, pTemp );
  2059. //
  2060. // Fill in the fields of the line structure. Some fields will need
  2061. // to be filled in later.
  2062. //
  2063. pTemp->SourceId = pSource->Id;
  2064. pTemp->Type = pSource->NodeType;
  2065. pTemp->Communication = pSource->Communication;
  2066. pTemp->Line.cbStruct = sizeof( MIXERLINE );
  2067. pTemp->Line.dwSource = (DWORD) -1;
  2068. pTemp->Line.dwDestination = (DWORD) -1;
  2069. pTemp->Line.fdwLine = MIXERLINE_LINEF_SOURCE |
  2070. MIXERLINE_LINEF_ACTIVE;
  2071. kmxlGetPinName( pmxobj->pfo, pSource->Id, pTemp );
  2072. // DPF(DL_TRACE|FA_MIXER,( "Building path for %s (%d).",
  2073. // PinCategoryToString( &pSource->NodeType ),
  2074. // pSource->Id
  2075. // ) );
  2076. //
  2077. // Build the controls for this line and identify the destination(s)
  2078. // it conntects to.
  2079. //
  2080. Status = kmxlBuildPath(
  2081. pmxobj,
  2082. pSource, // The source line to build controls for
  2083. pSource, // The node to start with
  2084. pTemp, // The line structure to add to
  2085. &listSourceLines, // The list of all source lines
  2086. listDests // THe list of all destinations
  2087. );
  2088. if( !NT_SUCCESS( Status ) ) {
  2089. DPF(DL_WARNING|FA_MIXER,("kmxlBuildPath failed Status=%X",Status) );
  2090. goto exit;
  2091. }
  2092. pSource = kmxlNextNode( pSource );
  2093. } // while( pSource )
  2094. pTemp = kmxlFirstInList( listSourceLines );
  2095. while( pTemp ) {
  2096. MaxChannelsForLine = 1;
  2097. pControl = kmxlFirstInList( pTemp->Controls );
  2098. while( pControl ) {
  2099. ASSERT( IsValidControl( pControl ) );
  2100. if ( pControl->NumChannels > MaxChannelsForLine) {
  2101. MaxChannelsForLine = pControl->NumChannels;
  2102. }
  2103. pControl = kmxlNextControl( pControl );
  2104. }
  2105. if( pTemp->Controls == NULL ) {
  2106. pTemp->Line.cChannels = 1; // should this default to 1 or 2?
  2107. } else {
  2108. pTemp->Line.cChannels = MaxChannelsForLine;
  2109. }
  2110. pTemp = kmxlNextLine( pTemp );
  2111. }
  2112. return( listSourceLines );
  2113. exit:
  2114. //
  2115. // Something went wrong. Clean up all memory allocated and return NULL
  2116. // to indicate the error.
  2117. //
  2118. while( listSourceLines ) {
  2119. pTemp = kmxlRemoveFirstLine( listSourceLines );
  2120. while( pTemp && pTemp->Controls ) {
  2121. pControl = kmxlRemoveFirstControl( pTemp->Controls );
  2122. kmxlFreeControl( pControl );
  2123. }
  2124. AudioFreeMemory_Unknown( &pTemp );
  2125. }
  2126. return( NULL );
  2127. }
  2128. ///////////////////////////////////////////////////////////////////////
  2129. //
  2130. // kmxlBuildPath
  2131. //
  2132. // Builds the controls for a source line. A source line ends when a
  2133. // SUM node, a destination node, a node contained in a destination line
  2134. // is encountered. When splits are encountered in the topology, new
  2135. // lines need to be created and the controls on those lines enumerated.
  2136. //
  2137. // kmxlBuildPath will recurse to find the controls on subnodes.
  2138. //
  2139. //
  2140. NTSTATUS
  2141. kmxlBuildPath(
  2142. IN PMIXEROBJECT pmxobj,
  2143. IN PMXLNODE pSource, // The source node for this path
  2144. IN PMXLNODE pNode, // The current node in the path
  2145. IN PMXLLINE pLine, // The current line
  2146. IN OUT LINELIST* plistLines, // The list of lines build so far
  2147. IN NODELIST listDests // The list of the destinations
  2148. )
  2149. {
  2150. NTSTATUS Status;
  2151. PMXLCONTROL pControl = NULL;
  2152. PMXLLINE pNewLine = NULL;
  2153. ULONG nControls;
  2154. PPEERNODE pChild = NULL;
  2155. ASSERT( pmxobj );
  2156. ASSERT( pSource );
  2157. ASSERT( pNode );
  2158. ASSERT( pLine );
  2159. ASSERT( plistLines );
  2160. PAGED_CODE();
  2161. DPF(DL_TRACE|FA_MIXER,( "Building path for %d(0x%x) (%s) NODE=%08x",
  2162. pNode->Id,pNode->Id,
  2163. NodeTypeToString( &pNode->NodeType ),
  2164. pNode ) );
  2165. //
  2166. // Check to see if this is the end of this line.
  2167. //
  2168. if( ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ) ||
  2169. ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) ||
  2170. ( pNode->Type == DESTINATION ) ||
  2171. ( kmxlIsDestinationNode( listDests, pNode ) ) )
  2172. {
  2173. //
  2174. // Find the destination node and update the line structure.
  2175. // If this IS the destination node, then set the ID in the line
  2176. // structure and return. There is no need to check the children,
  2177. // since there won't be any.
  2178. //
  2179. if( pNode->Type == DESTINATION ) {
  2180. pLine->DestId = pNode->Id;
  2181. RETURN( STATUS_SUCCESS );
  2182. }
  2183. //
  2184. // Find the destination node for the source. It is possible to
  2185. // have branches in the topology, so this may recurse.
  2186. //
  2187. pLine->DestId = kmxlFindDestinationForNode(
  2188. pmxobj,
  2189. pNode,
  2190. pNode,
  2191. pLine,
  2192. plistLines
  2193. );
  2194. RETURN( STATUS_SUCCESS );
  2195. }
  2196. //
  2197. // Retrieve and translate the node for the first child, appending any
  2198. // controls created onto the list of controls for this line.
  2199. //
  2200. pChild = kmxlFirstChildNode( pNode );
  2201. if( pChild == NULL ) {
  2202. RETURN( STATUS_SUCCESS );
  2203. }
  2204. //
  2205. // Save the number of controls here. If a split occurs beneath this
  2206. // node, we don't want to include children followed on the first
  2207. // child's path.
  2208. //
  2209. nControls = kmxlListLength( pLine->Controls );
  2210. if (kmxlTranslateNodeToControl(pmxobj, pChild->pNode, &pControl) ) {
  2211. if( pControl && IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) ) {
  2212. if( kmxlIsDestinationNode( listDests, pChild->pNode ) ) {
  2213. pControl->Parameters.bPlaceholder = TRUE;
  2214. }
  2215. }
  2216. kmxlAppendListToEndOfList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2217. }
  2218. //
  2219. // Recurse to build the controls for this child.
  2220. //
  2221. Status = kmxlBuildPath(
  2222. pmxobj,
  2223. pSource,
  2224. pChild->pNode,
  2225. pLine,
  2226. plistLines,
  2227. listDests
  2228. );
  2229. if( !NT_SUCCESS( Status ) ) {
  2230. RETURN( Status );
  2231. }
  2232. //
  2233. // For the rest of the children
  2234. //
  2235. // Create a new line based on pSource.
  2236. // Duplicate the list of controls in pLine.
  2237. // Recurse over the child node.
  2238. //
  2239. pChild = kmxlNextPeerNode( pChild );
  2240. while( pChild ) {
  2241. pNewLine = kmxlAllocateLine( TAG_AudL_LINE );
  2242. if( pNewLine == NULL ) {
  2243. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2244. }
  2245. //
  2246. // Insert this new node into the list of source lines
  2247. //
  2248. RtlCopyMemory( pNewLine, pLine, sizeof( MXLLINE ) );
  2249. pNewLine->List.Next = NULL;
  2250. pNewLine->Controls = NULL;
  2251. kmxlAddToEndOfList( *plistLines, pNewLine );
  2252. //
  2253. // Since this is a new line, the control structures also need to be
  2254. // duplicated.
  2255. //
  2256. Status = kmxlDuplicateLineControls( pNewLine, pLine, nControls );
  2257. if( !NT_SUCCESS( Status ) ) {
  2258. RETURN( Status );
  2259. }
  2260. //
  2261. // Just as for the first child, translate the node, append the
  2262. // controls to the list of controls for this list, and recurse
  2263. // to build the controls for its children.
  2264. //
  2265. if (kmxlTranslateNodeToControl(
  2266. pmxobj,
  2267. pChild->pNode,
  2268. &pControl ) ) {
  2269. kmxlAppendListToEndOfList( (PSLIST*) &pNewLine->Controls, (PSLIST) pControl );
  2270. }
  2271. Status = kmxlBuildPath(
  2272. pmxobj,
  2273. pSource,
  2274. pChild->pNode,
  2275. pNewLine,
  2276. plistLines,
  2277. listDests
  2278. );
  2279. if( !NT_SUCCESS( Status ) ) {
  2280. RETURN( Status );
  2281. }
  2282. pChild = kmxlNextPeerNode( pChild );
  2283. } // while( pChild )
  2284. RETURN( STATUS_SUCCESS );
  2285. }
  2286. ///////////////////////////////////////////////////////////////////////
  2287. //
  2288. // kmxlIsDestinationNode
  2289. //
  2290. // Searches all the list of controls on the given list of destinations
  2291. // to see if the node appears in any of those lists.
  2292. //
  2293. //
  2294. BOOL
  2295. kmxlIsDestinationNode(
  2296. IN NODELIST listDests, // The list of destinations
  2297. IN PMXLNODE pNode // The node to check
  2298. )
  2299. {
  2300. PMXLNODE pTemp;
  2301. PPEERNODE pParent;
  2302. PAGED_CODE();
  2303. if( pNode->Type == SOURCE ) {
  2304. return( FALSE );
  2305. }
  2306. if( pNode->Type == DESTINATION ) {
  2307. return( TRUE );
  2308. }
  2309. ASSERT(pNode->Type == NODE);
  2310. //
  2311. // Loop over each of the destinations
  2312. //
  2313. pTemp = kmxlFirstInList( listDests );
  2314. while( pTemp ) {
  2315. //
  2316. // Loop over the parent.
  2317. //
  2318. pParent = kmxlFirstParentNode( pTemp );
  2319. while( pParent ) {
  2320. if( ( pParent->pNode->Type == NODE ) &&
  2321. ( pParent->pNode->Id == pNode->Id) ) {
  2322. return( TRUE );
  2323. }
  2324. if( ( IsEqualGUID( &pParent->pNode->NodeType, &KSNODETYPE_SUM ) ) ||
  2325. ( IsEqualGUID( &pParent->pNode->NodeType, &KSNODETYPE_MUX ) ) ||
  2326. ( pParent->pNode->Type == SOURCE ) )
  2327. {
  2328. break;
  2329. }
  2330. //
  2331. // Check for the node Ids matching.
  2332. //
  2333. pParent = kmxlFirstParentNode( pParent->pNode );
  2334. }
  2335. pTemp = kmxlNextNode( pTemp );
  2336. }
  2337. return( FALSE );
  2338. }
  2339. ///////////////////////////////////////////////////////////////////////
  2340. //
  2341. // kmxlDuplicateLine
  2342. //
  2343. // Duplicates a line and the associated controls.
  2344. //
  2345. //
  2346. NTSTATUS
  2347. kmxlDuplicateLine(
  2348. IN PMXLLINE* ppTargetLine, // Pointer to the new line
  2349. IN PMXLLINE pSourceLine // The line to duplicate
  2350. )
  2351. {
  2352. PAGED_CODE();
  2353. ASSERT( ppTargetLine );
  2354. ASSERT( pSourceLine );
  2355. DPF(DL_TRACE|FA_MIXER,( "Duplicated line with source=%d.",
  2356. pSourceLine->SourceId ) );
  2357. //
  2358. // Allocate a new line structure and copy the information from the
  2359. // source line.
  2360. //
  2361. *ppTargetLine = kmxlAllocateLine( TAG_AudL_LINE );
  2362. if( *ppTargetLine == NULL ) {
  2363. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2364. }
  2365. ASSERT( *ppTargetLine );
  2366. // DPF(DL_TRACE|FA_MIXER,( "Duplicated %s (%d).",
  2367. // PinCategoryToString( &pSourceLine->Type ),
  2368. // pSourceLine->SourceId
  2369. // ) );
  2370. RtlCopyMemory( *ppTargetLine, pSourceLine, sizeof( MXLLINE ) );
  2371. //
  2372. // Null out the controls and next pointer. This line does not have
  2373. // either of its own yet.
  2374. //
  2375. (*ppTargetLine)->List.Next = NULL;
  2376. (*ppTargetLine)->Controls = NULL;
  2377. //
  2378. // Duplicate all the controls for the source line.
  2379. //
  2380. return( kmxlDuplicateLineControls(
  2381. *ppTargetLine,
  2382. pSourceLine,
  2383. kmxlListLength( pSourceLine->Controls )
  2384. )
  2385. );
  2386. }
  2387. ///////////////////////////////////////////////////////////////////////
  2388. //
  2389. // kmxlDuplicateLineControls
  2390. //
  2391. // Duplicates the controls for a line by allocating a new control
  2392. // structure for each and copying the information to the new node.
  2393. //
  2394. //
  2395. NTSTATUS
  2396. kmxlDuplicateLineControls(
  2397. IN PMXLLINE pTargetLine, // The line to put the controls into
  2398. IN PMXLLINE pSourceLine, // The line with the controls to dup
  2399. IN ULONG nCount // The number of controls to dup
  2400. )
  2401. {
  2402. PMXLCONTROL pControl,
  2403. pNewControl;
  2404. NTSTATUS Status;
  2405. PAGED_CODE();
  2406. ASSERT( pTargetLine->Controls == NULL );
  2407. if( nCount == 0 ) {
  2408. RETURN( STATUS_SUCCESS );
  2409. }
  2410. //
  2411. // Iterate over the list allocating and copying the controls
  2412. //
  2413. pControl = kmxlFirstInList( pSourceLine->Controls );
  2414. while( pControl ) {
  2415. ASSERT( IsValidControl( pControl ) );
  2416. //
  2417. // Allocate a new control structure.
  2418. //
  2419. pNewControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2420. if( pNewControl == NULL ) {
  2421. goto exit;
  2422. }
  2423. //
  2424. // Copy the entire MXLCONTROL structure and NULL out the
  2425. // List.Next field. This control will be part of a different
  2426. // list.
  2427. //
  2428. RtlCopyMemory( pNewControl, pControl, sizeof( MXLCONTROL ) );
  2429. pNewControl->List.Next = NULL;
  2430. pNewControl->pChannelStepping = NULL;
  2431. //
  2432. // Copy the channel steppings from the original control
  2433. //
  2434. ASSERT(pControl->NumChannels > 0);
  2435. Status = AudioAllocateMemory_Paged(pNewControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  2436. TAG_AuDD_CHANNEL,
  2437. DEFAULT_MEMORY,
  2438. &pNewControl->pChannelStepping );
  2439. if( !NT_SUCCESS( Status ) ) {
  2440. pNewControl->NumChannels = 0;
  2441. goto exit;
  2442. }
  2443. RtlCopyMemory( pNewControl->pChannelStepping,
  2444. pControl->pChannelStepping,
  2445. pNewControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
  2446. //
  2447. // We just made a copy of a MUX node. Mark the datastructures
  2448. // is has as a copy so it doesn't get freed from underneath
  2449. // somebody else.
  2450. //
  2451. if( IsEqualGUID( pNewControl->NodeType, &KSNODETYPE_MUX ) ) {
  2452. pNewControl->Parameters.bHasCopy = TRUE;
  2453. }
  2454. kmxlAddToList( pTargetLine->Controls, pNewControl );
  2455. //
  2456. // Decrement and check the number of controls copied. If we copied
  2457. // the requested number, stop.
  2458. //
  2459. --nCount;
  2460. if( nCount == 0 ) {
  2461. break;
  2462. }
  2463. pControl = kmxlNextControl( pControl );
  2464. }
  2465. RETURN( STATUS_SUCCESS );
  2466. exit:
  2467. //
  2468. // Failed to allocate the control structure. Free up all the
  2469. // controls already allocated and return an error.
  2470. //
  2471. while( pTargetLine->Controls ) {
  2472. pControl = kmxlRemoveFirstControl( pTargetLine->Controls );
  2473. kmxlFreeControl( pControl );
  2474. }
  2475. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2476. }
  2477. ///////////////////////////////////////////////////////////////////////
  2478. //
  2479. // kmxlFindDestinationForNode
  2480. //
  2481. // Finds a destination for the given node, duplicating lines if splits
  2482. // are found in the topology.
  2483. //
  2484. //
  2485. ULONG
  2486. kmxlFindDestinationForNode(
  2487. IN PMIXEROBJECT pmxobj,
  2488. IN PMXLNODE pNode, // The node to find dest for
  2489. IN PMXLNODE pParent, // The original parent
  2490. IN PMXLLINE pLine, // The current line it's on
  2491. IN OUT LINELIST* plistLines // The list of all lines
  2492. )
  2493. {
  2494. PPEERNODE pChild, pPeerChild;
  2495. PMXLLINE pNewLine;
  2496. PMXLNODE pShadow = pNode;
  2497. PAGED_CODE();
  2498. DPF(DL_TRACE|FA_MIXER,( "Finding destination for node %d(0x%x) (%s), parent %d(0x%x) (%s).",
  2499. pNode->Id,pNode->Id,
  2500. NodeTypeToString( &pNode->NodeType ),
  2501. pParent->Id,pParent->Id,
  2502. NodeTypeToString( &pNode->NodeType ) ) );
  2503. ASSERT( pmxobj ) ;
  2504. ASSERT( pNode );
  2505. ASSERT( pParent );
  2506. ASSERT( pLine );
  2507. ASSERT( plistLines );
  2508. if( pNode->Type == DESTINATION ) {
  2509. return( pNode->Id );
  2510. }
  2511. //
  2512. // Loop over the first children.
  2513. //
  2514. pChild = kmxlFirstChildNode( pNode );
  2515. while( pChild ) {
  2516. DPF(DL_TRACE|FA_MIXER,( "First child is %d(0x%x) (%s) NODE:%08x.",
  2517. pChild->pNode->Id,
  2518. pChild->pNode->Id,
  2519. NodeTypeToString( &pChild->pNode->NodeType ),
  2520. pChild->pNode ) );
  2521. if( pChild->pNode == pParent ) {
  2522. DPF(DL_TRACE|FA_MIXER,( "Child node is same as original parent!" ) );
  2523. return( INVALID_ID );
  2524. }
  2525. //
  2526. // Loop over the rest of the children.
  2527. //
  2528. pPeerChild = kmxlNextPeerNode( pChild );
  2529. while( pPeerChild ) {
  2530. if( pPeerChild->pNode == pParent ) {
  2531. DPF(DL_TRACE|FA_MIXER,( "Child node is same as original parent!" ) );
  2532. return( INVALID_ID );
  2533. }
  2534. DPF(DL_TRACE|FA_MIXER,( "Peer node of %d(0x%x) (%s) is %d(0x%x) (%s).",
  2535. pChild->pNode->Id,pChild->pNode->Id,
  2536. NodeTypeToString( &pChild->pNode->NodeType ),
  2537. pPeerChild->pNode->Id,pPeerChild->pNode->Id,
  2538. NodeTypeToString( &pPeerChild->pNode->NodeType ) ) );
  2539. //
  2540. // This line has more than 1 child. Duplicate this line
  2541. // and add it to the list of lines.
  2542. //
  2543. if( !NT_SUCCESS( kmxlDuplicateLine( &pNewLine, pLine ) ) ) {
  2544. DPF(DL_WARNING|FA_MIXER,("kmxlDuplicateLine failed") );
  2545. continue;
  2546. }
  2547. kmxlAddToEndOfList( *plistLines, pNewLine );
  2548. if( IsEqualGUID( &pPeerChild->pNode->NodeType, &KSNODETYPE_MUX ) ) {
  2549. //
  2550. // We've found a MUX after a SUM or another MUX node. Mark
  2551. // the current line as invalid and build a new, virtual
  2552. // line that feeds into the MUX.
  2553. //
  2554. pNewLine->DestId = INVALID_ID;
  2555. kmxlBuildVirtualMuxLine(
  2556. pmxobj,
  2557. pShadow,
  2558. pPeerChild->pNode,
  2559. plistLines
  2560. );
  2561. } else {
  2562. //
  2563. // Now to find the destination for this new line. Recurse
  2564. // on the node of this child.
  2565. //
  2566. pNewLine->DestId = kmxlFindDestinationForNode(
  2567. pmxobj,
  2568. pPeerChild->pNode,
  2569. pParent,
  2570. pNewLine,
  2571. plistLines
  2572. );
  2573. }
  2574. DPF(DL_TRACE|FA_MIXER,( "Found %x as dest for %d(0x%x) (%s).",
  2575. pNewLine->DestId, pPeerChild->pNode->Id,pPeerChild->pNode->Id,
  2576. NodeTypeToString( &pPeerChild->pNode->NodeType ),
  2577. pPeerChild->pNode ) );
  2578. pPeerChild = kmxlNextPeerNode( pPeerChild );
  2579. }
  2580. if( IsEqualGUID( &pChild->pNode->NodeType, &KSNODETYPE_MUX ) ) {
  2581. //
  2582. // We've found a MUX after a SUM or another MUX node. Mark
  2583. // the current line as invalid and build a new, virtual
  2584. // line that feeds into the MUX.
  2585. //
  2586. kmxlBuildVirtualMuxLine(
  2587. pmxobj,
  2588. pShadow,
  2589. pChild->pNode,
  2590. plistLines
  2591. );
  2592. return( INVALID_ID );
  2593. }
  2594. //
  2595. // Found the destination!
  2596. //
  2597. if( pChild->pNode->Type == DESTINATION ) {
  2598. DPF(DL_TRACE|FA_MIXER,( "Found %x as dest for %d.",
  2599. pChild->pNode->Id,
  2600. pNode->Id ) );
  2601. return( pChild->pNode->Id );
  2602. }
  2603. pShadow = pChild->pNode;
  2604. pChild = kmxlFirstChildNode( pChild->pNode );
  2605. }
  2606. DPF(DL_WARNING|FA_MIXER,("returning INVALID_ID") );
  2607. return( INVALID_ID );
  2608. }
  2609. ///////////////////////////////////////////////////////////////////////
  2610. //
  2611. // kmxlBuildVirtualMuxLine
  2612. //
  2613. //
  2614. NTSTATUS
  2615. kmxlBuildVirtualMuxLine(
  2616. IN PMIXEROBJECT pmxobj,
  2617. IN PMXLNODE pParent,
  2618. IN PMXLNODE pMux,
  2619. IN OUT LINELIST* plistLines
  2620. )
  2621. {
  2622. PMXLLINE pLine, pTemp;
  2623. PMXLNODE pNode;
  2624. PMXLCONTROL pControl;
  2625. MXLCONTROL Control;
  2626. PAGED_CODE();
  2627. //
  2628. // Allocate a new line to represent the virtual mux input line.
  2629. //
  2630. pLine = kmxlAllocateLine( TAG_AudL_LINE );
  2631. if( pLine == NULL ) {
  2632. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2633. }
  2634. DPF(DL_TRACE|FA_MIXER,("Virtual line %08x for Parent NODE:%08x",pLine,pParent) );
  2635. //
  2636. // Translate the mux control so that it will appear in this line.
  2637. //
  2638. if (kmxlTranslateNodeToControl(
  2639. pmxobj,
  2640. pMux,
  2641. &pControl
  2642. ) ) {
  2643. pControl->Parameters.bPlaceholder = TRUE;
  2644. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2645. }
  2646. //
  2647. // Now start searching up from the parent.
  2648. //
  2649. pNode = pParent;
  2650. while( pNode ) {
  2651. //
  2652. // Translate the control.
  2653. //
  2654. if (kmxlTranslateNodeToControl(
  2655. pmxobj,
  2656. pNode,
  2657. &pControl
  2658. ) ) {
  2659. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2660. }
  2661. //
  2662. // If we found a node with multiple parents, then this will be the
  2663. // "pin" for this line.
  2664. //
  2665. if( ( kmxlParentListLength( pNode ) > 1 ) ||
  2666. ( pNode->Type == SOURCE ) ||
  2667. ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) ||
  2668. ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ) ) {
  2669. //
  2670. // Check to see if this node has already been used in a virtual
  2671. // line.
  2672. //
  2673. pTemp = kmxlFirstInList( *plistLines );
  2674. while( pTemp ) {
  2675. if( pTemp->SourceId == ( 0x8000 + pNode->Id ) ) {
  2676. while( pLine->Controls ) {
  2677. pControl = kmxlRemoveFirstControl( pLine->Controls );
  2678. kmxlFreeControl( pControl );
  2679. }
  2680. AudioFreeMemory_Unknown( &pLine );
  2681. RETURN( STATUS_SUCCESS );
  2682. }
  2683. pTemp = kmxlNextLine( pTemp );
  2684. }
  2685. //
  2686. // Set up the pin. The name will be the name of the node.
  2687. //
  2688. pLine->SourceId = 0x8000 + pNode->Id;
  2689. Control.NodeType = &pNode->NodeType;
  2690. kmxlGetNodeName( pmxobj->pfo, pNode->Id, &Control );
  2691. RtlCopyMemory(
  2692. pLine->Line.szShortName,
  2693. Control.Control.szShortName,
  2694. min(
  2695. sizeof( pLine->Line.szShortName ),
  2696. sizeof( Control.Control.szShortName )
  2697. )
  2698. );
  2699. RtlCopyMemory(
  2700. pLine->Line.szName,
  2701. Control.Control.szName,
  2702. min(
  2703. sizeof( pLine->Line.szName ),
  2704. sizeof( Control.Control.szName )
  2705. )
  2706. );
  2707. break;
  2708. }
  2709. pNode = (kmxlFirstParentNode( pNode ))->pNode;
  2710. }
  2711. //
  2712. // By making this line type of "SUM" (which technically it is), it
  2713. // will guarantee that this line gets a target type of UNDEFINED.
  2714. //
  2715. pLine->Type = KSNODETYPE_SUM;
  2716. pLine->Communication = KSPIN_COMMUNICATION_NONE;
  2717. pLine->Line.cbStruct = sizeof( MIXERLINE );
  2718. pLine->Line.dwSource = (DWORD) -1;
  2719. pLine->Line.dwDestination = (DWORD) -1;
  2720. pLine->Line.fdwLine = MIXERLINE_LINEF_SOURCE |
  2721. MIXERLINE_LINEF_ACTIVE;
  2722. kmxlAddToEndOfList( plistLines, pLine );
  2723. pLine->DestId = kmxlFindDestinationForNode(
  2724. pmxobj, pMux, pMux, pLine, plistLines
  2725. );
  2726. RETURN( STATUS_SUCCESS );
  2727. }
  2728. ///////////////////////////////////////////////////////////////////////
  2729. //
  2730. // kmxlTranslateNodeToControl
  2731. //
  2732. //
  2733. // Translates a NodeType GUID into a mixer line control. The memory
  2734. // for the control(s) is allocated and as much information about the
  2735. // control is filled in.
  2736. //
  2737. // NOTES
  2738. // This function returns the number of controls added to the ppControl
  2739. // array.
  2740. //
  2741. // Returns the number of controls actually created.
  2742. //
  2743. //
  2744. ULONG
  2745. kmxlTranslateNodeToControl(
  2746. IN PMIXEROBJECT pmxobj,
  2747. IN PMXLNODE pNode, // The node to translate into a control
  2748. OUT PMXLCONTROL* ppControl // The control to fill in
  2749. )
  2750. {
  2751. PMXLCONTROL pControl;
  2752. NTSTATUS Status = STATUS_SUCCESS;
  2753. ASSERT( pmxobj );
  2754. ASSERT( pNode );
  2755. ASSERT( ppControl );
  2756. PAGED_CODE();
  2757. //
  2758. // Bug fix. The caller might not clear this. This needs to be NULL do
  2759. // the caller doesn't think controls were created when the function
  2760. // fails.
  2761. //
  2762. *ppControl = NULL;
  2763. //
  2764. // If the node is NULL, there's nothing to do.
  2765. //
  2766. if( pNode == NULL ) {
  2767. *ppControl = NULL;
  2768. return( 0 );
  2769. }
  2770. DPF(DL_TRACE|FA_MIXER,( "Translating %d(0x%x) ( %s ) NODE:%08x",
  2771. pNode->Id,pNode->Id,
  2772. NodeTypeToString( &pNode->NodeType ),
  2773. pNode ) );
  2774. ///////////////////////////////////////////////////////////////////
  2775. if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_AGC ) ) {
  2776. ///////////////////////////////////////////////////////////////////
  2777. //
  2778. // AGC is represented by an ONOFF control.
  2779. //
  2780. // AGC is a UNIFORM (mono) control.
  2781. //
  2782. ///////////////////////////////////////////////////////////////////
  2783. //
  2784. // Check to see if the node properly supports AGC.
  2785. //
  2786. Status = kmxlSupportsControl( pmxobj->pfo, pNode->Id, KSPROPERTY_AUDIO_AGC );
  2787. if (!NT_SUCCESS(Status)) {
  2788. DPF(DL_TRACE|FA_MIXER,( "AGC node fails property!" ) );
  2789. goto exit;
  2790. }
  2791. //
  2792. // Allocate the new control structure.
  2793. //
  2794. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2795. if( *ppControl == NULL ) {
  2796. DPF(DL_ERROR|FA_MIXER,( "failed to allocate AGC control!" ) );
  2797. goto exit;
  2798. }
  2799. //
  2800. // Fill in as much information as possible.
  2801. //
  2802. (*ppControl)->NodeType = &KSNODETYPE_AGC;
  2803. (*ppControl)->Id = pNode->Id;
  2804. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_AGC;
  2805. (*ppControl)->bScaled = FALSE;
  2806. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  2807. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
  2808. (*ppControl)->Control.cMultipleItems = 0;
  2809. (*ppControl)->Control.Bounds.dwMinimum = 0;
  2810. (*ppControl)->Control.Bounds.dwMaximum = 1;
  2811. (*ppControl)->Control.Metrics.cSteps = 0;
  2812. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
  2813. if (!NT_SUCCESS(Status))
  2814. {
  2815. kmxlFreeControl( *ppControl );
  2816. *ppControl = NULL;
  2817. goto exit;
  2818. } else {
  2819. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  2820. ASSERT( IsValidControl( *ppControl ) );
  2821. }
  2822. ///////////////////////////////////////////////////////////////////
  2823. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_LOUDNESS ) ) {
  2824. ///////////////////////////////////////////////////////////////////
  2825. //
  2826. // LOUNDNESS is represented by an ONOFF-type control.
  2827. //
  2828. // LOUDNESS is a UNIFORM (mono) control.
  2829. //
  2830. ///////////////////////////////////////////////////////////////////
  2831. //
  2832. // Check to see if the node properly supports LOUDNESS.
  2833. //
  2834. Status = kmxlSupportsControl( pmxobj->pfo, pNode->Id, KSPROPERTY_AUDIO_LOUDNESS );
  2835. if (!NT_SUCCESS(Status)) {
  2836. DPF(DL_TRACE|FA_MIXER,( "Loudness node fails property!" ) );
  2837. goto exit;
  2838. }
  2839. //
  2840. // Allocate the new control structure
  2841. //
  2842. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2843. if( *ppControl == NULL ) {
  2844. goto exit;
  2845. }
  2846. //
  2847. // Fill in as much information as possible.
  2848. //
  2849. (*ppControl)->NodeType = &KSNODETYPE_LOUDNESS;
  2850. (*ppControl)->Id = pNode->Id;
  2851. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_LOUDNESS;
  2852. (*ppControl)->bScaled = FALSE;
  2853. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  2854. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_LOUDNESS;
  2855. (*ppControl)->Control.cMultipleItems = 0;
  2856. (*ppControl)->Control.Bounds.dwMinimum = 0;
  2857. (*ppControl)->Control.Bounds.dwMaximum = 1;
  2858. (*ppControl)->Control.Metrics.cSteps = 0;
  2859. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
  2860. if (!NT_SUCCESS(Status))
  2861. {
  2862. kmxlFreeControl( *ppControl );
  2863. *ppControl = NULL;
  2864. goto exit;
  2865. } else {
  2866. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  2867. ASSERT( IsValidControl( *ppControl ) );
  2868. }
  2869. ///////////////////////////////////////////////////////////////////
  2870. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUTE ) ) {
  2871. ///////////////////////////////////////////////////////////////////
  2872. //
  2873. // MUTE is represented by an ONOFF-type control.
  2874. //
  2875. // MUTE is a UNIFORM (mono) control.
  2876. //
  2877. ///////////////////////////////////////////////////////////////////
  2878. //
  2879. // Check to see if the node properly supports MUTE.
  2880. //
  2881. Status = kmxlSupportsControl(
  2882. pmxobj->pfo,
  2883. pNode->Id,
  2884. KSPROPERTY_AUDIO_MUTE );
  2885. if (!NT_SUCCESS(Status)) {
  2886. DPF(DL_TRACE|FA_MIXER,( "Mute node fails property!" ) );
  2887. goto exit;
  2888. }
  2889. //
  2890. // Allocate the new control structure
  2891. //
  2892. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2893. if( *ppControl == NULL ) {
  2894. goto exit;
  2895. }
  2896. //
  2897. // Fill in as much information as possible.
  2898. //
  2899. (*ppControl)->NodeType = &KSNODETYPE_MUTE;
  2900. (*ppControl)->Id = pNode->Id;
  2901. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_MUTE;
  2902. (*ppControl)->bScaled = FALSE;
  2903. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  2904. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
  2905. (*ppControl)->Control.cMultipleItems = 0;
  2906. (*ppControl)->Control.Bounds.dwMinimum = 0;
  2907. (*ppControl)->Control.Bounds.dwMaximum = 1;
  2908. (*ppControl)->Control.Metrics.cSteps = 0;
  2909. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
  2910. if (!NT_SUCCESS(Status))
  2911. {
  2912. kmxlFreeControl( *ppControl );
  2913. *ppControl = NULL;
  2914. goto exit;
  2915. } else {
  2916. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  2917. ASSERT( IsValidControl( *ppControl ) );
  2918. }
  2919. ///////////////////////////////////////////////////////////////////
  2920. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_TONE ) ) {
  2921. ///////////////////////////////////////////////////////////////////
  2922. //
  2923. // A TONE node can represent up to 3 controls:
  2924. // Treble: A fader control
  2925. // Bass: A fader control
  2926. // Bass Boost: A OnOff control
  2927. //
  2928. // Both Treble and Bass are UNIFORM (mono) controls.
  2929. //
  2930. // To determine which control(s) the TONE node represents, a helper
  2931. // function is called to query the particular property. If the
  2932. // helper function succeeds, a control is created for that property.
  2933. //
  2934. ///////////////////////////////////////////////////////////////////
  2935. Status = kmxlSupportsControl( pmxobj->pfo,
  2936. pNode->Id,
  2937. KSPROPERTY_AUDIO_BASS_BOOST );
  2938. if (NT_SUCCESS(Status)) {
  2939. //
  2940. // Bass boost control is supported. Allocate a new structure.
  2941. //
  2942. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2943. if( pControl == NULL ) {
  2944. goto exit;
  2945. }
  2946. //
  2947. // Fill in as much information as possible.
  2948. //
  2949. pControl->NodeType = &KSNODETYPE_TONE;
  2950. pControl->Id = pNode->Id;
  2951. pControl->PropertyId = KSPROPERTY_AUDIO_BASS_BOOST;
  2952. pControl->bScaled = FALSE;
  2953. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  2954. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
  2955. pControl->Control.cMultipleItems = 0;
  2956. pControl->Control.Bounds.dwMinimum = 0;
  2957. pControl->Control.Bounds.dwMaximum = 1;
  2958. pControl->Control.Metrics.cSteps = 0;
  2959. Status = kmxlGetControlChannels( pmxobj->pfo, pControl );
  2960. if (!NT_SUCCESS(Status))
  2961. {
  2962. kmxlFreeControl( pControl );
  2963. pControl = NULL;
  2964. goto exit;
  2965. }
  2966. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  2967. ASSERT( IsValidControl( pControl ) );
  2968. //
  2969. // Add this new control to the list.
  2970. //
  2971. kmxlAddToList( *ppControl, pControl );
  2972. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2973. if( pControl ) {
  2974. RtlCopyMemory( pControl, *ppControl, sizeof( MXLCONTROL ) );
  2975. //
  2976. // Copy the channel steppings from the original control
  2977. //
  2978. //
  2979. // Sense we copied the control above, we might have gotten
  2980. // a pChannelStepping pointer in the copy. We'll NULL that out
  2981. // for the memory allocation.
  2982. //
  2983. pControl->pChannelStepping = NULL;
  2984. ASSERT(pControl->NumChannels > 0);
  2985. Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  2986. TAG_AuDC_CHANNEL,
  2987. DEFAULT_MEMORY,
  2988. &pControl->pChannelStepping );
  2989. if( !NT_SUCCESS( Status ) ) {
  2990. pControl->NumChannels = 0;
  2991. kmxlFreeControl( pControl );
  2992. pControl = NULL;
  2993. goto exit;
  2994. }
  2995. RtlCopyMemory( pControl->pChannelStepping,
  2996. (*ppControl)->pChannelStepping,
  2997. pControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
  2998. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_BASS_BOOST;
  2999. kmxlAddToList( *ppControl, pControl );
  3000. ASSERT( IsValidControl( pControl ) );
  3001. }
  3002. }
  3003. Status = kmxlSupportsBassControl( pmxobj->pfo, pNode->Id );
  3004. if (NT_SUCCESS(Status)) {
  3005. //
  3006. // Bass control is supported. Allocate a new structure.
  3007. //
  3008. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3009. if( pControl == NULL ) {
  3010. goto exit;
  3011. }
  3012. //
  3013. // Fill in as much information as possible.
  3014. //
  3015. pControl->NodeType = &KSNODETYPE_TONE;
  3016. pControl->Id = pNode->Id;
  3017. pControl->PropertyId = KSPROPERTY_AUDIO_BASS;
  3018. pControl->bScaled = TRUE;
  3019. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3020. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_BASS;
  3021. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3022. pControl->Control.cMultipleItems = 0;
  3023. pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3024. pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3025. pControl->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3026. Status = kmxlGetControlRange( pmxobj->pfo, pControl );
  3027. if (!NT_SUCCESS(Status))
  3028. {
  3029. kmxlFreeControl( pControl );
  3030. pControl = NULL;
  3031. goto exit;
  3032. } else {
  3033. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  3034. //
  3035. // Add this new control to the list.
  3036. //
  3037. ASSERT( IsValidControl( pControl ) );
  3038. kmxlAddToList( *ppControl, pControl );
  3039. }
  3040. }
  3041. Status = kmxlSupportsTrebleControl( pmxobj->pfo, pNode->Id );
  3042. if (NT_SUCCESS(Status)) {
  3043. //
  3044. // Treble is supported. Allocate a new control structure.
  3045. //
  3046. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3047. if( pControl == NULL ) {
  3048. goto exit;
  3049. }
  3050. //
  3051. // Fill in as much information as possible.
  3052. //
  3053. pControl->NodeType = &KSNODETYPE_TONE;
  3054. pControl->Id = pNode->Id;
  3055. pControl->PropertyId = KSPROPERTY_AUDIO_TREBLE;
  3056. pControl->bScaled = TRUE;
  3057. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3058. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_TREBLE;
  3059. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3060. pControl->Control.cMultipleItems = 0;
  3061. pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3062. pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3063. pControl->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3064. Status = kmxlGetControlRange( pmxobj->pfo, pControl );
  3065. if (!NT_SUCCESS(Status))
  3066. {
  3067. kmxlFreeControl( pControl );
  3068. pControl = NULL;
  3069. goto exit;
  3070. } else {
  3071. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  3072. //
  3073. // Add this new control to the list.
  3074. //
  3075. ASSERT( IsValidControl( pControl ) );
  3076. kmxlAddToList( *ppControl, pControl );
  3077. }
  3078. }
  3079. ///////////////////////////////////////////////////////////////////
  3080. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_VOLUME ) ) {
  3081. ///////////////////////////////////////////////////////////////////
  3082. //
  3083. // A VOLUME is a fader-type control
  3084. //
  3085. // To determine if a node supports volume changes
  3086. //
  3087. ///////////////////////////////////////////////////////////////////
  3088. //
  3089. // Check to see if the node properly supports volume
  3090. //
  3091. Status = kmxlSupportsControl(
  3092. pmxobj->pfo,
  3093. pNode->Id,
  3094. KSPROPERTY_AUDIO_VOLUMELEVEL
  3095. );
  3096. if (!NT_SUCCESS(Status)) {
  3097. DPF(DL_TRACE|FA_MIXER,( "Volume node fails property!" ) );
  3098. goto exit;
  3099. }
  3100. //
  3101. // Allocate the new control structure
  3102. //
  3103. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3104. if( *ppControl == NULL ) {
  3105. goto exit;
  3106. }
  3107. //
  3108. // Fill in as much information as possible.
  3109. //
  3110. (*ppControl)->NodeType = &KSNODETYPE_VOLUME;
  3111. (*ppControl)->Id = pNode->Id;
  3112. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_VOLUMELEVEL;
  3113. (*ppControl)->bScaled = TRUE;
  3114. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3115. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  3116. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3117. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3118. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3119. (*ppControl)->Control.cMultipleItems = 0;
  3120. Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
  3121. if (!NT_SUCCESS(Status))
  3122. {
  3123. kmxlFreeControl( *ppControl );
  3124. *ppControl = NULL;
  3125. goto exit;
  3126. }
  3127. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3128. ASSERT( IsValidControl( *ppControl ) );
  3129. ///////////////////////////////////////////////////////////////////
  3130. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_PEAKMETER ) ) {
  3131. ///////////////////////////////////////////////////////////////////
  3132. //
  3133. // To determine if a node supports peak meter properties
  3134. //
  3135. ///////////////////////////////////////////////////////////////////
  3136. //
  3137. // Check to see if the node properly supports peakmeter
  3138. //
  3139. Status = kmxlSupportsControl(
  3140. pmxobj->pfo,
  3141. pNode->Id,
  3142. KSPROPERTY_AUDIO_PEAKMETER
  3143. );
  3144. if (!NT_SUCCESS(Status)) {
  3145. DPF(DL_TRACE|FA_MIXER,( "Peakmeter node fails property!" ) );
  3146. goto exit;
  3147. }
  3148. //
  3149. // Allocate the new control structure
  3150. //
  3151. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3152. if( *ppControl == NULL ) {
  3153. goto exit;
  3154. }
  3155. //
  3156. // Fill in as much information as possible.
  3157. //
  3158. (*ppControl)->NodeType = &KSNODETYPE_PEAKMETER;
  3159. (*ppControl)->Id = pNode->Id;
  3160. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_PEAKMETER;
  3161. (*ppControl)->bScaled = FALSE;
  3162. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3163. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER;
  3164. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3165. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3166. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3167. (*ppControl)->Control.cMultipleItems = 0;
  3168. Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
  3169. if (!NT_SUCCESS(Status))
  3170. {
  3171. kmxlFreeControl( *ppControl );
  3172. *ppControl = NULL;
  3173. goto exit;
  3174. }
  3175. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3176. ASSERT( IsValidControl( *ppControl ) );
  3177. ///////////////////////////////////////////////////////////////////
  3178. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) {
  3179. ///////////////////////////////////////////////////////////////////
  3180. //
  3181. // A MUX is a single select type control.
  3182. //
  3183. ///////////////////////////////////////////////////////////////////
  3184. {
  3185. ULONG Line;
  3186. //
  3187. // Do a quick check and see if the mux responds properly.
  3188. // If not, just get out of here quick.
  3189. //
  3190. if( !NT_SUCCESS( kmxlGetNodeProperty(
  3191. pmxobj->pfo,
  3192. &KSPROPSETID_Audio,
  3193. KSPROPERTY_AUDIO_MUX_SOURCE,
  3194. pNode->Id,
  3195. 0,
  3196. NULL,
  3197. &Line,
  3198. sizeof( Line ) ) ) )
  3199. {
  3200. goto exit;
  3201. }
  3202. //
  3203. // Look to see if a control has already been generated for this
  3204. // node. If so, the control information can be used from it
  3205. // instead of creating a new one.
  3206. //
  3207. pControl = kmxlFirstInList( pmxobj->listMuxControls );
  3208. while( pControl ) {
  3209. ASSERT( IsValidControl( pControl ) );
  3210. if( pControl->Id == pNode->Id ) {
  3211. break;
  3212. }
  3213. pControl = kmxlNextControl( pControl );
  3214. }
  3215. //
  3216. // Allocate the new control structure
  3217. //
  3218. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3219. if( *ppControl == NULL ) {
  3220. goto exit;
  3221. }
  3222. if( pControl == NULL ) {
  3223. //
  3224. // This node has not been seen before. Fill in as much info as
  3225. // possible.
  3226. //
  3227. (*ppControl)->NodeType = &KSNODETYPE_MUX;
  3228. (*ppControl)->Id = pNode->Id;
  3229. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_MUX_SOURCE;
  3230. (*ppControl)->bScaled = FALSE;
  3231. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3232. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
  3233. (*ppControl)->Control.cMultipleItems = kmxlGetNumMuxLines(
  3234. pmxobj->pTopology,
  3235. pNode->Id
  3236. );
  3237. (*ppControl)->Control.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE |
  3238. MIXERCONTROL_CONTROLF_UNIFORM;
  3239. (*ppControl)->Control.Bounds.dwMinimum = 0;
  3240. (*ppControl)->Control.Bounds.dwMaximum = (*ppControl)->Control.cMultipleItems - 1;
  3241. (*ppControl)->Control.Metrics.cSteps = (*ppControl)->Control.cMultipleItems;
  3242. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3243. kmxlGetMuxLineNames( pmxobj, *ppControl );
  3244. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3245. if( pControl == NULL ) {
  3246. kmxlFreeControl( *ppControl );
  3247. *ppControl = NULL;
  3248. goto exit;
  3249. }
  3250. //
  3251. // Make a copy of this control for the mux list
  3252. //
  3253. (*ppControl)->Control.dwControlID = pmxobj->dwControlId++;
  3254. RtlCopyMemory( pControl, *ppControl, sizeof( MXLCONTROL ) );
  3255. ASSERT( IsValidControl( pControl ) );
  3256. pControl->Parameters.bHasCopy = TRUE;
  3257. (*ppControl)->Parameters.bHasCopy = FALSE;
  3258. kmxlAddToList( pmxobj->listMuxControls, pControl );
  3259. } else {
  3260. RtlCopyMemory( *ppControl, pControl, sizeof( MXLCONTROL ) );
  3261. ASSERT( IsValidControl( *ppControl ) );
  3262. (*ppControl)->Parameters.bHasCopy = TRUE;
  3263. (*ppControl)->List.Next = NULL;
  3264. }
  3265. }
  3266. #ifdef STEREO_ENHANCE
  3267. ///////////////////////////////////////////////////////////////////
  3268. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_STEREO_WIDE ) ) {
  3269. ///////////////////////////////////////////////////////////////////
  3270. //
  3271. // Stereo Enhance is a boolean control.
  3272. //
  3273. ///////////////////////////////////////////////////////////////////
  3274. //
  3275. // Check to see if the node properly supports stereo wide
  3276. //
  3277. Status = kmxlSupportsControl(
  3278. pfoInstance,
  3279. pNode->Id,
  3280. KSPROPERTY_AUDIO_WIDE_MODE
  3281. );
  3282. if (!NT_SUCCESS(Status)) {
  3283. DPF(DL_TRACE|FA_MIXER,( "Stereo Wide node fails property!" ) );
  3284. goto exit;
  3285. }
  3286. //
  3287. // Allocate the new control structure
  3288. //
  3289. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3290. if( *ppControl == NULL ) {
  3291. goto exit;
  3292. }
  3293. //
  3294. // Fill in as much information as possible.
  3295. //
  3296. (*ppControl)->NodeType = &KSNODETYPE_STEREO_ENHANCE;
  3297. (*ppControl)->Id = pNode->Id;
  3298. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_WIDE_MODE;
  3299. (*ppControl)->bScaled = FALSE;
  3300. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3301. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_STEREOENH;
  3302. (*ppControl)->Control.cMultipleItems = 0;
  3303. (*ppControl)->Control.Bounds.dwMinimum = 0;
  3304. (*ppControl)->Control.Bounds.dwMaximum = 1;
  3305. (*ppControl)->Control.Metrics.cSteps = 0;
  3306. Status = kmxlGetControlChannels( pfoInstance, *ppControl );
  3307. if (!NT_SUCCESS(Status))
  3308. {
  3309. kmxlFreeControl( *ppControl );
  3310. *ppControl = NULL;
  3311. goto exit;
  3312. }
  3313. kmxlGetNodeName( pfoInstance, pNode->Id, (*ppControl));
  3314. #endif
  3315. ///////////////////////////////////////////////////////////////////
  3316. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_STEREO_WIDE ) ) {
  3317. ///////////////////////////////////////////////////////////////////
  3318. //
  3319. // Check to see if the node properly supports stereo wide
  3320. //
  3321. Status = kmxlSupportsControl(
  3322. pmxobj->pfo,
  3323. pNode->Id,
  3324. KSPROPERTY_AUDIO_WIDENESS
  3325. );
  3326. if (!NT_SUCCESS(Status)) {
  3327. DPF(DL_TRACE|FA_MIXER,( "Stereo wide node fails property!" ) );
  3328. goto exit;
  3329. }
  3330. //
  3331. // Allocate the new control structure
  3332. //
  3333. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3334. if( *ppControl == NULL ) {
  3335. goto exit;
  3336. }
  3337. //
  3338. // Fill in as much information as possible.
  3339. //
  3340. (*ppControl)->NodeType = &KSNODETYPE_STEREO_WIDE;
  3341. (*ppControl)->Id = pNode->Id;
  3342. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_WIDENESS;
  3343. (*ppControl)->bScaled = FALSE;
  3344. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3345. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
  3346. (*ppControl)->Control.cMultipleItems = 0;
  3347. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3348. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3349. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3350. Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
  3351. if (!NT_SUCCESS(Status))
  3352. {
  3353. kmxlFreeControl( *ppControl );
  3354. *ppControl = NULL;
  3355. goto exit;
  3356. }
  3357. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3358. ASSERT( IsValidControl( *ppControl ) );
  3359. ///////////////////////////////////////////////////////////////////
  3360. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_CHORUS ) ) {
  3361. ///////////////////////////////////////////////////////////////////
  3362. //
  3363. // Check to see if the node properly supports chorus
  3364. //
  3365. Status = kmxlSupportsControl(
  3366. pmxobj->pfo,
  3367. pNode->Id,
  3368. KSPROPERTY_AUDIO_CHORUS_LEVEL
  3369. );
  3370. if (!NT_SUCCESS(Status)) {
  3371. DPF(DL_TRACE|FA_MIXER,( "Chorus node fails property!" ) );
  3372. goto exit;
  3373. }
  3374. //
  3375. // Allocate the new control structure
  3376. //
  3377. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3378. if( *ppControl == NULL ) {
  3379. goto exit;
  3380. }
  3381. //
  3382. // Fill in as much information as possible.
  3383. //
  3384. (*ppControl)->NodeType = &KSNODETYPE_CHORUS;
  3385. (*ppControl)->Id = pNode->Id;
  3386. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_CHORUS_LEVEL;
  3387. (*ppControl)->bScaled = FALSE;
  3388. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3389. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
  3390. (*ppControl)->Control.cMultipleItems = 0;
  3391. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3392. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3393. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3394. // (*ppControl)->Control.Metrics.cSteps = 0xFFFF;
  3395. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl ); // Should we also get the range?
  3396. if (!NT_SUCCESS(Status))
  3397. {
  3398. kmxlFreeControl( *ppControl );
  3399. *ppControl = NULL;
  3400. goto exit;
  3401. } else {
  3402. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3403. ASSERT( IsValidControl( *ppControl ) );
  3404. }
  3405. ///////////////////////////////////////////////////////////////////
  3406. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_REVERB ) ) {
  3407. ///////////////////////////////////////////////////////////////////
  3408. //
  3409. // Check to see if the node properly supports reverb
  3410. //
  3411. Status = kmxlSupportsControl(
  3412. pmxobj->pfo,
  3413. pNode->Id,
  3414. KSPROPERTY_AUDIO_REVERB_LEVEL
  3415. );
  3416. if (!NT_SUCCESS(Status)) {
  3417. DPF(DL_TRACE|FA_MIXER,( "Reverb node fails property!" ) );
  3418. goto exit;
  3419. }
  3420. //
  3421. // Allocate the new control structure
  3422. //
  3423. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3424. if( *ppControl == NULL ) {
  3425. goto exit;
  3426. }
  3427. //
  3428. // Fill in as much information as possible.
  3429. //
  3430. (*ppControl)->NodeType = &KSNODETYPE_REVERB;
  3431. (*ppControl)->Id = pNode->Id;
  3432. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_REVERB_LEVEL;
  3433. (*ppControl)->bScaled = FALSE;
  3434. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3435. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
  3436. (*ppControl)->Control.cMultipleItems = 0;
  3437. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3438. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3439. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3440. // (*ppControl)->Control.Metrics.cSteps = 0xFFFF;
  3441. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl ); // Should we also get the range?
  3442. if (!NT_SUCCESS(Status))
  3443. {
  3444. kmxlFreeControl( *ppControl );
  3445. *ppControl = NULL;
  3446. goto exit;
  3447. } else {
  3448. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3449. ASSERT( IsValidControl( *ppControl ) );
  3450. }
  3451. ///////////////////////////////////////////////////////////////////
  3452. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUPERMIX ) ) {
  3453. ///////////////////////////////////////////////////////////////////
  3454. //
  3455. // SuperMix nodes can be supported as MUTE controls if the MUTE
  3456. // property is supported.
  3457. //
  3458. ///////////////////////////////////////////////////////////////////
  3459. PKSAUDIO_MIXCAP_TABLE pMixCaps;
  3460. PLONG pReferenceCount = NULL;
  3461. ULONG i,
  3462. Size;
  3463. BOOL bMutable;
  3464. BOOL bVolume = FALSE;
  3465. PKSAUDIO_MIXLEVEL pMixLevels = NULL;
  3466. #ifdef SUPERMIX_AS_VOL
  3467. ULONG Channels;
  3468. #endif
  3469. if( !NT_SUCCESS( kmxlGetSuperMixCaps( pmxobj->pfo, pNode->Id, &pMixCaps ) ) ) {
  3470. goto exit;
  3471. }
  3472. Status = AudioAllocateMemory_Paged(sizeof( LONG ),
  3473. TAG_AudS_SUPERMIX,
  3474. ZERO_FILL_MEMORY,
  3475. &pReferenceCount );
  3476. if( !NT_SUCCESS( Status ) ) {
  3477. AudioFreeMemory_Unknown( &pMixCaps );
  3478. *ppControl = NULL;
  3479. goto exit;
  3480. }
  3481. *pReferenceCount=0;
  3482. Size = pMixCaps->InputChannels * pMixCaps->OutputChannels;
  3483. Status = AudioAllocateMemory_Paged(Size * sizeof( KSAUDIO_MIXLEVEL ),
  3484. TAG_Audl_MIXLEVEL,
  3485. ZERO_FILL_MEMORY,
  3486. &pMixLevels );
  3487. if( !NT_SUCCESS( Status ) ) {
  3488. AudioFreeMemory_Unknown( &pMixCaps );
  3489. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3490. *ppControl = NULL;
  3491. goto exit;
  3492. }
  3493. Status = kmxlGetNodeProperty(
  3494. pmxobj->pfo,
  3495. &KSPROPSETID_Audio,
  3496. KSPROPERTY_AUDIO_MIX_LEVEL_TABLE,
  3497. pNode->Id,
  3498. 0,
  3499. NULL,
  3500. pMixLevels,
  3501. Size * sizeof( KSAUDIO_MIXLEVEL )
  3502. );
  3503. if( !NT_SUCCESS( Status ) ) {
  3504. AudioFreeMemory_Unknown( &pMixCaps );
  3505. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3506. AudioFreeMemory_Unknown( &pMixLevels );
  3507. DPF(DL_WARNING|FA_MIXER,("kmxlGetNodeProperty failed Status=%X",Status) );
  3508. *ppControl = NULL;
  3509. goto exit;
  3510. }
  3511. bMutable = TRUE;
  3512. for( i = 0; i < Size; i++ ) {
  3513. //
  3514. // If the channel is mutable, then all is well for this entry.
  3515. //
  3516. if( pMixCaps->Capabilities[ i ].Mute ) {
  3517. continue;
  3518. }
  3519. //
  3520. // The the entry is not mutable but is fully attenuated,
  3521. // this will work too.
  3522. //
  3523. if( ( pMixCaps->Capabilities[ i ].Minimum == LONG_MIN ) &&
  3524. ( pMixCaps->Capabilities[ i ].Maximum == LONG_MIN ) &&
  3525. ( pMixCaps->Capabilities[ i ].Reset == LONG_MIN ) )
  3526. {
  3527. continue;
  3528. }
  3529. bMutable = FALSE;
  3530. break;
  3531. }
  3532. #ifdef SUPERMIX_AS_VOL
  3533. bVolume = TRUE;
  3534. Channels = 0;
  3535. for( i = 0; i < Size; i += pMixCaps->OutputChannels + 1 ) {
  3536. if( ( pMixCaps->Capabilities[ i ].Maximum -
  3537. pMixCaps->Capabilities[ i ].Minimum ) > 0 )
  3538. {
  3539. ++Channels;
  3540. continue;
  3541. }
  3542. bVolume = FALSE;
  3543. break;
  3544. }
  3545. #endif
  3546. //
  3547. // This node cannot be used as a MUTE control.
  3548. //
  3549. if( !bMutable && !bVolume ) {
  3550. AudioFreeMemory_Unknown( &pMixCaps );
  3551. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3552. AudioFreeMemory_Unknown( &pMixLevels );
  3553. *ppControl = NULL;
  3554. goto exit;
  3555. }
  3556. if( bMutable ) {
  3557. //
  3558. // The Supermix is verifiably usable as a MUTE. Fill in all the
  3559. // details.
  3560. //
  3561. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3562. if( pControl != NULL ) {
  3563. pControl->NodeType = &KSNODETYPE_SUPERMIX;
  3564. pControl->Id = pNode->Id;
  3565. pControl->PropertyId = KSPROPERTY_AUDIO_MIX_LEVEL_TABLE;
  3566. pControl->bScaled = FALSE;
  3567. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3568. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
  3569. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3570. pControl->Control.cMultipleItems = 0;
  3571. pControl->Control.Bounds.dwMinimum = 0;
  3572. pControl->Control.Bounds.dwMaximum = 1;
  3573. pControl->Control.Metrics.cSteps = 0;
  3574. InterlockedIncrement(pReferenceCount);
  3575. pControl->Parameters.pReferenceCount = pReferenceCount;
  3576. pControl->Parameters.Size = pMixCaps->InputChannels *
  3577. pMixCaps->OutputChannels;
  3578. pControl->Parameters.pMixCaps = pMixCaps;
  3579. pControl->Parameters.pMixLevels = pMixLevels;
  3580. Status = AudioAllocateMemory_Paged(sizeof( CHANNEL_STEPPING ),
  3581. TAG_AuDE_CHANNEL,
  3582. ZERO_FILL_MEMORY,
  3583. &pControl->pChannelStepping );
  3584. if( !NT_SUCCESS( Status ) ) {
  3585. AudioFreeMemory_Unknown( &pMixCaps );
  3586. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3587. AudioFreeMemory_Unknown( &pMixLevels );
  3588. *ppControl = NULL;
  3589. goto exit;
  3590. }
  3591. pControl->NumChannels = 1;
  3592. pControl->pChannelStepping->MinValue = pMixCaps->Capabilities[ 0 ].Minimum;
  3593. pControl->pChannelStepping->MaxValue = pMixCaps->Capabilities[ 0 ].Maximum;
  3594. pControl->pChannelStepping->Steps = 32;
  3595. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  3596. kmxlAddToList( *ppControl, pControl );
  3597. ASSERT( IsValidControl( pControl ) );
  3598. }
  3599. }
  3600. #ifdef SUPERMIX_AS_VOL
  3601. if( bVolume ) {
  3602. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3603. if( pControl != NULL ) {
  3604. pControl->NodeType = &KSNODETYPE_SUPERMIX;
  3605. pControl->Id = pNode->Id;
  3606. pControl->PropertyId = KSPROPERTY_AUDIO_MIX_LEVEL_TABLE;
  3607. pControl->bScaled = TRUE;
  3608. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3609. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  3610. pControl->Control.cMultipleItems = 0;
  3611. pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3612. pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3613. pControl->Control.Metrics.cSteps = 32;
  3614. InterlockedIncrement(pReferenceCount);
  3615. pControl->Parameters.pReferenceCount = pReferenceCount;
  3616. pControl->Parameters.Size = pMixCaps->InputChannels *
  3617. pMixCaps->OutputChannels;
  3618. pControl->Parameters.pMixCaps = pMixCaps;
  3619. pControl->Parameters.pMixLevels = pMixLevels;
  3620. if( Channels == 1 ) {
  3621. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3622. } else {
  3623. pControl->Control.fdwControl = 0;
  3624. }
  3625. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl );
  3626. kmxlAddToList( *ppControl, pControl );
  3627. ASSERT( IsValidControl( pControl ) );
  3628. }
  3629. }
  3630. #endif // SUPERMIX_AS_VOL
  3631. if( *ppControl == NULL ) {
  3632. AudioFreeMemory_Unknown( &pMixCaps );
  3633. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3634. AudioFreeMemory_Unknown( &pMixLevels );
  3635. }
  3636. }
  3637. exit:
  3638. if( *ppControl ) {
  3639. DPF(DL_TRACE|FA_MIXER,( "Translated %d controls.", kmxlListLength( *ppControl ) ) );
  3640. return( kmxlListLength( *ppControl ) );
  3641. } else {
  3642. DPF(DL_TRACE|FA_MIXER,( "Translated no controls." ) );
  3643. return( 0 );
  3644. }
  3645. }
  3646. #define KsAudioPropertyToString( Property ) \
  3647. Property == KSPROPERTY_AUDIO_VOLUMELEVEL ? "Volume" : \
  3648. Property == KSPROPERTY_AUDIO_MUTE ? "Mute" : \
  3649. Property == KSPROPERTY_AUDIO_BASS ? "Bass" : \
  3650. Property == KSPROPERTY_AUDIO_TREBLE ? "Treble" : \
  3651. Property == KSPROPERTY_AUDIO_AGC ? "AGC" : \
  3652. Property == KSPROPERTY_AUDIO_LOUDNESS ? "Loudness" : \
  3653. Property == KSPROPERTY_AUDIO_PEAKMETER ? "Peakmeter" : \
  3654. "Unknown"
  3655. ///////////////////////////////////////////////////////////////////////
  3656. //
  3657. // kmxlSupportsControl
  3658. //
  3659. // Queries for property on control to see if it is actually supported
  3660. //
  3661. //
  3662. NTSTATUS
  3663. kmxlSupportsControl(
  3664. IN PFILE_OBJECT pfoInstance, // The instance to check for
  3665. IN ULONG Node, // The node id to query
  3666. IN ULONG Property // The property to check for
  3667. )
  3668. {
  3669. NTSTATUS Status;
  3670. LONG Level;
  3671. ASSERT( pfoInstance );
  3672. PAGED_CODE();
  3673. //
  3674. // Check to see if the property works on the first channel.
  3675. //
  3676. Status = kmxlGetAudioNodeProperty(
  3677. pfoInstance,
  3678. Property,
  3679. Node,
  3680. 0, // Channel 0 - first channel
  3681. NULL, 0,
  3682. &Level, sizeof( Level )
  3683. );
  3684. if( !NT_SUCCESS( Status ) ) {
  3685. DPF(DL_WARNING|FA_MIXER,( "SupportsControl for (%d,%X) failed on first channel with %x.",
  3686. Node, Property, Status ) );
  3687. }
  3688. RETURN( Status );
  3689. }
  3690. ///////////////////////////////////////////////////////////////////////
  3691. //
  3692. // kmxlSupportsMultiChannelControl
  3693. //
  3694. // Queries for property on the second channel of the control to see
  3695. // independent levels can be set. It is assumed that the first channel
  3696. // already succeeded in kmxlSupportsControl
  3697. //
  3698. //
  3699. NTSTATUS
  3700. kmxlSupportsMultiChannelControl(
  3701. IN PFILE_OBJECT pfoInstance, // The instance to check for
  3702. IN ULONG Node, // The node id to query
  3703. IN ULONG Property // The property to check for
  3704. )
  3705. {
  3706. NTSTATUS Status;
  3707. LONG Level;
  3708. ASSERT( pfoInstance );
  3709. PAGED_CODE();
  3710. //
  3711. // Just check the property on the second channel because we have already checked
  3712. // the first channel already.
  3713. //
  3714. Status = kmxlGetAudioNodeProperty(
  3715. pfoInstance,
  3716. Property,
  3717. Node,
  3718. 1, // Second channel equals a channel value of 1
  3719. NULL, 0,
  3720. &Level, sizeof( Level )
  3721. );
  3722. RETURN( Status );
  3723. }
  3724. NTSTATUS
  3725. kmxlAssignLineAndControlIdsWorker(
  3726. IN PMIXEROBJECT pmxobj,
  3727. IN LINELIST listLines, // The list to assign ids for
  3728. IN ULONG ListType, // LIST_SOURCE or LIST_DESTINATION
  3729. IN OUT ULONG *pLineID,
  3730. IN GUID *pDestGuid
  3731. )
  3732. {
  3733. NTSTATUS Status = STATUS_SUCCESS;
  3734. PMXLLINE pLine = NULL;
  3735. PMXLCONTROL pControl = NULL;
  3736. ULONG LineID = 0;
  3737. ULONG Dest;
  3738. PAGED_CODE();
  3739. ASSERT ( ListType==SOURCE_LIST || ListType==DESTINATION_LIST );
  3740. if (pLineID!=NULL) {
  3741. LineID=*pLineID;
  3742. }
  3743. //
  3744. // Loop through each of the line structures
  3745. //
  3746. pLine = kmxlFirstInList( listLines );
  3747. if( pLine == NULL ) {
  3748. RETURN( Status );
  3749. }
  3750. Dest = pLine->DestId;
  3751. while( pLine ) {
  3752. //
  3753. // For destinations, set the dwDestination field and set
  3754. // the dwSource field for sources.
  3755. //
  3756. if( ListType == DESTINATION_LIST ) {
  3757. // Check if this line has already been assigned an ID.
  3758. // If so, then go to next line in list.
  3759. if (pLine->Line.dwDestination!=(DWORD)(-1)) {
  3760. pLine = kmxlNextLine( pLine );
  3761. continue;
  3762. }
  3763. // Now if we can only number lines of a particular GUID,
  3764. // then make sure this destination line type matches that guid.
  3765. if (pDestGuid!=NULL && !IsEqualGUID( pDestGuid, &pLine->Type )) {
  3766. pLine = kmxlNextLine( pLine );
  3767. continue;
  3768. }
  3769. //
  3770. // Assign the destination Id. Create the line Id by
  3771. // using -1 for the source in the highword and the
  3772. // destination in the loword.
  3773. //
  3774. pLine->Line.dwDestination = LineID++;
  3775. pLine->Line.dwLineID = MAKELONG(
  3776. pLine->Line.dwDestination,
  3777. -1
  3778. );
  3779. if (pLineID!=NULL) {
  3780. *pLineID=LineID;
  3781. }
  3782. } else if( ListType == SOURCE_LIST ) {
  3783. pLine->Line.dwSource = LineID++;
  3784. } else {
  3785. RETURN( STATUS_INVALID_PARAMETER );
  3786. }
  3787. //
  3788. // Set up the number of controls on this line.
  3789. //
  3790. pLine->Line.cControls = kmxlListLength( pLine->Controls );
  3791. //
  3792. // Loop through the controls, assigning them a control ID
  3793. // that is a pointer to the MXLCONTROL structure for that
  3794. // control.
  3795. //
  3796. pControl = kmxlFirstInList( pLine->Controls );
  3797. while( pControl ) {
  3798. if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) {
  3799. //
  3800. // MUX controls are already numbered by this point. Just skip
  3801. // it and go onto the next one.
  3802. //
  3803. pControl = kmxlNextControl( pControl );
  3804. continue;
  3805. }
  3806. pControl->Control.dwControlID = pmxobj->dwControlId++;
  3807. pControl = kmxlNextControl( pControl );
  3808. }
  3809. pLine = kmxlNextLine( pLine );
  3810. if( pLine == NULL ) {
  3811. continue;
  3812. }
  3813. if( ( ListType == SOURCE_LIST ) && ( pLine->DestId != Dest ) ) {
  3814. LineID = 0;
  3815. Dest = pLine->DestId;
  3816. }
  3817. }
  3818. RETURN( Status );
  3819. }
  3820. #define GUIDCOUNT 13
  3821. ///////////////////////////////////////////////////////////////////////
  3822. //
  3823. // kmxlAssignLineAndControlIds
  3824. //
  3825. // Loops through the list of lines and assigns ids for those line.
  3826. // For destinations, the Id starts a 0 and is incremented each time.
  3827. // The line id is a long of -1 and the dest id. For sources, the
  3828. // line Ids will need to be specified elsewhere so only dwSource
  3829. // field is assigned.
  3830. //
  3831. // For controls, each control is given an Id of the address to the
  3832. // MXLCONTROL structure.
  3833. //
  3834. //
  3835. NTSTATUS
  3836. kmxlAssignLineAndControlIds(
  3837. IN PMIXEROBJECT pmxobj,
  3838. IN LINELIST listLines, // The list to assign ids for
  3839. IN ULONG ListType // LIST_SOURCE or LIST_DESTINATION
  3840. )
  3841. {
  3842. PAGED_CODE();
  3843. ASSERT ( ListType==SOURCE_LIST || ListType==DESTINATION_LIST );
  3844. if (SOURCE_LIST==ListType) {
  3845. return( kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType, NULL, NULL) );
  3846. }
  3847. else if (DESTINATION_LIST==ListType) {
  3848. // In order to help sndvol32 do the right thing as far as which
  3849. // lines displayed as the default playback and record lines, we
  3850. // number lines based on what their destinations are.
  3851. // We use guid the pLine->Type field to decide how to number lines.
  3852. // Lines are prioritized in the following way: speakers, then
  3853. // headphones, then telephones. Non prioritized guids are assigned
  3854. // last in whatever order they appear in the list.
  3855. ULONG LineID=0;
  3856. ULONG i;
  3857. GUID prioritizeddestinationguids[GUIDCOUNT]= {
  3858. STATIC_KSNODETYPE_ROOM_SPEAKER,
  3859. STATIC_KSNODETYPE_DESKTOP_SPEAKER,
  3860. STATIC_KSNODETYPE_SPEAKER,
  3861. STATIC_KSNODETYPE_COMMUNICATION_SPEAKER,
  3862. STATIC_KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO,
  3863. STATIC_KSNODETYPE_ANALOG_CONNECTOR,
  3864. STATIC_KSNODETYPE_SPDIF_INTERFACE,
  3865. STATIC_KSNODETYPE_HEADPHONES,
  3866. STATIC_KSNODETYPE_TELEPHONE,
  3867. STATIC_KSNODETYPE_PHONE_LINE,
  3868. STATIC_KSNODETYPE_DOWN_LINE_PHONE,
  3869. STATIC_PINNAME_CAPTURE,
  3870. STATIC_KSCATEGORY_AUDIO,
  3871. };
  3872. // Cycle through the list for each prioritized guid and number
  3873. // those lines that match that particular guid.
  3874. for (i=0; i<GUIDCOUNT; i++) {
  3875. kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType,
  3876. &LineID, &prioritizeddestinationguids[i]);
  3877. }
  3878. // Now, number anything left over with a number that depends solely on
  3879. // its random order in the list.
  3880. return( kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType, &LineID, NULL) );
  3881. }
  3882. else {
  3883. RETURN( STATUS_INVALID_PARAMETER );
  3884. }
  3885. }
  3886. ///////////////////////////////////////////////////////////////////////
  3887. //
  3888. // kmxlAssignDestinationsToSources
  3889. //
  3890. // Loops through each source looking for a destination lines that
  3891. // have a matching destination id. Source line Ids are assigned
  3892. // by putting the source id in the hiword and the dest id in the
  3893. // loword.
  3894. //
  3895. //
  3896. NTSTATUS
  3897. kmxlAssignDestinationsToSources(
  3898. IN LINELIST listSourceLines, // The list of all source lines
  3899. IN LINELIST listDestLines // The list of all dest lines
  3900. )
  3901. {
  3902. PMXLLINE pSource = NULL,
  3903. pDest = NULL;
  3904. PAGED_CODE();
  3905. //
  3906. // For each source line, loop throught the destinations until a
  3907. // line is found matching the Id. The dwDestination field will
  3908. // be the zero-index Id of the destination.
  3909. //
  3910. pSource = kmxlFirstInList( listSourceLines );
  3911. while( pSource ) {
  3912. pDest = kmxlFirstInList( listDestLines );
  3913. while( pDest ) {
  3914. if( pSource->DestId == pDest->DestId ) {
  3915. //
  3916. // Heh, whatchya know?
  3917. //
  3918. pSource->Line.dwDestination = pDest->Line.dwDestination;
  3919. pSource->Line.dwLineID = MAKELONG(
  3920. (WORD) pSource->Line.dwDestination,
  3921. (WORD) pSource->Line.dwSource
  3922. );
  3923. break;
  3924. }
  3925. pDest = kmxlNextLine( pDest );
  3926. }
  3927. pSource = kmxlNextLine( pSource );
  3928. }
  3929. RETURN( STATUS_SUCCESS );
  3930. }
  3931. ///////////////////////////////////////////////////////////////////////
  3932. //
  3933. // kmxlUpdateDestinationConnectionCount
  3934. //
  3935. // For each of the destinations, loop through each of the sources
  3936. // and find those that connect to this destination. That count is
  3937. // then stored in the MIXERLINE.cConnections for the line.
  3938. //
  3939. //
  3940. NTSTATUS
  3941. kmxlUpdateDestintationConnectionCount(
  3942. IN LINELIST listSourceLines, // The list of source lines
  3943. IN LINELIST listDestLines // The list of destination lines
  3944. )
  3945. {
  3946. PMXLLINE pDest,
  3947. pSource;
  3948. ULONG Count;
  3949. PAGED_CODE();
  3950. //
  3951. // Loop through each destination finding all the sources that connect
  3952. // to it. The total number of sources connecting to a destination
  3953. // is sourced in the cConnections field of the MIXERLINE struct.
  3954. //
  3955. pDest = kmxlFirstInList( listDestLines );
  3956. while( pDest ) {
  3957. //
  3958. // Initialize the source ID. This will mark this as a valid
  3959. // destination.
  3960. //
  3961. pDest->SourceId = (ULONG) -1;
  3962. Count = 0;
  3963. //
  3964. // Loop through the sources looking for sources that connect to
  3965. // the current destination.
  3966. //
  3967. pSource = kmxlFirstInList( listSourceLines );
  3968. while( pSource ) {
  3969. //
  3970. // Found a match. Increment the count.
  3971. //
  3972. if( pSource->DestId == pDest->DestId ) {
  3973. ++Count;
  3974. }
  3975. pSource = kmxlNextLine( pSource );
  3976. }
  3977. pDest->Line.cConnections = Count;
  3978. pDest = kmxlNextLine( pDest );
  3979. }
  3980. RETURN( STATUS_SUCCESS );
  3981. }
  3982. VOID
  3983. CleanupLine(
  3984. PMXLLINE pLine
  3985. )
  3986. {
  3987. PMXLCONTROL pControl;
  3988. while( pLine->Controls ) {
  3989. pControl = kmxlRemoveFirstControl( pLine->Controls );
  3990. kmxlFreeControl( pControl );
  3991. }
  3992. AudioFreeMemory( sizeof(MXLLINE),&pLine );
  3993. }
  3994. ///////////////////////////////////////////////////////////////////////
  3995. //
  3996. // kmxlEliminateInvalidLines
  3997. //
  3998. // Loops through the lines removing lines that are invalid. Refer
  3999. // to the function for IsValidLine() for details on what is an invalid
  4000. // line.
  4001. //
  4002. //
  4003. NTSTATUS
  4004. kmxlEliminateInvalidLines(
  4005. IN LINELIST* listLines // The list of lines
  4006. )
  4007. {
  4008. PMXLLINE pLine, pTemp, pShadow;
  4009. PAGED_CODE();
  4010. //
  4011. // Eliminate all invalid lines at the start of the list.
  4012. //
  4013. pLine = kmxlFirstInList( *listLines );
  4014. while( pLine ) {
  4015. //
  4016. // Found the first valid line. Break out of this loop.
  4017. //
  4018. if( Is_Valid_Line( pLine ) ) {
  4019. break;
  4020. }
  4021. //
  4022. // This is an invalid line. Remove it from the list, free up
  4023. // all its control structures, and free the line structure.
  4024. //
  4025. pTemp = kmxlRemoveFirstLine( pLine );
  4026. CleanupLine(pTemp);
  4027. }
  4028. //
  4029. // Assign listLines to point to the first valid line.
  4030. //
  4031. *listLines = pLine;
  4032. if( pLine == NULL ) {
  4033. RETURN( STATUS_SUCCESS );
  4034. }
  4035. //
  4036. // At this point, pLine is a valid line. Keeping a hold on the prev
  4037. // line, loop through the lines eliminating the invalid ones.
  4038. //
  4039. pShadow = pLine;
  4040. while( pShadow && kmxlNextLine( pShadow ) ) {
  4041. pLine = kmxlNextLine( pShadow );
  4042. if( pLine && !Is_Valid_Line( pLine ) ) {
  4043. //
  4044. // Remove the invalid line from the list
  4045. //
  4046. pShadow->List.Next = pLine->List.Next;
  4047. pLine->List.Next = NULL;
  4048. CleanupLine(pLine);
  4049. continue;
  4050. }
  4051. pShadow = kmxlNextLine( pShadow );
  4052. }
  4053. // All the invalid lines have been eliminated. Now eliminate bad
  4054. // duplicates.
  4055. pShadow = kmxlFirstInList( *listLines );
  4056. while( pShadow ) {
  4057. //
  4058. // Walk all the lines looking for a match.
  4059. //
  4060. pLine = kmxlNextLine( pShadow );
  4061. pTemp = NULL;
  4062. while( pLine ) {
  4063. if( ( pShadow->SourceId == pLine->SourceId ) &&
  4064. ( pShadow->DestId == pLine->DestId ) )
  4065. {
  4066. DPF(DL_TRACE|FA_MIXER,( "Line %x is equal to line %x!",
  4067. pShadow->Line.dwLineID,
  4068. pLine->Line.dwLineID
  4069. ) );
  4070. //
  4071. // Found a match.
  4072. //
  4073. if( pTemp == NULL )
  4074. {
  4075. //
  4076. // pShadow is our previous line. Remove this line from the
  4077. // list.
  4078. //
  4079. pShadow->List.Next = pLine->List.Next;
  4080. pLine->List.Next = NULL;
  4081. CleanupLine(pLine);
  4082. //
  4083. // Now adjust pLine to the next line and loop
  4084. //
  4085. pLine = kmxlNextLine( pShadow );
  4086. continue;
  4087. } else {
  4088. //
  4089. // pTemp is our previous line. Remove this line from the
  4090. // list.
  4091. //
  4092. pTemp->List.Next = pLine->List.Next;
  4093. pLine->List.Next = NULL;
  4094. CleanupLine(pLine);
  4095. //
  4096. // Now adjust pLine to the next line and loop
  4097. //
  4098. pLine = kmxlNextLine( pTemp );
  4099. continue;
  4100. }
  4101. }
  4102. pTemp = pLine; //temp is previous line
  4103. pLine = kmxlNextLine( pLine );
  4104. }
  4105. pShadow = kmxlNextLine( pShadow );
  4106. }
  4107. RETURN( STATUS_SUCCESS );
  4108. }
  4109. ///////////////////////////////////////////////////////////////////////
  4110. //
  4111. // kmxlAssignComponentIds
  4112. //
  4113. // Loops through all the destinations then the sources and determines
  4114. // their component type and target types.
  4115. //
  4116. //
  4117. VOID
  4118. kmxlAssignComponentIds(
  4119. IN PMIXEROBJECT pmxobj,
  4120. IN LINELIST listSourceLines,
  4121. IN LINELIST listDestLines
  4122. )
  4123. {
  4124. PMXLLINE pLine;
  4125. PAGED_CODE();
  4126. //
  4127. // Loop through the destinations...
  4128. //
  4129. pLine = kmxlFirstInList( listDestLines );
  4130. while( pLine ) {
  4131. pLine->Line.dwComponentType = kmxlDetermineDestinationType(
  4132. pmxobj,
  4133. pLine
  4134. );
  4135. pLine = kmxlNextLine( pLine );
  4136. }
  4137. //
  4138. // Loop through the sources...
  4139. //
  4140. pLine = kmxlFirstInList( listSourceLines );
  4141. while( pLine ) {
  4142. pLine->Line.dwComponentType = kmxlDetermineSourceType(
  4143. pmxobj,
  4144. pLine
  4145. );
  4146. pLine = kmxlNextLine( pLine );
  4147. }
  4148. }
  4149. ///////////////////////////////////////////////////////////////////////
  4150. //
  4151. // kmxlUpdateMuxLines
  4152. //
  4153. // Updates the name, line ID, and componenttype of a line that has
  4154. // a mux control on it. The MixerControlDetails array is searched for
  4155. // an entry that has a matching source id and replaced with the info
  4156. // from this line.
  4157. //
  4158. //
  4159. VOID
  4160. kmxlUpdateMuxLines(
  4161. IN PMXLLINE pLine,
  4162. IN PMXLCONTROL pControl
  4163. )
  4164. {
  4165. ULONG i;
  4166. PAGED_CODE();
  4167. for( i = 0; i < pControl->Parameters.Count; i++ ) {
  4168. if( ( pLine->SourceId == pControl->Parameters.lpmcd_lt[ i ].dwParam1 ) &&
  4169. ( pControl->Parameters.lpmcd_lt[ i ].dwParam2 == (DWORD) -1 ) )
  4170. {
  4171. wcscpy(
  4172. pControl->Parameters.lpmcd_lt[ i ].szName,
  4173. pLine->Line.szName
  4174. );
  4175. pControl->Parameters.lpmcd_lt[ i ].dwParam1 =
  4176. pLine->Line.dwLineID;
  4177. pControl->Parameters.lpmcd_lt[ i ].dwParam2 =
  4178. pLine->Line.dwComponentType;
  4179. }
  4180. }
  4181. }
  4182. ///////////////////////////////////////////////////////////////////////
  4183. //
  4184. // kmxlAssignMuxIds
  4185. //
  4186. // Updates the source IDs stored in the MixerControlDetails array of
  4187. // the muxes and removes the muxes placed in lines as place holders.
  4188. //
  4189. //
  4190. NTSTATUS
  4191. kmxlAssignMuxIds(
  4192. IN PMIXEROBJECT pmxobj,
  4193. IN LINELIST listLines
  4194. )
  4195. {
  4196. PMXLLINE pLine;
  4197. PMXLCONTROL pControl;
  4198. CONTROLLIST listControls = NULL;
  4199. PAGED_CODE();
  4200. pLine = kmxlFirstInList( listLines );
  4201. while( pLine ) {
  4202. //
  4203. // Loop through the controls by removing them from the line's
  4204. // control list and building a new control list. This new
  4205. // control list will have the extra mux controls removed.
  4206. //
  4207. pControl = kmxlRemoveFirstControl( pLine->Controls );
  4208. while( pControl ) {
  4209. if( IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) ) {
  4210. kmxlUpdateMuxLines( pLine, pControl );
  4211. if( pControl->Parameters.bPlaceholder ) {
  4212. //
  4213. // This mux was here only to mark this line. Free
  4214. // up only the control memory and leave the parameters
  4215. // memeory alone.
  4216. //
  4217. ASSERT( pControl->pChannelStepping == NULL);
  4218. AudioFreeMemory_Unknown( &pControl );
  4219. --pLine->Line.cControls;
  4220. } else {
  4221. //
  4222. // This is a real mux control. Add it back into the
  4223. // list.
  4224. //
  4225. kmxlAddToEndOfList( listControls, pControl );
  4226. }
  4227. } else {
  4228. //
  4229. // Wasn't a mux. Put it onto the end of the new control
  4230. // list.
  4231. kmxlAddToEndOfList( listControls, pControl );
  4232. }
  4233. //
  4234. // Remove the next one!
  4235. //
  4236. pControl = kmxlRemoveFirstControl( pLine->Controls );
  4237. }
  4238. //
  4239. // Reassign the new control list back into this line.
  4240. //
  4241. pLine->Controls = listControls;
  4242. pLine = kmxlNextLine( pLine );
  4243. listControls = NULL;
  4244. }
  4245. RETURN( STATUS_SUCCESS );
  4246. }
  4247. ///////////////////////////////////////////////////////////////////////
  4248. //
  4249. // TargetCommon
  4250. //
  4251. // Fills in the common fields of the target function.
  4252. //
  4253. //
  4254. VOID
  4255. TargetCommon(
  4256. IN PMIXEROBJECT pmxobj,
  4257. IN PMXLLINE pLine,
  4258. IN DWORD DeviceType
  4259. )
  4260. {
  4261. PWDMACONTEXT pWdmaContext;
  4262. PWAVEDEVICE paWaveOutDevs, paWaveInDevs;
  4263. PMIDIDEVICE paMidiOutDevs, paMidiInDevs;
  4264. ULONG i;
  4265. PAGED_CODE();
  4266. pWdmaContext = pmxobj->pMixerDevice->pWdmaContext;
  4267. paWaveOutDevs = pWdmaContext->WaveOutDevs;
  4268. paWaveInDevs = pWdmaContext->WaveInDevs;
  4269. paMidiOutDevs = pWdmaContext->MidiOutDevs;
  4270. paMidiInDevs = pWdmaContext->MidiInDevs;
  4271. for( i = 0; i < MAXNUMDEVS; i++ ) {
  4272. if( DeviceType == WaveOutDevice ) {
  4273. if( (paWaveOutDevs[i].Device != UNUSED_DEVICE) &&
  4274. !MyWcsicmp(pmxobj->DeviceInterface, paWaveOutDevs[ i ].DeviceInterface) ) {
  4275. WAVEOUTCAPS wc;
  4276. ((PWAVEOUTCAPSA)(PVOID)&wc)->wMid=UNICODE_TAG;
  4277. wdmaudGetDevCaps( pWdmaContext, WaveOutDevice, i, (BYTE*) &wc, sizeof( WAVEOUTCAPS ) );
  4278. wcsncpy( pLine->Line.Target.szPname, wc.szPname, MAXPNAMELEN );
  4279. pLine->Line.Target.wMid = wc.wMid;
  4280. pLine->Line.Target.wPid = wc.wPid;
  4281. pLine->Line.Target.vDriverVersion = wc.vDriverVersion;
  4282. return;
  4283. }
  4284. }
  4285. if( DeviceType == WaveInDevice ) {
  4286. if( (paWaveInDevs[i].Device != UNUSED_DEVICE) &&
  4287. !MyWcsicmp(pmxobj->DeviceInterface, paWaveInDevs[ i ].DeviceInterface) ) {
  4288. WAVEINCAPS wc;
  4289. ((PWAVEINCAPSA)(PVOID)&wc)->wMid=UNICODE_TAG;
  4290. wdmaudGetDevCaps( pWdmaContext, WaveInDevice, i, (BYTE*) &wc, sizeof( WAVEINCAPS ) );
  4291. wcsncpy( pLine->Line.Target.szPname, wc.szPname, MAXPNAMELEN );
  4292. pLine->Line.Target.wMid = wc.wMid;
  4293. pLine->Line.Target.wPid = wc.wPid;
  4294. pLine->Line.Target.vDriverVersion = wc.vDriverVersion;
  4295. return;
  4296. }
  4297. }
  4298. if( DeviceType == MidiOutDevice ) {
  4299. if( (paMidiOutDevs[i].Device != UNUSED_DEVICE) &&
  4300. !MyWcsicmp(pmxobj->DeviceInterface, paMidiOutDevs[ i ].DeviceInterface) ) {
  4301. MIDIOUTCAPS mc;
  4302. ((PMIDIOUTCAPSA)(PVOID)&mc)->wMid=UNICODE_TAG;
  4303. wdmaudGetDevCaps( pWdmaContext, MidiOutDevice, i, (BYTE*) &mc, sizeof( MIDIOUTCAPS ) );
  4304. wcsncpy( pLine->Line.Target.szPname, mc.szPname, MAXPNAMELEN );
  4305. pLine->Line.Target.wMid = mc.wMid;
  4306. pLine->Line.Target.wPid = mc.wPid;
  4307. pLine->Line.Target.vDriverVersion = mc.vDriverVersion;
  4308. return;
  4309. }
  4310. }
  4311. if( DeviceType == MidiInDevice ) {
  4312. if( (paMidiInDevs[i].Device != UNUSED_DEVICE) &&
  4313. !MyWcsicmp(pmxobj->DeviceInterface, paMidiInDevs[ i ].DeviceInterface) ) {
  4314. MIDIINCAPS mc;
  4315. ((PMIDIINCAPSA)(PVOID)&mc)->wMid=UNICODE_TAG;
  4316. wdmaudGetDevCaps( pWdmaContext, MidiInDevice, i, (BYTE*) &mc, sizeof( MIDIINCAPS ) );
  4317. wcsncpy( pLine->Line.Target.szPname, mc.szPname, MAXPNAMELEN) ;
  4318. pLine->Line.Target.wMid = mc.wMid;
  4319. pLine->Line.Target.wPid = mc.wPid;
  4320. pLine->Line.Target.vDriverVersion = mc.vDriverVersion;
  4321. return;
  4322. }
  4323. }
  4324. }
  4325. }
  4326. ///////////////////////////////////////////////////////////////////////
  4327. //
  4328. // TargetTypeWaveOut
  4329. //
  4330. // Fills in the fields of aLine's target structure to be a waveout
  4331. // target.
  4332. //
  4333. //
  4334. VOID
  4335. TargetTypeWaveOut(
  4336. IN PMIXEROBJECT pmxobj,
  4337. IN PMXLLINE pLine
  4338. )
  4339. {
  4340. PAGED_CODE();
  4341. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
  4342. TargetCommon( pmxobj, pLine, WaveOutDevice );
  4343. }
  4344. ///////////////////////////////////////////////////////////////////////
  4345. //
  4346. // TargetTypeWaveIn
  4347. //
  4348. // Fills in the fields of aLine's target structure to be a wavein
  4349. // target.
  4350. //
  4351. //
  4352. #define TargetTypeWaveIn( pmxobj, pLine ) \
  4353. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN; \
  4354. (pLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_WAVEIN; \
  4355. TargetCommon( pmxobj, pLine, WaveInDevice )
  4356. ///////////////////////////////////////////////////////////////////////
  4357. //
  4358. // TargetTypeMidiOut
  4359. //
  4360. // Fills in the fields of aLine's target structure to be a midi out
  4361. // target.
  4362. //
  4363. //
  4364. #define TargetTypeMidiOut( pmxobj, pLine ) \
  4365. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; \
  4366. (pLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_MIDIOUT; \
  4367. TargetCommon( pmxobj, pLine, MidiOutDevice )
  4368. ///////////////////////////////////////////////////////////////////////
  4369. //
  4370. // TargetTypeMidiIn
  4371. //
  4372. // Fills in the fields of aLine's target structure to be a midi in
  4373. // target.
  4374. //
  4375. //
  4376. #define TargetTypeMidiIn( pmxobj, pLine ) \
  4377. (aLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; \
  4378. (aLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_MIDIIN; \
  4379. TargetCommon( pmxobj, pLine, MidiInDevice )
  4380. ///////////////////////////////////////////////////////////////////////
  4381. //
  4382. // TargetTypeAuxCD
  4383. //
  4384. // Fills in the fields of aLine's target structure to be a CD
  4385. // target.
  4386. //
  4387. //
  4388. #define TargetTypeAuxCD( pmxobj, pLine ) \
  4389. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_AUX; \
  4390. TargetCommon( pmxobj, pLine, WaveOutDevice ); \
  4391. (pLine)->Line.Target.wPid = MM_MSFT_SB16_AUX_CD
  4392. ///////////////////////////////////////////////////////////////////////
  4393. //
  4394. // TargetTypeAuxLine
  4395. //
  4396. // Fills in the fields of aLine's target structure to be a aux line
  4397. // target.
  4398. //
  4399. //
  4400. #define TargetTypeAuxLine( pmxobj, pLine ) \
  4401. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_AUX; \
  4402. TargetCommon( pmxobj, pLine, WaveOutDevice );\
  4403. (pLine)->Line.Target.wPid = MM_MSFT_SB16_AUX_LINE
  4404. ///////////////////////////////////////////////////////////////////////
  4405. //
  4406. // kmxlDetermineDestinationType
  4407. //
  4408. // Determines the destination and target types by using the Type
  4409. // GUID stored in the line structure.
  4410. //
  4411. //
  4412. ULONG
  4413. kmxlDetermineDestinationType(
  4414. IN PMIXEROBJECT pmxobj, // Instance data
  4415. IN PMXLLINE pLine // The line to determine type of
  4416. )
  4417. {
  4418. PAGED_CODE();
  4419. //
  4420. // Speaker type destinations
  4421. //
  4422. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SPEAKER ) ||
  4423. IsEqualGUID( &pLine->Type, &KSNODETYPE_DESKTOP_SPEAKER ) ||
  4424. IsEqualGUID( &pLine->Type, &KSNODETYPE_ROOM_SPEAKER ) ||
  4425. IsEqualGUID( &pLine->Type, &KSNODETYPE_COMMUNICATION_SPEAKER ) ) {
  4426. TargetTypeWaveOut( pmxobj, pLine );
  4427. return( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS );
  4428. }
  4429. //
  4430. // WaveIn type destinations
  4431. //
  4432. if( IsEqualGUID( &pLine->Type, &KSCATEGORY_AUDIO )
  4433. || IsEqualGUID( &pLine->Type, &PINNAME_CAPTURE )
  4434. ) {
  4435. TargetTypeWaveIn( pmxobj, pLine );
  4436. return( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
  4437. }
  4438. //
  4439. // Headphone destination
  4440. //
  4441. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_HEADPHONES ) ||
  4442. IsEqualGUID( &pLine->Type, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO ) ) {
  4443. TargetTypeWaveOut( pmxobj, pLine );
  4444. return( MIXERLINE_COMPONENTTYPE_DST_HEADPHONES );
  4445. }
  4446. //
  4447. // Telephone destination
  4448. //
  4449. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_TELEPHONE ) ||
  4450. IsEqualGUID( &pLine->Type, &KSNODETYPE_PHONE_LINE ) ||
  4451. IsEqualGUID( &pLine->Type, &KSNODETYPE_DOWN_LINE_PHONE ) )
  4452. {
  4453. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4454. return( MIXERLINE_COMPONENTTYPE_DST_TELEPHONE );
  4455. }
  4456. //
  4457. // Ambiguous destination type. Figure out the destination type by looking
  4458. // at the Communication.
  4459. //
  4460. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_ANALOG_CONNECTOR ) ||
  4461. IsEqualGUID( &pLine->Type, &KSNODETYPE_SPDIF_INTERFACE ) ) {
  4462. if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
  4463. TargetTypeWaveOut( pmxobj, pLine );
  4464. return( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS );
  4465. } else {
  4466. TargetTypeWaveIn( pmxobj, pLine );
  4467. return( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
  4468. }
  4469. }
  4470. //
  4471. // Does not match the others. Default to Undefined destination.
  4472. //
  4473. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4474. return( MIXERLINE_COMPONENTTYPE_DST_UNDEFINED );
  4475. }
  4476. ///////////////////////////////////////////////////////////////////////
  4477. //
  4478. // kmxlDetermineSourceType
  4479. //
  4480. // Determines the destination and target types by using the Type
  4481. // GUID stored in the line structure.
  4482. //
  4483. //
  4484. ULONG
  4485. kmxlDetermineSourceType(
  4486. IN PMIXEROBJECT pmxobj, // Instance data
  4487. IN PMXLLINE pLine // The line to determine type of
  4488. )
  4489. {
  4490. PAGED_CODE();
  4491. //
  4492. // All microphone type sources are a microphone source.
  4493. //
  4494. //
  4495. // We are only checking two microphone GUIDs here. We may
  4496. // want to consider the rest of the microphone types in
  4497. // ksmedia.h
  4498. //
  4499. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_MICROPHONE )
  4500. || IsEqualGUID( &pLine->Type, &KSNODETYPE_DESKTOP_MICROPHONE )
  4501. )
  4502. {
  4503. TargetTypeWaveIn( pmxobj, pLine );
  4504. return( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
  4505. }
  4506. //
  4507. // Legacy audio connector and the speaker type sources represent a
  4508. // waveout source.
  4509. //
  4510. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR )
  4511. || IsEqualGUID( &pLine->Type, &KSNODETYPE_SPEAKER )
  4512. || IsEqualGUID( &pLine->Type, &KSCATEGORY_AUDIO )
  4513. )
  4514. {
  4515. TargetTypeWaveOut( pmxobj, pLine );
  4516. return( MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT );
  4517. }
  4518. //
  4519. // CD player is a compact disc source.
  4520. //
  4521. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_CD_PLAYER ) ) {
  4522. TargetTypeAuxCD( pmxobj, pLine );
  4523. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4524. return( MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC );
  4525. }
  4526. //
  4527. // Synthesizer is a sythesizer source.
  4528. //
  4529. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SYNTHESIZER ) ) {
  4530. TargetTypeMidiOut( pmxobj, pLine );
  4531. return( MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER );
  4532. }
  4533. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_LINE_CONNECTOR ) ) {
  4534. TargetTypeAuxLine( pmxobj, pLine );
  4535. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4536. return( MIXERLINE_COMPONENTTYPE_SRC_LINE );
  4537. }
  4538. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_PHONE_LINE ) ||
  4539. IsEqualGUID( &pLine->Type, &KSNODETYPE_TELEPHONE ) ||
  4540. IsEqualGUID( &pLine->Type, &KSNODETYPE_DOWN_LINE_PHONE ) )
  4541. {
  4542. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4543. return( MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE );
  4544. }
  4545. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_ANALOG_CONNECTOR ) ) {
  4546. //
  4547. // Ambiguous src type. Figure out the destination type by looking
  4548. // at the Communication.
  4549. //
  4550. if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
  4551. TargetTypeWaveIn( pmxobj, pLine );
  4552. }
  4553. else {
  4554. TargetTypeWaveOut( pmxobj, pLine );
  4555. }
  4556. return( MIXERLINE_COMPONENTTYPE_SRC_ANALOG );
  4557. }
  4558. //
  4559. // Digital in/out (SPDIF) source
  4560. //
  4561. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SPDIF_INTERFACE ) ) {
  4562. //
  4563. // Ambiguous src type. Figure out the destination type by looking
  4564. // at the Communication.
  4565. //
  4566. if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
  4567. TargetTypeWaveIn( pmxobj, pLine );
  4568. }
  4569. else {
  4570. TargetTypeWaveOut( pmxobj, pLine );
  4571. }
  4572. return( MIXERLINE_COMPONENTTYPE_SRC_DIGITAL );
  4573. }
  4574. //
  4575. // All others are lumped under Undefined source.
  4576. //
  4577. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4578. return( MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED );
  4579. }
  4580. ///////////////////////////////////////////////////////////////////////
  4581. //
  4582. // PinCategoryToString
  4583. //
  4584. // Converts the Pin category GUIDs to a string.
  4585. #ifdef DEBUG
  4586. #pragma LOCKED_CODE
  4587. #endif
  4588. #define _EG_(x,y) if (IsEqualGUID( NodeType, &x)) { return y; }
  4589. const char*
  4590. PinCategoryToString
  4591. (
  4592. IN CONST GUID* NodeType // The GUID to translate
  4593. )
  4594. {
  4595. _EG_(KSNODETYPE_MICROPHONE,"Microphone");
  4596. _EG_(KSNODETYPE_DESKTOP_MICROPHONE,"Desktop Microphone");
  4597. _EG_(KSNODETYPE_SPEAKER,"Speaker");
  4598. _EG_(KSNODETYPE_HEADPHONES,"Headphones");
  4599. _EG_(KSNODETYPE_LEGACY_AUDIO_CONNECTOR,"Wave");
  4600. _EG_(KSNODETYPE_CD_PLAYER,"CD Player");
  4601. _EG_(KSNODETYPE_SYNTHESIZER,"Synthesizer");
  4602. _EG_(KSCATEGORY_AUDIO,"Wave");
  4603. _EG_(PINNAME_CAPTURE,"Wave In");
  4604. _EG_(KSNODETYPE_LINE_CONNECTOR,"Aux Line");
  4605. _EG_(KSNODETYPE_TELEPHONE,"Telephone");
  4606. _EG_(KSNODETYPE_PHONE_LINE,"Phone Line");
  4607. _EG_(KSNODETYPE_DOWN_LINE_PHONE,"Downline Phone");
  4608. _EG_(KSNODETYPE_ANALOG_CONNECTOR,"Analog connector");
  4609. //New debug names...
  4610. _EG_(KSAUDFNAME_MONO_OUT,"Mono Out");
  4611. _EG_(KSAUDFNAME_STEREO_MIX,"Stereo Mix");
  4612. _EG_(KSAUDFNAME_MONO_MIX,"Mono Mix");
  4613. _EG_(KSAUDFNAME_AUX,"Aux");
  4614. _EG_(KSAUDFNAME_VIDEO,"Video");
  4615. _EG_(KSAUDFNAME_LINE_IN,"Line In");
  4616. DPF(DL_WARNING|FA_MIXER,("Path Trap send me GUID - dt %08X _GUID",NodeType) );
  4617. return "Unknown Pin Category";
  4618. }
  4619. ///////////////////////////////////////////////////////////////////////
  4620. //
  4621. // NodeTypeToString
  4622. //
  4623. // Converts a NodeType GUID to a string
  4624. //
  4625. //
  4626. const char*
  4627. NodeTypeToString
  4628. (
  4629. IN CONST GUID* NodeType // The GUID to translate
  4630. )
  4631. {
  4632. _EG_(KSNODETYPE_DAC,"DAC");
  4633. _EG_(KSNODETYPE_ADC,"ADC");
  4634. _EG_(KSNODETYPE_SRC,"SRC");
  4635. _EG_(KSNODETYPE_SUPERMIX,"SuperMIX");
  4636. _EG_(KSNODETYPE_SUM,"Sum");
  4637. _EG_(KSNODETYPE_MUTE,"Mute");
  4638. _EG_(KSNODETYPE_VOLUME,"Volume");
  4639. _EG_(KSNODETYPE_TONE,"Tone");
  4640. _EG_(KSNODETYPE_AGC,"AGC");
  4641. _EG_(KSNODETYPE_DELAY,"Delay");
  4642. _EG_(KSNODETYPE_LOUDNESS,"LOUDNESS");
  4643. _EG_(KSNODETYPE_3D_EFFECTS,"3D Effects");
  4644. _EG_(KSNODETYPE_DEV_SPECIFIC,"Dev Specific");
  4645. _EG_(KSNODETYPE_STEREO_WIDE,"Stereo Wide");
  4646. _EG_(KSNODETYPE_REVERB,"Reverb");
  4647. _EG_(KSNODETYPE_CHORUS,"Chorus");
  4648. _EG_(KSNODETYPE_ACOUSTIC_ECHO_CANCEL,"AEC");
  4649. _EG_(KSNODETYPE_EQUALIZER,"Equalizer");
  4650. _EG_(KSNODETYPE_MUX,"Mux");
  4651. _EG_(KSNODETYPE_DEMUX,"Demux");
  4652. _EG_(KSNODETYPE_STEREO_ENHANCE,"Stereo Enhance");
  4653. _EG_(KSNODETYPE_SYNTHESIZER,"Synthesizer");
  4654. _EG_(KSNODETYPE_PEAKMETER,"Peakmeter");
  4655. _EG_(KSNODETYPE_LINE_CONNECTOR,"Line Connector");
  4656. _EG_(KSNODETYPE_SPEAKER,"Speaker");
  4657. _EG_(KSNODETYPE_DESKTOP_SPEAKER,"");
  4658. _EG_(KSNODETYPE_ROOM_SPEAKER,"Room Speaker");
  4659. _EG_(KSNODETYPE_COMMUNICATION_SPEAKER,"Communication Speaker");
  4660. _EG_(KSNODETYPE_LOW_FREQUENCY_EFFECTS_SPEAKER,"? Whatever...");
  4661. _EG_(KSNODETYPE_HANDSET,"Handset");
  4662. _EG_(KSNODETYPE_HEADSET,"Headset");
  4663. _EG_(KSNODETYPE_SPEAKERPHONE_NO_ECHO_REDUCTION,"Speakerphone no echo reduction");
  4664. _EG_(KSNODETYPE_ECHO_SUPPRESSING_SPEAKERPHONE,"Echo Suppressing Speakerphone");
  4665. _EG_(KSNODETYPE_ECHO_CANCELING_SPEAKERPHONE,"Echo Canceling Speakerphone");
  4666. _EG_(KSNODETYPE_CD_PLAYER,"CD Player");
  4667. _EG_(KSNODETYPE_MICROPHONE,"Microphone");
  4668. _EG_(KSNODETYPE_DESKTOP_MICROPHONE,"Desktop Microphone");
  4669. _EG_(KSNODETYPE_PERSONAL_MICROPHONE,"Personal Microphone");
  4670. _EG_(KSNODETYPE_OMNI_DIRECTIONAL_MICROPHONE,"Omni Directional Microphone");
  4671. _EG_(KSNODETYPE_MICROPHONE_ARRAY,"Microphone Array");
  4672. _EG_(KSNODETYPE_PROCESSING_MICROPHONE_ARRAY,"Processing Microphone Array");
  4673. _EG_(KSNODETYPE_ANALOG_CONNECTOR,"Analog Connector");
  4674. _EG_(KSNODETYPE_PHONE_LINE,"Phone Line");
  4675. _EG_(KSNODETYPE_HEADPHONES,"Headphones");
  4676. _EG_(KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO,"Head Mounted Display Audio");
  4677. _EG_(KSNODETYPE_LEGACY_AUDIO_CONNECTOR,"Legacy Audio Connector");
  4678. // _EG_(KSNODETYPE_SURROUND_ENCODER,"Surround Encoder");
  4679. _EG_(KSNODETYPE_NOISE_SUPPRESS,"Noise Suppress");
  4680. _EG_(KSNODETYPE_DRM_DESCRAMBLE,"DRM Descramble");
  4681. _EG_(KSNODETYPE_SWMIDI,"SWMidi");
  4682. _EG_(KSNODETYPE_SWSYNTH,"SWSynth");
  4683. _EG_(KSNODETYPE_MULTITRACK_RECORDER,"Multitrack Recorder");
  4684. _EG_(KSNODETYPE_RADIO_TRANSMITTER,"Radio Transmitter");
  4685. _EG_(KSNODETYPE_TELEPHONE,"Telephone");
  4686. _EG_(KSAUDFNAME_MONO_OUT,"Mono Out");
  4687. _EG_(KSAUDFNAME_LINE_IN,"Line in");
  4688. _EG_(KSAUDFNAME_VIDEO,"Video");
  4689. _EG_(KSAUDFNAME_AUX,"Aux");
  4690. _EG_(KSAUDFNAME_MONO_MIX,"Mono Mix");
  4691. _EG_(KSAUDFNAME_STEREO_MIX,"Stereo Mix");
  4692. _EG_(KSCATEGORY_AUDIO,"Audio");
  4693. _EG_(PINNAME_VIDEO_CAPTURE,"Video Capture");
  4694. DPF(DL_WARNING|FA_MIXER,("Path Trap send me GUID - dt %08X _GUID",NodeType) );
  4695. return "Unknown NodeType";
  4696. }