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
56ECHOSTATUS 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
148ECHO_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
172ECHOSTATUS 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
227BOOL 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
256ECHOSTATUS 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
346ECHOSTATUS 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
409ECHOSTATUS 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
787ECHOSTATUS 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
836ECHOSTATUS 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
878ECHOSTATUS 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
908ECHOSTATUS 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
952ECHOSTATUS 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
963ECHOSTATUS 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
981ECHOSTATUS 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
1002ECHOSTATUS 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
1030ECHOSTATUS 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
1044ECHOSTATUS 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
1074ECHOSTATUS 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
1095ECHOSTATUS 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
1119ECHOSTATUS 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
1144ECHOSTATUS 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
1210ECHOSTATUS 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
1228ECHOSTATUS 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
1296ECHOSTATUS 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
1341ECHOSTATUS 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
1393ECHOSTATUS 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
1444ECHOSTATUS 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
1511ECHOSTATUS 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
1540ECHOSTATUS 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
1590static 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
1600static 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//
1645INT32 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
1684ECHOSTATUS 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
1719ECHOSTATUS 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
1748ECHOSTATUS 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
1777ECHOSTATUS 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
1806void 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
1827ECHOSTATUS 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
1858ECHOSTATUS 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
1887ECHOSTATUS 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
1980The output bus gain controls aren't actually implemented in the hardware;
1981insted they are virtual controls created by the generic code.
1982
1983The signal sent to an output bus is a mix of the monitors and output pipes
1984routed to that bus; the output bus gain is therefore implemented by tweaking
1985each 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
2000ECHOSTATUS 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
2027ECHOSTATUS 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
2054void 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