xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CEchoGals_mixer.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 // ****************************************************************************
2 //
3 //		CEchoGals_mixer.cpp
4 //
5 //		Implementation file for the CEchoGals driver class (mixer functions).
6 //		Set editor tabs to 3 for your viewing pleasure.
7 //
8 // ----------------------------------------------------------------------------
9 //
10 // This file is part of Echo Digital Audio's generic driver library.
11 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
12 // All rights reserved
13 // www.echoaudio.com
14 //
15 // This library is free software; you can redistribute it and/or
16 // modify it under the terms of the GNU Lesser General Public
17 // License as published by the Free Software Foundation; either
18 // version 2.1 of the License, or (at your option) any later version.
19 //
20 // This library is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 // Lesser General Public License for more details.
24 //
25 // You should have received a copy of the GNU Lesser General Public
26 // License along with this library; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 //
29 // ****************************************************************************
30 
31 #include "CEchoGals.h"
32 
33 #undef ECHO_DEBUGPRINTF
34 #define ECHO_DEBUGPRINTF(x)
35 
36 /****************************************************************************
37 
38 	CEchoGals mixer client management
39 
40  ****************************************************************************/
41 
42 //===========================================================================
43 //
44 // Open the mixer - create a mixer client structure for this client and
45 // return the cookie.  The cookie uniquely identifies this client to the
46 // mixer driver.
47 //
48 // Valid cookies are non-zero.  If you get a zero cookie, the open failed
49 // somehow.
50 //
51 // Clients can change mixer controls without calling OpenMixer first; it just
52 // means that they can't track control changes made by other clients.
53 //
54 //===========================================================================
55 
56 ECHOSTATUS CEchoGals::OpenMixer(NUINT &Cookie)
57 {
58 	ECHO_MIXER_CLIENT *pTemp;
59 
60 	if (m_fMixerDisabled)
61 		return ECHOSTATUS_MIXER_DISABLED;
62 
63 	//
64 	// If the cookie is non-zero, then use the specified value
65 	//
66 	if (0 != Cookie)
67 	{
68 		pTemp = m_pMixerClients;
69 		while (pTemp != NULL)
70 		{
71 			if (Cookie == pTemp->Cookie)
72 			{
73 				ECHO_DEBUGPRINTF(("CEchoGals::OpenMixer - cookie 0x%lx already in use\n",
74 										Cookie));
75 				return ECHOSTATUS_BAD_COOKIE;
76 			}
77 
78 			pTemp = pTemp->pNext;
79 		}
80 	}
81 	else
82 	{
83 		//
84 		// Make a new cookie
85 		//
86 		ULONGLONG ullTime;
87 
88 		m_pOsSupport->OsGetSystemTime(&ullTime);
89 		Cookie = (NUINT) ullTime;
90 		if (0 == Cookie)
91 			Cookie = 1;
92 
93 		//
94 		// Look through the existing mixer client list to ensure that this
95 		// cookie is unique.
96 		//
97 		pTemp = m_pMixerClients;
98 		while (pTemp != NULL)
99 		{
100 			if (Cookie == pTemp->Cookie)
101 			{
102 				//
103 				// Oops, someone is already using this cookie.  Increment
104 				// the new cookie and try again.
105 				//
106 				Cookie++;
107 				pTemp = m_pMixerClients;
108 			}
109 
110 			pTemp = pTemp->pNext;
111 		}
112 
113 	}
114 
115 
116 	//
117 	// Allocate the mixer client structure
118 	//
119 	ECHO_MIXER_CLIENT *pClient = NULL;
120 	ECHOSTATUS			Status;
121 
122 	Status = OsAllocateNonPaged(sizeof(ECHO_MIXER_CLIENT),(void **) &pClient);
123 	if (NULL == pClient)
124 	{
125 		Cookie = 0;
126 		return Status;
127 	}
128 
129 
130 	//
131 	// Store the new cookie and the new mixer client
132 	//
133 	pClient->Cookie = Cookie;
134 	pClient->pNext = m_pMixerClients;
135 	m_pMixerClients = pClient;
136 
137 	return ECHOSTATUS_OK;
138 
139 }	// OpenMixer
140 
141 
142 //===========================================================================
143 //
144 // Find a mixer client that matches a cookie
145 //
146 //===========================================================================
147 
148 ECHO_MIXER_CLIENT *CEchoGals::GetMixerClient(NUINT Cookie)
149 {
150 	ECHO_MIXER_CLIENT *pTemp;
151 
152 	pTemp = m_pMixerClients;
153 	while (NULL != pTemp)
154 	{
155 		if (Cookie == pTemp->Cookie)
156 			break;
157 
158 		pTemp = pTemp->pNext;
159 	}
160 
161 	return pTemp;
162 
163 }	// GetMixerClient
164 
165 
166 //===========================================================================
167 //
168 // Close the mixer - free the mixer client structure
169 //
170 //===========================================================================
171 
172 ECHOSTATUS CEchoGals::CloseMixer(NUINT Cookie)
173 {
174 	//
175 	//	Search the linked list and remove the client that matches the cookie
176 	//
177 	ECHO_MIXER_CLIENT *pTemp;
178 
179 	pTemp = m_pMixerClients;
180 	if (NULL == pTemp)
181 		return ECHOSTATUS_BAD_COOKIE;
182 
183 	//
184 	// Head of the list?
185 	//
186 	if (pTemp->Cookie == Cookie)
187 	{
188 		m_pMixerClients = pTemp->pNext;
189 		OsFreeNonPaged(pTemp);
190 
191 		return ECHOSTATUS_OK;
192 	}
193 
194 	//
195 	// Not the head of the list; search the list
196 	//
197 	while (NULL != pTemp->pNext)
198 	{
199 		if (Cookie == pTemp->pNext->Cookie)
200 		{
201 			ECHO_MIXER_CLIENT *pDeadClient;
202 
203 			pDeadClient = pTemp->pNext;
204 			pTemp->pNext = pDeadClient->pNext;
205 			OsFreeNonPaged(pDeadClient);
206 
207 			return ECHOSTATUS_OK;
208 		}
209 
210 		pTemp = pTemp->pNext;
211 	}
212 
213 	//
214 	// No soup for you!
215 	//
216 	return ECHOSTATUS_BAD_COOKIE;
217 
218 } // CloseMixer
219 
220 
221 //===========================================================================
222 //
223 // IsMixerOpen - returns true if at least one client has the mixer open
224 //
225 //===========================================================================
226 
227 BOOL CEchoGals::IsMixerOpen()
228 {
229 	if (NULL == m_pMixerClients)
230 		return FALSE;
231 
232 	return TRUE;
233 
234 }	// IsMixerOpen
235 
236 
237 //===========================================================================
238 //
239 // This function is called when a mixer control changes; add the change
240 //	to the queue for each client.
241 //
242 // Here's what the wCh1 and wCh2 parameters represent, based on the wType
243 // parameter:
244 //
245 // wType				wCh1				wCh2
246 // -----				----				----
247 // ECHO_BUS_OUT	Output bus		Ignored
248 // ECHO_BUS_IN		Input bus		Ignored
249 // ECHO_PIPE_OUT	Output pipe		Output bus
250 // ECHO_MONITOR	Input bus		Output bus
251 //
252 // ECHO_PIPE_IN is not used right now.
253 //
254 //===========================================================================
255 
256 ECHOSTATUS CEchoGals::MixerControlChanged
257 (
258 	WORD	wType,		// One of the ECHO_CHANNEL_TYPES
259 	WORD  wParameter, // One of the MXN_* values
260 	WORD 	wCh1,			// Depends on the wType value
261 	WORD  wCh2			// Also depends on wType value
262 )
263 {
264 	ECHO_MIXER_CLIENT *pClient = m_pMixerClients;
265 	PMIXER_NOTIFY		pNotify;
266 
267 	if (m_fMixerDisabled)
268 		return ECHOSTATUS_MIXER_DISABLED;
269 
270 	//
271 	// Go through all the clients and store this control change
272 	//
273 	while (NULL != pClient)
274 	{
275 		//
276 		// Search the circular buffer for this client and see if
277 		// this control change is already stored
278 		//
279 		DWORD				dwIndex,dwCount;
280 		BOOL				fFound;
281 
282 		dwCount = pClient->dwCount;
283 		dwIndex = pClient->dwTail;
284 		fFound = FALSE;
285 		while (dwCount > 0)
286 		{
287 			pNotify = pClient->Notifies + dwIndex;
288 			if (	(pNotify->wType == wType) &&
289 					(pNotify->wParameter == wParameter) &&
290 					(pNotify->u.wPipeOut == wCh1) && 	// can use any union member her
291 					(pNotify->wBusOut == wCh2))
292 			{
293 				//
294 				// Found this notify already in the circular buffer
295 				//
296 				fFound = TRUE;
297 				break;
298 			}
299 			dwIndex++;
300 			dwIndex &= MAX_MIXER_NOTIFIES - 1;
301 			dwCount--;
302 		}
303 
304 		//
305 		// If the notify was not found, add this notify to the circular buffer if
306 		// there is enough room.
307 		//
308 		if ( 	(FALSE == fFound) &&
309 				(pClient->dwCount != MAX_MIXER_NOTIFIES))
310 		{
311 			pNotify = pClient->Notifies + pClient->dwHead;
312 
313 			pNotify->wType 		= wType;
314 			pNotify->wParameter 	= wParameter;
315 
316 			if (ECHO_BUS_OUT == wType)
317 			{
318 				pNotify->u.wPipeOut = ECHO_CHANNEL_UNUSED;
319 				pNotify->wBusOut = wCh1;
320 			}
321 			else
322 			{
323 				pNotify->u.wPipeOut	= wCh1;		// can use any union member here also
324 				pNotify->wBusOut	= wCh2;
325 			}
326 
327 			pClient->dwCount += 1;
328 			pClient->dwHead = (pClient->dwHead + 1) & (MAX_MIXER_NOTIFIES - 1);
329 		}
330 
331 		pClient = pClient->pNext;
332 	}
333 
334 	return ECHOSTATUS_OK;
335 
336 }	// MixerControlChanged
337 
338 
339 //===========================================================================
340 //
341 // This method is called when a client wants to know what controls have
342 // changed.
343 //
344 //===========================================================================
345 
346 ECHOSTATUS CEchoGals::GetControlChanges
347 (
348 	PMIXER_MULTI_NOTIFY	pNotifies,
349 	NUINT	MixerCookie
350 )
351 {
352 	//
353 	// Match the cookie
354 	//
355 	ECHO_MIXER_CLIENT *pClient = GetMixerClient(MixerCookie);
356 
357 	if (NULL == pClient)
358 	{
359 		pNotifies->dwCount = 0;
360 		return ECHOSTATUS_BAD_COOKIE;
361 	}
362 
363 	//
364 	// Copy mixer notifies
365 	//
366 	PMIXER_NOTIFY	pDest,pSrc;
367 	DWORD				dwNumClientNotifies,dwNumReturned;
368 
369 	dwNumClientNotifies = pNotifies->dwCount;
370 	pDest = pNotifies->Notifies;
371 	dwNumReturned = 0;
372 	while ( (dwNumClientNotifies > 0) && (pClient->dwCount > 0))
373 	{
374 		pSrc = pClient->Notifies + pClient->dwTail;
375 
376 		OsCopyMemory(pDest,pSrc,sizeof(MIXER_NOTIFY));
377 
378 		pDest++;
379 
380 		pClient->dwTail = (pClient->dwTail + 1) & (MAX_MIXER_NOTIFIES - 1);
381 		pClient->dwCount -= 1;
382 
383 		dwNumClientNotifies--;
384 
385 		dwNumReturned++;
386 	}
387 
388 	pNotifies->dwCount = dwNumReturned;
389 
390 	return ECHOSTATUS_OK;
391 
392 }	// GetControlChanges
393 
394 
395 
396 
397 /****************************************************************************
398 
399 	CEchoGals mixer control
400 
401  ****************************************************************************/
402 
403 //===========================================================================
404 //
405 // Process mixer function - service a single mixer function
406 //
407 //===========================================================================
408 
409 ECHOSTATUS CEchoGals::ProcessMixerFunction
410 (
411 	PMIXER_FUNCTION	pMixerFunction,
412 	INT32 &					iRtnDataSz
413 )
414 {
415 	ECHOSTATUS			Status = ECHOSTATUS_OK;
416 
417 	if (m_fMixerDisabled)
418 		return ECHOSTATUS_MIXER_DISABLED;
419 
420 	switch ( pMixerFunction->iFunction )
421 	{
422 		case MXF_GET_CAPS :
423 			Status = GetCapabilities( &pMixerFunction->Data.Capabilities );
424 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
425 									 "MXF_GET_CAPS  Status %ld\n", Status) );
426 			break;
427 
428 		case MXF_GET_LEVEL :
429 			Status = GetAudioLineLevel( pMixerFunction);
430 			/*
431 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
432 									 "MXF_GET_LEVEL  Status %ld\n", Status) );
433 			*/
434 			break;
435 
436 		case MXF_SET_LEVEL :
437 			Status = SetAudioLineLevel( pMixerFunction);
438 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
439 									 "MXF_SET_LEVEL  Status %ld\n", Status) );
440 			break;
441 
442 		case MXF_GET_NOMINAL :
443 			Status = GetAudioNominal( pMixerFunction);
444 			/*
445 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
446 									 "MXF_GET_NOMINAL  Status %ld\n", Status) );
447 			*/
448 			break;
449 
450 		case MXF_SET_NOMINAL :
451 			Status = SetAudioNominal( pMixerFunction);
452 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
453 									 "MXF_SET_NOMINAL  Status %ld\n", Status) );
454 			break;
455 
456 		case MXF_GET_MONITOR :
457 			Status = GetAudioMonitor( pMixerFunction->Channel.wChannel,
458 											  pMixerFunction->Data.Monitor.wBusOut,
459 											  pMixerFunction->Data.Monitor.Data.iLevel );
460 			/*
461 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
462 									 "MXF_GET_MONITOR  Status %ld\n", Status) );
463 			*/
464 			break;
465 
466 		case MXF_SET_MONITOR :
467 			Status = SetAudioMonitor( pMixerFunction->Channel.wChannel,
468 											  pMixerFunction->Data.Monitor.wBusOut,
469 											  pMixerFunction->Data.Monitor.Data.iLevel );
470 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
471 									 "MXF_SET_MONITOR  Status %ld\n", Status) );
472 			break;
473 
474 		case MXF_GET_CLOCK_DETECT :
475 			Status = GetInputClockDetect( pMixerFunction->Data.dwClockDetectBits );
476 			break;
477 
478 		case MXF_GET_INPUT_CLOCK :
479 			Status = GetInputClock( pMixerFunction->Data.wClock );
480 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
481 									 "MXF_GET_INPUT_CLOCK  Status %ld\n", Status) );
482 			break;
483 
484 		case MXF_SET_INPUT_CLOCK :
485 			Status = SetInputClock( pMixerFunction->Data.wClock );
486 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
487 									 "MXF_SET_INPUT_CLOCK  Status %ld\n", Status) );
488 			break;
489 
490 
491 		case MXF_GET_OUTPUT_CLOCK :
492 			Status = GetOutputClock( pMixerFunction->Data.wClock );
493 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
494 									 "MXF_GET_OUTPUT_CLOCK  Status %ld\n", Status) );
495 			break;
496 
497 		case MXF_SET_OUTPUT_CLOCK :
498 			Status = SetOutputClock( pMixerFunction->Data.wClock );
499 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
500 									 "MXF_SET_OUTPUT_CLOCK  Status %ld\n", Status) );
501 			break;
502 
503 
504 		case MXF_GET_METERS :
505 
506 			if (NULL != GetDspCommObject())
507 			{
508 				Status = GetDspCommObject()->
509 								GetAudioMeters( &pMixerFunction->Data.Meters );
510 			}
511 			else
512 			{
513 				Status = ECHOSTATUS_DSP_DEAD;
514 			}
515 
516 			//ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
517 			// 						 "MXF_GET_METERS  Status %ld\n", Status) );
518 			break;
519 
520 		case MXF_GET_METERS_ON :
521 			Status = GetMetersOn( pMixerFunction->Data.bMetersOn );
522 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
523 									 "MXF_SET_METERS  Status %ld\n", Status) );
524 			break;
525 
526 		case MXF_SET_METERS_ON :
527 			Status = SetMetersOn( pMixerFunction->Data.bMetersOn );
528 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
529 									 "MXF_SET_METERS_ON  Status %ld\n", Status) );
530 			break;
531 
532 		case MXF_GET_PROF_SPDIF :
533 			if ( NULL == GetDspCommObject() )
534 			{
535 				Status = ECHOSTATUS_DSP_DEAD;
536 			}
537 			else
538 			{
539 				pMixerFunction->Data.bProfSpdif = IsProfessionalSpdif();
540 			}
541 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
542 									 "MXF_GET_PROF_SPDIF  Pro S/PDIF: 0x%x  Status %ld\n",
543 									 pMixerFunction->Data.bProfSpdif,
544 									 Status) );
545 			break;
546 
547 		case MXF_SET_PROF_SPDIF :
548 			SetProfessionalSpdif( pMixerFunction->Data.bProfSpdif );
549 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
550 									 "MXF_SET_PROF_SPDIF  Pro S/PDIF: 0x%x  Status %ld\n",
551 									 pMixerFunction->Data.bProfSpdif,
552 									 Status) );
553 			break;
554 
555 		case MXF_GET_MUTE :
556 			Status = GetAudioMute(pMixerFunction);
557 			/*
558 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
559 									 "MXF_GET_MUTE  Status %ld\n", Status) );
560 			*/
561 			break;
562 
563 		case MXF_SET_MUTE :
564 			Status = SetAudioMute(pMixerFunction);
565 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
566 									 "MXF_SET_MUTE  Status %ld\n", Status) );
567 			break;
568 
569 		case MXF_GET_MONITOR_MUTE :
570 			Status =
571 				GetAudioMonitorMute( pMixerFunction->Channel.wChannel,
572 										   pMixerFunction->Data.Monitor.wBusOut,
573 										   pMixerFunction->Data.Monitor.Data.bMuteOn );
574 			/*
575 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
576 									 "MXF_GET_MONITOR_MUTE  Status %ld\n", Status) );
577 			*/
578 			break;
579 
580 		case MXF_SET_MONITOR_MUTE :
581 			Status =
582 				SetAudioMonitorMute( pMixerFunction->Channel.wChannel,
583 										   pMixerFunction->Data.Monitor.wBusOut,
584 										   pMixerFunction->Data.Monitor.Data.bMuteOn );
585 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
586 									 "MXF_SET_MONITOR_MUTE  Status %ld\n", Status) );
587 			break;
588 
589 		case MXF_GET_MONITOR_PAN :
590 			Status =
591 				GetAudioMonitorPan( pMixerFunction->Channel.wChannel,
592 										  pMixerFunction->Data.Monitor.wBusOut,
593 										  pMixerFunction->Data.Monitor.Data.iPan);
594 			/*
595 
596 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
597 									 "MXF_GET_MONITOR_PAN  Status %ld\n", Status) );
598 			*/
599 
600 			break;
601 		case MXF_SET_MONITOR_PAN :
602 			Status =
603 				SetAudioMonitorPan( pMixerFunction->Channel.wChannel,
604 										  pMixerFunction->Data.Monitor.wBusOut,
605 										  pMixerFunction->Data.Monitor.Data.iPan );
606 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
607 									 "MXF_SET_MONITOR_PAN  Status %ld\n", Status) );
608 			break;
609 
610 		case MXF_GET_FLAGS :
611 			pMixerFunction->Data.wFlags = GetFlags();
612 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
613 									 "MXF_GET_FLAGS 0x%x  Status %ld\n",
614 									 pMixerFunction->Data.wFlags,
615 									 Status) );
616 			break;
617 		case MXF_SET_FLAGS :
618 			SetFlags( pMixerFunction->Data.wFlags );
619 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
620 									 "MXF_SET_FLAGS 0x%x  Status %ld\n",
621 									 pMixerFunction->Data.wFlags,
622 									 Status) );
623 			break;
624 		case MXF_CLEAR_FLAGS :
625 			ClearFlags( pMixerFunction->Data.wFlags );
626 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
627 									 "MXF_CLEAR_FLAGS 0x%x  Status %ld\n",
628 									 pMixerFunction->Data.wFlags,
629 									 Status) );
630 			break;
631 
632 		case MXF_GET_SAMPLERATE_LOCK :
633 			GetAudioLockedSampleRate( pMixerFunction->Data.dwLockedSampleRate );
634 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
635 									 "MXF_GET_SAMPLERATE_LOCK 0x%lx  Status %ld\n",
636 									 pMixerFunction->Data.dwLockedSampleRate,
637 									 Status) );
638 			break;
639 
640 		case MXF_SET_SAMPLERATE_LOCK :
641 			SetAudioLockedSampleRate( pMixerFunction->Data.dwLockedSampleRate );
642 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
643 									 "MXF_SET_SAMPLERATE_LOCK 0x%lx  Status %ld\n",
644 									 pMixerFunction->Data.dwLockedSampleRate,
645 									 Status) );
646 			break;
647 
648 		case MXF_GET_SAMPLERATE :
649 
650 			GetAudioSampleRate( &pMixerFunction->Data.dwSampleRate );
651 
652 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
653 									 "MXF_GET_SAMPLERATE 0x%lx  Status %ld\n",
654 									 pMixerFunction->Data.dwSampleRate,
655 									 Status) );
656 			break;
657 
658 #ifdef MIDI_SUPPORT
659 
660 		case MXF_GET_MIDI_IN_ACTIVITY :
661 			pMixerFunction->Data.bMidiActive = m_MidiIn.IsActive();
662 
663 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
664 									 "MXF_GET_MIDI_IN_ACTIVITY %s "
665 									 "Status %ld\n",
666 									 ( pMixerFunction->Data.bMidiActive )
667 										? "ACTIVE" : "INACTIVE",
668 									 Status) );
669 			break;
670 
671 
672 		case MXF_GET_MIDI_OUT_ACTIVITY :
673 			pMixerFunction->Data.bMidiActive =
674 				GetDspCommObject()->IsMidiOutActive();
675 
676 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
677 									 "MXF_GET_MIDI_OUT_ACTIVITY %s "
678 									 "Status %ld\n",
679 									 ( pMixerFunction->Data.bMidiActive )
680 										? "ACTIVE" : "INACTIVE",
681 									 Status) );
682 			break;
683 
684 #endif // MIDI_SUPPORT
685 
686 
687 		case MXF_GET_DIGITAL_MODE :
688 			Status = ECHOSTATUS_OK;
689 			pMixerFunction->Data.iDigMode = GetDigitalMode();
690 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
691 									 "MXF_GET_DIGITAL_MODE %s "
692 									 "Status %ld\n",
693 									 ( DIGITAL_MODE_SPDIF_RCA ==
694 											pMixerFunction->Data.iDigMode )
695 										? "S/PDIF RCA"
696 										: ( DIGITAL_MODE_SPDIF_OPTICAL ==
697 											pMixerFunction->Data.iDigMode )
698 												? "S/PDIF Optical" : "ADAT",
699 									 Status) );
700 			break;
701 
702 
703 		case MXF_SET_DIGITAL_MODE :
704 			Status = SetDigitalMode( (BYTE) pMixerFunction->Data.iDigMode );
705 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
706 									 "MXF_SET_DIGITAL_MODE %s "
707 									 "Status %ld\n",
708 									 ( DIGITAL_MODE_SPDIF_RCA ==
709 											pMixerFunction->Data.iDigMode )
710 										? "S/PDIF RCA"
711 										: ( DIGITAL_MODE_SPDIF_OPTICAL ==
712 											pMixerFunction->Data.iDigMode )
713 												? "S/PDIF Optical" : "ADAT",
714 									 Status) );
715 			break;
716 
717 
718 		case MXF_GET_PAN :
719 			Status = GetAudioPan( pMixerFunction);
720 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
721 									 "MXF_GET_PAN  Status %ld\n", Status) );
722 			break;
723 
724 		case MXF_SET_PAN :
725 			Status = SetAudioPan( pMixerFunction);
726 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
727 									 "MXF_SET_PAN  Status %ld\n", Status) );
728 			break;
729 
730 #ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT
731 
732 		case MXF_GET_DIG_IN_AUTO_MUTE	:
733 			Status =  GetDigitalInAutoMute( pMixerFunction );
734 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
735 									 "MXF_GET_DIG_IN_AUTO_MUTE  Status %ld\n", Status) );
736 			break;
737 
738 
739 		case MXF_SET_DIG_IN_AUTO_MUTE	:
740 			Status = SetDigitalInAutoMute( pMixerFunction );
741 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
742 									 "MXF_SET_DIG_IN_AUTO_MUTE  Status %ld\n", Status) );
743 			break;
744 
745 #endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT
746 
747 		case MXF_GET_AUDIO_LATENCY :
748 			GetAudioLatency( &(pMixerFunction->Data.Latency) );
749 			break;
750 
751 #ifdef PHANTOM_POWER_CONTROL
752 
753 		case MXF_GET_PHANTOM_POWER :
754 			GetPhantomPower( &(pMixerFunction->Data.fPhantomPower) );
755 			break;
756 
757 		case MXF_SET_PHANTOM_POWER :
758 			SetPhantomPower( pMixerFunction->Data.fPhantomPower );
759 			break;
760 
761 #endif
762 
763 		default :
764 			iRtnDataSz = 0;
765 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerFunction: "
766 									 "Function %ld not supported\n",
767 									 pMixerFunction->iFunction) );
768 			return ECHOSTATUS_NOT_SUPPORTED;
769 	}
770 
771 	pMixerFunction->RtnStatus = Status;
772 	iRtnDataSz = sizeof( MIXER_FUNCTION );
773 
774 	return Status;
775 
776 }	// ECHOSTATUS CEchoGals::ProcessMixerFunction
777 
778 
779 
780 
781 //===========================================================================
782 //
783 // Process multiple mixer functions
784 //
785 //===========================================================================
786 
787 ECHOSTATUS CEchoGals::ProcessMixerMultiFunction
788 (
789 	PMIXER_MULTI_FUNCTION	pMixerFunctions,	// Request from mixer
790 	INT32 &							iRtnDataSz			// # bytes returned (if any)
791 )
792 {
793 	ECHOSTATUS			Status = ECHOSTATUS_NOT_SUPPORTED;
794 	PMIXER_FUNCTION	pMixerFunction;
795 	INT32					iRtn, nCard, i;
796 
797 	if (m_fMixerDisabled)
798 		return ECHOSTATUS_MIXER_DISABLED;
799 
800 	iRtnDataSz = sizeof( MIXER_MULTI_FUNCTION ) - sizeof( MIXER_FUNCTION );
801 	pMixerFunction = &pMixerFunctions->MixerFunction[ 0 ];
802 	nCard = pMixerFunction->Channel.wCardId;
803 	for ( i = 0; i < pMixerFunctions->iCount; i++ )
804 	{
805 		pMixerFunction = &pMixerFunctions->MixerFunction[ i ];
806 		if ( nCard != pMixerFunction->Channel.wCardId )
807 		{
808 			ECHO_DEBUGPRINTF( ("CEchoGals::ProcessMixerMultiFunction: "
809 									 "All functions MUST be for the same card "
810 									 "exp %ld act %d!\n",
811 									 nCard,
812 									 pMixerFunction->Channel.wCardId) );
813 			return ECHOSTATUS_NOT_SUPPORTED;
814 		}
815 
816 		Status = ProcessMixerFunction(pMixerFunction,iRtn);
817 		iRtnDataSz += iRtn;
818 	}
819 
820 	return Status;
821 
822 }	// ECHOSTATUS CEchoGals::ProcessMixerMultiFunction
823 
824 
825 
826 
827 //===========================================================================
828 //
829 // Typically, if you are writing a console, you will be polling the driver
830 // to get the current peak and VU meters, clock detect bits, and
831 // control changes.  GetPolledStuff will fill out an ECHO_POLLED_STUFF
832 // structure with all of those things.
833 //
834 //===========================================================================
835 
836 ECHOSTATUS CEchoGals::GetPolledStuff
837 (
838 	ECHO_POLLED_STUFF *pPolledStuff,
839 	NUINT MixerCookie
840 )
841 {
842 	ECHO_MIXER_CLIENT *pClient;
843 	CDspCommObject *pDCO = GetDspCommObject();
844 
845 	if (m_fMixerDisabled)
846 		return ECHOSTATUS_MIXER_DISABLED;
847 
848 	if (NULL == pDCO)
849 		return ECHOSTATUS_DSP_DEAD;
850 
851 	//
852 	// Fill out the non-client-specific portions of the struct
853 	//
854 	pDCO->GetAudioMeters(&(pPolledStuff->Meters));
855 	GetInputClockDetect(pPolledStuff->dwClockDetectBits);
856 
857 	//
858 	// If there is a matching client, fill out the number
859 	// of notifies
860 	//
861 	pClient = GetMixerClient(MixerCookie);
862 	if (NULL == pClient)
863 		pPolledStuff->dwNumPendingNotifies = 0;
864 	else
865 		pPolledStuff->dwNumPendingNotifies = pClient->dwCount;
866 
867 	return ECHOSTATUS_OK;
868 
869 }	// GetPolledStuff
870 
871 
872 //===========================================================================
873 //
874 //	Get the pan setting for an output pipe (virtual outputs only)
875 //
876 //===========================================================================
877 
878 ECHOSTATUS CEchoGals::GetAudioPan
879 (
880 	PMIXER_FUNCTION	pMF
881 )
882 {
883 	WORD 			wPipe;
884 	WORD 			wBus;
885 	ECHOSTATUS 	Status;
886 
887 	if ( 	(pMF->Channel.dwType != ECHO_PIPE_OUT) ||
888 			( !HasVmixer() ) )
889 		return ECHOSTATUS_INVALID_CHANNEL;
890 
891 	wPipe = pMF->Channel.wChannel;
892 	wBus = pMF->Data.PipeOut.wBusOut;
893 	Status = m_PipeOutCtrl.GetPan(wPipe,
894 											wBus,
895 											pMF->Data.PipeOut.Data.iPan);
896 
897 	return Status;
898 
899 }	// ECHOSTATUS CEchoGals::GetAudioPan
900 
901 
902 //===========================================================================
903 //
904 //	Set the pan for an output pipe (virtual outputs only)
905 //
906 //===========================================================================
907 
908 ECHOSTATUS CEchoGals::SetAudioPan
909 (
910 	PMIXER_FUNCTION	pMF
911 )
912 {
913 	WORD 			wPipe;
914 	WORD 			wBus;
915 	ECHOSTATUS 	Status;
916 
917 	if ( 	(pMF->Channel.dwType != ECHO_PIPE_OUT) ||
918 			( !HasVmixer() ) )
919 		return ECHOSTATUS_INVALID_CHANNEL;
920 
921 	wPipe = pMF->Channel.wChannel;
922 	wBus = pMF->Data.PipeOut.wBusOut;
923 	Status = m_PipeOutCtrl.SetPan(wPipe,
924 											wBus,
925 											pMF->Data.PipeOut.Data.iPan);
926 
927 	return Status;
928 
929 }	// ECHOSTATUS CEchoGals::SetAudioPan
930 
931 
932 
933 
934 /****************************************************************************
935 
936 	CEchoGals clock control
937 
938 	The input clock is the sync source - is the audio for this card running
939 	off of the internal clock, synced to word clock, etc.
940 
941 	Output clock is only supported on Layla20 - Layla20 can transmit either
942 	word or super clock.
943 
944  ****************************************************************************/
945 
946 //===========================================================================
947 //
948 // Get input and output clocks - just return the stored value
949 //
950 //===========================================================================
951 
952 ECHOSTATUS CEchoGals::GetInputClock(WORD &wClock)
953 {
954 	if ( NULL == GetDspCommObject() )
955 		return ECHOSTATUS_DSP_DEAD;
956 
957 	wClock = GetDspCommObject()->GetInputClock();
958 
959 	return ECHOSTATUS_OK;
960 }
961 
962 
963 ECHOSTATUS CEchoGals::GetOutputClock(WORD &wClock)
964 {
965 	if ( NULL == GetDspCommObject() )
966 		return ECHOSTATUS_DSP_DEAD;
967 
968 	wClock = GetDspCommObject()->GetOutputClock();
969 
970 	return ECHOSTATUS_OK;
971 }
972 
973 
974 //===========================================================================
975 //
976 // Set input and output clocks - pass it down to the comm page and
977 // store the control change.
978 //
979 //===========================================================================
980 
981 ECHOSTATUS CEchoGals::SetInputClock(WORD wClock)
982 {
983 	ECHOSTATUS Status;
984 
985 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
986 		return ECHOSTATUS_DSP_DEAD;
987 
988 	ECHO_DEBUGPRINTF( ("CEchoGals::SetInputClock: ") );
989 
990 	Status = GetDspCommObject()->SetInputClock( wClock );
991 
992 	if (ECHOSTATUS_OK == Status)
993 	{
994 		MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
995 									MXN_INPUT_CLOCK);
996 	}
997 	return Status;
998 
999 }	// SetInputClock
1000 
1001 
1002 ECHOSTATUS CEchoGals::SetOutputClock(WORD wClock)
1003 {
1004 	ECHOSTATUS Status;
1005 
1006 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1007 		return ECHOSTATUS_DSP_DEAD;
1008 
1009 	ECHO_DEBUGPRINTF( ("CEchoGals::SetOutputClock: ") );
1010 
1011 	Status = GetDspCommObject()->SetOutputClock( wClock );
1012 
1013 	if (ECHOSTATUS_OK == Status)
1014 	{
1015 		MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
1016 									MXN_OUTPUT_CLOCK);
1017 	}
1018 	return Status;
1019 
1020 }
1021 
1022 
1023 //===========================================================================
1024 //
1025 // Get the currently detected clock bits - default method.  Overridden by
1026 // derived card classes.
1027 //
1028 //===========================================================================
1029 
1030 ECHOSTATUS CEchoGals::GetInputClockDetect(DWORD &dwClockDetectBits)
1031 {
1032 	dwClockDetectBits = ECHO_CLOCK_INTERNAL;
1033 
1034 	return ECHOSTATUS_OK;
1035 }
1036 
1037 
1038 //===========================================================================
1039 //
1040 // Set the locked sample rate
1041 //
1042 //===========================================================================
1043 
1044 ECHOSTATUS CEchoGals::SetAudioLockedSampleRate
1045 (
1046 	DWORD		dwSampleRate
1047 )
1048 {
1049 	ECHOSTATUS	Status;
1050 
1051 	Status = QueryAudioSampleRate( dwSampleRate );
1052 	if ( ECHOSTATUS_OK != Status )
1053 		return Status;
1054 
1055 	if (0 != (ECHOGALS_FLAG_SAMPLE_RATE_LOCKED & GetFlags()))
1056 	{
1057 		GetDspCommObject()->SetSampleRate( dwSampleRate );
1058 		m_dwSampleRate = dwSampleRate;
1059 	}
1060 
1061 	m_dwLockedSampleRate = dwSampleRate;
1062 
1063 	return ECHOSTATUS_OK;
1064 
1065 }	// ECHOSTATUS CEchoGals::SetAudioLockedSampleRate
1066 
1067 
1068 //===========================================================================
1069 //
1070 // Get the locked sample rate
1071 //
1072 //===========================================================================
1073 
1074 ECHOSTATUS CEchoGals::GetAudioLockedSampleRate
1075 (
1076 	DWORD	   &dwSampleRate
1077 )
1078 {
1079 	dwSampleRate = m_dwLockedSampleRate;
1080 
1081 	return ECHOSTATUS_OK;
1082 
1083 }	// ECHOSTATUS CEchoGals::GetAudioLockedSampleRate
1084 
1085 
1086 
1087 #ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT
1088 
1089 //===========================================================================
1090 //
1091 // Get the digital input auto mute flag from the comm page
1092 //
1093 //===========================================================================
1094 
1095 ECHOSTATUS CEchoGals::GetDigitalInAutoMute(PMIXER_FUNCTION pMixerFunction)
1096 {
1097 	BOOL fAutoMute;
1098 
1099 	if (0 == (m_wFlags & ECHOGALS_ROFLAG_DIGITAL_IN_AUTOMUTE))
1100 	{
1101 		pMixerFunction->Data.fDigitalInAutoMute = FALSE;
1102 		return ECHOSTATUS_NOT_SUPPORTED;
1103 	}
1104 
1105 	GetDspCommObject()->GetDigitalInputAutoMute(	fAutoMute );
1106 	pMixerFunction->Data.fDigitalInAutoMute = fAutoMute;
1107 
1108 	return ECHOSTATUS_OK;
1109 
1110 } // GetDigitalInAutoMute
1111 
1112 
1113 //===========================================================================
1114 //
1115 // Set the digital input auto mute flag
1116 //
1117 //===========================================================================
1118 
1119 ECHOSTATUS CEchoGals::SetDigitalInAutoMute(PMIXER_FUNCTION pMixerFunction)
1120 {
1121 	BOOL fAutoMute;
1122 
1123 	if (0 == (m_wFlags & ECHOGALS_ROFLAG_DIGITAL_IN_AUTOMUTE))
1124 		return ECHOSTATUS_NOT_SUPPORTED;
1125 
1126 	fAutoMute = pMixerFunction->Data.fDigitalInAutoMute;
1127 	GetDspCommObject()->SetDigitalInputAutoMute( fAutoMute );
1128 
1129 	return ECHOSTATUS_OK;
1130 
1131 } // SetDigitalInAutoMute
1132 
1133 #endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT
1134 
1135 
1136 //===========================================================================
1137 //
1138 // Get the gain for an output bus, input bus, or output pipe.
1139 //
1140 // Gain levels are in units of dB * 256.
1141 //
1142 //===========================================================================
1143 
1144 ECHOSTATUS CEchoGals::GetAudioLineLevel
1145 (
1146 	PMIXER_FUNCTION	pMF
1147 )
1148 {
1149 	WORD 			wPipe;
1150 	WORD 			wBus;
1151 	ECHOSTATUS 	Status;
1152 
1153 	switch (pMF->Channel.dwType)
1154 	{
1155 		case ECHO_BUS_OUT :
1156 
1157 			wBus = pMF->Channel.wChannel;
1158 
1159 			if (wBus < GetNumBussesOut())
1160 			{
1161 				pMF->Data.iLevel = m_BusOutLineLevels[wBus].GetGain();
1162 				Status = ECHOSTATUS_OK;
1163 			}
1164 			else
1165 			{
1166 				Status = ECHOSTATUS_INVALID_CHANNEL;
1167 			}
1168 
1169 			break;
1170 
1171 		case ECHO_BUS_IN :
1172 
1173 			wBus = pMF->Channel.wChannel;
1174 			if (wBus < GetNumBussesIn())
1175 			{
1176 				pMF->Data.iLevel = m_BusInLineLevels[wBus].GetGain();
1177 				Status = ECHOSTATUS_OK;
1178 			}
1179 			else
1180 			{
1181 				Status = ECHOSTATUS_INVALID_CHANNEL;
1182 			}
1183 			break;
1184 
1185 		case ECHO_PIPE_OUT :
1186 
1187 			wPipe = pMF->Channel.wChannel;
1188 			wBus = pMF->Data.PipeOut.wBusOut;
1189  			Status = m_PipeOutCtrl.GetGain(	wPipe,
1190 														wBus,
1191 														pMF->Data.PipeOut.Data.iLevel);
1192 			break;
1193 
1194 		default:
1195 			Status = ECHOSTATUS_INVALID_PARAM;
1196 			break;
1197 	}
1198 
1199 	return Status;
1200 
1201 }	// ECHOSTATUS CEchoGals::GetAudioLineLevel
1202 
1203 
1204 //===========================================================================
1205 //
1206 // Utility function to check that a setting is within the correct range.
1207 //
1208 //===========================================================================
1209 
1210 ECHOSTATUS CheckSetting(INT32 iValue,INT32 iMin,INT32 iMax)
1211 {
1212 	if ( (iValue > iMax) || (iValue < iMin))
1213 		return ECHOSTATUS_INVALID_PARAM;
1214 
1215 	return ECHOSTATUS_OK;
1216 
1217 }	// CheckSetting
1218 
1219 
1220 //===========================================================================
1221 //
1222 // Set the gain for an output bus, input bus, or output pipe.
1223 //
1224 // Gain levels are in units of dB * 256.
1225 //
1226 //===========================================================================
1227 
1228 ECHOSTATUS CEchoGals::SetAudioLineLevel
1229 (
1230 	PMIXER_FUNCTION	pMF
1231 )
1232 {
1233 	WORD 			wPipe;
1234 	WORD 			wBus;
1235 	ECHOSTATUS 	Status;
1236 	INT32 			iLevel;
1237 
1238 	switch (pMF->Channel.dwType)
1239 	{
1240 		case ECHO_BUS_OUT :
1241 
1242 			wBus = pMF->Channel.wChannel;
1243 			iLevel = pMF->Data.iLevel;
1244 
1245 			Status = CheckSetting(iLevel,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT);
1246 			if (ECHOSTATUS_OK != Status)
1247 				break;
1248 
1249 			Status = m_BusOutLineLevels[wBus].SetGain(iLevel);
1250 			break;
1251 
1252 		case ECHO_BUS_IN :
1253 
1254 			wBus = pMF->Channel.wChannel;
1255 			iLevel = pMF->Data.iLevel;
1256 
1257 			Status = CheckSetting(iLevel,ECHOGAIN_MININP,ECHOGAIN_MAXINP);
1258 			if (ECHOSTATUS_OK != Status)
1259 				break;
1260 
1261 			Status = m_BusInLineLevels[wBus].SetGain(iLevel);
1262 			break;
1263 
1264 		case ECHO_PIPE_OUT :
1265 
1266 			wPipe = pMF->Channel.wChannel;
1267 			wBus = pMF->Data.PipeOut.wBusOut;
1268 			iLevel = pMF->Data.PipeOut.Data.iLevel;
1269 
1270 			Status = CheckSetting(iLevel,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT);
1271 			if (ECHOSTATUS_OK != Status)
1272 				break;
1273 
1274  			Status = m_PipeOutCtrl.SetGain(	wPipe,
1275 														wBus,
1276 														iLevel);
1277 			break;
1278 
1279 		default:
1280 			Status = ECHOSTATUS_INVALID_PARAM;
1281 			break;
1282 	}
1283 
1284 	return Status;
1285 
1286 }	// ECHOSTATUS CEchoGals::SetAudioLineLevel
1287 
1288 
1289 //===========================================================================
1290 //
1291 // Get the nominal level for an output or input bus.  The nominal level is
1292 // also referred to as the +4/-10 switch.
1293 //
1294 //===========================================================================
1295 
1296 ECHOSTATUS CEchoGals::GetAudioNominal
1297 (
1298 	PMIXER_FUNCTION	pMF
1299 )
1300 {
1301 	BYTE					byNominal;
1302 	ECHOSTATUS			Status;
1303 	CDspCommObject *	pDspCommObj = GetDspCommObject();
1304 	WORD					wCh;
1305 
1306 	if ( NULL == pDspCommObj || pDspCommObj->IsBoardBad() )
1307 		return ECHOSTATUS_DSP_DEAD;
1308 
1309 	switch (pMF->Channel.dwType)
1310 	{
1311 		case ECHO_BUS_OUT :
1312 			wCh = pMF->Channel.wChannel;
1313 			break;
1314 
1315 		case ECHO_BUS_IN :
1316 			wCh = pMF->Channel.wChannel + GetNumBussesOut();
1317 			break;
1318 
1319 		default :
1320 			return ECHOSTATUS_INVALID_CHANNEL;
1321 	}
1322 
1323 	Status = pDspCommObj->GetNominalLevel( wCh, &byNominal );
1324 
1325 	if ( ECHOSTATUS_OK != Status )
1326 		return Status;
1327 
1328 	pMF->Data.iNominal = ( byNominal ) ? -10 : 4;
1329 
1330 	return ECHOSTATUS_OK;
1331 }	// ECHOSTATUS CEchoGals::GetAudioNominal
1332 
1333 
1334 //===========================================================================
1335 //
1336 // Set the nominal level for an output or input bus.  The nominal level is
1337 // also referred to as the +4/-10 switch.
1338 //
1339 //===========================================================================
1340 
1341 ECHOSTATUS CEchoGals::SetAudioNominal
1342 (
1343 	PMIXER_FUNCTION	pMF
1344 )
1345 {
1346 	ECHOSTATUS	Status;
1347 	WORD			wCh;
1348 	INT32			iNominal;
1349 
1350 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1351 		return ECHOSTATUS_DSP_DEAD;
1352 
1353 	switch (pMF->Channel.dwType)
1354 	{
1355 		case ECHO_BUS_OUT :
1356 			wCh = pMF->Channel.wChannel;
1357 			break;
1358 
1359 		case ECHO_BUS_IN :
1360 			wCh = pMF->Channel.wChannel + GetNumBussesOut();
1361 			break;
1362 
1363 		default :
1364 			return ECHOSTATUS_INVALID_CHANNEL;
1365 	}
1366 
1367 	iNominal = pMF->Data.iNominal;
1368 
1369 	if ((iNominal!= -10) && (iNominal != 4))
1370 		return ECHOSTATUS_INVALID_PARAM;
1371 
1372 	Status =
1373 		GetDspCommObject()->SetNominalLevel( wCh,
1374 														( iNominal == -10 ) );
1375 
1376 	if ( ECHOSTATUS_OK != Status )
1377 		return Status;
1378 
1379 	Status = MixerControlChanged( (WORD) pMF->Channel.dwType,
1380 											MXN_NOMINAL,
1381 											pMF->Channel.wChannel);
1382 	return Status;
1383 
1384 }	// ECHOSTATUS CEchoGals::SetAudioNominal
1385 
1386 
1387 //===========================================================================
1388 //
1389 // Set the mute for an output bus, input bus, or output pipe.
1390 //
1391 //===========================================================================
1392 
1393 ECHOSTATUS CEchoGals::SetAudioMute
1394 (
1395 		PMIXER_FUNCTION	pMF
1396 )
1397 {
1398 	WORD 			wPipe;
1399 	WORD 			wBus;
1400 	ECHOSTATUS 	Status;
1401 	BOOL			bMute;
1402 
1403 	switch (pMF->Channel.dwType)
1404 	{
1405 		case ECHO_BUS_OUT :
1406 
1407 			wBus = pMF->Channel.wChannel;
1408 			bMute = pMF->Data.bMuteOn;
1409 			Status = m_BusOutLineLevels[wBus].SetMute(bMute);
1410 			break;
1411 
1412 		case ECHO_BUS_IN :
1413 
1414 			wBus = pMF->Channel.wChannel;
1415 			bMute = pMF->Data.bMuteOn;
1416 			Status = m_BusInLineLevels[wBus].SetMute(bMute);
1417 			break;
1418 
1419 		case ECHO_PIPE_OUT :
1420 
1421 			wPipe = pMF->Channel.wChannel;
1422 			wBus = pMF->Data.PipeOut.wBusOut;
1423 			bMute = pMF->Data.PipeOut.Data.bMuteOn;
1424  			Status = m_PipeOutCtrl.SetMute(	wPipe,
1425 														wBus,
1426 														bMute);
1427 			break;
1428 
1429 		default:
1430 			Status = ECHOSTATUS_INVALID_PARAM;
1431 			break;
1432 	}
1433 
1434 	return Status;
1435 }	// ECHOSTATUS CEchoGals::SetAudioMute
1436 
1437 
1438 //===========================================================================
1439 //
1440 // Get the mute for an output bus, input bus, or output pipe.
1441 //
1442 //===========================================================================
1443 
1444 ECHOSTATUS CEchoGals::GetAudioMute
1445 (
1446 		PMIXER_FUNCTION	pMF
1447 )
1448 {
1449 	WORD 			wPipe;
1450 	WORD 			wBus;
1451 	ECHOSTATUS 	Status;
1452 
1453 	switch (pMF->Channel.dwType)
1454 	{
1455 		case ECHO_BUS_OUT :
1456 
1457 			wBus = pMF->Channel.wChannel;
1458 
1459 			if (wBus < GetNumBussesOut())
1460 			{
1461 				pMF->Data.bMuteOn = m_BusOutLineLevels[wBus].IsMuteOn();
1462 				Status = ECHOSTATUS_OK;
1463 			}
1464 			else
1465 			{
1466 				Status = ECHOSTATUS_INVALID_CHANNEL;
1467 			}
1468 
1469 			break;
1470 
1471 		case ECHO_BUS_IN :
1472 
1473 			wBus = pMF->Channel.wChannel;
1474 
1475 			if (wBus < GetNumBussesIn())
1476 			{
1477 				pMF->Data.bMuteOn = m_BusInLineLevels[wBus].IsMuteOn();
1478 				Status = ECHOSTATUS_OK;
1479 			}
1480 			else
1481 			{
1482 				Status = ECHOSTATUS_INVALID_CHANNEL;
1483 			}
1484 			break;
1485 
1486 		case ECHO_PIPE_OUT :
1487 
1488 			wPipe = pMF->Channel.wChannel;
1489 			wBus = pMF->Data.PipeOut.wBusOut;
1490  			Status = m_PipeOutCtrl.GetMute(	wPipe,
1491 														wBus,
1492 														pMF->Data.PipeOut.Data.bMuteOn);
1493 			break;
1494 
1495 		default:
1496 			Status = ECHOSTATUS_INVALID_PARAM;
1497 			break;
1498 	}
1499 
1500 	return Status;
1501 
1502 }	// ECHOSTATUS CEchoGals::GetAudioMute
1503 
1504 
1505 //===========================================================================
1506 //
1507 // Get the monitor gain for a single input bus mixed to a single output bus.
1508 //
1509 //===========================================================================
1510 
1511 ECHOSTATUS CEchoGals::GetAudioMonitor
1512 (
1513 	WORD	wBusIn,
1514 	WORD	wBusOut,
1515 	INT32 &	iGain
1516 )
1517 {
1518 	if ( wBusIn  >= GetNumBussesIn() ||
1519 		  wBusOut >= GetNumBussesOut() )
1520 	{
1521 		return ECHOSTATUS_INVALID_INDEX;
1522 	}
1523 
1524 	//
1525 	// Get the monitor value
1526 	//
1527 	m_MonitorCtrl.GetGain(wBusIn,wBusOut,iGain);
1528 
1529 	return ECHOSTATUS_OK;
1530 
1531 }	// ECHOSTATUS CEchoGals::GetAudioMonitor
1532 
1533 
1534 //===========================================================================
1535 //
1536 // Set the monitor gain for a single input bus mixed to a single output bus.
1537 //
1538 //===========================================================================
1539 
1540 ECHOSTATUS CEchoGals::SetAudioMonitor
1541 (
1542 	WORD	wBusIn,
1543 	WORD	wBusOut,
1544 	INT32	iGain
1545 )
1546 {
1547 	ECHOSTATUS	Status;
1548 
1549 	if ( wBusIn  >= GetNumBussesIn() ||
1550 		  wBusOut >= GetNumBussesOut() )
1551 	{
1552 		return ECHOSTATUS_INVALID_INDEX;
1553 	}
1554 
1555 	Status = CheckSetting(iGain,ECHOGAIN_MINOUT,ECHOGAIN_MAXOUT);
1556 	if (ECHOSTATUS_OK == Status)
1557 	{
1558 		//
1559 		// Set the monitor gain
1560 		//
1561 		m_MonitorCtrl.SetGain(wBusIn,wBusOut,iGain);
1562 	}
1563 
1564 	return Status;
1565 
1566 }	// ECHOSTATUS CEchoGals::SetAudioMonitor
1567 
1568 
1569 //===========================================================================
1570 //
1571 //	Helper functions for doing log conversions on pan values
1572 //
1573 // The parameter iNum is a fixed point 32 bit number in 16.16 format;
1574 // that is, 16 bits of integer and 16 bits of decimal
1575 // To convert a float number to fixed point, simply multiply by 2^16 and round
1576 //
1577 // Valid range for iNum is from +1.0 (0x10000) to 0.
1578 //
1579 //===========================================================================
1580 
1581 #define FIXED_BASE		16							// 16 bits of fraction
1582 #define FIXED_ONE_HALF	((INT32) 0x00008000)	// 0.5 in 16.16 format
1583 #define COEFF_A2			((INT32) 0xffffa9ac)	// -.3372223
1584 #define COEFF_A1			((INT32) 0x0000ff8a)	//  .9981958
1585 #define COEFF_A0			((INT32) 0xffff5661)	// -.6626105
1586 
1587 #define DB_CONVERT		0x60546		// 6.02... in 16.16
1588 
1589 // Note use of double precision here to prevent overflow
1590 static INT32 FixedMult( INT32 iNum1, INT32 iNum2 )
1591 {
1592 	LONGLONG llNum;
1593 
1594 	llNum = (LONGLONG) iNum1 * (LONGLONG) iNum2;
1595 
1596 	return (INT32) (llNum >> FIXED_BASE);
1597 }	// INT32 FixedMult( INT32 iNum1, INT32 iNum2 )
1598 
1599 
1600 static INT32 log2( INT32 iNum )
1601 {
1602 	INT32 iNumShifts;
1603 	INT32 iTemp;
1604 
1605 	// log2 is undefined for zero, so return -infinity (or close enough)
1606 	if ( 0 == iNum )
1607 		return ECHOGAIN_MUTED;
1608 
1609 	// Step 1 - Normalize and save the number of shifts
1610 	// Keep shifting iNum up until iNum > 0.5
1611 	iNumShifts = 0;
1612 	while ( iNum < FIXED_ONE_HALF )
1613 	{
1614 		iNumShifts++;
1615 		iNum <<= 1;
1616 	}
1617 
1618 	// Step 2 - Calculate LOG2 by polynomial approximation.  8 bit accuracy.
1619 	//
1620 	// LOG2(x) = 4.0* (-.3372223 x*x + .9981958 x - .6626105)
1621 	//                           a2             a1            a0
1622 	// where  0.5 <= x < 1.0
1623 	//
1624 
1625 	// Compute polynomial sum
1626 	iTemp = FixedMult( iNum, iNum );				// iTemp now has iNum squared
1627 	iTemp = FixedMult( iTemp, COEFF_A2 );
1628 	iTemp += FixedMult( iNum, COEFF_A1 );
1629 	iTemp += COEFF_A0;
1630 
1631 	// Multiply by four
1632 	iTemp <<= 2;
1633 
1634 	// Account for the normalize shifts
1635 	iTemp -= ( iNumShifts << FIXED_BASE );
1636 
1637 	return( iTemp );
1638 }	// INT32 log2( INT32 iNum )
1639 
1640 
1641 //
1642 //	Convert pan value to Db X 256
1643 //	Pan value is 0 - MAX_MIXER_PAN
1644 //
1645 INT32 PanToDb( INT32 iPan )
1646 {
1647 	if ( iPan >= ( MAX_MIXER_PAN - 1 ) )
1648 		return( 0 );
1649 	if ( iPan <= 1 )
1650 		return( ECHOGAIN_MUTED );
1651 	//
1652 	//	Convert pan to 16.16
1653 	//
1654 	iPan = ( iPan << 16 ) / MAX_MIXER_PAN;
1655 	//
1656 	//	Take the log
1657 	//
1658 	iPan = log2( iPan );
1659 	//
1660 	// To convert to decibels*256, just multiply by the conversion factor
1661 	//
1662 	iPan = FixedMult( iPan << 8, DB_CONVERT );
1663 	//
1664 	// To round, add one half and then mask off the fractional bits
1665 	//
1666 	iPan = ( iPan + FIXED_ONE_HALF ) >> FIXED_BASE;
1667 	return( iPan );
1668 }	// INT32 PanToDb( INT32 iPan )
1669 
1670 
1671 //===========================================================================
1672 //
1673 // Set the monitor pan
1674 //
1675 //	For this to work effectively, both the input and output channels must
1676 //	both either be odd or even.  Thus even channel numbers are for the
1677 //	left channel and odd channel numbers are for the right channel.
1678 //	Pan values will be computed for both channels.
1679 //
1680 // iPan ranges from 0 (hard left) to MAX_MIXER_PAN (hard right)
1681 //
1682 //===========================================================================
1683 
1684 ECHOSTATUS CEchoGals::SetAudioMonitorPan
1685 (
1686 	WORD	wBusIn,
1687 	WORD	wBusOut,
1688 	INT32	iPan						// New pan
1689 )
1690 {
1691 	ECHOSTATUS Status;
1692 
1693 	if ( wBusIn  >= GetNumBussesIn() ||
1694 		  wBusOut >= GetNumBussesOut() )
1695 	{
1696 		return ECHOSTATUS_INVALID_INDEX;
1697 	}
1698 
1699 	Status = CheckSetting(iPan,0,MAX_MIXER_PAN);
1700 	if (ECHOSTATUS_OK == Status)
1701 	{
1702 		//
1703 		// Set the pan
1704 		//
1705 		m_MonitorCtrl.SetPan(wBusIn,wBusOut,iPan);
1706 	}
1707 
1708 	return Status;
1709 
1710 }	// ECHOSTATUS CEchoGals::SetAudioMonitorPan
1711 
1712 
1713 //===========================================================================
1714 //
1715 // Get the monitor pan
1716 //
1717 //===========================================================================
1718 
1719 ECHOSTATUS CEchoGals::GetAudioMonitorPan
1720 (
1721 	WORD	wBusIn,
1722 	WORD	wBusOut,
1723 	INT32 &	iPan						// Returns current pan (0 - MAX_MIXER_PAN)
1724 )
1725 {
1726 	if ( wBusIn  >= GetNumBussesIn() ||
1727 		  wBusOut >= GetNumBussesOut() )
1728 	{
1729 		return ECHOSTATUS_INVALID_INDEX;
1730 	}
1731 
1732 	//
1733 	// Get the pan
1734 	//
1735 	m_MonitorCtrl.GetPan(wBusIn,wBusOut,iPan);
1736 
1737 	return ECHOSTATUS_OK;
1738 
1739 }	// ECHOSTATUS CEchoGals::GetAudioMonitorPan
1740 
1741 
1742 //===========================================================================
1743 //
1744 // Set the monitor mute
1745 //
1746 //===========================================================================
1747 
1748 ECHOSTATUS CEchoGals::SetAudioMonitorMute
1749 (
1750 	WORD	wBusIn,
1751 	WORD	wBusOut,
1752 	BOOL	bMute						// New state
1753 )
1754 {
1755 	if ( wBusIn  >= GetNumBussesIn() ||
1756 		  wBusOut >= GetNumBussesOut() )
1757 	{
1758 		return ECHOSTATUS_INVALID_INDEX;
1759 	}
1760 
1761 	//
1762 	// Set the mute
1763 	//
1764 	m_MonitorCtrl.SetMute(wBusIn,wBusOut,bMute);
1765 
1766 	return ECHOSTATUS_OK;
1767 
1768 }	// ECHOSTATUS CEchoGals::SetAudioMonitorMute
1769 
1770 
1771 //===========================================================================
1772 //
1773 // Get the monitor mute
1774 //
1775 //===========================================================================
1776 
1777 ECHOSTATUS CEchoGals::GetAudioMonitorMute
1778 (
1779 	WORD	wBusIn,
1780 	WORD	wBusOut,
1781 	BOOL 	&bMute	  				// Returns current state
1782 )
1783 {
1784 	if ( wBusIn  >= GetNumBussesIn() ||
1785 		  wBusOut >= GetNumBussesOut() )
1786 	{
1787 		return ECHOSTATUS_INVALID_INDEX;
1788 	}
1789 
1790 	//
1791 	// Get the mute
1792 	//
1793 	m_MonitorCtrl.GetMute(wBusIn,wBusOut,bMute);
1794 
1795 	return ECHOSTATUS_OK;
1796 
1797 }	// ECHOSTATUS CEchoGals::GetAudioMonitorMute
1798 
1799 
1800 //===========================================================================
1801 //
1802 // Set the S/PDIF output format to professional or consumer
1803 //
1804 //===========================================================================
1805 
1806 void CEchoGals::SetProfessionalSpdif( BOOL bNewStatus )
1807 {
1808 	ECHO_DEBUGPRINTF(("CEchoGals::SetProfessionalSpdif %d\n",bNewStatus));
1809 
1810 	if ( NULL != GetDspCommObject() )
1811 	{
1812 		GetDspCommObject()->SetProfessionalSpdif( bNewStatus );
1813 		MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
1814 									MXN_SPDIF );
1815 	}
1816 }	// void CEchoGals::SetProfessionalSpdif( BOOL bNewStatus )
1817 
1818 
1819 //===========================================================================
1820 //
1821 // Set driver flags
1822 //
1823 //	See ECHOGALS_FLAG_??? definitions in EchoGalsXface.h
1824 //
1825 //===========================================================================
1826 
1827 ECHOSTATUS CEchoGals::SetFlags
1828 (
1829 	WORD	wFlags
1830 )
1831 {
1832 	//
1833 	// Mask off the read-only flags so they don't change
1834 	//
1835 	wFlags &= ECHOGALS_FLAG_WRITABLE_MASK;
1836 
1837 	//
1838 	// Set the flags & mark the flags as changed
1839 	//
1840 	m_wFlags |= wFlags;
1841 
1842 	MixerControlChanged(	ECHO_NO_CHANNEL_TYPE,
1843 								MXN_FLAGS );
1844 
1845 	return ECHOSTATUS_OK;
1846 
1847 }	// ECHOSTATUS CEchoGals::SetFlags
1848 
1849 
1850 //===========================================================================
1851 //
1852 // Clear driver flags
1853 //
1854 //	See ECHOGALS_FLAG_??? definitions in EchoGalsXface.h
1855 //
1856 //===========================================================================
1857 
1858 ECHOSTATUS CEchoGals::ClearFlags
1859 (
1860 	WORD	wFlags
1861 )
1862 {
1863 	//
1864 	// Mask off the read-only flags so they don't change
1865 	//
1866 	wFlags &= ECHOGALS_FLAG_WRITABLE_MASK;
1867 
1868 	//
1869 	// Clear the flags & mark the flags as changed
1870 	//
1871 	m_wFlags &= ~wFlags;
1872 
1873 	MixerControlChanged(	ECHO_NO_CHANNEL_TYPE,
1874 								MXN_FLAGS );
1875 
1876 	return ECHOSTATUS_OK;
1877 
1878 }	// ECHOSTATUS CEchoGals::ClearFlags
1879 
1880 
1881 //===========================================================================
1882 //
1883 // Set the digital mode - currently for Gina24, Layla24, and Mona
1884 //
1885 //===========================================================================
1886 
1887 ECHOSTATUS CEchoGals::SetDigitalMode
1888 (
1889 	BYTE byDigitalMode
1890 )
1891 {
1892 	ECHOSTATUS	Status;
1893 	BYTE			byPreviousDigitalMode;
1894 
1895 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1896 		return ECHOSTATUS_DSP_DEAD;
1897 
1898 	if ( 0 == GetDspCommObject()->GetDigitalModes() )
1899 		return ECHOSTATUS_DIGITAL_MODE_NOT_SUPPORTED;
1900 
1901 	if ( TRUE == GetDspCommObject()->IsTransportActive() )
1902 	{
1903 		ECHO_DEBUGPRINTF( ( 	"CEchoGals::SetDigitalMode()  Cannot set the digital "
1904 									"mode while transport is running\n"));
1905 		return ECHOSTATUS_BUSY;
1906 	}
1907 	byPreviousDigitalMode = GetDspCommObject()->GetDigitalMode();
1908 	Status = GetDspCommObject()->SetDigitalMode( byDigitalMode );
1909 	MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
1910 								MXN_DIGITAL_MODE );
1911 	MixerControlChanged( ECHO_NO_CHANNEL_TYPE,
1912 								MXN_INPUT_CLOCK );
1913 
1914 	//
1915 	//	If we successfully changed the digital mode from or to ADAT, then
1916 	//	make sure all output, input and monitor levels are updated by the
1917 	//	DSP comm object.
1918 	//
1919 	if ( ECHOSTATUS_OK == Status &&
1920 		  byPreviousDigitalMode != byDigitalMode &&
1921 		  ( DIGITAL_MODE_ADAT == byPreviousDigitalMode ||
1922 		    DIGITAL_MODE_ADAT == byDigitalMode ) )
1923 	{
1924 		WORD	i, j,wBus,wPipe;
1925 
1926 		for ( i = 0; i < GetNumBussesIn(); i++ )
1927 		{
1928 			for ( j = 0; j < GetNumBussesOut(); j += 2 )
1929 			{
1930 				m_MonitorCtrl.SetGain(i,j,ECHOGAIN_UPDATE,FALSE);
1931 			}
1932 		}
1933 
1934 		for ( wBus = 0; wBus < GetNumBussesOut(); wBus++)
1935 		{
1936 			for ( wPipe = 0; wPipe < GetNumPipesOut(); wPipe++)
1937 			{
1938 				m_PipeOutCtrl.SetGain(wPipe,wBus,ECHOGAIN_UPDATE,FALSE);
1939 			}
1940 		}
1941 
1942 		for ( i = 0; i < GetNumBussesOut(); i++ )
1943 		{
1944 			m_BusOutLineLevels[ i ].SetGain(ECHOGAIN_UPDATE,FALSE);
1945 		}
1946 
1947 		for ( i = 0; i < GetNumBussesIn(); i++ )
1948 		{
1949 			m_BusInLineLevels[ i ].SetGain( ECHOGAIN_UPDATE );
1950 		}
1951 
1952 		//
1953 		// Now set them all at once, since all the
1954 		// fImmediate parameters were set to FALSE.  Do the output
1955 		// bus _and_ the output pipe in case this is a vmixer card.
1956 		//
1957 		m_BusOutLineLevels[0].SetGain(ECHOGAIN_UPDATE,TRUE);
1958 		m_PipeOutCtrl.SetGain(0,0,ECHOGAIN_UPDATE,TRUE);
1959 	}
1960 
1961 	//
1962 	// If the card has just been put in ADAT mode, it is possible
1963 	// that the locked sample rate is greater than 48KHz, which is not allowed
1964 	// in ADAT mode.  If this happens, change the locked rate to 48.
1965 	//
1966 	if ( 	(DIGITAL_MODE_ADAT == byDigitalMode) &&
1967 			(m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED) &&
1968 			(m_dwLockedSampleRate > 48000) )
1969 	{
1970 		m_dwLockedSampleRate = 48000;
1971 	}
1972 
1973 	return Status;
1974 
1975 }	// ECHOSTATUS CEchoGals::SetDigitalMode( ... )
1976 
1977 
1978 /*
1979 
1980 The output bus gain controls aren't actually implemented in the hardware;
1981 insted they are virtual controls created by the generic code.
1982 
1983 The signal sent to an output bus is a mix of the monitors and output pipes
1984 routed to that bus; the output bus gain is therefore implemented by tweaking
1985 each appropriate monitor and output pipe gain.
1986 
1987 */
1988 
1989 
1990 //===========================================================================
1991 //
1992 // Adjust all the monitor levels for a particular output bus
1993 //
1994 // For efficiency, fImmediate is set to FALSE in the call
1995 // to SetGain; all the monitor and pipe out gains are
1996 // sent to the DSP at once by AdjustPipesOutForBusOut.
1997 //
1998 //===========================================================================
1999 
2000 ECHOSTATUS CEchoGals::AdjustMonitorsForBusOut(WORD wBusOut)
2001 {
2002 	WORD wBusIn;
2003 
2004 	//
2005 	// Poke the monitors
2006 	//
2007 	for (wBusIn = 0; wBusIn < GetNumBussesIn(); wBusIn++)
2008 	{
2009 		m_MonitorCtrl.SetGain(wBusIn,wBusOut,ECHOGAIN_UPDATE,FALSE);
2010 	}
2011 
2012 	return ECHOSTATUS_OK;
2013 
2014 }	// AdjustMonitorsForBusOut
2015 
2016 
2017 //===========================================================================
2018 //
2019 // Adjust all the pipe out levels for a particular output bus
2020 //
2021 // For efficiency, fImmediate is set to FALSE in the call
2022 // to SetGain; all the monitor and pipe out gains are
2023 // sent to the DSP at once by AdjustPipesOutForBusOut.
2024 //
2025 //===========================================================================
2026 
2027 ECHOSTATUS CEchoGals::AdjustPipesOutForBusOut(WORD wBusOut,INT32 iBusOutGain)
2028 {
2029 	ECHO_DEBUGPRINTF(("CEchoGals::AdjustPipesOutForBusOut wBusOut %d  iBusOutGain %ld\n",
2030 							wBusOut,
2031 							iBusOutGain));
2032 
2033 	//
2034 	// Round down to the nearest even bus
2035 	//
2036 	wBusOut &= 0xfffe;
2037 
2038 	m_PipeOutCtrl.SetGain(wBusOut,wBusOut,ECHOGAIN_UPDATE,FALSE);
2039 	wBusOut++;
2040 	m_PipeOutCtrl.SetGain(wBusOut,wBusOut,ECHOGAIN_UPDATE,TRUE);
2041 
2042 	return ECHOSTATUS_OK;
2043 
2044 }	// AdjustPipesOutForBusOut
2045 
2046 
2047 
2048 //===========================================================================
2049 //
2050 // GetAudioLatency - returns the latency for a single pipe
2051 //
2052 //===========================================================================
2053 
2054 void CEchoGals::GetAudioLatency(ECHO_AUDIO_LATENCY *pLatency)
2055 {
2056 	DWORD dwLatency;
2057 
2058 	if (FALSE == pLatency->wIsInput)
2059 	{
2060 		if (pLatency->wPipe >= GetFirstDigitalBusOut())
2061 			dwLatency = m_wDigitalOutputLatency;
2062 		else
2063 			dwLatency = m_wAnalogOutputLatency;
2064 	}
2065 	else
2066 	{
2067 		if (pLatency->wPipe >= GetFirstDigitalBusIn())
2068 			dwLatency = m_wDigitalInputLatency;
2069 		else
2070 			dwLatency = m_wAnalogInputLatency;
2071 	}
2072 
2073 	pLatency->dwLatency = dwLatency;
2074 
2075 }	// GetAudioLatency
2076