xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CPipeOutCtrl.cpp (revision e79e4e7c9e432c90415f79809b7160e864f79001)
1 // ****************************************************************************
2 //
3 //		CPipeOutCtrl.cpp
4 //
5 //		Class to control output pipes on cards with or without vmixers.
6 //
7 //		Copyright Echo Digital Audio Corporation (c) 1998 - 2002
8 //		All rights reserved
9 //		www.echoaudio.com
10 //
11 //		Permission is hereby granted, free of charge, to any person obtaining a
12 //		copy of this software and associated documentation files (the
13 //		"Software"), to deal with the Software without restriction, including
14 //		without limitation the rights to use, copy, modify, merge, publish,
15 //		distribute, sublicense, and/or sell copies of the Software, and to
16 //		permit persons to whom the Software is furnished to do so, subject to
17 //		the following conditions:
18 //
19 //		- Redistributions of source code must retain the above copyright
20 //		notice, this list of conditions and the following disclaimers.
21 //
22 //		- Redistributions in binary form must reproduce the above copyright
23 //		notice, this list of conditions and the following disclaimers in the
24 //		documentation and/or other materials provided with the distribution.
25 //
26 //		- Neither the name of Echo Digital Audio, nor the names of its
27 //		contributors may be used to endorse or promote products derived from
28 //		this Software without specific prior written permission.
29 //
30 //		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 //		EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 //		MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33 //		IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
34 //		ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
35 //		TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
36 //		SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
37 //
38 // ****************************************************************************
39 
40 #include "CEchoGals.h"
41 #include "CPipeOutCtrl.h"
42 
43 extern INT32 PanToDb( INT32 iPan );
44 
45 //*****************************************************************************
46 //
47 // Init
48 //
49 //*****************************************************************************
50 
51 ECHOSTATUS CPipeOutCtrl::Init(CEchoGals *pEG)
52 {
53 	DWORD	dwBytes;
54 	WORD	wPipe,wStereoBus;
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_wNumPipesOut = pEG->GetNumPipesOut();
66 	m_wNumBussesOut = pEG->GetNumBussesOut();
67 	m_fHasVmixer = pEG->HasVmixer();
68 
69 	//
70 	// Allocate the arrays
71 	//
72 	if (m_fHasVmixer)
73 	{
74 		WORD wNumStereoBusses = m_wNumBussesOut >> 1;
75 
76 		//
77 		// Allocate arrays for vmixer support
78 		//
79 		dwBytes = sizeof(INT8) * m_wNumPipesOut * wNumStereoBusses;
80 		OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
81 		if (NULL == m_Gains)
82 		{
83 			return ECHOSTATUS_NO_MEM;
84 		}
85 
86 		dwBytes = sizeof(BYTE) * m_wNumPipesOut * wNumStereoBusses;
87 		OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
88 		if (NULL == m_Mutes)
89 		{
90 			Cleanup();
91 			return ECHOSTATUS_NO_MEM;
92 		}
93 
94 		dwBytes = sizeof(WORD) * m_wNumPipesOut * wNumStereoBusses;
95 		OsAllocateNonPaged(dwBytes,(void **) &m_Pans);
96 		if (NULL == m_Pans)
97 		{
98 			Cleanup();
99 			return ECHOSTATUS_NO_MEM;
100 		}
101 
102 		dwBytes = sizeof(PAN_DB) * m_wNumPipesOut * wNumStereoBusses;
103 		OsAllocateNonPaged(dwBytes,(void **) &m_PanDbs);
104 		if (NULL == m_PanDbs)
105 		{
106 			Cleanup();
107 			return ECHOSTATUS_NO_MEM;
108 		}
109 
110 		//
111 		// Initialize pans and mutes
112 		//
113 		for (wPipe = 0; wPipe < m_wNumPipesOut; wPipe++)
114 			for (wStereoBus = 0; wStereoBus < wNumStereoBusses; wStereoBus++)
115 			{
116 				WORD wIndex;
117 
118 				wIndex = GetIndex(wPipe,wStereoBus << 1);
119 
120 				//
121 				//	Pans
122 				//
123 				if (0 == (wPipe & 1))
124 				{
125 					//
126 					// Even channel - pan hard left
127 					//
128 					m_Pans[wIndex] = 0;
129 					m_PanDbs[wIndex].iLeft = 0;
130 					m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( ECHOGAIN_MUTED );
131 				}
132 				else
133 				{
134 					//
135 					// Odd channel - pan hard right
136 					//
137 					m_Pans[wIndex] = MAX_MIXER_PAN;
138 					m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( ECHOGAIN_MUTED );
139 					m_PanDbs[wIndex].iRight = 0;
140 				}
141 
142 				//
143 				// Mutes
144 				//
145 				if ((wPipe >> 1) == wStereoBus)
146 				{
147 					m_Mutes[wIndex] = FALSE;
148 				}
149 				else
150 				{
151 					m_Mutes[wIndex] = TRUE;
152 				}
153 
154 				//
155 				// Set the gain to the DSP; use fImmedate = FALSE here
156 				// to make this faster
157 				//
158 				SetGain(wPipe,wStereoBus << 1,ECHOGAIN_UPDATE,FALSE);
159 
160 			}
161 
162 			//
163 			// Set the gain one more time with the immediate flag set to
164 			// make sure the DSP gets the message
165 			//
166 			SetGain(0,0,ECHOGAIN_UPDATE,TRUE);
167 	}
168 	else
169 	{
170 		//
171 		// Allocate arrays for no vmixer support - don't need pans
172 		//
173 		dwBytes = sizeof(INT8) * m_wNumPipesOut;
174 		OsAllocateNonPaged(dwBytes,(void **) &m_Gains);
175 		if (NULL == m_Gains)
176 		{
177 			return ECHOSTATUS_NO_MEM;
178 		}
179 
180 		dwBytes = sizeof(BYTE) * m_wNumPipesOut;
181 		OsAllocateNonPaged(dwBytes,(void **) &m_Mutes);
182 		if (NULL == m_Mutes)
183 		{
184 			OsFreeNonPaged(m_Gains);
185 			return ECHOSTATUS_NO_MEM;
186 		}
187 
188 	}
189 
190 	return ECHOSTATUS_OK;
191 
192 }	// Init
193 
194 
195 //*****************************************************************************
196 //
197 // Cleanup - free allocated memory
198 //
199 //*****************************************************************************
200 
201 void CPipeOutCtrl::Cleanup()
202 {
203 	if (m_Gains)
204 		OsFreeNonPaged(m_Gains);
205 
206 	if (m_Mutes)
207 		OsFreeNonPaged(m_Mutes);
208 
209 	if (m_Pans)
210 		OsFreeNonPaged(m_Pans);
211 
212 	if (m_PanDbs)
213 		OsFreeNonPaged(m_PanDbs);
214 
215 }	// Cleanup
216 
217 
218 //*****************************************************************************
219 //
220 // Set and get gain
221 //
222 // For cards without vmixers, output bus gain is not handled by the DSP.
223 // Instead, the driver adjusts the output pipe volumes by the output bus gain
224 // and sends that value to the DSP.
225 //
226 // For cards with vmixers, the output bus gain is handled by the DSP, so
227 // the gain setting does not need to take into account the output bus gain
228 // stored by the driver.
229 //
230 //*****************************************************************************
231 
232 ECHOSTATUS CPipeOutCtrl::SetGain
233 (
234 	WORD 	wPipeOut,
235 	WORD 	wBusOut,
236 	INT32 	iGain,
237 	BOOL 	fImmediate
238 )
239 {
240 	ECHOSTATUS Status;
241 
242 	if ( NULL == m_pEG)
243 		return ECHOSTATUS_DSP_DEAD;
244 
245 	if (!m_fHasVmixer && (wPipeOut != wBusOut))
246 		return ECHOSTATUS_OK;
247 
248 	if ((NULL == m_Gains) || (NULL == m_Mutes))
249 		return ECHOSTATUS_NO_MEM;
250 
251 	WORD wIndex = GetIndex(wPipeOut,wBusOut);
252 
253 	/*
254 	ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetGain pipe %d  bus %d  gain 0x%lx  index %d\n",
255 							wPipeOut,wBusOut,iGain,wIndex));
256 	*/
257 
258 	if (ECHOGAIN_UPDATE == iGain)
259 	{
260 		iGain = DSP_TO_GENERIC( m_Gains[ wIndex ] );
261 	}
262 	else
263 	{
264 		if (iGain > ECHOGAIN_MAXOUT)
265 			iGain = ECHOGAIN_MAXOUT;
266 		else if (iGain < ECHOGAIN_MUTED)
267 			iGain = ECHOGAIN_MUTED;
268 
269 		m_Gains[ wIndex ] = GENERIC_TO_DSP( iGain );
270 
271 		//
272 		// Store the notify
273 		//
274 		m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_LEVEL,wPipeOut,wBusOut);
275 	}
276 
277 	if (m_fHasVmixer)
278 	{
279 		wBusOut &= 0xfffe;
280 
281 		if (NULL == m_Pans)
282 			return ECHOSTATUS_NO_MEM;
283 
284 		//
285 		// For vmixer cards, the DSP handles the output bus gain,
286 		// so no need to account for it here.  Vmixer output pipes
287 		// do have to handle panning.
288 		//
289 		int iLeft = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iLeft );
290 		int iRight = iGain + DSP_TO_GENERIC( m_PanDbs[wIndex].iRight );
291 
292 		if (m_Mutes[wIndex])
293 		{
294 			iLeft = ECHOGAIN_MUTED;
295 			iRight = ECHOGAIN_MUTED;
296 		}
297 		else
298 		{
299 			if (iLeft < ECHOGAIN_MUTED)
300 				iLeft = ECHOGAIN_MUTED;
301 			else if (iLeft > ECHOGAIN_MAXOUT)
302 				iLeft = ECHOGAIN_MAXOUT;
303 
304 			if (iRight < ECHOGAIN_MUTED)
305 				iRight = ECHOGAIN_MUTED;
306 			else if (iRight > ECHOGAIN_MAXOUT)
307 				iRight = ECHOGAIN_MAXOUT;
308 		}
309 
310 		//
311 		// Set the left channel gain
312 		//
313 		Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
314 																			wBusOut,
315 																			iLeft,
316 																			FALSE);
317 		if (ECHOSTATUS_OK == Status)
318 		{
319 			Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
320 																				wBusOut + 1,
321 																				iRight,
322 																				fImmediate);
323 		}
324 
325 	}
326 	else
327 	{
328 		int iBusOutGain;
329 
330 		//
331 		// Add this output pipe gain to the output bus gain
332 		// Since these gains are in decibels, it's OK to just add them
333 		//
334 		iBusOutGain = m_pEG->m_BusOutLineLevels[wBusOut].GetGain();
335 		iGain += iBusOutGain;
336 
337 		//
338 		// Mute this output pipe if this output bus is muted
339 		//
340 		if (m_Mutes[ wIndex ] ||
341 			 (m_pEG->m_BusOutLineLevels[wBusOut].IsMuteOn()) )
342 		{
343 			iGain = ECHOGAIN_MUTED;
344 		}
345 		else
346 		{
347 			//
348 			// Clamp the output pipe gain if necessary
349 			//
350 			if (iGain < ECHOGAIN_MUTED)
351 				iGain = ECHOGAIN_MUTED;
352 			else if (iGain > ECHOGAIN_MAXOUT)
353 				iGain = ECHOGAIN_MAXOUT;
354 
355 		}
356 
357 		//
358 		// Set the gain
359 		//
360 		Status = m_pEG->GetDspCommObject()->SetPipeOutGain(wPipeOut,
361 																			wBusOut,
362 																			iGain,
363 																			fImmediate);
364 
365 	}
366 
367 	return Status;
368 }
369 
370 
371 ECHOSTATUS CPipeOutCtrl::GetGain(WORD wPipeOut, WORD wBusOut, INT32 &iGain)
372 {
373 	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
374 
375 	if (NULL == m_Gains)
376 		return ECHOSTATUS_NO_MEM;
377 
378 	iGain = DSP_TO_GENERIC( m_Gains[wIndex] );
379 
380 	/*
381 	ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetGain pipe %d  bus %d  gain 0x%lx  index %d\n",
382 							wPipeOut,wBusOut,iGain,wIndex));
383 	*/
384 
385 	return ECHOSTATUS_OK;
386 }
387 
388 
389 //*****************************************************************************
390 //
391 // Set and get mute
392 //
393 //*****************************************************************************
394 
395 ECHOSTATUS CPipeOutCtrl::SetMute
396 (
397 	WORD wPipeOut,
398 	WORD wBusOut,
399 	BOOL bMute,
400 	BOOL fImmediate
401 )
402 {
403 	if (!m_fHasVmixer && (wPipeOut != wBusOut))
404 		return ECHOSTATUS_OK;
405 
406 	if (NULL == m_Mutes)
407 		return ECHOSTATUS_NO_MEM;
408 
409 	WORD wIndex = GetIndex(wPipeOut,wBusOut);
410 
411 	/*
412 	ECHO_DEBUGPRINTF(("CPipeOutCtrl::SetMute wPipeOut %d  wBusOut %d  bMute %ld\n",
413 							wPipeOut,wBusOut,bMute));
414 	*/
415 
416 	//
417 	// Store the mute
418 	//
419  	m_Mutes[ wIndex ] = (BYTE) bMute;
420 
421 	//
422 	// Store the notify
423 	//
424 	m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_MUTE,wPipeOut,wBusOut);
425 
426 	//
427 	// Call the SetGain function to do all the heavy lifting
428 	// Use the ECHOGAIN_UPDATE value to tell the function to
429 	// recalculate the gain setting using the currently stored value.
430 	//
431 	return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE,fImmediate);
432 }
433 
434 
435 ECHOSTATUS CPipeOutCtrl::GetMute(WORD wPipeOut, WORD wBusOut, BOOL &bMute)
436 {
437 	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
438 
439 	if (NULL == m_Mutes)
440 		return ECHOSTATUS_NO_MEM;
441 
442 	bMute = (BOOL) m_Mutes[ wIndex ];
443 
444 	/*
445 	ECHO_DEBUGPRINTF(("CPipeOutCtrl::GetMute wPipeOut %d  wBusOut %d  bMute %ld\n",
446 							wPipeOut,wBusOut,bMute));
447 	*/
448 
449 	return ECHOSTATUS_OK;
450 }
451 
452 
453 //*****************************************************************************
454 //
455 // Set and get pan (vmixer only)
456 //
457 //*****************************************************************************
458 
459 ECHOSTATUS CPipeOutCtrl::SetPan(WORD wPipeOut, WORD wBusOut, INT32 iPan)
460 {
461 	if (!m_fHasVmixer)
462 		return ECHOSTATUS_OK;
463 
464 	if (NULL == m_Pans)
465 		return ECHOSTATUS_NO_MEM;
466 
467 	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
468 
469 	//
470 	// Clamp it and stash it
471 	//
472 	if (iPan < 0)
473 		iPan = 0;
474 	else if (iPan > MAX_MIXER_PAN)
475 		iPan = MAX_MIXER_PAN;
476 
477 	m_Pans[wIndex] = (WORD) iPan;
478 
479 	//
480 	// Store the notify
481 	//
482 	m_pEG->MixerControlChanged(ECHO_PIPE_OUT,MXN_PAN,wPipeOut,wBusOut);
483 
484 	//
485 	//	Convert this pan setting into left and right dB values
486 	//
487 	m_PanDbs[wIndex].iLeft = GENERIC_TO_DSP( PanToDb(MAX_MIXER_PAN - iPan) );
488 	m_PanDbs[wIndex].iRight = GENERIC_TO_DSP( PanToDb(iPan) );
489 
490 	//
491 	// Again, SetGain does all the hard work
492 	//
493 	return SetGain(wPipeOut,wBusOut,ECHOGAIN_UPDATE);
494 }
495 
496 
497 ECHOSTATUS CPipeOutCtrl::GetPan(WORD wPipeOut, WORD wBusOut, INT32 &iPan)
498 {
499 	WORD	wIndex = GetIndex(wPipeOut,wBusOut);
500 
501 	if (NULL == m_Pans)
502 		return ECHOSTATUS_NO_MEM;
503 
504 	iPan = m_Pans[ wIndex ];
505 
506 	return ECHOSTATUS_OK;
507 }
508