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