xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CPipeOutCtrl.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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 
33 extern INT32 PanToDb( INT32 iPan );
34 
35 
36 //*****************************************************************************
37 //
38 // Destructor (this class uses the default constructor)
39 //
40 //*****************************************************************************
41 
42 CPipeOutCtrl::~CPipeOutCtrl()
43 {
44 	Cleanup();
45 }
46 
47 
48 //*****************************************************************************
49 //
50 // Init
51 //
52 //*****************************************************************************
53 
54 ECHOSTATUS 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 
204 void 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 
235 ECHOSTATUS 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 
404 ECHOSTATUS 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 
435 ECHOSTATUS 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 
482 ECHOSTATUS 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 
513 ECHOSTATUS 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 
559 ECHOSTATUS 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