1// ****************************************************************************
2//
3//		CPipeOutCtrl.cpp
4//
5//		Class to control output pipes on cards with or without vmixers.
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 "CPipeOutCtrl.h"
32
33extern INT32 PanToDb( INT32 iPan );
34
35
36//*****************************************************************************
37//
38// Destructor (this class uses the default constructor)
39//
40//*****************************************************************************
41
42CPipeOutCtrl::~CPipeOutCtrl()
43{
44	Cleanup();
45}
46
47
48//*****************************************************************************
49//
50// Init
51//
52//*****************************************************************************
53
54ECHOSTATUS CPipeOutCtrl::Init(CEchoGals *pEG)
55{
56	DWORD	dwBytes;
57	WORD	wPipe,wStereoBus;
58
59	m_Gains = NULL;
60	m_Mutes = NULL;
61	m_Pans = NULL;
62	m_PanDbs = NULL;
63
64	//
65	// Cache stuff
66	//
67	m_pEG = pEG;
68	m_wNumPipesOut = pEG->GetNumPipesOut();
69	m_wNumBussesOut = pEG->GetNumBussesOut();
70	m_fHasVmixer = pEG->HasVmixer();
71
72	//
73	// Allocate the arrays
74	//
75	if (m_fHasVmixer)
76	{
77		WORD wNumStereoBusses = m_wNumBussesOut >> 1;
78
79		//
80		// Allocate arrays for vmixer support
81		//
82		dwBytes = sizeof(INT8) * m_wNumPipesOut * wNumStereoBusses;
83		OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
84		if (NULL == m_Gains)
85		{
86			return ECHOSTATUS_NO_MEM;
87		}
88
89		dwBytes = sizeof(BYTE) * m_wNumPipesOut * wNumStereoBusses;
90		OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
91		if (NULL == m_Mutes)
92		{
93			Cleanup();
94			return ECHOSTATUS_NO_MEM;
95		}
96
97		dwBytes = sizeof(WORD) * m_wNumPipesOut * wNumStereoBusses;
98		OsAllocateNonPaged(dwBytes,(void **) &m_Pans);
99		if (NULL == m_Pans)
100		{
101			Cleanup();
102			return ECHOSTATUS_NO_MEM;
103		}
104
105		dwBytes = sizeof(PAN_DB) * m_wNumPipesOut * wNumStereoBusses;
106		OsAllocateNonPaged(dwBytes,(void **) &m_PanDbs);
107		if (NULL == m_PanDbs)
108		{
109			Cleanup();
110			return ECHOSTATUS_NO_MEM;
111		}
112
113		//
114		// Initialize pans and mutes
115		//
116		for (wPipe = 0; wPipe < m_wNumPipesOut; wPipe++)
117			for (wStereoBus = 0; wStereoBus < wNumStereoBusses; wStereoBus++)
118			{
119				WORD wIndex;
120
121				wIndex = GetIndex(wPipe,wStereoBus << 1);
122
123				//
124				//	Pans
125				//
126				if (0 == (wPipe & 1))
127				{
128					//
129					// Even channel - pan hard left
130					//
131					m_Pans[wIndex] = 0;
132					m_PanDbs[wIndex].iLeft = 0;
133					m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( ECHOGAIN_MUTED );
134				}
135				else
136				{
137					//
138					// Odd channel - pan hard right
139					//
140					m_Pans[wIndex] = MAX_MIXER_PAN;
141					m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( ECHOGAIN_MUTED );
142					m_PanDbs[wIndex].iRight = 0;
143				}
144
145				//
146				// Mutes
147				//
148				if ((wPipe >> 1) == wStereoBus)
149				{
150					m_Mutes[wIndex] = FALSE;
151				}
152				else
153				{
154					m_Mutes[wIndex] = TRUE;
155				}
156
157				//
158				// Set the gain to the DSP; use fImmedate = FALSE here
159				// to make this faster
160				//
161				SetGain(wPipe,wStereoBus << 1,ECHOGAIN_UPDATE,FALSE);
162
163			}
164
165			//
166			// Set the gain one more time with the immediate flag set to
167			// make sure the DSP gets the message
168			//
169			SetGain(0,0,ECHOGAIN_UPDATE,TRUE);
170	}
171	else
172	{
173		//
174		// Allocate arrays for no vmixer support - don't need pans
175		//
176		dwBytes = sizeof(INT8) * m_wNumPipesOut;
177		OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
178		if (NULL == m_Gains)
179		{
180			return ECHOSTATUS_NO_MEM;
181		}
182
183		dwBytes = sizeof(BYTE) * m_wNumPipesOut;
184		OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
185		if (NULL == m_Mutes)
186		{
187			OsFreeNonPaged(m_Gains);
188			return ECHOSTATUS_NO_MEM;
189		}
190
191	}
192
193	return ECHOSTATUS_OK;
194
195}	// Init
196
197
198//*****************************************************************************
199//
200// Cleanup - free allocated memory
201//
202//*****************************************************************************
203
204void CPipeOutCtrl::Cleanup()
205{
206	if (m_Gains)
207		OsFreeNonPaged(m_Gains);
208
209	if (m_Mutes)
210		OsFreeNonPaged(m_Mutes);
211
212	if (m_Pans)
213		OsFreeNonPaged(m_Pans);
214
215	if (m_PanDbs)
216		OsFreeNonPaged(m_PanDbs);
217
218}	// Cleanup
219
220
221//*****************************************************************************
222//
223// Set and get gain
224//
225// For cards without vmixers, output bus gain is not handled by the DSP.
226// Instead, the driver adjusts the output pipe volumes by the output bus gain
227// and sends that value to the DSP.
228//
229// For cards with vmixers, the output bus gain is handled by the DSP, so
230// the gain setting does not need to take into account the output bus gain
231// stored by the driver.
232//
233//*****************************************************************************
234
235ECHOSTATUS CPipeOutCtrl::SetGain
236(
237	WORD 	wPipeOut,
238	WORD 	wBusOut,
239	INT32 iGain,
240	BOOL 	fImmediate
241)
242{
243	INT32 iBusOutGain;
244	ECHOSTATUS Status;
245
246	if ( NULL == m_pEG)
247		return ECHOSTATUS_DSP_DEAD;
248
249	if (!m_fHasVmixer && (wPipeOut != wBusOut))
250		return ECHOSTATUS_OK;
251
252	if ((NULL == m_Gains) || (NULL == m_Mutes))
253		return ECHOSTATUS_NO_MEM;
254
255	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
256	{
257		ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain - out of range pipe %d bus %d\n",
258								wPipeOut,wBusOut));
259		return ECHOSTATUS_INVALID_PARAM;
260	}
261
262	WORD wIndex = GetIndex(wPipeOut,wBusOut);
263
264	/*
265	ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain pipe %d  bus %d  gain 0x%lx  index %d\n",
266							wPipeOut,wBusOut,iGain,wIndex));
267	*/
268
269	if (ECHOGAIN_UPDATE == iGain)
270	{
271		iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] );
272	}
273	else
274	{
275		if (iGain > ECHOGAIN_MAXOUT)
276			iGain = ECHOGAIN_MAXOUT;
277		else if (iGain < ECHOGAIN_MUTED)
278			iGain = ECHOGAIN_MUTED;
279
280		m_Gains[ wIndex ] = (INT8) GENERIC_TO_DSP( iGain );
281
282		//
283		// Store the notify
284		//
285		m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_LEVEL,wPipeOut,wBusOut);
286	}
287
288	if (m_fHasVmixer)
289	{
290		wBusOut &= 0xfffe;
291
292		if (NULL == m_Pans)
293			return ECHOSTATUS_NO_MEM;
294
295		//
296		// For vmixer cards, the DSP handles the output bus gain,
297		// so no need to account for it here.  Vmixer output pipes
298		// do have to handle panning.
299		//
300		INT32 iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft );
301		INT32 iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight );
302
303		//
304		// Add master gain values
305		//
306		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain();
307		iLeft += iBusOutGain;
308		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut+1].GetGain();
309		iRight += iBusOutGain;
310
311		//
312		// Muting and clamping
313		//
314		if (m_Mutes[wIndex])
315		{
316			iLeft = ECHOGAIN_MUTED;
317			iRight = ECHOGAIN_MUTED;
318		}
319		else
320		{
321			if (  (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) ||
322					(iLeft < ECHOGAIN_MUTED))
323			{
324				iLeft = ECHOGAIN_MUTED;
325			}
326			else if (iLeft > ECHOGAIN_MAXOUT)
327			{
328				iLeft = ECHOGAIN_MAXOUT;
329			}
330
331			if (  (m_pEG->m_BusOutLineLevels[wBusOut + 1].IsMuteOn()) ||
332					(iRight < ECHOGAIN_MUTED))
333			{
334				iRight = ECHOGAIN_MUTED;
335			}
336			else if (iRight > ECHOGAIN_MAXOUT)
337			{
338				iRight = ECHOGAIN_MAXOUT;
339			}
340		}
341
342		//
343		// Set the left channel gain
344		//
345		Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
346																			wBusOut,
347																			iLeft,
348																			FALSE);
349		if (ECHOSTATUS_OK == Status)
350		{
351			//
352			// And the right channel
353			//
354			Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
355																				wBusOut + 1,
356																				iRight,
357																				fImmediate);
358		}
359
360	}
361	else
362	{
363		//
364		// Add this output pipe gain to the output bus gain
365		// Since these gains are in decibels, it's OK to just add them
366		//
367		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain();
368		iGain += iBusOutGain;
369
370		//
371		// Mute this output pipe if this output bus is muted
372		//
373		if (m_Mutes[ wIndex ] ||
374			 (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) )
375		{
376			iGain = ECHOGAIN_MUTED;
377		}
378		else
379		{
380			//
381			// Clamp the output pipe gain if necessary
382			//
383			if (iGain < ECHOGAIN_MUTED)
384				iGain = ECHOGAIN_MUTED;
385			else if (iGain > ECHOGAIN_MAXOUT)
386				iGain = ECHOGAIN_MAXOUT;
387
388		}
389
390		//
391		// Set the gain
392		//
393		Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
394																			wBusOut,
395																			iGain,
396																			fImmediate);
397
398	}
399
400	return Status;
401}
402
403
404ECHOSTATUS CPipeOutCtrl::GetGain(WORD wPipeOut, WORD wBusOut, INT32 &iGain)
405{
406	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
407
408	if (NULL == m_Gains)
409		return ECHOSTATUS_NO_MEM;
410
411	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
412	{
413		ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain - out of range pipe %d bus %d\n",
414								wPipeOut,wBusOut));
415		return ECHOSTATUS_INVALID_PARAM;
416	}
417
418	iGain = DSP_TO_GENERIC( m_Gains[wIndex] );
419
420	/*
421	ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain pipe %d  bus %d  gain 0x%lx  index %d\n",
422							wPipeOut,wBusOut,iGain,wIndex));
423	*/
424
425	return ECHOSTATUS_OK;
426}
427
428
429//*****************************************************************************
430//
431// Set and get mute
432//
433//*****************************************************************************
434
435ECHOSTATUS CPipeOutCtrl::SetMute
436(
437	WORD wPipeOut,
438	WORD wBusOut,
439	BOOL bMute,
440	BOOL fImmediate
441)
442{
443	if (!m_fHasVmixer && (wPipeOut != wBusOut))
444		return ECHOSTATUS_OK;
445
446	if (NULL == m_Mutes)
447		return ECHOSTATUS_NO_MEM;
448
449	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
450	{
451		ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute - out of range pipe %d bus %d\n",
452								wPipeOut,wBusOut));
453		return ECHOSTATUS_INVALID_PARAM;
454	}
455
456	WORD wIndex = GetIndex(wPipeOut,wBusOut);
457
458	/*
459	ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute wPipeOut %d  wBusOut %d  bMute %ld\n",
460							wPipeOut,wBusOut,bMute));
461	*/
462
463	//
464	// Store the mute
465	//
466 	m_Mutes[ wIndex ] = (BYTE) bMute;
467
468	//
469	// Store the notify
470	//
471	m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_MUTE,wPipeOut,wBusOut);
472
473	//
474	// Call the SetGain function to do all the heavy lifting
475	// Use the ECHOGAIN_UPDATE value to tell the function to
476	// recalculate the gain setting using the currently stored value.
477	//
478	return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE,fImmediate);
479}
480
481
482ECHOSTATUS CPipeOutCtrl::GetMute(WORD wPipeOut, WORD wBusOut, BOOL &bMute)
483{
484	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
485
486	if (NULL == m_Mutes)
487		return ECHOSTATUS_NO_MEM;
488
489	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
490	{
491		ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute - out of range pipe %d bus %d\n",
492								wPipeOut,wBusOut));
493		return ECHOSTATUS_INVALID_PARAM;
494	}
495
496	bMute = (BOOL) m_Mutes[ wIndex ];
497
498	/*
499	ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute wPipeOut %d  wBusOut %d  bMute %ld\n",
500							wPipeOut,wBusOut,bMute));
501	*/
502
503	return ECHOSTATUS_OK;
504}
505
506
507//*****************************************************************************
508//
509// Set and get pan (vmixer only)
510//
511//*****************************************************************************
512
513ECHOSTATUS CPipeOutCtrl::SetPan(WORD wPipeOut, WORD wBusOut, INT32 iPan)
514{
515	if (!m_fHasVmixer)
516		return ECHOSTATUS_OK;
517
518	if (NULL == m_Pans)
519		return ECHOSTATUS_NO_MEM;
520
521	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
522	{
523		ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetPan - out of range pipe %d bus %d\n",
524								wPipeOut,wBusOut));
525		return ECHOSTATUS_INVALID_PARAM;
526	}
527
528
529	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
530
531	//
532	// Clamp it and stash it
533	//
534	if (iPan < 0)
535		iPan = 0;
536	else if (iPan > MAX_MIXER_PAN)
537		iPan = MAX_MIXER_PAN;
538
539	m_Pans[wIndex] = (WORD) iPan;
540
541	//
542	// Store the notify
543	//
544	m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_PAN,wPipeOut,wBusOut);
545
546	//
547	//	Convert this pan setting into left and right dB values
548	//
549	m_PanDbs[wIndex].iLeft = (INT8) GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) );
550	m_PanDbs[wIndex].iRight = (INT8) GENERIC_TO_DSP( PanToDb(iPan) );
551
552	//
553	// Again, SetGain does all the hard work
554	//
555	return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE);
556}
557
558
559ECHOSTATUS CPipeOutCtrl::GetPan(WORD wPipeOut, WORD wBusOut, INT32 &iPan)
560{
561	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
562
563	if (NULL == m_Pans)
564		return ECHOSTATUS_NO_MEM;
565
566	if ((wPipeOut >= m_wNumPipesOut) || (wBusOut >= m_wNumBussesOut))
567	{
568		ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetPan - out of range pipe %d bus %d\n",
569								wPipeOut,wBusOut));
570		return ECHOSTATUS_INVALID_PARAM;
571	}
572
573	iPan = m_Pans[ wIndex ];
574
575	return ECHOSTATUS_OK;
576}
577