Leaked source code of windows server 2003
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.

5676 lines
170 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. kmxlRemoveNoteNodeFromList(pnnode);
  1202. //
  1203. // There can be multiple Controls on this Notification node.
  1204. //
  1205. while( (pnnode->pcLink != NULL) &&
  1206. ( (pcLink=kmxlRemoveControlFromNoteList(pnnode,pnnode->pcLink->pControl)) != NULL) )
  1207. {
  1208. //
  1209. // To get here, pcLink is Valid. If it was the last pControl, then
  1210. // we want to turn off change notifications on it.
  1211. //
  1212. if( pnnode->pcLink == NULL )
  1213. {
  1214. //
  1215. // There are no references to this node, we can free it.
  1216. //
  1217. Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
  1218. DPFASSERT( Status == STATUS_SUCCESS );
  1219. DPFBTRAP();
  1220. }
  1221. kmxlFreeControlLink(pcLink);
  1222. DPFBTRAP();
  1223. }
  1224. kmxlFreeNoteNode(pnnode);
  1225. }
  1226. DPF(DL_TRACE|FA_NOTE,("pWdmaContext %08X going away",pContext) );
  1227. kmxlReleaseNoteMutex();
  1228. }
  1229. /////////////////////////////////////////////////////////////////////////////
  1230. //
  1231. // kmxlCleanupNotelist
  1232. //
  1233. // Driver is unloading, turn everything off and free the memory!
  1234. //
  1235. VOID
  1236. kmxlCleanupNoteList(
  1237. )
  1238. {
  1239. NTSTATUS Status;
  1240. PNOTENODE pnnode,pnnodeFree;
  1241. PCONTROLLINK pcLink;
  1242. PAGED_CODE();
  1243. kmxlGrabNoteMutex();
  1244. //
  1245. // If we find this context is still alive in our list, someone leaked a control.
  1246. // Things should have been cleaned up when the controls went away! But, for
  1247. // some reasons they didn't.
  1248. //
  1249. // There could be multiple pContext nodes in list.
  1250. //
  1251. pnnode=firstnotenode;
  1252. while( pnnode )
  1253. {
  1254. DPFASSERT(IsValidNoteNode(pnnode));
  1255. DPF(DL_ERROR|FA_NOTE,("pnnode(%08X) found in Notification List!",pnnode) );
  1256. kmxlRemoveNoteNodeFromList(pnnode);
  1257. //
  1258. // There can be multiple Controls on this Notification node.
  1259. //
  1260. while( (pnnode->pcLink != NULL) &&
  1261. ( (pcLink=kmxlRemoveControlFromNoteList(pnnode,pnnode->pcLink->pControl)) != NULL) )
  1262. {
  1263. //
  1264. // To get here, pcLink is Valid. If it was the last pControl, then
  1265. // we want to turn off change notifications on it.
  1266. //
  1267. if( pnnode->pcLink == NULL )
  1268. {
  1269. //
  1270. // There are no references to this node, we can free it.
  1271. //
  1272. Status=kmxlDisableControlChange(pnnode->pmxd->pfo,pnnode->NodeId,&pnnode->NodeEventData);
  1273. DPFASSERT( Status == STATUS_SUCCESS );
  1274. DPFBTRAP();
  1275. }
  1276. kmxlFreeControlLink(pcLink);
  1277. DPFBTRAP();
  1278. }
  1279. pnnodeFree=pnnode;
  1280. pnnode=pnnode->pNext;
  1281. kmxlFreeNoteNode(pnnodeFree);
  1282. DPFBTRAP();
  1283. }
  1284. DPF(DL_TRACE|FA_NOTE,("Done with cleanup") );
  1285. kmxlReleaseNoteMutex();
  1286. }
  1287. /////////////////////////////////////////////////////////////////////////////
  1288. //
  1289. // kmxlPersistHWControlWorker
  1290. //
  1291. // When kmxlPersistHWControlWorker gets called, the pData value is a pointer
  1292. // to a globally allocated NOTENODE structure. Thus, we have all the context
  1293. // we need with regard to persisting the control. We just need to make sure
  1294. // that our node didn't go away between the time the evern was scheduled and
  1295. // when we got called.
  1296. //
  1297. VOID
  1298. kmxlPersistHWControlWorker(
  1299. PVOID pReference
  1300. )
  1301. {
  1302. NTSTATUS Status;
  1303. PMXLCONTROL pControl;
  1304. PVOID paDetails = NULL; // kmxlPersistSingleControl() allocates paDetails
  1305. // via kmxlGetCurrentControlValue()
  1306. PNOTENODE pnnode;
  1307. PHWLINK phwlink;
  1308. PLIST_ENTRY ple;
  1309. PAGED_CODE();
  1310. //
  1311. // We set HardwareCallbackSchedule to 0 here so that we can start adding new
  1312. // events for handling hardware notifications. Note: we do that here at the
  1313. // start of the routine so that there will not be a window where we have
  1314. // something in the list that we never get a event scheduled for. In other
  1315. // words, this routine handles empty lists.
  1316. //
  1317. HardwareCallbackScheduled=0;
  1318. //
  1319. // while we have events in our queue, get one and service it.
  1320. //
  1321. while((ple = ExInterlockedRemoveHeadList(&HardwareCallbackListHead,
  1322. &HardwareCallbackSpinLock)) != NULL)
  1323. {
  1324. phwlink = CONTAINING_RECORD(ple, HWLINK, Next);
  1325. DPFASSERT(phwlink->dwSig == HWLINK_SIGNATURE);
  1326. //
  1327. // Get our data for this event and then free the link that was allocated in
  1328. // the DPC handler.
  1329. //
  1330. pnnode=phwlink->pnnode;
  1331. AudioFreeMemory(sizeof(HWLINK),&phwlink);
  1332. //
  1333. // We are going to be working in this context for a while. Thus, we're going
  1334. // to enter our mtxNote mutex to make sure that our node doesn't go away
  1335. // while we're persisting the values!
  1336. //
  1337. kmxlGrabNoteMutex();
  1338. //
  1339. // Now our list can't chnage! Is this node still valid? If we don't find
  1340. // it in the list, it was removed before this event fired. Thus, there is
  1341. // nothing that we can do. --- Free mutex and leave.
  1342. //
  1343. Status=kmxlFindNodeInNoteList(pnnode);
  1344. if( NT_SUCCESS(Status) )
  1345. {
  1346. DPF( DL_TRACE|FA_HARDWAREEVENT ,
  1347. ("Entering NodeId %d LineID %X ControlID %d ControlType = %X",
  1348. pnnode->NodeId, pnnode->LineID, pnnode->ControlID, pnnode->ControlType) );
  1349. //
  1350. // Yes. It's still valid. Persist the control.
  1351. //
  1352. pControl=kmxlFindControlTypeInList(pnnode,pnnode->ControlType);
  1353. if( pControl )
  1354. {
  1355. Status = kmxlPersistSingleControl(
  1356. pnnode->pmxd->pfo,
  1357. pnnode->pmxd,
  1358. pControl, // pControl here...
  1359. paDetails
  1360. );
  1361. }
  1362. if( !NT_SUCCESS(Status) )
  1363. {
  1364. //
  1365. // On shutdown, we might get an event that fires after things have
  1366. // been cleaned up.
  1367. //
  1368. if( Status != STATUS_TOO_LATE )
  1369. {
  1370. DPF(DL_WARNING|FA_NOTE, ("Failure from kmxlPersistSingleControl Status=%X",Status) );
  1371. }
  1372. }
  1373. else {
  1374. DPF(DL_TRACE|FA_HARDWAREEVENT ,("Done - success") );
  1375. }
  1376. } else {
  1377. DPF(DL_WARNING|FA_NOTE,("pnnode=%08X has been removed!",pnnode) );
  1378. }
  1379. //
  1380. // Persist this control!
  1381. //
  1382. kmxlReleaseNoteMutex();
  1383. }
  1384. DPF(DL_TRACE|FA_HARDWAREEVENT ,("exit") );
  1385. }
  1386. /////////////////////////////////////////////////////////////////////////////
  1387. //
  1388. // kmxlGetLineForControl
  1389. //
  1390. // For every line on this mixer device, look at every control to see if we
  1391. // can find this control. If found, return this line pointer.
  1392. //
  1393. NTSTATUS
  1394. kmxlEnableAllControls(
  1395. IN PMIXEROBJECT pmxobj
  1396. )
  1397. {
  1398. NTSTATUS Status=STATUS_SUCCESS;
  1399. PMIXERDEVICE pmxd;
  1400. PMXLLINE pLine;
  1401. PMXLCONTROL pControl;
  1402. PAGED_CODE();
  1403. //
  1404. // The first time through we will most likily not have a pfo in the MIXERDEVICE
  1405. // structure, thus fill it in!
  1406. //
  1407. DPFASSERT(pmxobj->dwSig == MIXEROBJECT_SIGNATURE );
  1408. DPFASSERT(pmxobj->pMixerDevice != NULL );
  1409. DPFASSERT(pmxobj->pMixerDevice->dwSig == MIXERDEVICE_SIGNATURE );
  1410. pmxd=pmxobj->pMixerDevice;
  1411. if( pmxd->pfo == NULL )
  1412. {
  1413. DPF(DL_WARNING|FA_NOTE,("fo is NULL, it should have been set!") );
  1414. //
  1415. // We need to assign a SysAudio FILE_OBJECT to this mixer device so that
  1416. // we can talk to it.
  1417. //
  1418. if( NULL==(pmxd->pfo=kmxlOpenSysAudio())) {
  1419. DPF(DL_WARNING|FA_NOTE,("OpenSysAudio failed") );
  1420. return STATUS_UNSUCCESSFUL;
  1421. }
  1422. Status = SetSysAudioProperty(
  1423. pmxd->pfo,
  1424. KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE,
  1425. sizeof( pmxd->Device ),
  1426. &pmxd->Device
  1427. );
  1428. if( !NT_SUCCESS( Status ) ) {
  1429. kmxlCloseSysAudio( pmxd->pfo );
  1430. pmxd->pfo=NULL;
  1431. DPF(DL_WARNING|FA_NOTE,("SetSysAudioProperty failed %X",Status) );
  1432. return Status;
  1433. }
  1434. }
  1435. DPFASSERT(IsValidMixerObject(pmxobj));
  1436. for(pLine = kmxlFirstInList( pmxd->listLines );
  1437. pLine != NULL;
  1438. pLine = kmxlNextLine( pLine )
  1439. )
  1440. {
  1441. DPFASSERT(IsValidLine(pLine));
  1442. for(pControl = kmxlFirstInList( pLine->Controls );
  1443. pControl != NULL;
  1444. pControl = kmxlNextControl( pControl )
  1445. )
  1446. {
  1447. DPFASSERT(IsValidControl(pControl));
  1448. //
  1449. // Enable Notifications if it supports it here.
  1450. //
  1451. DPF(DL_TRACE|FA_NOTE,("pControl->Id=%d, pControl->Control.dwControlID=%d",
  1452. pControl->Id,pControl->Control.dwControlID) );
  1453. Status = kmxlEnableControlChangeNotifications(pmxobj,pLine,pControl);
  1454. }
  1455. }
  1456. return Status;
  1457. }
  1458. VOID
  1459. kmxlCloseMixerDevice(
  1460. IN OUT PMIXERDEVICE pmxd
  1461. )
  1462. {
  1463. if(pmxd->pfo)
  1464. {
  1465. kmxlCloseSysAudio( pmxd->pfo );
  1466. pmxd->pfo = NULL;
  1467. }
  1468. }
  1469. /////////////////////////////////////////////////////////////////////////////
  1470. //
  1471. // GetHardwareEventData
  1472. //
  1473. // Called by user mode driver to get the notification information.
  1474. //
  1475. VOID GetHardwareEventData(LPDEVICEINFO pDeviceInfo)
  1476. {
  1477. PAGED_CODE();
  1478. if (emptyindex!=loadindex) {
  1479. (pDeviceInfo->dwID)[0]=callbacks[emptyindex%CALLBACKARRAYSIZE].dwControlID;
  1480. pDeviceInfo->dwLineID=callbacks[emptyindex%CALLBACKARRAYSIZE].dwLineID;
  1481. pDeviceInfo->dwCallbackType=callbacks[emptyindex%CALLBACKARRAYSIZE].dwCallbackType;
  1482. pDeviceInfo->ControlCallbackCount=1;
  1483. emptyindex++;
  1484. }
  1485. pDeviceInfo->mmr=MMSYSERR_NOERROR;
  1486. }
  1487. ///////////////////////////////////////////////////////////////////////
  1488. //
  1489. // kmxdInit
  1490. //
  1491. // Checks to see if the mixer lines have been built for the given
  1492. // index. If not, it calls kmxlBuildLines() to build up the lines.
  1493. //
  1494. // The topology information is kept around so that it can be dumped
  1495. // via a debugger command.
  1496. //
  1497. //
  1498. NTSTATUS
  1499. kmxlInit(
  1500. IN PFILE_OBJECT pfo, // Handle of the topology driver instance
  1501. IN PMIXERDEVICE pMixer
  1502. )
  1503. {
  1504. NTSTATUS Status = STATUS_SUCCESS;
  1505. HANDLE hKey;
  1506. ULONG ResultLength;
  1507. PAGED_CODE();
  1508. //
  1509. // Check to see if the lines have already been built for this device.
  1510. // If so, return success.
  1511. //
  1512. if( pMixer->listLines ) {
  1513. RETURN( STATUS_SUCCESS );
  1514. }
  1515. //
  1516. // Build the lines for this device.
  1517. //
  1518. Status = kmxlBuildLines(
  1519. pMixer,
  1520. pfo,
  1521. &pMixer->listLines,
  1522. &pMixer->cDestinations,
  1523. &pMixer->Topology
  1524. );
  1525. if( NT_SUCCESS( Status ) ) {
  1526. Status = kmxlOpenInterfaceKey( pfo, pMixer->Device, &hKey );
  1527. if( !NT_SUCCESS( Status ) ) {
  1528. pMixer->Mapping = MIXER_MAPPING_LOGRITHMIC;
  1529. Status = STATUS_SUCCESS;
  1530. goto exit;
  1531. }
  1532. Status = kmxlRegQueryValue( hKey,
  1533. L"CurveType",
  1534. &pMixer->Mapping,
  1535. sizeof( pMixer->Mapping ),
  1536. &ResultLength
  1537. );
  1538. if( !NT_SUCCESS( Status ) ) {
  1539. kmxlRegCloseKey( hKey );
  1540. pMixer->Mapping = MIXER_MAPPING_LOGRITHMIC;
  1541. Status = STATUS_SUCCESS;
  1542. goto exit;
  1543. }
  1544. kmxlRegCloseKey( hKey );
  1545. }
  1546. exit:
  1547. //
  1548. // Free up the topology allocated when the lines are built (RETAIL only).
  1549. //
  1550. #ifndef DEBUG
  1551. if(pMixer->Topology.Categories) {
  1552. ExFreePool(
  1553. ( (( PKSMULTIPLE_ITEM )
  1554. pMixer->Topology.Categories )) - 1 );
  1555. pMixer->Topology.Categories = NULL;
  1556. }
  1557. if(pMixer->Topology.TopologyNodes) {
  1558. ExFreePool(
  1559. ( (( PKSMULTIPLE_ITEM )
  1560. pMixer->Topology.TopologyNodes )) - 1 );
  1561. pMixer->Topology.TopologyNodes = NULL;
  1562. }
  1563. if(pMixer->Topology.TopologyConnections) {
  1564. ExFreePool(
  1565. ( (( PKSMULTIPLE_ITEM )
  1566. pMixer->Topology.TopologyConnections )) - 1 );
  1567. pMixer->Topology.TopologyConnections = NULL;
  1568. }
  1569. #endif // !DEBUG
  1570. RETURN( Status );
  1571. }
  1572. ////////////////////////////////////////////////////////////////////////
  1573. //
  1574. // kmxdDeInit
  1575. //
  1576. // Loops through each of the lines freeing the control structures and
  1577. // then the line structures.
  1578. //
  1579. //
  1580. NTSTATUS
  1581. kmxlDeInit(
  1582. PMIXERDEVICE pMixer
  1583. )
  1584. {
  1585. PMXLLINE pLine = NULL;
  1586. PMXLCONTROL pControl = NULL;
  1587. PAGED_CODE();
  1588. if( pMixer->Device != UNUSED_DEVICE ) {
  1589. while( pMixer->listLines ) {
  1590. pLine = kmxlRemoveFirstLine( pMixer->listLines );
  1591. while( pLine && pLine->Controls ) {
  1592. pControl = kmxlRemoveFirstControl( pLine->Controls );
  1593. kmxlFreeControl( pControl );
  1594. }
  1595. AudioFreeMemory( sizeof(MXLLINE),&pLine );
  1596. }
  1597. //
  1598. // Here we need to close sysaudio as used in this mixer device.
  1599. //
  1600. kmxlCloseMixerDevice(pMixer);
  1601. ASSERT( pMixer->listLines == NULL );
  1602. //
  1603. // Free up the topology (DEBUG only)
  1604. //
  1605. #ifdef DEBUG
  1606. if(pMixer->Topology.Categories) {
  1607. ExFreePool(( (( PKSMULTIPLE_ITEM )
  1608. pMixer->Topology.Categories )) - 1 );
  1609. pMixer->Topology.Categories = NULL;
  1610. }
  1611. if(pMixer->Topology.TopologyNodes) {
  1612. ExFreePool(( (( PKSMULTIPLE_ITEM )
  1613. pMixer->Topology.TopologyNodes )) - 1 );
  1614. pMixer->Topology.TopologyNodes = NULL;
  1615. }
  1616. if(pMixer->Topology.TopologyConnections) {
  1617. ExFreePool(( (( PKSMULTIPLE_ITEM )
  1618. pMixer->Topology.TopologyConnections )) - 1 );
  1619. pMixer->Topology.TopologyConnections = NULL;
  1620. }
  1621. #endif // !DEBUG
  1622. } // if
  1623. RETURN( STATUS_SUCCESS );
  1624. }
  1625. ///////////////////////////////////////////////////////////////////////
  1626. //
  1627. // kmxlBuildLines
  1628. //
  1629. // Builds up the line structures and count of destinations for the
  1630. // given instance.
  1631. //
  1632. //
  1633. NTSTATUS
  1634. kmxlBuildLines(
  1635. IN PMIXERDEVICE pMixer, // The mixer
  1636. IN PFILE_OBJECT pfoInstance, // The FILE_OBJECT of a filter instance
  1637. IN OUT LINELIST* plistLines, // Pointer to the list of all lines
  1638. IN OUT PULONG pcDestinations, // Pointer to the number of dests
  1639. IN OUT PKSTOPOLOGY pTopology // Pointer to a topology structure
  1640. )
  1641. {
  1642. NTSTATUS Status = STATUS_SUCCESS;
  1643. MIXEROBJECT mxobj;
  1644. LINELIST listSourceLines = NULL;
  1645. NODELIST listSources = NULL;
  1646. NODELIST listDests = NULL;
  1647. PMXLNODE pTemp = NULL;
  1648. ULONG i;
  1649. PAGED_CODE();
  1650. ASSERT( pfoInstance );
  1651. ASSERT( plistLines );
  1652. ASSERT( pcDestinations );
  1653. ASSERT( pTopology );
  1654. RtlZeroMemory( &mxobj, sizeof( MIXEROBJECT ) );
  1655. // Set up the MIXEROBJECT. Note that this structure is used only within
  1656. // the scope of this function, so it is okay to simply copy the
  1657. // DeviceInterface pointer from the MIXERDEV structure.
  1658. mxobj.pfo = pfoInstance;
  1659. mxobj.pTopology = pTopology;
  1660. mxobj.pMixerDevice = pMixer;
  1661. mxobj.DeviceInterface = pMixer->DeviceInterface;
  1662. #ifdef DEBUG
  1663. mxobj.dwSig = MIXEROBJECT_SIGNATURE;
  1664. #endif
  1665. //
  1666. // Read the topology from the device
  1667. //
  1668. DPF(DL_TRACE|FA_MIXER,("Querying Topology") );
  1669. Status = kmxlQueryTopology( mxobj.pfo, mxobj.pTopology );
  1670. if( !NT_SUCCESS( Status ) ) {
  1671. goto exit;
  1672. }
  1673. //
  1674. // Build up the node table. The node table is the mixer line's internal
  1675. // representation of the topology for easier processing.
  1676. //
  1677. DPF(DL_TRACE|FA_MIXER,("Building Node Table") );
  1678. mxobj.pNodeTable = kmxlBuildNodeTable( mxobj.pTopology );
  1679. if( !mxobj.pNodeTable ) {
  1680. Status = STATUS_INSUFFICIENT_RESOURCES;
  1681. DPF(DL_WARNING|FA_MIXER,("kmxlBuildNodeTable failed") );
  1682. goto exit;
  1683. }
  1684. //
  1685. // Parse the topology and build the necessary data structures
  1686. // to walk the topology.
  1687. //
  1688. DPF(DL_TRACE|FA_MIXER,("Parsing Topology") );
  1689. Status = kmxlParseTopology(
  1690. &mxobj,
  1691. &listSources,
  1692. &listDests );
  1693. if( !NT_SUCCESS( Status ) ) {
  1694. DPF(DL_WARNING|FA_MIXER,("kmxlParseTopoloty failed Status=%X",Status) );
  1695. goto exit;
  1696. }
  1697. //
  1698. // Build up a list of destination lines.
  1699. //
  1700. DPF(DL_TRACE|FA_MIXER,("Building Destination lines") );
  1701. *plistLines = kmxlBuildDestinationLines(
  1702. &mxobj,
  1703. listDests
  1704. );
  1705. if( !(*plistLines) ) {
  1706. Status = STATUS_INSUFFICIENT_RESOURCES;
  1707. DPF(DL_WARNING|FA_MIXER,("kmxlBuildDestinationLines failed") );
  1708. goto exit;
  1709. }
  1710. //
  1711. // Assign the line Ids and the Control Ids for the destinations. Also,
  1712. // fill in the number of destinations.
  1713. //
  1714. kmxlAssignLineAndControlIds( &mxobj, (*plistLines), DESTINATION_LIST );
  1715. *pcDestinations = kmxlListLength( (*plistLines) );
  1716. //
  1717. // Build up a list of source lines
  1718. //
  1719. DPF(DL_TRACE|FA_MIXER,("Building Source lines") );
  1720. listSourceLines = kmxlBuildSourceLines(
  1721. &mxobj,
  1722. listSources,
  1723. listDests
  1724. );
  1725. if( !listSourceLines ) {
  1726. Status = STATUS_INSUFFICIENT_RESOURCES;
  1727. DPF(DL_WARNING|FA_MIXER,("kmxlBuildSourceLines failed") );
  1728. goto exit;
  1729. }
  1730. //
  1731. // Polish off the lines. First sort them by destination so that
  1732. // the source Ids will be assigned correctly.
  1733. //
  1734. DPF(DL_TRACE|FA_MIXER,("Sort By Destination") );
  1735. kmxlSortByDestination( &listSourceLines );
  1736. DPF(DL_TRACE|FA_MIXER,("Assign Line and Control Ids") );
  1737. kmxlAssignLineAndControlIds( &mxobj, listSourceLines, SOURCE_LIST );
  1738. //
  1739. // Now assign destinations to sources and construct the line Ids for
  1740. // the source lines.
  1741. //
  1742. DPF(DL_TRACE|FA_MIXER,("Assign Destinations to Sources") );
  1743. kmxlAssignDestinationsToSources( listSourceLines, (*plistLines) );
  1744. //
  1745. // Update the number of sources mapping to each of the destinations.
  1746. //
  1747. DPF(DL_TRACE|FA_MIXER,("Update Destination Connection Count") );
  1748. kmxlUpdateDestintationConnectionCount( listSourceLines, (*plistLines) );
  1749. //
  1750. // Assign the dwComponentIds for the source and destination lines.
  1751. //
  1752. DPF(DL_TRACE|FA_MIXER,("Assign Conponent IDs") );
  1753. kmxlAssignComponentIds( &mxobj, listSourceLines, (*plistLines) );
  1754. //
  1755. // Construct a single list of lines. Destinations will be first in
  1756. // increasing numerical order by line id, folowed by sources in
  1757. // increasing numerical order.
  1758. //
  1759. kmxlAppendListToEndOfList( (PSLIST*) plistLines, (PSLIST) listSourceLines );
  1760. //
  1761. // Eliminate any lines that are invalid.
  1762. //
  1763. DPF(DL_TRACE|FA_MIXER,("Eliminate Invalid Lines") );
  1764. kmxlEliminateInvalidLines( plistLines );
  1765. //
  1766. // Update the mux line IDs to match real line IDs
  1767. //
  1768. DPF(DL_TRACE|FA_MIXER,("Assign Mux IDs") );
  1769. kmxlAssignMuxIds( &mxobj, *plistLines );
  1770. //
  1771. // Here is where we want to Enable Change Notifications on all controls
  1772. // that support notifications.
  1773. //
  1774. DPF(DL_TRACE|FA_MIXER,("Enable All Controls") );
  1775. kmxlEnableAllControls(&mxobj);
  1776. exit:
  1777. //
  1778. // If we got here because of an error, clean up all the mixer lines
  1779. //
  1780. if( !NT_SUCCESS( Status ) ) {
  1781. PMXLLINE pLine;
  1782. PMXLCONTROL pControl;
  1783. while( (*plistLines) ) {
  1784. pLine = kmxlRemoveFirstLine( (*plistLines) );
  1785. while( pLine && pLine->Controls ) {
  1786. pControl = kmxlRemoveFirstControl( pLine->Controls );
  1787. kmxlFreeControl( pControl );
  1788. }
  1789. AudioFreeMemory( sizeof(MXLLINE),&pLine );
  1790. }
  1791. }
  1792. //
  1793. // Free up the mux control list. Note that we don't want to free
  1794. // the controls using kmxlFreeControl() because we need the special
  1795. // mux instance data to persist.
  1796. //
  1797. {
  1798. PMXLCONTROL pControl;
  1799. while( mxobj.listMuxControls ) {
  1800. pControl = kmxlRemoveFirstControl( mxobj.listMuxControls );
  1801. ASSERT( pControl->pChannelStepping == NULL);
  1802. AudioFreeMemory( sizeof(MXLCONTROL),&pControl );
  1803. }
  1804. }
  1805. //
  1806. // Free up the source and destination lists. Both types of these lists
  1807. // are allocated list nodes and allocated nodes. Both need to be freed.
  1808. // The Children and Parent lists, though, are only allocated list nodes.
  1809. // The actual nodes are contained in the node table and will be deallocated
  1810. // in one chunk in the next block of code.
  1811. //
  1812. while( listSources ) {
  1813. pTemp = kmxlRemoveFirstNode( listSources );
  1814. kmxlFreePeerList( pTemp->Children );
  1815. AudioFreeMemory( sizeof(MXLNODE),&pTemp );
  1816. }
  1817. while( listDests ) {
  1818. pTemp = kmxlRemoveFirstNode( listDests );
  1819. kmxlFreePeerList( pTemp->Parents );
  1820. AudioFreeMemory( sizeof(MXLNODE),&pTemp );
  1821. }
  1822. //
  1823. // Free up the peer lists for the children and parents inside the
  1824. // nodes of the node table. Finally, deallocate the array of nodes.
  1825. //
  1826. if( mxobj.pNodeTable ) {
  1827. for( i = 0; i < mxobj.pTopology->TopologyNodesCount; i++ ) {
  1828. kmxlFreePeerList( mxobj.pNodeTable[ i ].Children );
  1829. kmxlFreePeerList( mxobj.pNodeTable[ i ].Parents );
  1830. }
  1831. AudioFreeMemory_Unknown( &mxobj.pNodeTable );
  1832. }
  1833. RETURN( Status );
  1834. }
  1835. ///////////////////////////////////////////////////////////////////////
  1836. ///////////////////////////////////////////////////////////////////////
  1837. // //
  1838. // M I X E R L I N E F U N C T I O N S //
  1839. // //
  1840. ///////////////////////////////////////////////////////////////////////
  1841. ///////////////////////////////////////////////////////////////////////
  1842. ///////////////////////////////////////////////////////////////////////
  1843. //
  1844. // kmxlBuildDestinationLines
  1845. //
  1846. // Loops through each of the destination nodes, allocates a line
  1847. // structure for it, and calls kmxlBuildDestinationControls to
  1848. // build the controls for that line.
  1849. //
  1850. //
  1851. LINELIST
  1852. kmxlBuildDestinationLines(
  1853. IN PMIXEROBJECT pmxobj,
  1854. IN NODELIST listDests // The list of destination nodes
  1855. )
  1856. {
  1857. LINELIST listDestLines = NULL;
  1858. PMXLNODE pDest = NULL;
  1859. PMXLLINE pLine = NULL;
  1860. PMXLCONTROL pControl = NULL;
  1861. ULONG MaxChannelsForLine;
  1862. ASSERT( pmxobj );
  1863. ASSERT( listDests );
  1864. PAGED_CODE();
  1865. //
  1866. // Loop over all the destination node allocating a line structure
  1867. // for each.
  1868. //
  1869. pDest = kmxlFirstInList( listDests );
  1870. while( pDest ) {
  1871. //
  1872. // Allocate a new line structure and add it to the list of
  1873. // destination lines.
  1874. //
  1875. pLine = kmxlAllocateLine( TAG_AudL_LINE );
  1876. if( !pLine ) {
  1877. goto exit;
  1878. }
  1879. kmxlAddToList( listDestLines, pLine );
  1880. //
  1881. // Fill in the details of the line structure. Some fields will
  1882. // be filled in later.
  1883. //
  1884. pLine->DestId = pDest->Id;
  1885. pLine->Type = pDest->NodeType;
  1886. pLine->Communication = pDest->Communication;
  1887. pLine->Line.cbStruct = sizeof( MIXERLINE );
  1888. pLine->Line.dwSource = (DWORD) -1;
  1889. pLine->Line.dwDestination = (DWORD) -1;
  1890. kmxlGetPinName( pmxobj->pfo, pDest->Id, pLine );
  1891. //
  1892. // HACK! The ACTIVE flag should only be set when the line is active
  1893. // but then no lines show up in SNDVOL32. It works if the flag is
  1894. // always set to ACTIVE for destinations. Also, the number of channels
  1895. // should be queried not hard coded. WDM Audio does not provide a
  1896. // way to easily query this.
  1897. //
  1898. pLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
  1899. pLine->Line.cChannels = 1; // should this default to 1 or 2?
  1900. //
  1901. // Build up a list of the controls on this destination
  1902. //
  1903. if( !NT_SUCCESS( kmxlBuildDestinationControls(
  1904. pmxobj,
  1905. pDest,
  1906. pLine
  1907. ) ) )
  1908. {
  1909. DPF(DL_WARNING|FA_MIXER,("kmxlBuildDestinationControls failed") );
  1910. goto exit;
  1911. }
  1912. pDest = kmxlNextNode( pDest );
  1913. }
  1914. pLine = kmxlFirstInList( listDestLines );
  1915. while( pLine ) {
  1916. MaxChannelsForLine = 1;
  1917. pControl = kmxlFirstInList( pLine->Controls );
  1918. while( pControl ) {
  1919. ASSERT( IsValidControl( pControl ) );
  1920. if ( pControl->NumChannels > MaxChannelsForLine) {
  1921. MaxChannelsForLine = pControl->NumChannels;
  1922. }
  1923. pControl = kmxlNextControl( pControl );
  1924. }
  1925. if( pLine->Controls == NULL ) {
  1926. pLine->Line.cChannels = 1; // should this default to 1 or 2?
  1927. } else {
  1928. pLine->Line.cChannels = MaxChannelsForLine;
  1929. }
  1930. pLine = kmxlNextLine( pLine );
  1931. }
  1932. return( listDestLines );
  1933. exit:
  1934. //
  1935. // A memory allocation failed. Clean up the destination lines and
  1936. // return failure.
  1937. //
  1938. while( listDestLines ) {
  1939. pLine = kmxlRemoveFirstLine( listDestLines );
  1940. while( pLine && pLine->Controls ) {
  1941. pControl = kmxlRemoveFirstControl( pLine->Controls );
  1942. kmxlFreeControl( pControl );
  1943. }
  1944. AudioFreeMemory_Unknown( &pLine );
  1945. }
  1946. return( NULL );
  1947. }
  1948. ///////////////////////////////////////////////////////////////////////
  1949. //
  1950. // BuildDestinationControls
  1951. //
  1952. // Starts at the destination node and translates all the parent nodes
  1953. // to mixer line controls. This process stops when the first SUM node
  1954. // is encountered, indicating the end of a destination line.
  1955. //
  1956. //
  1957. NTSTATUS
  1958. kmxlBuildDestinationControls(
  1959. IN PMIXEROBJECT pmxobj,
  1960. IN PMXLNODE pDest, // The destination to built controls for
  1961. IN PMXLLINE pLine // The line to add the controls to
  1962. )
  1963. {
  1964. PPEERNODE pTemp = NULL;
  1965. PMXLCONTROL pControl;
  1966. PAGED_CODE();
  1967. ASSERT( pmxobj );
  1968. ASSERT( pLine );
  1969. //
  1970. // Start at the immediate parent of the node passed.
  1971. //
  1972. pTemp = kmxlFirstParentNode( pDest );
  1973. while( pTemp ) {
  1974. if( IsEqualGUID( &pTemp->pNode->NodeType, &KSNODETYPE_SUM ) ||
  1975. ( pTemp->pNode->Type == SOURCE ) ||
  1976. ( pTemp->pNode->Type == DESTINATION ) ) {
  1977. //
  1978. // We've found a SUM node. Discontinue the loop... we've
  1979. // found all the controls.
  1980. //
  1981. break;
  1982. }
  1983. if( IsEqualGUID( &pTemp->pNode->NodeType, &KSNODETYPE_MUX ) ) {
  1984. if (kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl )) {
  1985. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  1986. }
  1987. break;
  1988. }
  1989. if( ( kmxlParentListLength( pTemp->pNode ) > 1 ) ) {
  1990. //
  1991. // Found a node with multiple parents that is not a SUM node.
  1992. // Can't handle that here so add any controls for this node
  1993. // and discontinue the loop.
  1994. //
  1995. if( kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl ) ) {
  1996. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  1997. }
  1998. break;
  1999. }
  2000. //
  2001. // By going up through the parents and inserting nodes at
  2002. // the front of the list, the list will contain the controls
  2003. // in the right order.
  2004. //
  2005. if( kmxlTranslateNodeToControl( pmxobj, pTemp->pNode, &pControl ) ) {
  2006. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2007. }
  2008. pTemp = kmxlFirstParentNode( pTemp->pNode );
  2009. }
  2010. DPF(DL_TRACE|FA_MIXER,(
  2011. "Found %d controls on destination %d:",
  2012. kmxlListLength( pLine->Controls ),
  2013. pDest->Id
  2014. ) );
  2015. RETURN( STATUS_SUCCESS );
  2016. }
  2017. ///////////////////////////////////////////////////////////////////////
  2018. //
  2019. // kmxlBuildSourceLines
  2020. //
  2021. // Loops through each of the source nodes, allocating a new line
  2022. // structure, and calling kmxlBuildPath() to build the controls
  2023. // for the line (and possibly creating new lines if there are splits
  2024. // in the topology).
  2025. //
  2026. //
  2027. LINELIST
  2028. kmxlBuildSourceLines(
  2029. IN PMIXEROBJECT pmxobj,
  2030. IN NODELIST listSources, // The list of source nodes
  2031. IN NODELIST listDests // The list of dest. nodes
  2032. )
  2033. {
  2034. NTSTATUS Status;
  2035. LINELIST listSourceLines = NULL;
  2036. PMXLNODE pSource = NULL;
  2037. PMXLLINE pTemp = NULL;
  2038. PMXLCONTROL pControl;
  2039. ULONG MaxChannelsForLine;
  2040. ASSERT( pmxobj );
  2041. ASSERT( pmxobj->pfo );
  2042. ASSERT( pmxobj->pNodeTable );
  2043. ASSERT( listSources );
  2044. ASSERT( listDests );
  2045. PAGED_CODE();
  2046. pSource = kmxlFirstInList( listSources );
  2047. while( pSource ) {
  2048. //
  2049. // Allocate a new line structure and insert it into the list of
  2050. // source lines.
  2051. //
  2052. pTemp = kmxlAllocateLine( TAG_AudL_LINE );
  2053. if( !pTemp ) {
  2054. goto exit;
  2055. }
  2056. kmxlAddToEndOfList( listSourceLines, pTemp );
  2057. //
  2058. // Fill in the fields of the line structure. Some fields will need
  2059. // to be filled in later.
  2060. //
  2061. pTemp->SourceId = pSource->Id;
  2062. pTemp->Type = pSource->NodeType;
  2063. pTemp->Communication = pSource->Communication;
  2064. pTemp->Line.cbStruct = sizeof( MIXERLINE );
  2065. pTemp->Line.dwSource = (DWORD) -1;
  2066. pTemp->Line.dwDestination = (DWORD) -1;
  2067. pTemp->Line.fdwLine = MIXERLINE_LINEF_SOURCE |
  2068. MIXERLINE_LINEF_ACTIVE;
  2069. kmxlGetPinName( pmxobj->pfo, pSource->Id, pTemp );
  2070. // DPF(DL_TRACE|FA_MIXER,( "Building path for %s (%d).",
  2071. // PinCategoryToString( &pSource->NodeType ),
  2072. // pSource->Id
  2073. // ) );
  2074. //
  2075. // Build the controls for this line and identify the destination(s)
  2076. // it conntects to.
  2077. //
  2078. Status = kmxlBuildPath(
  2079. pmxobj,
  2080. pSource, // The source line to build controls for
  2081. pSource, // The node to start with
  2082. pTemp, // The line structure to add to
  2083. &listSourceLines, // The list of all source lines
  2084. listDests // THe list of all destinations
  2085. );
  2086. if( !NT_SUCCESS( Status ) ) {
  2087. DPF(DL_WARNING|FA_MIXER,("kmxlBuildPath failed Status=%X",Status) );
  2088. goto exit;
  2089. }
  2090. pSource = kmxlNextNode( pSource );
  2091. } // while( pSource )
  2092. pTemp = kmxlFirstInList( listSourceLines );
  2093. while( pTemp ) {
  2094. MaxChannelsForLine = 1;
  2095. pControl = kmxlFirstInList( pTemp->Controls );
  2096. while( pControl ) {
  2097. ASSERT( IsValidControl( pControl ) );
  2098. if ( pControl->NumChannels > MaxChannelsForLine) {
  2099. MaxChannelsForLine = pControl->NumChannels;
  2100. }
  2101. pControl = kmxlNextControl( pControl );
  2102. }
  2103. if( pTemp->Controls == NULL ) {
  2104. pTemp->Line.cChannels = 1; // should this default to 1 or 2?
  2105. } else {
  2106. pTemp->Line.cChannels = MaxChannelsForLine;
  2107. }
  2108. pTemp = kmxlNextLine( pTemp );
  2109. }
  2110. return( listSourceLines );
  2111. exit:
  2112. //
  2113. // Something went wrong. Clean up all memory allocated and return NULL
  2114. // to indicate the error.
  2115. //
  2116. while( listSourceLines ) {
  2117. pTemp = kmxlRemoveFirstLine( listSourceLines );
  2118. while( pTemp && pTemp->Controls ) {
  2119. pControl = kmxlRemoveFirstControl( pTemp->Controls );
  2120. kmxlFreeControl( pControl );
  2121. }
  2122. AudioFreeMemory_Unknown( &pTemp );
  2123. }
  2124. return( NULL );
  2125. }
  2126. ///////////////////////////////////////////////////////////////////////
  2127. //
  2128. // kmxlBuildPath
  2129. //
  2130. // Builds the controls for a source line. A source line ends when a
  2131. // SUM node, a destination node, a node contained in a destination line
  2132. // is encountered. When splits are encountered in the topology, new
  2133. // lines need to be created and the controls on those lines enumerated.
  2134. //
  2135. // kmxlBuildPath will recurse to find the controls on subnodes.
  2136. //
  2137. //
  2138. NTSTATUS
  2139. kmxlBuildPath(
  2140. IN PMIXEROBJECT pmxobj,
  2141. IN PMXLNODE pSource, // The source node for this path
  2142. IN PMXLNODE pNode, // The current node in the path
  2143. IN PMXLLINE pLine, // The current line
  2144. IN OUT LINELIST* plistLines, // The list of lines build so far
  2145. IN NODELIST listDests // The list of the destinations
  2146. )
  2147. {
  2148. NTSTATUS Status;
  2149. PMXLCONTROL pControl = NULL;
  2150. PMXLLINE pNewLine = NULL;
  2151. ULONG nControls;
  2152. PPEERNODE pChild = NULL;
  2153. ASSERT( pmxobj );
  2154. ASSERT( pSource );
  2155. ASSERT( pNode );
  2156. ASSERT( pLine );
  2157. ASSERT( plistLines );
  2158. PAGED_CODE();
  2159. DPF(DL_TRACE|FA_MIXER,( "Building path for %d(0x%x) (%s) NODE=%08x",
  2160. pNode->Id,pNode->Id,
  2161. NodeTypeToString( &pNode->NodeType ),
  2162. pNode ) );
  2163. //
  2164. // Check to see if this is the end of this line.
  2165. //
  2166. if( ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ) ||
  2167. ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) ||
  2168. ( pNode->Type == DESTINATION ) ||
  2169. ( kmxlIsDestinationNode( listDests, pNode ) ) )
  2170. {
  2171. //
  2172. // Find the destination node and update the line structure.
  2173. // If this IS the destination node, then set the ID in the line
  2174. // structure and return. There is no need to check the children,
  2175. // since there won't be any.
  2176. //
  2177. if( pNode->Type == DESTINATION ) {
  2178. pLine->DestId = pNode->Id;
  2179. RETURN( STATUS_SUCCESS );
  2180. }
  2181. //
  2182. // Find the destination node for the source. It is possible to
  2183. // have branches in the topology, so this may recurse.
  2184. //
  2185. pLine->DestId = kmxlFindDestinationForNode(
  2186. pmxobj,
  2187. pNode,
  2188. pNode,
  2189. pLine,
  2190. plistLines
  2191. );
  2192. RETURN( STATUS_SUCCESS );
  2193. }
  2194. //
  2195. // Retrieve and translate the node for the first child, appending any
  2196. // controls created onto the list of controls for this line.
  2197. //
  2198. pChild = kmxlFirstChildNode( pNode );
  2199. if( pChild == NULL ) {
  2200. RETURN( STATUS_SUCCESS );
  2201. }
  2202. //
  2203. // Save the number of controls here. If a split occurs beneath this
  2204. // node, we don't want to include children followed on the first
  2205. // child's path.
  2206. //
  2207. nControls = kmxlListLength( pLine->Controls );
  2208. if (kmxlTranslateNodeToControl(pmxobj, pChild->pNode, &pControl) ) {
  2209. if( pControl && IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) ) {
  2210. if( kmxlIsDestinationNode( listDests, pChild->pNode ) ) {
  2211. pControl->Parameters.bPlaceholder = TRUE;
  2212. }
  2213. }
  2214. kmxlAppendListToEndOfList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2215. }
  2216. //
  2217. // Recurse to build the controls for this child.
  2218. //
  2219. Status = kmxlBuildPath(
  2220. pmxobj,
  2221. pSource,
  2222. pChild->pNode,
  2223. pLine,
  2224. plistLines,
  2225. listDests
  2226. );
  2227. if( !NT_SUCCESS( Status ) ) {
  2228. RETURN( Status );
  2229. }
  2230. //
  2231. // For the rest of the children
  2232. //
  2233. // Create a new line based on pSource.
  2234. // Duplicate the list of controls in pLine.
  2235. // Recurse over the child node.
  2236. //
  2237. pChild = kmxlNextPeerNode( pChild );
  2238. while( pChild ) {
  2239. pNewLine = kmxlAllocateLine( TAG_AudL_LINE );
  2240. if( pNewLine == NULL ) {
  2241. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2242. }
  2243. //
  2244. // Insert this new node into the list of source lines
  2245. //
  2246. RtlCopyMemory( pNewLine, pLine, sizeof( MXLLINE ) );
  2247. pNewLine->List.Next = NULL;
  2248. pNewLine->Controls = NULL;
  2249. kmxlAddToEndOfList( *plistLines, pNewLine );
  2250. //
  2251. // Since this is a new line, the control structures also need to be
  2252. // duplicated.
  2253. //
  2254. Status = kmxlDuplicateLineControls( pNewLine, pLine, nControls );
  2255. if( !NT_SUCCESS( Status ) ) {
  2256. RETURN( Status );
  2257. }
  2258. //
  2259. // Just as for the first child, translate the node, append the
  2260. // controls to the list of controls for this list, and recurse
  2261. // to build the controls for its children.
  2262. //
  2263. if (kmxlTranslateNodeToControl(
  2264. pmxobj,
  2265. pChild->pNode,
  2266. &pControl ) ) {
  2267. kmxlAppendListToEndOfList( (PSLIST*) &pNewLine->Controls, (PSLIST) pControl );
  2268. }
  2269. Status = kmxlBuildPath(
  2270. pmxobj,
  2271. pSource,
  2272. pChild->pNode,
  2273. pNewLine,
  2274. plistLines,
  2275. listDests
  2276. );
  2277. if( !NT_SUCCESS( Status ) ) {
  2278. RETURN( Status );
  2279. }
  2280. pChild = kmxlNextPeerNode( pChild );
  2281. } // while( pChild )
  2282. RETURN( STATUS_SUCCESS );
  2283. }
  2284. ///////////////////////////////////////////////////////////////////////
  2285. //
  2286. // kmxlIsDestinationNode
  2287. //
  2288. // Searches all the list of controls on the given list of destinations
  2289. // to see if the node appears in any of those lists.
  2290. //
  2291. //
  2292. BOOL
  2293. kmxlIsDestinationNode(
  2294. IN NODELIST listDests, // The list of destinations
  2295. IN PMXLNODE pNode // The node to check
  2296. )
  2297. {
  2298. PMXLNODE pTemp;
  2299. PPEERNODE pParent;
  2300. PAGED_CODE();
  2301. if( pNode->Type == SOURCE ) {
  2302. return( FALSE );
  2303. }
  2304. if( pNode->Type == DESTINATION ) {
  2305. return( TRUE );
  2306. }
  2307. ASSERT(pNode->Type == NODE);
  2308. //
  2309. // Loop over each of the destinations
  2310. //
  2311. pTemp = kmxlFirstInList( listDests );
  2312. while( pTemp ) {
  2313. //
  2314. // Loop over the parent.
  2315. //
  2316. pParent = kmxlFirstParentNode( pTemp );
  2317. while( pParent ) {
  2318. if( ( pParent->pNode->Type == NODE ) &&
  2319. ( pParent->pNode->Id == pNode->Id) ) {
  2320. return( TRUE );
  2321. }
  2322. if( ( IsEqualGUID( &pParent->pNode->NodeType, &KSNODETYPE_SUM ) ) ||
  2323. ( IsEqualGUID( &pParent->pNode->NodeType, &KSNODETYPE_MUX ) ) ||
  2324. ( pParent->pNode->Type == SOURCE ) )
  2325. {
  2326. break;
  2327. }
  2328. //
  2329. // Check for the node Ids matching.
  2330. //
  2331. pParent = kmxlFirstParentNode( pParent->pNode );
  2332. }
  2333. pTemp = kmxlNextNode( pTemp );
  2334. }
  2335. return( FALSE );
  2336. }
  2337. ///////////////////////////////////////////////////////////////////////
  2338. //
  2339. // kmxlDuplicateLine
  2340. //
  2341. // Duplicates a line and the associated controls.
  2342. //
  2343. //
  2344. NTSTATUS
  2345. kmxlDuplicateLine(
  2346. IN PMXLLINE* ppTargetLine, // Pointer to the new line
  2347. IN PMXLLINE pSourceLine // The line to duplicate
  2348. )
  2349. {
  2350. PAGED_CODE();
  2351. ASSERT( ppTargetLine );
  2352. ASSERT( pSourceLine );
  2353. DPF(DL_TRACE|FA_MIXER,( "Duplicated line with source=%d.",
  2354. pSourceLine->SourceId ) );
  2355. //
  2356. // Allocate a new line structure and copy the information from the
  2357. // source line.
  2358. //
  2359. *ppTargetLine = kmxlAllocateLine( TAG_AudL_LINE );
  2360. if( *ppTargetLine == NULL ) {
  2361. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2362. }
  2363. ASSERT( *ppTargetLine );
  2364. // DPF(DL_TRACE|FA_MIXER,( "Duplicated %s (%d).",
  2365. // PinCategoryToString( &pSourceLine->Type ),
  2366. // pSourceLine->SourceId
  2367. // ) );
  2368. RtlCopyMemory( *ppTargetLine, pSourceLine, sizeof( MXLLINE ) );
  2369. //
  2370. // Null out the controls and next pointer. This line does not have
  2371. // either of its own yet.
  2372. //
  2373. (*ppTargetLine)->List.Next = NULL;
  2374. (*ppTargetLine)->Controls = NULL;
  2375. //
  2376. // Duplicate all the controls for the source line.
  2377. //
  2378. return( kmxlDuplicateLineControls(
  2379. *ppTargetLine,
  2380. pSourceLine,
  2381. kmxlListLength( pSourceLine->Controls )
  2382. )
  2383. );
  2384. }
  2385. ///////////////////////////////////////////////////////////////////////
  2386. //
  2387. // kmxlDuplicateLineControls
  2388. //
  2389. // Duplicates the controls for a line by allocating a new control
  2390. // structure for each and copying the information to the new node.
  2391. //
  2392. //
  2393. NTSTATUS
  2394. kmxlDuplicateLineControls(
  2395. IN PMXLLINE pTargetLine, // The line to put the controls into
  2396. IN PMXLLINE pSourceLine, // The line with the controls to dup
  2397. IN ULONG nCount // The number of controls to dup
  2398. )
  2399. {
  2400. PMXLCONTROL pControl,
  2401. pNewControl;
  2402. NTSTATUS Status;
  2403. PAGED_CODE();
  2404. ASSERT( pTargetLine->Controls == NULL );
  2405. if( nCount == 0 ) {
  2406. RETURN( STATUS_SUCCESS );
  2407. }
  2408. //
  2409. // Iterate over the list allocating and copying the controls
  2410. //
  2411. pControl = kmxlFirstInList( pSourceLine->Controls );
  2412. while( pControl ) {
  2413. ASSERT( IsValidControl( pControl ) );
  2414. //
  2415. // Allocate a new control structure.
  2416. //
  2417. pNewControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2418. if( pNewControl == NULL ) {
  2419. goto exit;
  2420. }
  2421. //
  2422. // Copy the entire MXLCONTROL structure and NULL out the
  2423. // List.Next field. This control will be part of a different
  2424. // list.
  2425. //
  2426. RtlCopyMemory( pNewControl, pControl, sizeof( MXLCONTROL ) );
  2427. pNewControl->List.Next = NULL;
  2428. pNewControl->pChannelStepping = NULL;
  2429. //
  2430. // Copy the channel steppings from the original control
  2431. //
  2432. ASSERT(pControl->NumChannels > 0);
  2433. Status = AudioAllocateMemory_Paged(pNewControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  2434. TAG_AuDD_CHANNEL,
  2435. DEFAULT_MEMORY,
  2436. &pNewControl->pChannelStepping );
  2437. if( !NT_SUCCESS( Status ) ) {
  2438. pNewControl->NumChannels = 0;
  2439. goto exit;
  2440. }
  2441. RtlCopyMemory( pNewControl->pChannelStepping,
  2442. pControl->pChannelStepping,
  2443. pNewControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
  2444. //
  2445. // We just made a copy of a MUX node. Mark the datastructures
  2446. // is has as a copy so it doesn't get freed from underneath
  2447. // somebody else.
  2448. //
  2449. if( IsEqualGUID( pNewControl->NodeType, &KSNODETYPE_MUX ) ) {
  2450. pNewControl->Parameters.bHasCopy = TRUE;
  2451. }
  2452. kmxlAddToList( pTargetLine->Controls, pNewControl );
  2453. //
  2454. // Decrement and check the number of controls copied. If we copied
  2455. // the requested number, stop.
  2456. //
  2457. --nCount;
  2458. if( nCount == 0 ) {
  2459. break;
  2460. }
  2461. pControl = kmxlNextControl( pControl );
  2462. }
  2463. RETURN( STATUS_SUCCESS );
  2464. exit:
  2465. //
  2466. // Failed to allocate the control structure. Free up all the
  2467. // controls already allocated and return an error.
  2468. //
  2469. while( pTargetLine->Controls ) {
  2470. pControl = kmxlRemoveFirstControl( pTargetLine->Controls );
  2471. kmxlFreeControl( pControl );
  2472. }
  2473. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2474. }
  2475. ///////////////////////////////////////////////////////////////////////
  2476. //
  2477. // kmxlFindDestinationForNode
  2478. //
  2479. // Finds a destination for the given node, duplicating lines if splits
  2480. // are found in the topology.
  2481. //
  2482. //
  2483. ULONG
  2484. kmxlFindDestinationForNode(
  2485. IN PMIXEROBJECT pmxobj,
  2486. IN PMXLNODE pNode, // The node to find dest for
  2487. IN PMXLNODE pParent, // The original parent
  2488. IN PMXLLINE pLine, // The current line it's on
  2489. IN OUT LINELIST* plistLines // The list of all lines
  2490. )
  2491. {
  2492. PPEERNODE pChild, pPeerChild;
  2493. PMXLLINE pNewLine;
  2494. PMXLNODE pShadow = pNode;
  2495. PAGED_CODE();
  2496. DPF(DL_TRACE|FA_MIXER,( "Finding destination for node %d(0x%x) (%s), parent %d(0x%x) (%s).",
  2497. pNode->Id,pNode->Id,
  2498. NodeTypeToString( &pNode->NodeType ),
  2499. pParent->Id,pParent->Id,
  2500. NodeTypeToString( &pNode->NodeType ) ) );
  2501. ASSERT( pmxobj ) ;
  2502. ASSERT( pNode );
  2503. ASSERT( pParent );
  2504. ASSERT( pLine );
  2505. ASSERT( plistLines );
  2506. if( pNode->Type == DESTINATION ) {
  2507. return( pNode->Id );
  2508. }
  2509. //
  2510. // Loop over the first children.
  2511. //
  2512. pChild = kmxlFirstChildNode( pNode );
  2513. while( pChild ) {
  2514. DPF(DL_TRACE|FA_MIXER,( "First child is %d(0x%x) (%s) NODE:%08x.",
  2515. pChild->pNode->Id,
  2516. pChild->pNode->Id,
  2517. NodeTypeToString( &pChild->pNode->NodeType ),
  2518. pChild->pNode ) );
  2519. if( pChild->pNode == pParent ) {
  2520. DPF(DL_TRACE|FA_MIXER,( "Child node is same as original parent!" ) );
  2521. return( INVALID_ID );
  2522. }
  2523. //
  2524. // Loop over the rest of the children.
  2525. //
  2526. pPeerChild = kmxlNextPeerNode( pChild );
  2527. while( pPeerChild ) {
  2528. if( pPeerChild->pNode == pParent ) {
  2529. DPF(DL_TRACE|FA_MIXER,( "Child node is same as original parent!" ) );
  2530. return( INVALID_ID );
  2531. }
  2532. DPF(DL_TRACE|FA_MIXER,( "Peer node of %d(0x%x) (%s) is %d(0x%x) (%s).",
  2533. pChild->pNode->Id,pChild->pNode->Id,
  2534. NodeTypeToString( &pChild->pNode->NodeType ),
  2535. pPeerChild->pNode->Id,pPeerChild->pNode->Id,
  2536. NodeTypeToString( &pPeerChild->pNode->NodeType ) ) );
  2537. //
  2538. // This line has more than 1 child. Duplicate this line
  2539. // and add it to the list of lines.
  2540. //
  2541. if( !NT_SUCCESS( kmxlDuplicateLine( &pNewLine, pLine ) ) ) {
  2542. DPF(DL_WARNING|FA_MIXER,("kmxlDuplicateLine failed") );
  2543. continue;
  2544. }
  2545. kmxlAddToEndOfList( *plistLines, pNewLine );
  2546. if( IsEqualGUID( &pPeerChild->pNode->NodeType, &KSNODETYPE_MUX ) ) {
  2547. //
  2548. // We've found a MUX after a SUM or another MUX node. Mark
  2549. // the current line as invalid and build a new, virtual
  2550. // line that feeds into the MUX.
  2551. //
  2552. pNewLine->DestId = INVALID_ID;
  2553. kmxlBuildVirtualMuxLine(
  2554. pmxobj,
  2555. pShadow,
  2556. pPeerChild->pNode,
  2557. plistLines
  2558. );
  2559. } else {
  2560. //
  2561. // Now to find the destination for this new line. Recurse
  2562. // on the node of this child.
  2563. //
  2564. pNewLine->DestId = kmxlFindDestinationForNode(
  2565. pmxobj,
  2566. pPeerChild->pNode,
  2567. pParent,
  2568. pNewLine,
  2569. plistLines
  2570. );
  2571. }
  2572. DPF(DL_TRACE|FA_MIXER,( "Found %x as dest for %d(0x%x) (%s).",
  2573. pNewLine->DestId, pPeerChild->pNode->Id,pPeerChild->pNode->Id,
  2574. NodeTypeToString( &pPeerChild->pNode->NodeType ),
  2575. pPeerChild->pNode ) );
  2576. pPeerChild = kmxlNextPeerNode( pPeerChild );
  2577. }
  2578. if( IsEqualGUID( &pChild->pNode->NodeType, &KSNODETYPE_MUX ) ) {
  2579. //
  2580. // We've found a MUX after a SUM or another MUX node. Mark
  2581. // the current line as invalid and build a new, virtual
  2582. // line that feeds into the MUX.
  2583. //
  2584. kmxlBuildVirtualMuxLine(
  2585. pmxobj,
  2586. pShadow,
  2587. pChild->pNode,
  2588. plistLines
  2589. );
  2590. return( INVALID_ID );
  2591. }
  2592. //
  2593. // Found the destination!
  2594. //
  2595. if( pChild->pNode->Type == DESTINATION ) {
  2596. DPF(DL_TRACE|FA_MIXER,( "Found %x as dest for %d.",
  2597. pChild->pNode->Id,
  2598. pNode->Id ) );
  2599. return( pChild->pNode->Id );
  2600. }
  2601. pShadow = pChild->pNode;
  2602. pChild = kmxlFirstChildNode( pChild->pNode );
  2603. }
  2604. DPF(DL_WARNING|FA_MIXER,("returning INVALID_ID") );
  2605. return( INVALID_ID );
  2606. }
  2607. ///////////////////////////////////////////////////////////////////////
  2608. //
  2609. // kmxlBuildVirtualMuxLine
  2610. //
  2611. //
  2612. NTSTATUS
  2613. kmxlBuildVirtualMuxLine(
  2614. IN PMIXEROBJECT pmxobj,
  2615. IN PMXLNODE pParent,
  2616. IN PMXLNODE pMux,
  2617. IN OUT LINELIST* plistLines
  2618. )
  2619. {
  2620. PMXLLINE pLine, pTemp;
  2621. PMXLNODE pNode;
  2622. PMXLCONTROL pControl;
  2623. MXLCONTROL Control;
  2624. PAGED_CODE();
  2625. //
  2626. // Allocate a new line to represent the virtual mux input line.
  2627. //
  2628. pLine = kmxlAllocateLine( TAG_AudL_LINE );
  2629. if( pLine == NULL ) {
  2630. RETURN( STATUS_INSUFFICIENT_RESOURCES );
  2631. }
  2632. DPF(DL_TRACE|FA_MIXER,("Virtual line %08x for Parent NODE:%08x",pLine,pParent) );
  2633. //
  2634. // Translate the mux control so that it will appear in this line.
  2635. //
  2636. if (kmxlTranslateNodeToControl(
  2637. pmxobj,
  2638. pMux,
  2639. &pControl
  2640. ) ) {
  2641. pControl->Parameters.bPlaceholder = TRUE;
  2642. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2643. }
  2644. //
  2645. // Now start searching up from the parent.
  2646. //
  2647. pNode = pParent;
  2648. while( pNode ) {
  2649. //
  2650. // Translate the control.
  2651. //
  2652. if (kmxlTranslateNodeToControl(
  2653. pmxobj,
  2654. pNode,
  2655. &pControl
  2656. ) ) {
  2657. kmxlAppendListToList( (PSLIST*) &pLine->Controls, (PSLIST) pControl );
  2658. }
  2659. //
  2660. // If we found a node with multiple parents, then this will be the
  2661. // "pin" for this line.
  2662. //
  2663. if( ( kmxlParentListLength( pNode ) > 1 ) ||
  2664. ( pNode->Type == SOURCE ) ||
  2665. ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) ||
  2666. ( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUM ) ) ) {
  2667. //
  2668. // Check to see if this node has already been used in a virtual
  2669. // line.
  2670. //
  2671. pTemp = kmxlFirstInList( *plistLines );
  2672. while( pTemp ) {
  2673. if( pTemp->SourceId == ( 0x8000 + pNode->Id ) ) {
  2674. while( pLine->Controls ) {
  2675. pControl = kmxlRemoveFirstControl( pLine->Controls );
  2676. kmxlFreeControl( pControl );
  2677. }
  2678. AudioFreeMemory_Unknown( &pLine );
  2679. RETURN( STATUS_SUCCESS );
  2680. }
  2681. pTemp = kmxlNextLine( pTemp );
  2682. }
  2683. //
  2684. // Set up the pin. The name will be the name of the node.
  2685. //
  2686. pLine->SourceId = 0x8000 + pNode->Id;
  2687. Control.NodeType = &pNode->NodeType;
  2688. kmxlGetNodeName( pmxobj->pfo, pNode->Id, &Control );
  2689. RtlCopyMemory(
  2690. pLine->Line.szShortName,
  2691. Control.Control.szShortName,
  2692. min(
  2693. sizeof( pLine->Line.szShortName ),
  2694. sizeof( Control.Control.szShortName )
  2695. )
  2696. );
  2697. RtlCopyMemory(
  2698. pLine->Line.szName,
  2699. Control.Control.szName,
  2700. min(
  2701. sizeof( pLine->Line.szName ),
  2702. sizeof( Control.Control.szName )
  2703. )
  2704. );
  2705. break;
  2706. }
  2707. pNode = (kmxlFirstParentNode( pNode ))->pNode;
  2708. }
  2709. //
  2710. // By making this line type of "SUM" (which technically it is), it
  2711. // will guarantee that this line gets a target type of UNDEFINED.
  2712. //
  2713. pLine->Type = KSNODETYPE_SUM;
  2714. pLine->Communication = KSPIN_COMMUNICATION_NONE;
  2715. pLine->Line.cbStruct = sizeof( MIXERLINE );
  2716. pLine->Line.dwSource = (DWORD) -1;
  2717. pLine->Line.dwDestination = (DWORD) -1;
  2718. pLine->Line.fdwLine = MIXERLINE_LINEF_SOURCE |
  2719. MIXERLINE_LINEF_ACTIVE;
  2720. kmxlAddToEndOfList( plistLines, pLine );
  2721. pLine->DestId = kmxlFindDestinationForNode(
  2722. pmxobj, pMux, pMux, pLine, plistLines
  2723. );
  2724. RETURN( STATUS_SUCCESS );
  2725. }
  2726. ///////////////////////////////////////////////////////////////////////
  2727. //
  2728. // kmxlTranslateNodeToControl
  2729. //
  2730. //
  2731. // Translates a NodeType GUID into a mixer line control. The memory
  2732. // for the control(s) is allocated and as much information about the
  2733. // control is filled in.
  2734. //
  2735. // NOTES
  2736. // This function returns the number of controls added to the ppControl
  2737. // array.
  2738. //
  2739. // Returns the number of controls actually created.
  2740. //
  2741. //
  2742. ULONG
  2743. kmxlTranslateNodeToControl(
  2744. IN PMIXEROBJECT pmxobj,
  2745. IN PMXLNODE pNode, // The node to translate into a control
  2746. OUT PMXLCONTROL* ppControl // The control to fill in
  2747. )
  2748. {
  2749. PMXLCONTROL pControl;
  2750. NTSTATUS Status = STATUS_SUCCESS;
  2751. ASSERT( pmxobj );
  2752. ASSERT( pNode );
  2753. ASSERT( ppControl );
  2754. PAGED_CODE();
  2755. //
  2756. // Bug fix. The caller might not clear this. This needs to be NULL do
  2757. // the caller doesn't think controls were created when the function
  2758. // fails.
  2759. //
  2760. *ppControl = NULL;
  2761. //
  2762. // If the node is NULL, there's nothing to do.
  2763. //
  2764. if( pNode == NULL ) {
  2765. *ppControl = NULL;
  2766. return( 0 );
  2767. }
  2768. DPF(DL_TRACE|FA_MIXER,( "Translating %d(0x%x) ( %s ) NODE:%08x",
  2769. pNode->Id,pNode->Id,
  2770. NodeTypeToString( &pNode->NodeType ),
  2771. pNode ) );
  2772. ///////////////////////////////////////////////////////////////////
  2773. if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_AGC ) ) {
  2774. ///////////////////////////////////////////////////////////////////
  2775. //
  2776. // AGC is represented by an ONOFF control.
  2777. //
  2778. // AGC is a UNIFORM (mono) control.
  2779. //
  2780. ///////////////////////////////////////////////////////////////////
  2781. //
  2782. // Check to see if the node properly supports AGC.
  2783. //
  2784. Status = kmxlSupportsControl( pmxobj->pfo, pNode->Id, KSPROPERTY_AUDIO_AGC );
  2785. if (!NT_SUCCESS(Status)) {
  2786. DPF(DL_TRACE|FA_MIXER,( "AGC node fails property!" ) );
  2787. goto exit;
  2788. }
  2789. //
  2790. // Allocate the new control structure.
  2791. //
  2792. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2793. if( *ppControl == NULL ) {
  2794. goto exit;
  2795. }
  2796. //
  2797. // Fill in as much information as possible.
  2798. //
  2799. (*ppControl)->NodeType = &KSNODETYPE_AGC;
  2800. (*ppControl)->Id = pNode->Id;
  2801. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_AGC;
  2802. (*ppControl)->bScaled = FALSE;
  2803. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  2804. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
  2805. (*ppControl)->Control.cMultipleItems = 0;
  2806. (*ppControl)->Control.Bounds.dwMinimum = 0;
  2807. (*ppControl)->Control.Bounds.dwMaximum = 1;
  2808. (*ppControl)->Control.Metrics.cSteps = 0;
  2809. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
  2810. if (!NT_SUCCESS(Status))
  2811. {
  2812. kmxlFreeControl( *ppControl );
  2813. *ppControl = NULL;
  2814. goto exit;
  2815. } else {
  2816. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  2817. ASSERT( IsValidControl( *ppControl ) );
  2818. }
  2819. ///////////////////////////////////////////////////////////////////
  2820. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_LOUDNESS ) ) {
  2821. ///////////////////////////////////////////////////////////////////
  2822. //
  2823. // LOUNDNESS is represented by an ONOFF-type control.
  2824. //
  2825. // LOUDNESS is a UNIFORM (mono) control.
  2826. //
  2827. ///////////////////////////////////////////////////////////////////
  2828. //
  2829. // Check to see if the node properly supports LOUDNESS.
  2830. //
  2831. Status = kmxlSupportsControl( pmxobj->pfo, pNode->Id, KSPROPERTY_AUDIO_LOUDNESS );
  2832. if (!NT_SUCCESS(Status)) {
  2833. DPF(DL_TRACE|FA_MIXER,( "Loudness node fails property!" ) );
  2834. goto exit;
  2835. }
  2836. //
  2837. // Allocate the new control structure
  2838. //
  2839. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2840. if( *ppControl == NULL ) {
  2841. goto exit;
  2842. }
  2843. //
  2844. // Fill in as much information as possible.
  2845. //
  2846. (*ppControl)->NodeType = &KSNODETYPE_LOUDNESS;
  2847. (*ppControl)->Id = pNode->Id;
  2848. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_LOUDNESS;
  2849. (*ppControl)->bScaled = FALSE;
  2850. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  2851. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_LOUDNESS;
  2852. (*ppControl)->Control.cMultipleItems = 0;
  2853. (*ppControl)->Control.Bounds.dwMinimum = 0;
  2854. (*ppControl)->Control.Bounds.dwMaximum = 1;
  2855. (*ppControl)->Control.Metrics.cSteps = 0;
  2856. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
  2857. if (!NT_SUCCESS(Status))
  2858. {
  2859. kmxlFreeControl( *ppControl );
  2860. *ppControl = NULL;
  2861. goto exit;
  2862. } else {
  2863. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  2864. ASSERT( IsValidControl( *ppControl ) );
  2865. }
  2866. ///////////////////////////////////////////////////////////////////
  2867. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUTE ) ) {
  2868. ///////////////////////////////////////////////////////////////////
  2869. //
  2870. // MUTE is represented by an ONOFF-type control.
  2871. //
  2872. // MUTE is a UNIFORM (mono) control.
  2873. //
  2874. ///////////////////////////////////////////////////////////////////
  2875. //
  2876. // Check to see if the node properly supports MUTE.
  2877. //
  2878. Status = kmxlSupportsControl(
  2879. pmxobj->pfo,
  2880. pNode->Id,
  2881. KSPROPERTY_AUDIO_MUTE );
  2882. if (!NT_SUCCESS(Status)) {
  2883. DPF(DL_TRACE|FA_MIXER,( "Mute node fails property!" ) );
  2884. goto exit;
  2885. }
  2886. //
  2887. // Allocate the new control structure
  2888. //
  2889. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2890. if( *ppControl == NULL ) {
  2891. goto exit;
  2892. }
  2893. //
  2894. // Fill in as much information as possible.
  2895. //
  2896. (*ppControl)->NodeType = &KSNODETYPE_MUTE;
  2897. (*ppControl)->Id = pNode->Id;
  2898. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_MUTE;
  2899. (*ppControl)->bScaled = FALSE;
  2900. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  2901. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
  2902. (*ppControl)->Control.cMultipleItems = 0;
  2903. (*ppControl)->Control.Bounds.dwMinimum = 0;
  2904. (*ppControl)->Control.Bounds.dwMaximum = 1;
  2905. (*ppControl)->Control.Metrics.cSteps = 0;
  2906. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl );
  2907. if (!NT_SUCCESS(Status))
  2908. {
  2909. kmxlFreeControl( *ppControl );
  2910. *ppControl = NULL;
  2911. goto exit;
  2912. } else {
  2913. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  2914. ASSERT( IsValidControl( *ppControl ) );
  2915. }
  2916. ///////////////////////////////////////////////////////////////////
  2917. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_TONE ) ) {
  2918. ///////////////////////////////////////////////////////////////////
  2919. //
  2920. // A TONE node can represent up to 3 controls:
  2921. // Treble: A fader control
  2922. // Bass: A fader control
  2923. // Bass Boost: A OnOff control
  2924. //
  2925. // Both Treble and Bass are UNIFORM (mono) controls.
  2926. //
  2927. // To determine which control(s) the TONE node represents, a helper
  2928. // function is called to query the particular property. If the
  2929. // helper function succeeds, a control is created for that property.
  2930. //
  2931. ///////////////////////////////////////////////////////////////////
  2932. Status = kmxlSupportsControl( pmxobj->pfo,
  2933. pNode->Id,
  2934. KSPROPERTY_AUDIO_BASS_BOOST );
  2935. if (NT_SUCCESS(Status)) {
  2936. //
  2937. // Bass boost control is supported. Allocate a new structure.
  2938. //
  2939. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2940. if( pControl == NULL ) {
  2941. goto exit;
  2942. }
  2943. //
  2944. // Fill in as much information as possible.
  2945. //
  2946. pControl->NodeType = &KSNODETYPE_TONE;
  2947. pControl->Id = pNode->Id;
  2948. pControl->PropertyId = KSPROPERTY_AUDIO_BASS_BOOST;
  2949. pControl->bScaled = FALSE;
  2950. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  2951. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
  2952. pControl->Control.cMultipleItems = 0;
  2953. pControl->Control.Bounds.dwMinimum = 0;
  2954. pControl->Control.Bounds.dwMaximum = 1;
  2955. pControl->Control.Metrics.cSteps = 0;
  2956. Status = kmxlGetControlChannels( pmxobj->pfo, pControl );
  2957. if (!NT_SUCCESS(Status))
  2958. {
  2959. kmxlFreeControl( pControl );
  2960. pControl = NULL;
  2961. goto exit;
  2962. }
  2963. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  2964. ASSERT( IsValidControl( pControl ) );
  2965. //
  2966. // Add this new control to the list.
  2967. //
  2968. kmxlAddToList( *ppControl, pControl );
  2969. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  2970. if( pControl ) {
  2971. RtlCopyMemory( pControl, *ppControl, sizeof( MXLCONTROL ) );
  2972. //
  2973. // Copy the channel steppings from the original control
  2974. //
  2975. //
  2976. // Sense we copied the control above, we might have gotten
  2977. // a pChannelStepping pointer in the copy. We'll NULL that out
  2978. // for the memory allocation.
  2979. //
  2980. pControl->pChannelStepping = NULL;
  2981. ASSERT(pControl->NumChannels > 0);
  2982. Status = AudioAllocateMemory_Paged(pControl->NumChannels * sizeof( CHANNEL_STEPPING ),
  2983. TAG_AuDC_CHANNEL,
  2984. DEFAULT_MEMORY,
  2985. &pControl->pChannelStepping );
  2986. if( !NT_SUCCESS( Status ) ) {
  2987. pControl->NumChannels = 0;
  2988. kmxlFreeControl( pControl );
  2989. pControl = NULL;
  2990. goto exit;
  2991. }
  2992. RtlCopyMemory( pControl->pChannelStepping,
  2993. (*ppControl)->pChannelStepping,
  2994. pControl->NumChannels * sizeof( CHANNEL_STEPPING ) );
  2995. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_BASS_BOOST;
  2996. kmxlAddToList( *ppControl, pControl );
  2997. ASSERT( IsValidControl( pControl ) );
  2998. }
  2999. }
  3000. Status = kmxlSupportsBassControl( pmxobj->pfo, pNode->Id );
  3001. if (NT_SUCCESS(Status)) {
  3002. //
  3003. // Bass control is supported. Allocate a new structure.
  3004. //
  3005. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3006. if( pControl == NULL ) {
  3007. goto exit;
  3008. }
  3009. //
  3010. // Fill in as much information as possible.
  3011. //
  3012. pControl->NodeType = &KSNODETYPE_TONE;
  3013. pControl->Id = pNode->Id;
  3014. pControl->PropertyId = KSPROPERTY_AUDIO_BASS;
  3015. pControl->bScaled = TRUE;
  3016. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3017. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_BASS;
  3018. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3019. pControl->Control.cMultipleItems = 0;
  3020. pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3021. pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3022. pControl->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3023. Status = kmxlGetControlRange( pmxobj->pfo, pControl );
  3024. if (!NT_SUCCESS(Status))
  3025. {
  3026. kmxlFreeControl( pControl );
  3027. pControl = NULL;
  3028. goto exit;
  3029. } else {
  3030. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  3031. //
  3032. // Add this new control to the list.
  3033. //
  3034. ASSERT( IsValidControl( pControl ) );
  3035. kmxlAddToList( *ppControl, pControl );
  3036. }
  3037. }
  3038. Status = kmxlSupportsTrebleControl( pmxobj->pfo, pNode->Id );
  3039. if (NT_SUCCESS(Status)) {
  3040. //
  3041. // Treble is supported. Allocate a new control structure.
  3042. //
  3043. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3044. if( pControl == NULL ) {
  3045. goto exit;
  3046. }
  3047. //
  3048. // Fill in as much information as possible.
  3049. //
  3050. pControl->NodeType = &KSNODETYPE_TONE;
  3051. pControl->Id = pNode->Id;
  3052. pControl->PropertyId = KSPROPERTY_AUDIO_TREBLE;
  3053. pControl->bScaled = TRUE;
  3054. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3055. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_TREBLE;
  3056. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3057. pControl->Control.cMultipleItems = 0;
  3058. pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3059. pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3060. pControl->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3061. Status = kmxlGetControlRange( pmxobj->pfo, pControl );
  3062. if (!NT_SUCCESS(Status))
  3063. {
  3064. kmxlFreeControl( pControl );
  3065. pControl = NULL;
  3066. goto exit;
  3067. } else {
  3068. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  3069. //
  3070. // Add this new control to the list.
  3071. //
  3072. ASSERT( IsValidControl( pControl ) );
  3073. kmxlAddToList( *ppControl, pControl );
  3074. }
  3075. }
  3076. ///////////////////////////////////////////////////////////////////
  3077. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_VOLUME ) ) {
  3078. ///////////////////////////////////////////////////////////////////
  3079. //
  3080. // A VOLUME is a fader-type control
  3081. //
  3082. // To determine if a node supports volume changes
  3083. //
  3084. ///////////////////////////////////////////////////////////////////
  3085. //
  3086. // Check to see if the node properly supports volume
  3087. //
  3088. Status = kmxlSupportsControl(
  3089. pmxobj->pfo,
  3090. pNode->Id,
  3091. KSPROPERTY_AUDIO_VOLUMELEVEL
  3092. );
  3093. if (!NT_SUCCESS(Status)) {
  3094. DPF(DL_TRACE|FA_MIXER,( "Volume node fails property!" ) );
  3095. goto exit;
  3096. }
  3097. //
  3098. // Allocate the new control structure
  3099. //
  3100. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3101. if( *ppControl == NULL ) {
  3102. goto exit;
  3103. }
  3104. //
  3105. // Fill in as much information as possible.
  3106. //
  3107. (*ppControl)->NodeType = &KSNODETYPE_VOLUME;
  3108. (*ppControl)->Id = pNode->Id;
  3109. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_VOLUMELEVEL;
  3110. (*ppControl)->bScaled = TRUE;
  3111. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3112. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  3113. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3114. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3115. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3116. (*ppControl)->Control.cMultipleItems = 0;
  3117. Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
  3118. if (!NT_SUCCESS(Status))
  3119. {
  3120. kmxlFreeControl( *ppControl );
  3121. *ppControl = NULL;
  3122. goto exit;
  3123. }
  3124. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3125. ASSERT( IsValidControl( *ppControl ) );
  3126. ///////////////////////////////////////////////////////////////////
  3127. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_PEAKMETER ) ) {
  3128. ///////////////////////////////////////////////////////////////////
  3129. //
  3130. // To determine if a node supports peak meter properties
  3131. //
  3132. ///////////////////////////////////////////////////////////////////
  3133. //
  3134. // Check to see if the node properly supports peakmeter
  3135. //
  3136. Status = kmxlSupportsControl(
  3137. pmxobj->pfo,
  3138. pNode->Id,
  3139. KSPROPERTY_AUDIO_PEAKMETER
  3140. );
  3141. if (!NT_SUCCESS(Status)) {
  3142. DPF(DL_TRACE|FA_MIXER,( "Peakmeter node fails property!" ) );
  3143. goto exit;
  3144. }
  3145. //
  3146. // Allocate the new control structure
  3147. //
  3148. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3149. if( *ppControl == NULL ) {
  3150. goto exit;
  3151. }
  3152. //
  3153. // Fill in as much information as possible.
  3154. //
  3155. (*ppControl)->NodeType = &KSNODETYPE_PEAKMETER;
  3156. (*ppControl)->Id = pNode->Id;
  3157. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_PEAKMETER;
  3158. (*ppControl)->bScaled = FALSE;
  3159. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3160. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER;
  3161. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3162. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3163. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3164. (*ppControl)->Control.cMultipleItems = 0;
  3165. Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
  3166. if (!NT_SUCCESS(Status))
  3167. {
  3168. kmxlFreeControl( *ppControl );
  3169. *ppControl = NULL;
  3170. goto exit;
  3171. }
  3172. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3173. ASSERT( IsValidControl( *ppControl ) );
  3174. ///////////////////////////////////////////////////////////////////
  3175. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_MUX ) ) {
  3176. ///////////////////////////////////////////////////////////////////
  3177. //
  3178. // A MUX is a single select type control.
  3179. //
  3180. ///////////////////////////////////////////////////////////////////
  3181. {
  3182. ULONG Line;
  3183. //
  3184. // Do a quick check and see if the mux responds properly.
  3185. // If not, just get out of here quick.
  3186. //
  3187. if( !NT_SUCCESS( kmxlGetNodeProperty(
  3188. pmxobj->pfo,
  3189. &KSPROPSETID_Audio,
  3190. KSPROPERTY_AUDIO_MUX_SOURCE,
  3191. pNode->Id,
  3192. 0,
  3193. NULL,
  3194. &Line,
  3195. sizeof( Line ) ) ) )
  3196. {
  3197. goto exit;
  3198. }
  3199. //
  3200. // Look to see if a control has already been generated for this
  3201. // node. If so, the control information can be used from it
  3202. // instead of creating a new one.
  3203. //
  3204. pControl = kmxlFirstInList( pmxobj->listMuxControls );
  3205. while( pControl ) {
  3206. ASSERT( IsValidControl( pControl ) );
  3207. if( pControl->Id == pNode->Id ) {
  3208. break;
  3209. }
  3210. pControl = kmxlNextControl( pControl );
  3211. }
  3212. //
  3213. // Allocate the new control structure
  3214. //
  3215. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3216. if( *ppControl == NULL ) {
  3217. goto exit;
  3218. }
  3219. if( pControl == NULL ) {
  3220. //
  3221. // This node has not been seen before. Fill in as much info as
  3222. // possible.
  3223. //
  3224. (*ppControl)->NodeType = &KSNODETYPE_MUX;
  3225. (*ppControl)->Id = pNode->Id;
  3226. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_MUX_SOURCE;
  3227. (*ppControl)->bScaled = FALSE;
  3228. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3229. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
  3230. (*ppControl)->Control.cMultipleItems = kmxlGetNumMuxLines(
  3231. pmxobj->pTopology,
  3232. pNode->Id
  3233. );
  3234. (*ppControl)->Control.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE |
  3235. MIXERCONTROL_CONTROLF_UNIFORM;
  3236. (*ppControl)->Control.Bounds.dwMinimum = 0;
  3237. (*ppControl)->Control.Bounds.dwMaximum = (*ppControl)->Control.cMultipleItems - 1;
  3238. (*ppControl)->Control.Metrics.cSteps = (*ppControl)->Control.cMultipleItems;
  3239. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3240. kmxlGetMuxLineNames( pmxobj, *ppControl );
  3241. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3242. if( pControl == NULL ) {
  3243. kmxlFreeControl( *ppControl );
  3244. *ppControl = NULL;
  3245. goto exit;
  3246. }
  3247. //
  3248. // Make a copy of this control for the mux list
  3249. //
  3250. (*ppControl)->Control.dwControlID = pmxobj->dwControlId++;
  3251. RtlCopyMemory( pControl, *ppControl, sizeof( MXLCONTROL ) );
  3252. ASSERT( IsValidControl( pControl ) );
  3253. pControl->Parameters.bHasCopy = TRUE;
  3254. (*ppControl)->Parameters.bHasCopy = FALSE;
  3255. kmxlAddToList( pmxobj->listMuxControls, pControl );
  3256. } else {
  3257. RtlCopyMemory( *ppControl, pControl, sizeof( MXLCONTROL ) );
  3258. ASSERT( IsValidControl( *ppControl ) );
  3259. (*ppControl)->Parameters.bHasCopy = TRUE;
  3260. (*ppControl)->List.Next = NULL;
  3261. }
  3262. }
  3263. #ifdef STEREO_ENHANCE
  3264. ///////////////////////////////////////////////////////////////////
  3265. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_STEREO_WIDE ) ) {
  3266. ///////////////////////////////////////////////////////////////////
  3267. //
  3268. // Stereo Enhance is a boolean control.
  3269. //
  3270. ///////////////////////////////////////////////////////////////////
  3271. //
  3272. // Check to see if the node properly supports stereo wide
  3273. //
  3274. Status = kmxlSupportsControl(
  3275. pfoInstance,
  3276. pNode->Id,
  3277. KSPROPERTY_AUDIO_WIDE_MODE
  3278. );
  3279. if (!NT_SUCCESS(Status)) {
  3280. DPF(DL_TRACE|FA_MIXER,( "Stereo Wide node fails property!" ) );
  3281. goto exit;
  3282. }
  3283. //
  3284. // Allocate the new control structure
  3285. //
  3286. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3287. if( *ppControl == NULL ) {
  3288. goto exit;
  3289. }
  3290. //
  3291. // Fill in as much information as possible.
  3292. //
  3293. (*ppControl)->NodeType = &KSNODETYPE_STEREO_ENHANCE;
  3294. (*ppControl)->Id = pNode->Id;
  3295. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_WIDE_MODE;
  3296. (*ppControl)->bScaled = FALSE;
  3297. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3298. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_STEREOENH;
  3299. (*ppControl)->Control.cMultipleItems = 0;
  3300. (*ppControl)->Control.Bounds.dwMinimum = 0;
  3301. (*ppControl)->Control.Bounds.dwMaximum = 1;
  3302. (*ppControl)->Control.Metrics.cSteps = 0;
  3303. Status = kmxlGetControlChannels( pfoInstance, *ppControl );
  3304. if (!NT_SUCCESS(Status))
  3305. {
  3306. kmxlFreeControl( *ppControl );
  3307. *ppControl = NULL;
  3308. goto exit;
  3309. }
  3310. kmxlGetNodeName( pfoInstance, pNode->Id, (*ppControl));
  3311. #endif
  3312. ///////////////////////////////////////////////////////////////////
  3313. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_STEREO_WIDE ) ) {
  3314. ///////////////////////////////////////////////////////////////////
  3315. //
  3316. // Check to see if the node properly supports stereo wide
  3317. //
  3318. Status = kmxlSupportsControl(
  3319. pmxobj->pfo,
  3320. pNode->Id,
  3321. KSPROPERTY_AUDIO_WIDENESS
  3322. );
  3323. if (!NT_SUCCESS(Status)) {
  3324. DPF(DL_TRACE|FA_MIXER,( "Stereo wide node fails property!" ) );
  3325. goto exit;
  3326. }
  3327. //
  3328. // Allocate the new control structure
  3329. //
  3330. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3331. if( *ppControl == NULL ) {
  3332. goto exit;
  3333. }
  3334. //
  3335. // Fill in as much information as possible.
  3336. //
  3337. (*ppControl)->NodeType = &KSNODETYPE_STEREO_WIDE;
  3338. (*ppControl)->Id = pNode->Id;
  3339. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_WIDENESS;
  3340. (*ppControl)->bScaled = FALSE;
  3341. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3342. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
  3343. (*ppControl)->Control.cMultipleItems = 0;
  3344. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3345. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3346. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3347. Status = kmxlGetControlRange( pmxobj->pfo, (*ppControl) );
  3348. if (!NT_SUCCESS(Status))
  3349. {
  3350. kmxlFreeControl( *ppControl );
  3351. *ppControl = NULL;
  3352. goto exit;
  3353. }
  3354. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3355. ASSERT( IsValidControl( *ppControl ) );
  3356. ///////////////////////////////////////////////////////////////////
  3357. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_CHORUS ) ) {
  3358. ///////////////////////////////////////////////////////////////////
  3359. //
  3360. // Check to see if the node properly supports chorus
  3361. //
  3362. Status = kmxlSupportsControl(
  3363. pmxobj->pfo,
  3364. pNode->Id,
  3365. KSPROPERTY_AUDIO_CHORUS_LEVEL
  3366. );
  3367. if (!NT_SUCCESS(Status)) {
  3368. DPF(DL_TRACE|FA_MIXER,( "Chorus node fails property!" ) );
  3369. goto exit;
  3370. }
  3371. //
  3372. // Allocate the new control structure
  3373. //
  3374. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3375. if( *ppControl == NULL ) {
  3376. goto exit;
  3377. }
  3378. //
  3379. // Fill in as much information as possible.
  3380. //
  3381. (*ppControl)->NodeType = &KSNODETYPE_CHORUS;
  3382. (*ppControl)->Id = pNode->Id;
  3383. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_CHORUS_LEVEL;
  3384. (*ppControl)->bScaled = FALSE;
  3385. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3386. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
  3387. (*ppControl)->Control.cMultipleItems = 0;
  3388. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3389. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3390. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3391. // (*ppControl)->Control.Metrics.cSteps = 0xFFFF;
  3392. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl ); // Should we also get the range?
  3393. if (!NT_SUCCESS(Status))
  3394. {
  3395. kmxlFreeControl( *ppControl );
  3396. *ppControl = NULL;
  3397. goto exit;
  3398. } else {
  3399. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3400. ASSERT( IsValidControl( *ppControl ) );
  3401. }
  3402. ///////////////////////////////////////////////////////////////////
  3403. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_REVERB ) ) {
  3404. ///////////////////////////////////////////////////////////////////
  3405. //
  3406. // Check to see if the node properly supports reverb
  3407. //
  3408. Status = kmxlSupportsControl(
  3409. pmxobj->pfo,
  3410. pNode->Id,
  3411. KSPROPERTY_AUDIO_REVERB_LEVEL
  3412. );
  3413. if (!NT_SUCCESS(Status)) {
  3414. DPF(DL_TRACE|FA_MIXER,( "Reverb node fails property!" ) );
  3415. goto exit;
  3416. }
  3417. //
  3418. // Allocate the new control structure
  3419. //
  3420. *ppControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3421. if( *ppControl == NULL ) {
  3422. goto exit;
  3423. }
  3424. //
  3425. // Fill in as much information as possible.
  3426. //
  3427. (*ppControl)->NodeType = &KSNODETYPE_REVERB;
  3428. (*ppControl)->Id = pNode->Id;
  3429. (*ppControl)->PropertyId = KSPROPERTY_AUDIO_REVERB_LEVEL;
  3430. (*ppControl)->bScaled = FALSE;
  3431. (*ppControl)->Control.cbStruct = sizeof( MIXERCONTROL );
  3432. (*ppControl)->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_FADER;
  3433. (*ppControl)->Control.cMultipleItems = 0;
  3434. (*ppControl)->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3435. (*ppControl)->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3436. (*ppControl)->Control.Metrics.cSteps = DEFAULT_STATICMETRICS_CSTEPS;
  3437. // (*ppControl)->Control.Metrics.cSteps = 0xFFFF;
  3438. Status = kmxlGetControlChannels( pmxobj->pfo, *ppControl ); // Should we also get the range?
  3439. if (!NT_SUCCESS(Status))
  3440. {
  3441. kmxlFreeControl( *ppControl );
  3442. *ppControl = NULL;
  3443. goto exit;
  3444. } else {
  3445. kmxlGetNodeName( pmxobj->pfo, pNode->Id, (*ppControl));
  3446. ASSERT( IsValidControl( *ppControl ) );
  3447. }
  3448. ///////////////////////////////////////////////////////////////////
  3449. } else if( IsEqualGUID( &pNode->NodeType, &KSNODETYPE_SUPERMIX ) ) {
  3450. ///////////////////////////////////////////////////////////////////
  3451. //
  3452. // SuperMix nodes can be supported as MUTE controls if the MUTE
  3453. // property is supported.
  3454. //
  3455. ///////////////////////////////////////////////////////////////////
  3456. PKSAUDIO_MIXCAP_TABLE pMixCaps;
  3457. PLONG pReferenceCount = NULL;
  3458. ULONG i,
  3459. Size;
  3460. BOOL bMutable;
  3461. BOOL bVolume = FALSE;
  3462. PKSAUDIO_MIXLEVEL pMixLevels = NULL;
  3463. #ifdef SUPERMIX_AS_VOL
  3464. ULONG Channels;
  3465. #endif
  3466. if( !NT_SUCCESS( kmxlGetSuperMixCaps( pmxobj->pfo, pNode->Id, &pMixCaps ) ) ) {
  3467. goto exit;
  3468. }
  3469. Status = AudioAllocateMemory_Paged(sizeof( LONG ),
  3470. TAG_AudS_SUPERMIX,
  3471. ZERO_FILL_MEMORY,
  3472. &pReferenceCount );
  3473. if( !NT_SUCCESS( Status ) ) {
  3474. AudioFreeMemory_Unknown( &pMixCaps );
  3475. *ppControl = NULL;
  3476. goto exit;
  3477. }
  3478. *pReferenceCount=0;
  3479. Size = pMixCaps->InputChannels * pMixCaps->OutputChannels;
  3480. Status = AudioAllocateMemory_Paged(Size * sizeof( KSAUDIO_MIXLEVEL ),
  3481. TAG_Audl_MIXLEVEL,
  3482. ZERO_FILL_MEMORY,
  3483. &pMixLevels );
  3484. if( !NT_SUCCESS( Status ) ) {
  3485. AudioFreeMemory_Unknown( &pMixCaps );
  3486. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3487. *ppControl = NULL;
  3488. goto exit;
  3489. }
  3490. Status = kmxlGetNodeProperty(
  3491. pmxobj->pfo,
  3492. &KSPROPSETID_Audio,
  3493. KSPROPERTY_AUDIO_MIX_LEVEL_TABLE,
  3494. pNode->Id,
  3495. 0,
  3496. NULL,
  3497. pMixLevels,
  3498. Size * sizeof( KSAUDIO_MIXLEVEL )
  3499. );
  3500. if( !NT_SUCCESS( Status ) ) {
  3501. AudioFreeMemory_Unknown( &pMixCaps );
  3502. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3503. AudioFreeMemory_Unknown( &pMixLevels );
  3504. DPF(DL_WARNING|FA_MIXER,("kmxlGetNodeProperty failed Status=%X",Status) );
  3505. *ppControl = NULL;
  3506. goto exit;
  3507. }
  3508. bMutable = TRUE;
  3509. for( i = 0; i < Size; i++ ) {
  3510. //
  3511. // If the channel is mutable, then all is well for this entry.
  3512. //
  3513. if( pMixCaps->Capabilities[ i ].Mute ) {
  3514. continue;
  3515. }
  3516. //
  3517. // The the entry is not mutable but is fully attenuated,
  3518. // this will work too.
  3519. //
  3520. if( ( pMixCaps->Capabilities[ i ].Minimum == LONG_MIN ) &&
  3521. ( pMixCaps->Capabilities[ i ].Maximum == LONG_MIN ) &&
  3522. ( pMixCaps->Capabilities[ i ].Reset == LONG_MIN ) )
  3523. {
  3524. continue;
  3525. }
  3526. bMutable = FALSE;
  3527. break;
  3528. }
  3529. #ifdef SUPERMIX_AS_VOL
  3530. bVolume = TRUE;
  3531. Channels = 0;
  3532. for( i = 0; i < Size; i += pMixCaps->OutputChannels + 1 ) {
  3533. if( ( pMixCaps->Capabilities[ i ].Maximum -
  3534. pMixCaps->Capabilities[ i ].Minimum ) > 0 )
  3535. {
  3536. ++Channels;
  3537. continue;
  3538. }
  3539. bVolume = FALSE;
  3540. break;
  3541. }
  3542. #endif
  3543. //
  3544. // This node cannot be used as a MUTE control.
  3545. //
  3546. if( !bMutable && !bVolume ) {
  3547. AudioFreeMemory_Unknown( &pMixCaps );
  3548. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3549. AudioFreeMemory_Unknown( &pMixLevels );
  3550. *ppControl = NULL;
  3551. goto exit;
  3552. }
  3553. if( bMutable ) {
  3554. //
  3555. // The Supermix is verifiably usable as a MUTE. Fill in all the
  3556. // details.
  3557. //
  3558. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3559. if( pControl != NULL ) {
  3560. pControl->NodeType = &KSNODETYPE_SUPERMIX;
  3561. pControl->Id = pNode->Id;
  3562. pControl->PropertyId = KSPROPERTY_AUDIO_MIX_LEVEL_TABLE;
  3563. pControl->bScaled = FALSE;
  3564. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3565. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
  3566. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3567. pControl->Control.cMultipleItems = 0;
  3568. pControl->Control.Bounds.dwMinimum = 0;
  3569. pControl->Control.Bounds.dwMaximum = 1;
  3570. pControl->Control.Metrics.cSteps = 0;
  3571. InterlockedIncrement(pReferenceCount);
  3572. pControl->Parameters.pReferenceCount = pReferenceCount;
  3573. pControl->Parameters.Size = pMixCaps->InputChannels *
  3574. pMixCaps->OutputChannels;
  3575. pControl->Parameters.pMixCaps = pMixCaps;
  3576. pControl->Parameters.pMixLevels = pMixLevels;
  3577. Status = AudioAllocateMemory_Paged(sizeof( CHANNEL_STEPPING ),
  3578. TAG_AuDE_CHANNEL,
  3579. ZERO_FILL_MEMORY,
  3580. &pControl->pChannelStepping );
  3581. if( !NT_SUCCESS( Status ) ) {
  3582. AudioFreeMemory_Unknown( &pMixCaps );
  3583. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3584. AudioFreeMemory_Unknown( &pMixLevels );
  3585. *ppControl = NULL;
  3586. goto exit;
  3587. }
  3588. pControl->NumChannels = 1;
  3589. pControl->pChannelStepping->MinValue = pMixCaps->Capabilities[ 0 ].Minimum;
  3590. pControl->pChannelStepping->MaxValue = pMixCaps->Capabilities[ 0 ].Maximum;
  3591. pControl->pChannelStepping->Steps = 32;
  3592. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl);
  3593. kmxlAddToList( *ppControl, pControl );
  3594. ASSERT( IsValidControl( pControl ) );
  3595. }
  3596. }
  3597. #ifdef SUPERMIX_AS_VOL
  3598. if( bVolume ) {
  3599. pControl = kmxlAllocateControl( TAG_AudC_CONTROL );
  3600. if( pControl != NULL ) {
  3601. pControl->NodeType = &KSNODETYPE_SUPERMIX;
  3602. pControl->Id = pNode->Id;
  3603. pControl->PropertyId = KSPROPERTY_AUDIO_MIX_LEVEL_TABLE;
  3604. pControl->bScaled = TRUE;
  3605. pControl->Control.cbStruct = sizeof( MIXERCONTROL );
  3606. pControl->Control.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  3607. pControl->Control.cMultipleItems = 0;
  3608. pControl->Control.Bounds.dwMinimum = DEFAULT_STATICBOUNDS_MIN;
  3609. pControl->Control.Bounds.dwMaximum = DEFAULT_STATICBOUNDS_MAX;
  3610. pControl->Control.Metrics.cSteps = 32;
  3611. InterlockedIncrement(pReferenceCount);
  3612. pControl->Parameters.pReferenceCount = pReferenceCount;
  3613. pControl->Parameters.Size = pMixCaps->InputChannels *
  3614. pMixCaps->OutputChannels;
  3615. pControl->Parameters.pMixCaps = pMixCaps;
  3616. pControl->Parameters.pMixLevels = pMixLevels;
  3617. if( Channels == 1 ) {
  3618. pControl->Control.fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
  3619. } else {
  3620. pControl->Control.fdwControl = 0;
  3621. }
  3622. kmxlGetNodeName( pmxobj->pfo, pNode->Id, pControl );
  3623. kmxlAddToList( *ppControl, pControl );
  3624. ASSERT( IsValidControl( pControl ) );
  3625. }
  3626. }
  3627. #endif // SUPERMIX_AS_VOL
  3628. if( *ppControl == NULL ) {
  3629. AudioFreeMemory_Unknown( &pMixCaps );
  3630. AudioFreeMemory( sizeof(LONG),&pReferenceCount );
  3631. AudioFreeMemory_Unknown( &pMixLevels );
  3632. }
  3633. }
  3634. exit:
  3635. if( *ppControl ) {
  3636. DPF(DL_TRACE|FA_MIXER,( "Translated %d controls.", kmxlListLength( *ppControl ) ) );
  3637. return( kmxlListLength( *ppControl ) );
  3638. } else {
  3639. DPF(DL_TRACE|FA_MIXER,( "Translated no controls." ) );
  3640. return( 0 );
  3641. }
  3642. }
  3643. #define KsAudioPropertyToString( Property ) \
  3644. Property == KSPROPERTY_AUDIO_VOLUMELEVEL ? "Volume" : \
  3645. Property == KSPROPERTY_AUDIO_MUTE ? "Mute" : \
  3646. Property == KSPROPERTY_AUDIO_BASS ? "Bass" : \
  3647. Property == KSPROPERTY_AUDIO_TREBLE ? "Treble" : \
  3648. Property == KSPROPERTY_AUDIO_AGC ? "AGC" : \
  3649. Property == KSPROPERTY_AUDIO_LOUDNESS ? "Loudness" : \
  3650. Property == KSPROPERTY_AUDIO_PEAKMETER ? "Peakmeter" : \
  3651. "Unknown"
  3652. ///////////////////////////////////////////////////////////////////////
  3653. //
  3654. // kmxlSupportsControl
  3655. //
  3656. // Queries for property on control to see if it is actually supported
  3657. //
  3658. //
  3659. NTSTATUS
  3660. kmxlSupportsControl(
  3661. IN PFILE_OBJECT pfoInstance, // The instance to check for
  3662. IN ULONG Node, // The node id to query
  3663. IN ULONG Property // The property to check for
  3664. )
  3665. {
  3666. NTSTATUS Status;
  3667. LONG Level;
  3668. ASSERT( pfoInstance );
  3669. PAGED_CODE();
  3670. //
  3671. // Check to see if the property works on the first channel.
  3672. //
  3673. Status = kmxlGetAudioNodeProperty(
  3674. pfoInstance,
  3675. Property,
  3676. Node,
  3677. 0, // Channel 0 - first channel
  3678. NULL, 0,
  3679. &Level, sizeof( Level )
  3680. );
  3681. if( !NT_SUCCESS( Status ) ) {
  3682. DPF(DL_WARNING|FA_MIXER,( "SupportsControl for (%d,%X) failed on first channel with %x.",
  3683. Node, Property, Status ) );
  3684. }
  3685. RETURN( Status );
  3686. }
  3687. ///////////////////////////////////////////////////////////////////////
  3688. //
  3689. // kmxlSupportsMultiChannelControl
  3690. //
  3691. // Queries for property on the second channel of the control to see
  3692. // independent levels can be set. It is assumed that the first channel
  3693. // already succeeded in kmxlSupportsControl
  3694. //
  3695. //
  3696. NTSTATUS
  3697. kmxlSupportsMultiChannelControl(
  3698. IN PFILE_OBJECT pfoInstance, // The instance to check for
  3699. IN ULONG Node, // The node id to query
  3700. IN ULONG Property // The property to check for
  3701. )
  3702. {
  3703. NTSTATUS Status;
  3704. LONG Level;
  3705. ASSERT( pfoInstance );
  3706. PAGED_CODE();
  3707. //
  3708. // Just check the property on the second channel because we have already checked
  3709. // the first channel already.
  3710. //
  3711. Status = kmxlGetAudioNodeProperty(
  3712. pfoInstance,
  3713. Property,
  3714. Node,
  3715. 1, // Second channel equals a channel value of 1
  3716. NULL, 0,
  3717. &Level, sizeof( Level )
  3718. );
  3719. RETURN( Status );
  3720. }
  3721. NTSTATUS
  3722. kmxlAssignLineAndControlIdsWorker(
  3723. IN PMIXEROBJECT pmxobj,
  3724. IN LINELIST listLines, // The list to assign ids for
  3725. IN ULONG ListType, // LIST_SOURCE or LIST_DESTINATION
  3726. IN OUT ULONG *pLineID,
  3727. IN GUID *pDestGuid
  3728. )
  3729. {
  3730. NTSTATUS Status = STATUS_SUCCESS;
  3731. PMXLLINE pLine = NULL;
  3732. PMXLCONTROL pControl = NULL;
  3733. ULONG LineID = 0;
  3734. ULONG Dest;
  3735. PAGED_CODE();
  3736. ASSERT ( ListType==SOURCE_LIST || ListType==DESTINATION_LIST );
  3737. if (pLineID!=NULL) {
  3738. LineID=*pLineID;
  3739. }
  3740. //
  3741. // Loop through each of the line structures
  3742. //
  3743. pLine = kmxlFirstInList( listLines );
  3744. if( pLine == NULL ) {
  3745. RETURN( Status );
  3746. }
  3747. Dest = pLine->DestId;
  3748. while( pLine ) {
  3749. //
  3750. // For destinations, set the dwDestination field and set
  3751. // the dwSource field for sources.
  3752. //
  3753. if( ListType == DESTINATION_LIST ) {
  3754. // Check if this line has already been assigned an ID.
  3755. // If so, then go to next line in list.
  3756. if (pLine->Line.dwDestination!=(DWORD)(-1)) {
  3757. pLine = kmxlNextLine( pLine );
  3758. continue;
  3759. }
  3760. // Now if we can only number lines of a particular GUID,
  3761. // then make sure this destination line type matches that guid.
  3762. if (pDestGuid!=NULL && !IsEqualGUID( pDestGuid, &pLine->Type )) {
  3763. pLine = kmxlNextLine( pLine );
  3764. continue;
  3765. }
  3766. //
  3767. // Assign the destination Id. Create the line Id by
  3768. // using -1 for the source in the highword and the
  3769. // destination in the loword.
  3770. //
  3771. pLine->Line.dwDestination = LineID++;
  3772. pLine->Line.dwLineID = MAKELONG(
  3773. pLine->Line.dwDestination,
  3774. -1
  3775. );
  3776. if (pLineID!=NULL) {
  3777. *pLineID=LineID;
  3778. }
  3779. } else if( ListType == SOURCE_LIST ) {
  3780. pLine->Line.dwSource = LineID++;
  3781. } else {
  3782. RETURN( STATUS_INVALID_PARAMETER );
  3783. }
  3784. //
  3785. // Set up the number of controls on this line.
  3786. //
  3787. pLine->Line.cControls = kmxlListLength( pLine->Controls );
  3788. //
  3789. // Loop through the controls, assigning them a control ID
  3790. // that is a pointer to the MXLCONTROL structure for that
  3791. // control.
  3792. //
  3793. pControl = kmxlFirstInList( pLine->Controls );
  3794. while( pControl ) {
  3795. if( pControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ) {
  3796. //
  3797. // MUX controls are already numbered by this point. Just skip
  3798. // it and go onto the next one.
  3799. //
  3800. pControl = kmxlNextControl( pControl );
  3801. continue;
  3802. }
  3803. pControl->Control.dwControlID = pmxobj->dwControlId++;
  3804. pControl = kmxlNextControl( pControl );
  3805. }
  3806. pLine = kmxlNextLine( pLine );
  3807. if( pLine == NULL ) {
  3808. continue;
  3809. }
  3810. if( ( ListType == SOURCE_LIST ) && ( pLine->DestId != Dest ) ) {
  3811. LineID = 0;
  3812. Dest = pLine->DestId;
  3813. }
  3814. }
  3815. RETURN( Status );
  3816. }
  3817. #define GUIDCOUNT 13
  3818. ///////////////////////////////////////////////////////////////////////
  3819. //
  3820. // kmxlAssignLineAndControlIds
  3821. //
  3822. // Loops through the list of lines and assigns ids for those line.
  3823. // For destinations, the Id starts a 0 and is incremented each time.
  3824. // The line id is a long of -1 and the dest id. For sources, the
  3825. // line Ids will need to be specified elsewhere so only dwSource
  3826. // field is assigned.
  3827. //
  3828. // For controls, each control is given an Id of the address to the
  3829. // MXLCONTROL structure.
  3830. //
  3831. //
  3832. NTSTATUS
  3833. kmxlAssignLineAndControlIds(
  3834. IN PMIXEROBJECT pmxobj,
  3835. IN LINELIST listLines, // The list to assign ids for
  3836. IN ULONG ListType // LIST_SOURCE or LIST_DESTINATION
  3837. )
  3838. {
  3839. PAGED_CODE();
  3840. ASSERT ( ListType==SOURCE_LIST || ListType==DESTINATION_LIST );
  3841. if (SOURCE_LIST==ListType) {
  3842. return( kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType, NULL, NULL) );
  3843. }
  3844. else if (DESTINATION_LIST==ListType) {
  3845. // In order to help sndvol32 do the right thing as far as which
  3846. // lines displayed as the default playback and record lines, we
  3847. // number lines based on what their destinations are.
  3848. // We use guid the pLine->Type field to decide how to number lines.
  3849. // Lines are prioritized in the following way: speakers, then
  3850. // headphones, then telephones. Non prioritized guids are assigned
  3851. // last in whatever order they appear in the list.
  3852. ULONG LineID=0;
  3853. ULONG i;
  3854. GUID prioritizeddestinationguids[GUIDCOUNT]= {
  3855. STATIC_KSNODETYPE_ROOM_SPEAKER,
  3856. STATIC_KSNODETYPE_DESKTOP_SPEAKER,
  3857. STATIC_KSNODETYPE_SPEAKER,
  3858. STATIC_KSNODETYPE_COMMUNICATION_SPEAKER,
  3859. STATIC_KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO,
  3860. STATIC_KSNODETYPE_ANALOG_CONNECTOR,
  3861. STATIC_KSNODETYPE_SPDIF_INTERFACE,
  3862. STATIC_KSNODETYPE_HEADPHONES,
  3863. STATIC_KSNODETYPE_TELEPHONE,
  3864. STATIC_KSNODETYPE_PHONE_LINE,
  3865. STATIC_KSNODETYPE_DOWN_LINE_PHONE,
  3866. STATIC_PINNAME_CAPTURE,
  3867. STATIC_KSCATEGORY_AUDIO,
  3868. };
  3869. // Cycle through the list for each prioritized guid and number
  3870. // those lines that match that particular guid.
  3871. for (i=0; i<GUIDCOUNT; i++) {
  3872. kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType,
  3873. &LineID, &prioritizeddestinationguids[i]);
  3874. }
  3875. // Now, number anything left over with a number that depends solely on
  3876. // its random order in the list.
  3877. return( kmxlAssignLineAndControlIdsWorker(pmxobj, listLines, ListType, &LineID, NULL) );
  3878. }
  3879. else {
  3880. RETURN( STATUS_INVALID_PARAMETER );
  3881. }
  3882. }
  3883. ///////////////////////////////////////////////////////////////////////
  3884. //
  3885. // kmxlAssignDestinationsToSources
  3886. //
  3887. // Loops through each source looking for a destination lines that
  3888. // have a matching destination id. Source line Ids are assigned
  3889. // by putting the source id in the hiword and the dest id in the
  3890. // loword.
  3891. //
  3892. //
  3893. NTSTATUS
  3894. kmxlAssignDestinationsToSources(
  3895. IN LINELIST listSourceLines, // The list of all source lines
  3896. IN LINELIST listDestLines // The list of all dest lines
  3897. )
  3898. {
  3899. PMXLLINE pSource = NULL,
  3900. pDest = NULL;
  3901. PAGED_CODE();
  3902. //
  3903. // For each source line, loop throught the destinations until a
  3904. // line is found matching the Id. The dwDestination field will
  3905. // be the zero-index Id of the destination.
  3906. //
  3907. pSource = kmxlFirstInList( listSourceLines );
  3908. while( pSource ) {
  3909. pDest = kmxlFirstInList( listDestLines );
  3910. while( pDest ) {
  3911. if( pSource->DestId == pDest->DestId ) {
  3912. //
  3913. // Heh, whatchya know?
  3914. //
  3915. pSource->Line.dwDestination = pDest->Line.dwDestination;
  3916. pSource->Line.dwLineID = MAKELONG(
  3917. (WORD) pSource->Line.dwDestination,
  3918. (WORD) pSource->Line.dwSource
  3919. );
  3920. break;
  3921. }
  3922. pDest = kmxlNextLine( pDest );
  3923. }
  3924. pSource = kmxlNextLine( pSource );
  3925. }
  3926. RETURN( STATUS_SUCCESS );
  3927. }
  3928. ///////////////////////////////////////////////////////////////////////
  3929. //
  3930. // kmxlUpdateDestinationConnectionCount
  3931. //
  3932. // For each of the destinations, loop through each of the sources
  3933. // and find those that connect to this destination. That count is
  3934. // then stored in the MIXERLINE.cConnections for the line.
  3935. //
  3936. //
  3937. NTSTATUS
  3938. kmxlUpdateDestintationConnectionCount(
  3939. IN LINELIST listSourceLines, // The list of source lines
  3940. IN LINELIST listDestLines // The list of destination lines
  3941. )
  3942. {
  3943. PMXLLINE pDest,
  3944. pSource;
  3945. ULONG Count;
  3946. PAGED_CODE();
  3947. //
  3948. // Loop through each destination finding all the sources that connect
  3949. // to it. The total number of sources connecting to a destination
  3950. // is sourced in the cConnections field of the MIXERLINE struct.
  3951. //
  3952. pDest = kmxlFirstInList( listDestLines );
  3953. while( pDest ) {
  3954. //
  3955. // Initialize the source ID. This will mark this as a valid
  3956. // destination.
  3957. //
  3958. pDest->SourceId = (ULONG) -1;
  3959. Count = 0;
  3960. //
  3961. // Loop through the sources looking for sources that connect to
  3962. // the current destination.
  3963. //
  3964. pSource = kmxlFirstInList( listSourceLines );
  3965. while( pSource ) {
  3966. //
  3967. // Found a match. Increment the count.
  3968. //
  3969. if( pSource->DestId == pDest->DestId ) {
  3970. ++Count;
  3971. }
  3972. pSource = kmxlNextLine( pSource );
  3973. }
  3974. pDest->Line.cConnections = Count;
  3975. pDest = kmxlNextLine( pDest );
  3976. }
  3977. RETURN( STATUS_SUCCESS );
  3978. }
  3979. VOID
  3980. CleanupLine(
  3981. PMXLLINE pLine
  3982. )
  3983. {
  3984. PMXLCONTROL pControl;
  3985. while( pLine->Controls ) {
  3986. pControl = kmxlRemoveFirstControl( pLine->Controls );
  3987. kmxlFreeControl( pControl );
  3988. }
  3989. AudioFreeMemory( sizeof(MXLLINE),&pLine );
  3990. }
  3991. ///////////////////////////////////////////////////////////////////////
  3992. //
  3993. // kmxlEliminateInvalidLines
  3994. //
  3995. // Loops through the lines removing lines that are invalid. Refer
  3996. // to the function for IsValidLine() for details on what is an invalid
  3997. // line.
  3998. //
  3999. //
  4000. NTSTATUS
  4001. kmxlEliminateInvalidLines(
  4002. IN LINELIST* listLines // The list of lines
  4003. )
  4004. {
  4005. PMXLLINE pLine, pTemp, pShadow;
  4006. PAGED_CODE();
  4007. //
  4008. // Eliminate all invalid lines at the start of the list.
  4009. //
  4010. pLine = kmxlFirstInList( *listLines );
  4011. while( pLine ) {
  4012. //
  4013. // Found the first valid line. Break out of this loop.
  4014. //
  4015. if( Is_Valid_Line( pLine ) ) {
  4016. break;
  4017. }
  4018. //
  4019. // This is an invalid line. Remove it from the list, free up
  4020. // all its control structures, and free the line structure.
  4021. //
  4022. pTemp = kmxlRemoveFirstLine( pLine );
  4023. CleanupLine(pTemp);
  4024. }
  4025. //
  4026. // Assign listLines to point to the first valid line.
  4027. //
  4028. *listLines = pLine;
  4029. if( pLine == NULL ) {
  4030. RETURN( STATUS_SUCCESS );
  4031. }
  4032. //
  4033. // At this point, pLine is a valid line. Keeping a hold on the prev
  4034. // line, loop through the lines eliminating the invalid ones.
  4035. //
  4036. pShadow = pLine;
  4037. while( pShadow && kmxlNextLine( pShadow ) ) {
  4038. pLine = kmxlNextLine( pShadow );
  4039. if( pLine && !Is_Valid_Line( pLine ) ) {
  4040. //
  4041. // Remove the invalid line from the list
  4042. //
  4043. pShadow->List.Next = pLine->List.Next;
  4044. pLine->List.Next = NULL;
  4045. CleanupLine(pLine);
  4046. continue;
  4047. }
  4048. pShadow = kmxlNextLine( pShadow );
  4049. }
  4050. // All the invalid lines have been eliminated. Now eliminate bad
  4051. // duplicates.
  4052. pShadow = kmxlFirstInList( *listLines );
  4053. while( pShadow ) {
  4054. //
  4055. // Walk all the lines looking for a match.
  4056. //
  4057. pLine = kmxlNextLine( pShadow );
  4058. pTemp = NULL;
  4059. while( pLine ) {
  4060. if( ( pShadow->SourceId == pLine->SourceId ) &&
  4061. ( pShadow->DestId == pLine->DestId ) )
  4062. {
  4063. DPF(DL_TRACE|FA_MIXER,( "Line %x is equal to line %x!",
  4064. pShadow->Line.dwLineID,
  4065. pLine->Line.dwLineID
  4066. ) );
  4067. //
  4068. // Found a match.
  4069. //
  4070. if( pTemp == NULL )
  4071. {
  4072. //
  4073. // pShadow is our previous line. Remove this line from the
  4074. // list.
  4075. //
  4076. pShadow->List.Next = pLine->List.Next;
  4077. pLine->List.Next = NULL;
  4078. CleanupLine(pLine);
  4079. //
  4080. // Now adjust pLine to the next line and loop
  4081. //
  4082. pLine = kmxlNextLine( pShadow );
  4083. continue;
  4084. } else {
  4085. //
  4086. // pTemp is our previous line. Remove this line from the
  4087. // list.
  4088. //
  4089. pTemp->List.Next = pLine->List.Next;
  4090. pLine->List.Next = NULL;
  4091. CleanupLine(pLine);
  4092. //
  4093. // Now adjust pLine to the next line and loop
  4094. //
  4095. pLine = kmxlNextLine( pTemp );
  4096. continue;
  4097. }
  4098. }
  4099. pTemp = pLine; //temp is previous line
  4100. pLine = kmxlNextLine( pLine );
  4101. }
  4102. pShadow = kmxlNextLine( pShadow );
  4103. }
  4104. RETURN( STATUS_SUCCESS );
  4105. }
  4106. ///////////////////////////////////////////////////////////////////////
  4107. //
  4108. // kmxlAssignComponentIds
  4109. //
  4110. // Loops through all the destinations then the sources and determines
  4111. // their component type and target types.
  4112. //
  4113. //
  4114. VOID
  4115. kmxlAssignComponentIds(
  4116. IN PMIXEROBJECT pmxobj,
  4117. IN LINELIST listSourceLines,
  4118. IN LINELIST listDestLines
  4119. )
  4120. {
  4121. PMXLLINE pLine;
  4122. PAGED_CODE();
  4123. //
  4124. // Loop through the destinations...
  4125. //
  4126. pLine = kmxlFirstInList( listDestLines );
  4127. while( pLine ) {
  4128. pLine->Line.dwComponentType = kmxlDetermineDestinationType(
  4129. pmxobj,
  4130. pLine
  4131. );
  4132. pLine = kmxlNextLine( pLine );
  4133. }
  4134. //
  4135. // Loop through the sources...
  4136. //
  4137. pLine = kmxlFirstInList( listSourceLines );
  4138. while( pLine ) {
  4139. pLine->Line.dwComponentType = kmxlDetermineSourceType(
  4140. pmxobj,
  4141. pLine
  4142. );
  4143. pLine = kmxlNextLine( pLine );
  4144. }
  4145. }
  4146. ///////////////////////////////////////////////////////////////////////
  4147. //
  4148. // kmxlUpdateMuxLines
  4149. //
  4150. // Updates the name, line ID, and componenttype of a line that has
  4151. // a mux control on it. The MixerControlDetails array is searched for
  4152. // an entry that has a matching source id and replaced with the info
  4153. // from this line.
  4154. //
  4155. //
  4156. VOID
  4157. kmxlUpdateMuxLines(
  4158. IN PMXLLINE pLine,
  4159. IN PMXLCONTROL pControl
  4160. )
  4161. {
  4162. ULONG i;
  4163. PAGED_CODE();
  4164. for( i = 0; i < pControl->Parameters.Count; i++ ) {
  4165. if( ( pLine->SourceId == pControl->Parameters.lpmcd_lt[ i ].dwParam1 ) &&
  4166. ( pControl->Parameters.lpmcd_lt[ i ].dwParam2 == (DWORD) -1 ) )
  4167. {
  4168. wcscpy(
  4169. pControl->Parameters.lpmcd_lt[ i ].szName,
  4170. pLine->Line.szName
  4171. );
  4172. pControl->Parameters.lpmcd_lt[ i ].dwParam1 =
  4173. pLine->Line.dwLineID;
  4174. pControl->Parameters.lpmcd_lt[ i ].dwParam2 =
  4175. pLine->Line.dwComponentType;
  4176. }
  4177. }
  4178. }
  4179. ///////////////////////////////////////////////////////////////////////
  4180. //
  4181. // kmxlAssignMuxIds
  4182. //
  4183. // Updates the source IDs stored in the MixerControlDetails array of
  4184. // the muxes and removes the muxes placed in lines as place holders.
  4185. //
  4186. //
  4187. NTSTATUS
  4188. kmxlAssignMuxIds(
  4189. IN PMIXEROBJECT pmxobj,
  4190. IN LINELIST listLines
  4191. )
  4192. {
  4193. PMXLLINE pLine;
  4194. PMXLCONTROL pControl;
  4195. CONTROLLIST listControls = NULL;
  4196. PAGED_CODE();
  4197. pLine = kmxlFirstInList( listLines );
  4198. while( pLine ) {
  4199. //
  4200. // Loop through the controls by removing them from the line's
  4201. // control list and building a new control list. This new
  4202. // control list will have the extra mux controls removed.
  4203. //
  4204. pControl = kmxlRemoveFirstControl( pLine->Controls );
  4205. while( pControl ) {
  4206. if( IsEqualGUID( pControl->NodeType, &KSNODETYPE_MUX ) ) {
  4207. kmxlUpdateMuxLines( pLine, pControl );
  4208. if( pControl->Parameters.bPlaceholder ) {
  4209. //
  4210. // This mux was here only to mark this line. Free
  4211. // up only the control memory and leave the parameters
  4212. // memeory alone.
  4213. //
  4214. ASSERT( pControl->pChannelStepping == NULL);
  4215. AudioFreeMemory_Unknown( &pControl );
  4216. --pLine->Line.cControls;
  4217. } else {
  4218. //
  4219. // This is a real mux control. Add it back into the
  4220. // list.
  4221. //
  4222. kmxlAddToEndOfList( listControls, pControl );
  4223. }
  4224. } else {
  4225. //
  4226. // Wasn't a mux. Put it onto the end of the new control
  4227. // list.
  4228. kmxlAddToEndOfList( listControls, pControl );
  4229. }
  4230. //
  4231. // Remove the next one!
  4232. //
  4233. pControl = kmxlRemoveFirstControl( pLine->Controls );
  4234. }
  4235. //
  4236. // Reassign the new control list back into this line.
  4237. //
  4238. pLine->Controls = listControls;
  4239. pLine = kmxlNextLine( pLine );
  4240. listControls = NULL;
  4241. }
  4242. RETURN( STATUS_SUCCESS );
  4243. }
  4244. ///////////////////////////////////////////////////////////////////////
  4245. //
  4246. // TargetCommon
  4247. //
  4248. // Fills in the common fields of the target function.
  4249. //
  4250. //
  4251. VOID
  4252. TargetCommon(
  4253. IN PMIXEROBJECT pmxobj,
  4254. IN PMXLLINE pLine,
  4255. IN DWORD DeviceType
  4256. )
  4257. {
  4258. PWDMACONTEXT pWdmaContext;
  4259. PWAVEDEVICE paWaveOutDevs, paWaveInDevs;
  4260. PMIDIDEVICE paMidiOutDevs, paMidiInDevs;
  4261. ULONG i;
  4262. PAGED_CODE();
  4263. pWdmaContext = pmxobj->pMixerDevice->pWdmaContext;
  4264. paWaveOutDevs = pWdmaContext->WaveOutDevs;
  4265. paWaveInDevs = pWdmaContext->WaveInDevs;
  4266. paMidiOutDevs = pWdmaContext->MidiOutDevs;
  4267. paMidiInDevs = pWdmaContext->MidiInDevs;
  4268. for( i = 0; i < MAXNUMDEVS; i++ ) {
  4269. if( DeviceType == WaveOutDevice ) {
  4270. if( (paWaveOutDevs[i].Device != UNUSED_DEVICE) &&
  4271. !MyWcsicmp(pmxobj->DeviceInterface, paWaveOutDevs[ i ].DeviceInterface) ) {
  4272. WAVEOUTCAPS wc;
  4273. ((PWAVEOUTCAPSA)(PVOID)&wc)->wMid=UNICODE_TAG;
  4274. wdmaudGetDevCaps( pWdmaContext, WaveOutDevice, i, (BYTE*) &wc, sizeof( WAVEOUTCAPS ) );
  4275. wcsncpy( pLine->Line.Target.szPname, wc.szPname, MAXPNAMELEN );
  4276. pLine->Line.Target.wMid = wc.wMid;
  4277. pLine->Line.Target.wPid = wc.wPid;
  4278. pLine->Line.Target.vDriverVersion = wc.vDriverVersion;
  4279. return;
  4280. }
  4281. }
  4282. if( DeviceType == WaveInDevice ) {
  4283. if( (paWaveInDevs[i].Device != UNUSED_DEVICE) &&
  4284. !MyWcsicmp(pmxobj->DeviceInterface, paWaveInDevs[ i ].DeviceInterface) ) {
  4285. WAVEINCAPS wc;
  4286. ((PWAVEINCAPSA)(PVOID)&wc)->wMid=UNICODE_TAG;
  4287. wdmaudGetDevCaps( pWdmaContext, WaveInDevice, i, (BYTE*) &wc, sizeof( WAVEINCAPS ) );
  4288. wcsncpy( pLine->Line.Target.szPname, wc.szPname, MAXPNAMELEN );
  4289. pLine->Line.Target.wMid = wc.wMid;
  4290. pLine->Line.Target.wPid = wc.wPid;
  4291. pLine->Line.Target.vDriverVersion = wc.vDriverVersion;
  4292. return;
  4293. }
  4294. }
  4295. if( DeviceType == MidiOutDevice ) {
  4296. if( (paMidiOutDevs[i].Device != UNUSED_DEVICE) &&
  4297. !MyWcsicmp(pmxobj->DeviceInterface, paMidiOutDevs[ i ].DeviceInterface) ) {
  4298. MIDIOUTCAPS mc;
  4299. ((PMIDIOUTCAPSA)(PVOID)&mc)->wMid=UNICODE_TAG;
  4300. wdmaudGetDevCaps( pWdmaContext, MidiOutDevice, i, (BYTE*) &mc, sizeof( MIDIOUTCAPS ) );
  4301. wcsncpy( pLine->Line.Target.szPname, mc.szPname, MAXPNAMELEN );
  4302. pLine->Line.Target.wMid = mc.wMid;
  4303. pLine->Line.Target.wPid = mc.wPid;
  4304. pLine->Line.Target.vDriverVersion = mc.vDriverVersion;
  4305. return;
  4306. }
  4307. }
  4308. if( DeviceType == MidiInDevice ) {
  4309. if( (paMidiInDevs[i].Device != UNUSED_DEVICE) &&
  4310. !MyWcsicmp(pmxobj->DeviceInterface, paMidiInDevs[ i ].DeviceInterface) ) {
  4311. MIDIINCAPS mc;
  4312. ((PMIDIINCAPSA)(PVOID)&mc)->wMid=UNICODE_TAG;
  4313. wdmaudGetDevCaps( pWdmaContext, MidiInDevice, i, (BYTE*) &mc, sizeof( MIDIINCAPS ) );
  4314. wcsncpy( pLine->Line.Target.szPname, mc.szPname, MAXPNAMELEN) ;
  4315. pLine->Line.Target.wMid = mc.wMid;
  4316. pLine->Line.Target.wPid = mc.wPid;
  4317. pLine->Line.Target.vDriverVersion = mc.vDriverVersion;
  4318. return;
  4319. }
  4320. }
  4321. }
  4322. }
  4323. ///////////////////////////////////////////////////////////////////////
  4324. //
  4325. // TargetTypeWaveOut
  4326. //
  4327. // Fills in the fields of aLine's target structure to be a waveout
  4328. // target.
  4329. //
  4330. //
  4331. VOID
  4332. TargetTypeWaveOut(
  4333. IN PMIXEROBJECT pmxobj,
  4334. IN PMXLLINE pLine
  4335. )
  4336. {
  4337. PAGED_CODE();
  4338. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
  4339. TargetCommon( pmxobj, pLine, WaveOutDevice );
  4340. }
  4341. ///////////////////////////////////////////////////////////////////////
  4342. //
  4343. // TargetTypeWaveIn
  4344. //
  4345. // Fills in the fields of aLine's target structure to be a wavein
  4346. // target.
  4347. //
  4348. //
  4349. #define TargetTypeWaveIn( pmxobj, pLine ) \
  4350. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN; \
  4351. (pLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_WAVEIN; \
  4352. TargetCommon( pmxobj, pLine, WaveInDevice )
  4353. ///////////////////////////////////////////////////////////////////////
  4354. //
  4355. // TargetTypeMidiOut
  4356. //
  4357. // Fills in the fields of aLine's target structure to be a midi out
  4358. // target.
  4359. //
  4360. //
  4361. #define TargetTypeMidiOut( pmxobj, pLine ) \
  4362. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; \
  4363. (pLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_MIDIOUT; \
  4364. TargetCommon( pmxobj, pLine, MidiOutDevice )
  4365. ///////////////////////////////////////////////////////////////////////
  4366. //
  4367. // TargetTypeMidiIn
  4368. //
  4369. // Fills in the fields of aLine's target structure to be a midi in
  4370. // target.
  4371. //
  4372. //
  4373. #define TargetTypeMidiIn( pmxobj, pLine ) \
  4374. (aLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT; \
  4375. (aLine)->Line.Target.wPid = MM_MSFT_WDMAUDIO_MIDIIN; \
  4376. TargetCommon( pmxobj, pLine, MidiInDevice )
  4377. ///////////////////////////////////////////////////////////////////////
  4378. //
  4379. // TargetTypeAuxCD
  4380. //
  4381. // Fills in the fields of aLine's target structure to be a CD
  4382. // target.
  4383. //
  4384. //
  4385. #define TargetTypeAuxCD( pmxobj, pLine ) \
  4386. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_AUX; \
  4387. TargetCommon( pmxobj, pLine, WaveOutDevice ); \
  4388. (pLine)->Line.Target.wPid = MM_MSFT_SB16_AUX_CD
  4389. ///////////////////////////////////////////////////////////////////////
  4390. //
  4391. // TargetTypeAuxLine
  4392. //
  4393. // Fills in the fields of aLine's target structure to be a aux line
  4394. // target.
  4395. //
  4396. //
  4397. #define TargetTypeAuxLine( pmxobj, pLine ) \
  4398. (pLine)->Line.Target.dwType = MIXERLINE_TARGETTYPE_AUX; \
  4399. TargetCommon( pmxobj, pLine, WaveOutDevice );\
  4400. (pLine)->Line.Target.wPid = MM_MSFT_SB16_AUX_LINE
  4401. ///////////////////////////////////////////////////////////////////////
  4402. //
  4403. // kmxlDetermineDestinationType
  4404. //
  4405. // Determines the destination and target types by using the Type
  4406. // GUID stored in the line structure.
  4407. //
  4408. //
  4409. ULONG
  4410. kmxlDetermineDestinationType(
  4411. IN PMIXEROBJECT pmxobj, // Instance data
  4412. IN PMXLLINE pLine // The line to determine type of
  4413. )
  4414. {
  4415. PAGED_CODE();
  4416. //
  4417. // Speaker type destinations
  4418. //
  4419. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SPEAKER ) ||
  4420. IsEqualGUID( &pLine->Type, &KSNODETYPE_DESKTOP_SPEAKER ) ||
  4421. IsEqualGUID( &pLine->Type, &KSNODETYPE_ROOM_SPEAKER ) ||
  4422. IsEqualGUID( &pLine->Type, &KSNODETYPE_COMMUNICATION_SPEAKER ) ) {
  4423. TargetTypeWaveOut( pmxobj, pLine );
  4424. return( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS );
  4425. }
  4426. //
  4427. // WaveIn type destinations
  4428. //
  4429. if( IsEqualGUID( &pLine->Type, &KSCATEGORY_AUDIO )
  4430. || IsEqualGUID( &pLine->Type, &PINNAME_CAPTURE )
  4431. ) {
  4432. TargetTypeWaveIn( pmxobj, pLine );
  4433. return( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
  4434. }
  4435. //
  4436. // Headphone destination
  4437. //
  4438. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_HEADPHONES ) ||
  4439. IsEqualGUID( &pLine->Type, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO ) ) {
  4440. TargetTypeWaveOut( pmxobj, pLine );
  4441. return( MIXERLINE_COMPONENTTYPE_DST_HEADPHONES );
  4442. }
  4443. //
  4444. // Telephone destination
  4445. //
  4446. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_TELEPHONE ) ||
  4447. IsEqualGUID( &pLine->Type, &KSNODETYPE_PHONE_LINE ) ||
  4448. IsEqualGUID( &pLine->Type, &KSNODETYPE_DOWN_LINE_PHONE ) )
  4449. {
  4450. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4451. return( MIXERLINE_COMPONENTTYPE_DST_TELEPHONE );
  4452. }
  4453. //
  4454. // Ambiguous destination type. Figure out the destination type by looking
  4455. // at the Communication.
  4456. //
  4457. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_ANALOG_CONNECTOR ) ||
  4458. IsEqualGUID( &pLine->Type, &KSNODETYPE_SPDIF_INTERFACE ) ) {
  4459. if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
  4460. TargetTypeWaveOut( pmxobj, pLine );
  4461. return( MIXERLINE_COMPONENTTYPE_DST_SPEAKERS );
  4462. } else {
  4463. TargetTypeWaveIn( pmxobj, pLine );
  4464. return( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
  4465. }
  4466. }
  4467. //
  4468. // Does not match the others. Default to Undefined destination.
  4469. //
  4470. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4471. return( MIXERLINE_COMPONENTTYPE_DST_UNDEFINED );
  4472. }
  4473. ///////////////////////////////////////////////////////////////////////
  4474. //
  4475. // kmxlDetermineSourceType
  4476. //
  4477. // Determines the destination and target types by using the Type
  4478. // GUID stored in the line structure.
  4479. //
  4480. //
  4481. ULONG
  4482. kmxlDetermineSourceType(
  4483. IN PMIXEROBJECT pmxobj, // Instance data
  4484. IN PMXLLINE pLine // The line to determine type of
  4485. )
  4486. {
  4487. PAGED_CODE();
  4488. //
  4489. // All microphone type sources are a microphone source.
  4490. //
  4491. //
  4492. // We are only checking two microphone GUIDs here. We may
  4493. // want to consider the rest of the microphone types in
  4494. // ksmedia.h
  4495. //
  4496. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_MICROPHONE )
  4497. || IsEqualGUID( &pLine->Type, &KSNODETYPE_DESKTOP_MICROPHONE )
  4498. )
  4499. {
  4500. TargetTypeWaveIn( pmxobj, pLine );
  4501. return( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
  4502. }
  4503. //
  4504. // Legacy audio connector and the speaker type sources represent a
  4505. // waveout source.
  4506. //
  4507. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR )
  4508. || IsEqualGUID( &pLine->Type, &KSNODETYPE_SPEAKER )
  4509. || IsEqualGUID( &pLine->Type, &KSCATEGORY_AUDIO )
  4510. )
  4511. {
  4512. TargetTypeWaveOut( pmxobj, pLine );
  4513. return( MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT );
  4514. }
  4515. //
  4516. // CD player is a compact disc source.
  4517. //
  4518. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_CD_PLAYER ) ) {
  4519. TargetTypeAuxCD( pmxobj, pLine );
  4520. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4521. return( MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC );
  4522. }
  4523. //
  4524. // Synthesizer is a sythesizer source.
  4525. //
  4526. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SYNTHESIZER ) ) {
  4527. TargetTypeMidiOut( pmxobj, pLine );
  4528. return( MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER );
  4529. }
  4530. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_LINE_CONNECTOR ) ) {
  4531. TargetTypeAuxLine( pmxobj, pLine );
  4532. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4533. return( MIXERLINE_COMPONENTTYPE_SRC_LINE );
  4534. }
  4535. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_PHONE_LINE ) ||
  4536. IsEqualGUID( &pLine->Type, &KSNODETYPE_TELEPHONE ) ||
  4537. IsEqualGUID( &pLine->Type, &KSNODETYPE_DOWN_LINE_PHONE ) )
  4538. {
  4539. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4540. return( MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE );
  4541. }
  4542. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_ANALOG_CONNECTOR ) ) {
  4543. //
  4544. // Ambiguous src type. Figure out the destination type by looking
  4545. // at the Communication.
  4546. //
  4547. if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
  4548. TargetTypeWaveIn( pmxobj, pLine );
  4549. }
  4550. else {
  4551. TargetTypeWaveOut( pmxobj, pLine );
  4552. }
  4553. return( MIXERLINE_COMPONENTTYPE_SRC_ANALOG );
  4554. }
  4555. //
  4556. // Digital in/out (SPDIF) source
  4557. //
  4558. if( IsEqualGUID( &pLine->Type, &KSNODETYPE_SPDIF_INTERFACE ) ) {
  4559. //
  4560. // Ambiguous src type. Figure out the destination type by looking
  4561. // at the Communication.
  4562. //
  4563. if (pLine->Communication == KSPIN_COMMUNICATION_BRIDGE) {
  4564. TargetTypeWaveIn( pmxobj, pLine );
  4565. }
  4566. else {
  4567. TargetTypeWaveOut( pmxobj, pLine );
  4568. }
  4569. return( MIXERLINE_COMPONENTTYPE_SRC_DIGITAL );
  4570. }
  4571. //
  4572. // All others are lumped under Undefined source.
  4573. //
  4574. pLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
  4575. return( MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED );
  4576. }
  4577. ///////////////////////////////////////////////////////////////////////
  4578. //
  4579. // PinCategoryToString
  4580. //
  4581. // Converts the Pin category GUIDs to a string.
  4582. #ifdef DEBUG
  4583. #pragma LOCKED_CODE
  4584. #endif
  4585. #define _EG_(x,y) if (IsEqualGUID( NodeType, &x)) { return y; }
  4586. const char*
  4587. PinCategoryToString
  4588. (
  4589. IN CONST GUID* NodeType // The GUID to translate
  4590. )
  4591. {
  4592. _EG_(KSNODETYPE_MICROPHONE,"Microphone");
  4593. _EG_(KSNODETYPE_DESKTOP_MICROPHONE,"Desktop Microphone");
  4594. _EG_(KSNODETYPE_SPEAKER,"Speaker");
  4595. _EG_(KSNODETYPE_HEADPHONES,"Headphones");
  4596. _EG_(KSNODETYPE_LEGACY_AUDIO_CONNECTOR,"Wave");
  4597. _EG_(KSNODETYPE_CD_PLAYER,"CD Player");
  4598. _EG_(KSNODETYPE_SYNTHESIZER,"Synthesizer");
  4599. _EG_(KSCATEGORY_AUDIO,"Wave");
  4600. _EG_(PINNAME_CAPTURE,"Wave In");
  4601. _EG_(KSNODETYPE_LINE_CONNECTOR,"Aux Line");
  4602. _EG_(KSNODETYPE_TELEPHONE,"Telephone");
  4603. _EG_(KSNODETYPE_PHONE_LINE,"Phone Line");
  4604. _EG_(KSNODETYPE_DOWN_LINE_PHONE,"Downline Phone");
  4605. _EG_(KSNODETYPE_ANALOG_CONNECTOR,"Analog connector");
  4606. //New debug names...
  4607. _EG_(KSAUDFNAME_MONO_OUT,"Mono Out");
  4608. _EG_(KSAUDFNAME_STEREO_MIX,"Stereo Mix");
  4609. _EG_(KSAUDFNAME_MONO_MIX,"Mono Mix");
  4610. _EG_(KSAUDFNAME_AUX,"Aux");
  4611. _EG_(KSAUDFNAME_VIDEO,"Video");
  4612. _EG_(KSAUDFNAME_LINE_IN,"Line In");
  4613. DPF(DL_WARNING|FA_MIXER,("Path Trap send me GUID - dt %08X _GUID",NodeType) );
  4614. return "Unknown Pin Category";
  4615. }
  4616. ///////////////////////////////////////////////////////////////////////
  4617. //
  4618. // NodeTypeToString
  4619. //
  4620. // Converts a NodeType GUID to a string
  4621. //
  4622. //
  4623. const char*
  4624. NodeTypeToString
  4625. (
  4626. IN CONST GUID* NodeType // The GUID to translate
  4627. )
  4628. {
  4629. _EG_(KSNODETYPE_DAC,"DAC");
  4630. _EG_(KSNODETYPE_ADC,"ADC");
  4631. _EG_(KSNODETYPE_SRC,"SRC");
  4632. _EG_(KSNODETYPE_SUPERMIX,"SuperMIX");
  4633. _EG_(KSNODETYPE_SUM,"Sum");
  4634. _EG_(KSNODETYPE_MUTE,"Mute");
  4635. _EG_(KSNODETYPE_VOLUME,"Volume");
  4636. _EG_(KSNODETYPE_TONE,"Tone");
  4637. _EG_(KSNODETYPE_AGC,"AGC");
  4638. _EG_(KSNODETYPE_DELAY,"Delay");
  4639. _EG_(KSNODETYPE_LOUDNESS,"LOUDNESS");
  4640. _EG_(KSNODETYPE_3D_EFFECTS,"3D Effects");
  4641. _EG_(KSNODETYPE_DEV_SPECIFIC,"Dev Specific");
  4642. _EG_(KSNODETYPE_STEREO_WIDE,"Stereo Wide");
  4643. _EG_(KSNODETYPE_REVERB,"Reverb");
  4644. _EG_(KSNODETYPE_CHORUS,"Chorus");
  4645. _EG_(KSNODETYPE_ACOUSTIC_ECHO_CANCEL,"AEC");
  4646. _EG_(KSNODETYPE_EQUALIZER,"Equalizer");
  4647. _EG_(KSNODETYPE_MUX,"Mux");
  4648. _EG_(KSNODETYPE_DEMUX,"Demux");
  4649. _EG_(KSNODETYPE_STEREO_ENHANCE,"Stereo Enhance");
  4650. _EG_(KSNODETYPE_SYNTHESIZER,"Synthesizer");
  4651. _EG_(KSNODETYPE_PEAKMETER,"Peakmeter");
  4652. _EG_(KSNODETYPE_LINE_CONNECTOR,"Line Connector");
  4653. _EG_(KSNODETYPE_SPEAKER,"Speaker");
  4654. _EG_(KSNODETYPE_DESKTOP_SPEAKER,"");
  4655. _EG_(KSNODETYPE_ROOM_SPEAKER,"Room Speaker");
  4656. _EG_(KSNODETYPE_COMMUNICATION_SPEAKER,"Communication Speaker");
  4657. _EG_(KSNODETYPE_LOW_FREQUENCY_EFFECTS_SPEAKER,"? Whatever...");
  4658. _EG_(KSNODETYPE_HANDSET,"Handset");
  4659. _EG_(KSNODETYPE_HEADSET,"Headset");
  4660. _EG_(KSNODETYPE_SPEAKERPHONE_NO_ECHO_REDUCTION,"Speakerphone no echo reduction");
  4661. _EG_(KSNODETYPE_ECHO_SUPPRESSING_SPEAKERPHONE,"Echo Suppressing Speakerphone");
  4662. _EG_(KSNODETYPE_ECHO_CANCELING_SPEAKERPHONE,"Echo Canceling Speakerphone");
  4663. _EG_(KSNODETYPE_CD_PLAYER,"CD Player");
  4664. _EG_(KSNODETYPE_MICROPHONE,"Microphone");
  4665. _EG_(KSNODETYPE_DESKTOP_MICROPHONE,"Desktop Microphone");
  4666. _EG_(KSNODETYPE_PERSONAL_MICROPHONE,"Personal Microphone");
  4667. _EG_(KSNODETYPE_OMNI_DIRECTIONAL_MICROPHONE,"Omni Directional Microphone");
  4668. _EG_(KSNODETYPE_MICROPHONE_ARRAY,"Microphone Array");
  4669. _EG_(KSNODETYPE_PROCESSING_MICROPHONE_ARRAY,"Processing Microphone Array");
  4670. _EG_(KSNODETYPE_ANALOG_CONNECTOR,"Analog Connector");
  4671. _EG_(KSNODETYPE_PHONE_LINE,"Phone Line");
  4672. _EG_(KSNODETYPE_HEADPHONES,"Headphones");
  4673. _EG_(KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO,"Head Mounted Display Audio");
  4674. _EG_(KSNODETYPE_LEGACY_AUDIO_CONNECTOR,"Legacy Audio Connector");
  4675. // _EG_(KSNODETYPE_SURROUND_ENCODER,"Surround Encoder");
  4676. _EG_(KSNODETYPE_NOISE_SUPPRESS,"Noise Suppress");
  4677. _EG_(KSNODETYPE_DRM_DESCRAMBLE,"DRM Descramble");
  4678. _EG_(KSNODETYPE_SWMIDI,"SWMidi");
  4679. _EG_(KSNODETYPE_SWSYNTH,"SWSynth");
  4680. _EG_(KSNODETYPE_MULTITRACK_RECORDER,"Multitrack Recorder");
  4681. _EG_(KSNODETYPE_RADIO_TRANSMITTER,"Radio Transmitter");
  4682. _EG_(KSNODETYPE_TELEPHONE,"Telephone");
  4683. _EG_(KSAUDFNAME_MONO_OUT,"Mono Out");
  4684. _EG_(KSAUDFNAME_LINE_IN,"Line in");
  4685. _EG_(KSAUDFNAME_VIDEO,"Video");
  4686. _EG_(KSAUDFNAME_AUX,"Aux");
  4687. _EG_(KSAUDFNAME_MONO_MIX,"Mono Mix");
  4688. _EG_(KSAUDFNAME_STEREO_MIX,"Stereo Mix");
  4689. _EG_(KSCATEGORY_AUDIO,"Audio");
  4690. _EG_(PINNAME_VIDEO_CAPTURE,"Video Capture");
  4691. DPF(DL_WARNING|FA_MIXER,("Path Trap send me GUID - dt %08X _GUID",NodeType) );
  4692. return "Unknown NodeType";
  4693. }