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