xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CPipeOutCtrl.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
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 		{
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 
206 void 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 
237 ECHOSTATUS 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 
406 ECHOSTATUS 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 
437 ECHOSTATUS 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 
484 ECHOSTATUS 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 
515 ECHOSTATUS 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 
561 ECHOSTATUS 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