1// ****************************************************************************
2//
3//		CMonitorCtrl.cpp
4//
5//		Class to control monitors
6//
7// ----------------------------------------------------------------------------
8//
9// This file is part of Echo Digital Audio's generic driver library.
10// Copyright Echo Digital Audio Corporation (c) 1998 - 2005
11// All rights reserved
12// www.echoaudio.com
13//
14// This library is free software; you can redistribute it and/or
15// modify it under the terms of the GNU Lesser General Public
16// License as published by the Free Software Foundation; either
17// version 2.1 of the License, or (at your option) any later version.
18//
19// This library is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22// Lesser General Public License for more details.
23//
24// You should have received a copy of the GNU Lesser General Public
25// License along with this library; if not, write to the Free Software
26// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27//
28// ****************************************************************************
29
30#include "CEchoGals.h"
31#include "CMonitorCtrl.h"
32
33//*****************************************************************************
34//
35// Destructor (this class uses the default constructor)
36//
37//*****************************************************************************
38
39CMonitorCtrl::~CMonitorCtrl()
40{
41	Cleanup();
42}
43
44
45//*****************************************************************************
46//
47// Init
48//
49//*****************************************************************************
50
51ECHOSTATUS CMonitorCtrl::Init(CEchoGals *pEG)
52{
53	DWORD	dwBytes;
54	DWORD	dwArraySize;
55
56	m_Gains = NULL;
57	m_Mutes = NULL;
58	m_Pans = NULL;
59	m_PanDbs = NULL;
60
61	//
62	// Cache stuff
63	//
64	m_pEG = pEG;
65	m_wNumBussesIn = pEG->GetNumBussesIn();
66	m_wNumBussesOut = pEG->GetNumBussesOut();
67
68	//
69	// Indigo has no inputs; attempting to allocate 0 bytes
70	// causes a BSOD on Windows ME.
71	//
72	if ((0 == m_wNumBussesIn) || (0 == m_wNumBussesOut))
73	{
74		ECHO_DEBUGPRINTF(("CMonitorCtrl::Init - this card has no inputs!\n"));
75		return ECHOSTATUS_OK;
76	}
77
78	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79	//
80	// Allocate the arrays
81	//
82	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83
84	dwArraySize = m_wNumBussesIn * (m_wNumBussesOut >> 1);
85
86	dwBytes = sizeof(INT8) * dwArraySize;
87	OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
88	if (NULL == m_Gains)
89	{
90		Cleanup();
91		return ECHOSTATUS_NO_MEM;
92	}
93
94	dwBytes = sizeof(WORD) * dwArraySize;
95	OsAllocateNonPaged(dwBytes,(void **) &m_Pans);
96	if (NULL == m_Pans)
97	{
98		Cleanup();
99		return ECHOSTATUS_NO_MEM;
100	}
101
102	dwBytes = sizeof(BYTE) * dwArraySize;
103	OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
104	if (NULL == m_Mutes)
105	{
106		Cleanup();
107		return ECHOSTATUS_NO_MEM;
108	}
109
110	dwBytes = sizeof(PAN_DB) * dwArraySize;
111	OsAllocateNonPaged(dwBytes,(void **) &m_PanDbs );
112	if (NULL == m_PanDbs)
113	{
114		Cleanup();
115		return ECHOSTATUS_NO_MEM;
116	}
117
118	//==============================================================
119	//
120	// Init the arrays
121	//
122	//==============================================================
123
124	WORD wBusIn,wBusOut,wIndex;
125
126	for (wBusIn = 0; wBusIn < m_wNumBussesIn; wBusIn++)
127		for (wBusOut = 0; wBusOut < m_wNumBussesOut; wBusOut += 2)
128		{
129			wIndex = GetIndex(wBusIn,wBusOut);
130
131			//
132			// Pan hard left for even inputs, hard right for odd
133			//
134			if (0 == (wBusIn & 1))
135			{
136				m_Pans[wIndex] = 0;
137				m_PanDbs[wIndex].iLeft = 0;
138				m_PanDbs[wIndex].iRight = (INT8) GENERIC_TO_DSP( ECHOGAIN_MUTED );
139			}
140			else
141			{
142				m_Pans[wIndex] = MAX_MIXER_PAN;
143				m_PanDbs[wIndex].iLeft = (INT8) GENERIC_TO_DSP( ECHOGAIN_MUTED );
144				m_PanDbs[wIndex].iRight = 0;
145			}
146
147			//
148			// Mute unless this is not a digital input
149			// and the input is going to the same-numbered output
150			//
151			if ( (wBusIn  < m_pEG->GetFirstDigitalBusIn()) &&
152				  ( (wBusIn & 0xfffe) == wBusOut ) )
153			{
154				m_Mutes[wIndex] = FALSE;
155			}
156			else
157			{
158				m_Mutes[wIndex] = TRUE;
159			}
160
161			//
162			// Put stuff in the comm page
163			//
164			SetGain(wBusIn,wBusOut,ECHOGAIN_UPDATE,FALSE);
165		}
166
167	//
168	// Now actually update the DSP
169	//
170	m_pEG->GetDspCommObject()->UpdateAudioOutLineLevel();
171
172
173	return ECHOSTATUS_OK;
174
175}	// Init
176
177
178//*****************************************************************************
179//
180// Cleanup - free allocated memory
181//
182//*****************************************************************************
183
184void CMonitorCtrl::Cleanup()
185{
186	if (m_Gains)
187		OsFreeNonPaged(m_Gains);
188
189	if (m_Mutes)
190		OsFreeNonPaged(m_Mutes);
191
192	if (m_Pans)
193		OsFreeNonPaged(m_Pans);
194
195	if (m_PanDbs)
196		OsFreeNonPaged(m_PanDbs);
197
198}	// Cleanup
199
200
201//*****************************************************************************
202//
203// Set and get gain
204//
205//*****************************************************************************
206
207ECHOSTATUS CMonitorCtrl::SetGain
208(
209	WORD 	wBusIn,
210	WORD 	wBusOut,
211	INT32 iGain,
212	BOOL 	fImmediate
213)
214{
215	ECHOSTATUS Status;
216
217	if (NULL == m_pEG)
218		return ECHOSTATUS_DSP_DEAD;
219
220	if (	(NULL == m_Gains) ||
221			(NULL == m_PanDbs) )
222		return ECHOSTATUS_NO_MEM;
223
224	if (	(wBusIn >= m_wNumBussesIn) || (wBusOut >= m_wNumBussesOut) )
225	{
226		ECHO_DEBUGPRINTF(("CMonitorCtrl::SetGain - out of range   in %d out %d\n",
227								wBusIn,wBusOut));
228		return ECHOSTATUS_INVALID_PARAM;
229	}
230
231	//
232	// Round down to the nearest even bus
233	//
234	wBusOut &= 0xfffe;
235
236	//
237	// Figure out the index into the array
238	//
239	WORD wIndex = GetIndex(wBusIn,wBusOut);
240
241	if (ECHOGAIN_UPDATE == iGain)
242	{
243		iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] );
244	}
245	else
246	{
247		if (iGain > ECHOGAIN_MAXOUT)
248			iGain = ECHOGAIN_MAXOUT;
249		else if (iGain < ECHOGAIN_MUTED)
250			iGain = ECHOGAIN_MUTED;
251
252		m_Gains[ wIndex ] = (INT8) GENERIC_TO_DSP( iGain );
253
254		//
255		// Gain has changed; store the notify
256		//
257		m_pEG->MixerControlChanged(ECHO_MONITOR,MXN_LEVEL,wBusIn,wBusOut);
258	}
259
260	//
261	// Use the gain that was passed in, the pan setting,
262	// and the mute to calculate the left and right gains
263	//
264	INT32 iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft );
265	INT32 iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight );
266
267	//
268	// Adjust left and right by the output bus gain
269	//
270	iLeft += m_pEG->m_BusOutLineLevels[wBusOut].GetGain();
271	iRight += m_pEG->m_BusOutLineLevels[wBusOut + 1].GetGain();
272
273	//
274	// Either mute or clamp
275	//
276	if (TRUE == m_Mutes[wIndex])
277	{
278		iLeft = ECHOGAIN_MUTED;
279		iRight = ECHOGAIN_MUTED;
280	}
281	else
282	{
283		if ( m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn() )
284		{
285			iLeft = ECHOGAIN_MUTED;
286		}
287		else
288		{
289			//
290			// Clamp left
291			//
292			if (iLeft > ECHOGAIN_MAXOUT)
293				iLeft = ECHOGAIN_MAXOUT;
294			else if (iLeft < ECHOGAIN_MUTED)
295				iLeft = ECHOGAIN_MUTED;
296		}
297
298		if ( m_pEG->m_BusOutLineLevels[wBusOut + 1].IsMuteOn() )
299		{
300			iRight = ECHOGAIN_MUTED;
301		}
302		else
303		{
304			//
305			// Clamp right
306			//
307			if (iRight > ECHOGAIN_MAXOUT)
308				iRight = ECHOGAIN_MAXOUT;
309			else if (iRight < ECHOGAIN_MUTED)
310				iRight = ECHOGAIN_MUTED;
311
312		}
313	}
314
315
316	//
317	// Set the left channel
318	//
319	if ( (NULL == m_pEG) ||
320			(NULL == m_pEG->GetDspCommObject() ) )
321		return ECHOSTATUS_DSP_DEAD;
322
323
324	Status = m_pEG->
325					GetDspCommObject()->
326						SetAudioMonitor( 	wBusOut,
327												wBusIn,
328												iLeft,
329												FALSE);
330
331	//
332	//	Set the right channel
333	//
334	if (ECHOSTATUS_OK == Status)
335	{
336		Status = m_pEG->
337						GetDspCommObject()->
338							SetAudioMonitor( 	wBusOut + 1,
339													wBusIn,
340													iRight,
341													fImmediate);
342	}
343
344	return Status;
345}
346
347
348ECHOSTATUS CMonitorCtrl::GetGain(WORD wBusIn, WORD wBusOut, INT32 &iGain)
349{
350	WORD	wIndex = GetIndex(wBusIn,wBusOut);
351
352	if (NULL == m_Gains)
353		return ECHOSTATUS_NO_MEM;
354
355	if (	(wBusIn >= m_wNumBussesIn) || (wBusOut >= m_wNumBussesOut) )
356	{
357		ECHO_DEBUGPRINTF(("CMonitorCtrl::GetGain - out of range in %d out %d\n",
358								wBusIn,wBusOut));
359		return ECHOSTATUS_INVALID_PARAM;
360	}
361
362	iGain = DSP_TO_GENERIC( m_Gains[wIndex] );
363
364	return ECHOSTATUS_OK;
365}
366
367
368//*****************************************************************************
369//
370// Set and get mute
371//
372//*****************************************************************************
373
374ECHOSTATUS CMonitorCtrl::SetMute
375(
376	WORD wBusIn,
377	WORD wBusOut,
378	BOOL bMute,
379	BOOL fImmediate
380)
381{
382	if (NULL == m_Mutes)
383		return ECHOSTATUS_NO_MEM;
384
385	if (	(wBusIn >= m_wNumBussesIn) || (wBusOut >= m_wNumBussesOut) )
386	{
387		ECHO_DEBUGPRINTF(("CMonitorCtrl::SetMute - out of range   in %d out %d\n",
388								wBusIn,wBusOut));
389		return ECHOSTATUS_INVALID_PARAM;
390	}
391
392	wBusOut &= 0xfffe;
393
394	WORD wIndex = GetIndex(wBusIn,wBusOut);
395
396	//
397	// Store the mute
398	//
399 	m_Mutes[ wIndex ] = (BYTE) bMute;
400
401	//
402	// Store the notify
403	//
404	m_pEG->MixerControlChanged(ECHO_MONITOR,MXN_MUTE,wBusIn,wBusOut);
405
406
407	//
408	// Call the SetGain function to do all the heavy lifting
409	// Use the ECHOGAIN_UPDATE value to tell the function to
410	// recalculate the gain setting using the currently stored value.
411	//
412	return SetGain(wBusIn,wBusOut,ECHOGAIN_UPDATE,fImmediate);
413}
414
415
416ECHOSTATUS CMonitorCtrl::GetMute(WORD wBusIn, WORD wBusOut, BOOL &bMute)
417{
418	wBusOut &= 0xfffe;
419
420	if (	(wBusIn >= m_wNumBussesIn) || (wBusOut >= m_wNumBussesOut) )
421	{
422		ECHO_DEBUGPRINTF(("CMonitorCtrl::GetMute - out of range   in %d out %d\n",
423								wBusIn,wBusOut));
424		return ECHOSTATUS_INVALID_PARAM;
425	}
426
427
428	WORD	wIndex = GetIndex(wBusIn,wBusOut);
429
430	if (NULL == m_Mutes)
431		return ECHOSTATUS_NO_MEM;
432
433	bMute = (BOOL) m_Mutes[ wIndex ];
434
435	return ECHOSTATUS_OK;
436}
437
438
439//*****************************************************************************
440//
441// Set and get pan
442//
443//*****************************************************************************
444
445ECHOSTATUS CMonitorCtrl::SetPan(WORD wBusIn, WORD wBusOut, INT32 iPan)
446{
447	if (	(wBusIn >= m_wNumBussesIn) || (wBusOut >= m_wNumBussesOut) )
448	{
449		ECHO_DEBUGPRINTF(("CMonitorCtrl::SetPan - out of range   in %d out %d\n",
450								wBusIn,wBusOut));
451		return ECHOSTATUS_INVALID_PARAM;
452	}
453
454	wBusOut &= 0xfffe;
455
456	WORD	wIndex = GetIndex(wBusIn,wBusOut);
457
458	if (NULL == m_Pans)
459		return ECHOSTATUS_NO_MEM;
460
461	//
462	// Clamp it and stash it
463	//
464	if (iPan < 0)
465		iPan = 0;
466	else if (iPan > MAX_MIXER_PAN)
467		iPan = MAX_MIXER_PAN;
468
469	m_Pans[wIndex] = (WORD) iPan;
470
471	//
472	//	Convert this pan setting into left and right dB values
473	//
474	m_PanDbs[wIndex].iLeft = (INT8) GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) );
475	m_PanDbs[wIndex].iRight = (INT8) GENERIC_TO_DSP( PanToDb(iPan) );
476
477	//
478	// Store the notify
479	//
480	m_pEG->MixerControlChanged(ECHO_MONITOR,MXN_PAN,wBusIn,wBusOut);
481
482	//
483	// Once again SetGain does all the hard work
484	//
485	return SetGain(wBusIn,wBusOut,ECHOGAIN_UPDATE);
486}
487
488
489ECHOSTATUS CMonitorCtrl::GetPan(WORD wBusIn, WORD wBusOut, INT32 &iPan)
490{
491	if (	(wBusIn >= m_wNumBussesIn) || (wBusOut >= m_wNumBussesOut) )
492	{
493		ECHO_DEBUGPRINTF(("CMonitorCtrl::GetPan - out of range   in %d out %d\n",
494								wBusIn,wBusOut));
495		return ECHOSTATUS_INVALID_PARAM;
496	}
497
498
499	wBusOut &= 0xfffe;
500
501	WORD	wIndex = GetIndex(wBusIn,wBusOut);
502
503	if (NULL == m_Pans)
504		return ECHOSTATUS_NO_MEM;
505
506	iPan = m_Pans[ wIndex ];
507
508	return ECHOSTATUS_OK;
509}
510