xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CEchoGals_transport.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 // ****************************************************************************
2 //
3 //		CEchoGals_transport.cpp
4 //
5 //		Audio transport methods for the CEchoGals driver class.
6 //		Set editor tabs to 3 for your viewing pleasure.
7 //
8 // ----------------------------------------------------------------------------
9 //
10 // This file is part of Echo Digital Audio's generic driver library.
11 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
12 // All rights reserved
13 // www.echoaudio.com
14 //
15 // This library is free software; you can redistribute it and/or
16 // modify it under the terms of the GNU Lesser General Public
17 // License as published by the Free Software Foundation; either
18 // version 2.1 of the License, or (at your option) any later version.
19 //
20 // This library is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 // Lesser General Public License for more details.
24 //
25 // You should have received a copy of the GNU Lesser General Public
26 // License along with this library; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 //
29 // ****************************************************************************
30 
31 #include "CEchoGals.h"
32 
33 #pragma optimize("",off)
34 
35 
36 /******************************************************************************
37 
38  Functions for opening and closing pipes
39 
40  ******************************************************************************/
41 
42 //===========================================================================
43 //
44 // OpenAudio is used to reserve audio pipes for your exclusive use.  The call
45 // will fail if someone else has already opened the pipes.  Calling OpenAudio
46 // is the first step if you want to play or record.
47 //
48 // If the fCheckHardware flag is true, then the open will fail
49 // if the DSP and ASIC firmware have not been loaded (usually means
50 // your external box is turned off).
51 //
52 //===========================================================================
53 
54 ECHOSTATUS CEchoGals::OpenAudio
55 (
56 	PECHOGALS_OPENAUDIOPARAMETERS	pOpenParameters,	// Info on pipe
57 	PWORD									pwPipeIndex,		// Pipe index ptr
58 	BOOL									fCheckHardware,
59 	CDaffyDuck							*pDuck
60 )
61 {
62 	CChannelMask	cmMask;
63 	WORD				wPipeMax, wPipe, wPipeIndex, i, wWidth;
64 	ECHOSTATUS 		Status;
65 
66 	ECHO_DEBUGPRINTF( ("CEchoGals::OpenAudio: %s %u "
67 							 "PipeWidth %d "
68 							 "Cyclic %u \n",
69 							 ( pOpenParameters->Pipe.bIsInput ) ? "Input" : "Output",
70 								pOpenParameters->Pipe.nPipe,
71 								pOpenParameters->Pipe.wInterleave,
72 								pOpenParameters->bIsCyclic) );
73 
74 	*pwPipeIndex = (WORD) -1;		// So it's never undefined
75 
76 	//
77 	// Make sure the hardware is OK
78 	//
79 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
80 	{
81 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") );
82 		return ECHOSTATUS_DSP_DEAD;
83 	}
84 
85 	//
86 	// Make sure the DSP & ASIC are up and running
87 	//	 - only if fCheckHardware is true
88 	//
89 	if (fCheckHardware)
90 	{
91 		Status = GetDspCommObject()->LoadFirmware();
92 
93 		if ( ECHOSTATUS_OK != Status )
94 			return Status;
95 	}
96 
97 	//
98 	// Validate the pipe number
99 	//
100 	wPipe = pOpenParameters->Pipe.nPipe;
101 	wWidth = pOpenParameters->Pipe.wInterleave;
102 
103 	if ( pOpenParameters->Pipe.bIsInput )
104 	{
105 		wPipeIndex = wPipe + GetNumPipesOut();
106 		wPipeMax = GetNumPipesIn();
107 	}
108 	else
109 	{
110 		wPipeIndex = wPipe;
111 		wPipeMax = GetNumPipesOut();
112 	}
113 
114 	if ( ( wPipe + wWidth ) > wPipeMax )
115 	{
116 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_INVALID_CHANNEL\n") );
117 		return ECHOSTATUS_INVALID_CHANNEL;
118 	}
119 
120 	//
121 	// If the width is more than two, make sure that this card
122 	// can handle super interleave
123 	//
124 	if (	(0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) &&
125 			(wWidth > 2)
126 		)
127 	{
128 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_NO_SUPER_INTERLEAVE\n") );
129 		return ECHOSTATUS_NO_SUPER_INTERLEAVE;
130 	}
131 
132 	//
133 	// See if the specified pipes are already open
134 	//
135 	for ( i = 0; i < pOpenParameters->Pipe.wInterleave; i++ )
136 	{
137 		cmMask.SetIndexInMask( wPipeIndex + i );
138 	}
139 
140 	if ( m_cmAudioOpen.Test( &cmMask ) )
141 	{
142 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_CHANNEL_ALREADY_OPEN\n") );
143 		return( ECHOSTATUS_CHANNEL_ALREADY_OPEN );
144 	}
145 
146 #ifdef AUTO_DUCK_ALLOCATE
147 	//
148 	// Make a daffy duck
149 	//
150 	if (NULL == pDuck)
151 	{
152 		pDuck = CDaffyDuck::MakeDaffyDuck(m_pOsSupport);
153 
154 		if (NULL == pDuck)
155 			return ECHOSTATUS_NO_MEM;
156 	}
157 
158 	SetDaffyDuck( wPipeIndex, pDuck );
159 
160 #else
161 
162 	//
163 	// Use the specified duck if one was passed in
164 	//
165 	if (NULL != pDuck)
166 		SetDaffyDuck( wPipeIndex, pDuck );
167 
168 #endif
169 
170 
171 	//
172 	// Reset the 64-bit DMA position
173 	//
174 	ResetDmaPos(wPipeIndex);
175 	GetDspCommObject()->ResetPipePosition(wPipeIndex);
176 
177 	//
178 	// Prep stuff
179 	//
180 	m_cmAudioOpen += cmMask;
181 	if ( pOpenParameters->bIsCyclic )
182 		m_cmAudioCyclic += cmMask;
183 	m_Pipes[ wPipeIndex ] = pOpenParameters->Pipe;
184 	*pwPipeIndex = wPipeIndex;
185 	m_ProcessId[ wPipeIndex ] = pOpenParameters->ProcessId;
186 	Reset( wPipeIndex );
187 
188 	ECHO_DEBUGPRINTF( ("\tECHOSTATUS_OK\n") );
189 	return ECHOSTATUS_OK;
190 
191 }	// ECHOSTATUS CEchoGals::OpenAudio
192 
193 
194 //===========================================================================
195 //
196 // CloseAudio is, naturally, the inverse of OpenAudio.
197 //
198 //===========================================================================
199 
200 ECHOSTATUS CEchoGals::CloseAudio
201 (
202 	PECHOGALS_CLOSEAUDIOPARAMETERS	pCloseParameters,
203 	BOOL										fFreeDuck
204 )
205 {
206 	CChannelMask	cmMask;
207 	ECHOSTATUS		Status;
208 	WORD				i;
209 	WORD				wPipeIndex;
210 
211 	wPipeIndex = pCloseParameters->wPipeIndex;
212 
213 	ECHO_DEBUGPRINTF( ("CEchoGals::CloseAudio: Pipe %u  ",
214 							 wPipeIndex) );
215 
216 	Status = VerifyAudioOpen( wPipeIndex );
217 	if ( ECHOSTATUS_OK != Status )
218 		return Status;
219 
220 	for ( i = 0;
221 			i < m_Pipes[ wPipeIndex ].wInterleave;
222 			i++ )
223 	{
224 		cmMask.SetIndexInMask( wPipeIndex + i );
225 	}
226 	Reset( wPipeIndex );
227 
228 	//
229 	// Free the scatter-gather list
230 	//
231 	if (NULL != m_DaffyDucks[wPipeIndex])
232 	{
233 		if (fFreeDuck)
234 			delete m_DaffyDucks[wPipeIndex];
235 
236 		m_DaffyDucks[wPipeIndex] = NULL;
237 	}
238 
239 	m_cmAudioOpen -= cmMask;
240 	m_cmAudioCyclic -= cmMask;
241 
242 	wPipeIndex = wPipeIndex;
243 	m_ProcessId[ wPipeIndex ] = NULL;
244 	m_Pipes[ wPipeIndex ].wInterleave = 0;
245 
246 	ECHO_DEBUGPRINTF( ("\tECHOSTATUS_OK\n") );
247 	return ECHOSTATUS_OK;
248 
249 }	// ECHOSTATUS CEchoGals::CloseAudio
250 
251 
252 //===========================================================================
253 //
254 // VerifyAudioOpen is a utility function; it tells you if
255 // a pipe is open or not.
256 //
257 //===========================================================================
258 
259 ECHOSTATUS CEchoGals::VerifyAudioOpen
260 (
261 	WORD		wPipeIndex
262 )
263 {
264 	CChannelMask	cmMask;
265 
266 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
267 	{
268 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") );
269 		return ECHOSTATUS_DSP_DEAD;
270 	}
271 
272 	cmMask.SetIndexInMask( wPipeIndex );
273 	if ( !( m_cmAudioOpen.Test( &cmMask ) ) )
274 	{
275 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_CHANNEL_NOT_OPEN\n") );
276 		return ECHOSTATUS_CHANNEL_NOT_OPEN;
277 	}
278 
279 	return ECHOSTATUS_OK;
280 
281 }	// ECHOSTATUS CEchoGals::VerifyAudioOpen
282 
283 
284 //===========================================================================
285 //
286 // GetActivePipes tells you which pipes are currently active; that is, which
287 // pipes are currently playing or recording.
288 //
289 //===========================================================================
290 
291 ECHOSTATUS CEchoGals::GetActivePipes
292 (
293 	PCChannelMask	pChannelMask
294 )
295 {
296 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
297 		return ECHOSTATUS_DSP_DEAD;
298 
299 	GetDspCommObject()->GetActivePipes( pChannelMask );
300 	return ECHOSTATUS_OK;
301 }	// void CEchoGals::GetActivePipes()
302 
303 
304 //===========================================================================
305 //
306 // Just like GetActivePipes, but this one tells you which pipes are currently
307 // open.
308 //
309 //===========================================================================
310 
311 ECHOSTATUS CEchoGals::GetOpenPipes
312 (
313 	PCChannelMask	pChannelMask
314 )
315 {
316 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
317 		return ECHOSTATUS_DSP_DEAD;
318 
319 	*pChannelMask = m_cmAudioOpen;
320 	return ECHOSTATUS_OK;
321 
322 }	// void CEchoGals::GetOpenPipes()
323 
324 
325 
326 
327 /******************************************************************************
328 
329  Functions for setting audio formats and the sample rate
330 
331  ******************************************************************************/
332 
333 //===========================================================================
334 //
335 // Validate an audio format.
336 //
337 // For comments on audio formats, refer to the definition of
338 // ECHOGALS_AUDIOFORMAT.
339 //
340 //===========================================================================
341 
342 ECHOSTATUS CEchoGals::QueryAudioFormat
343 (
344 	WORD							wPipeIndex,
345 	PECHOGALS_AUDIOFORMAT	pAudioFormat
346 )
347 {
348 	ECHOSTATUS Status = ECHOSTATUS_OK;
349 
350 	ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat:\n") );
351 
352 	//
353 	// If this pipe is open, make sure that this audio format
354 	// does not exceed the stored pipe width
355 	//
356 	WORD wInterleave = pAudioFormat->wDataInterleave;
357 	WORD wStoredPipeWidth = m_Pipes[ wPipeIndex ].wInterleave;
358 
359 	if (0 != wStoredPipeWidth)
360 	{
361 		if (wInterleave > wStoredPipeWidth)
362 		{
363 			ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - pipe was opened "
364 									"with a width of %d; interleave of %d invalid.\n",
365 									wStoredPipeWidth,
366 									pAudioFormat->wDataInterleave));
367 			return ECHOSTATUS_BAD_FORMAT;
368 		}
369 	}
370 
371 	//
372 	// Check for super interleave (i.e. interleave > 2)
373 	//
374 	if (wInterleave > 2)
375 	{
376 		//
377 		// Make sure the card is capable of super interleave
378 		//
379 		if (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK))
380 			return ECHOSTATUS_NO_SUPER_INTERLEAVE;
381 
382 		//
383 		// Interleave must be even & data must be little endian
384 		//
385 		if (	(0 != pAudioFormat->byDataAreBigEndian) ||
386 			  	(0 != (wInterleave & 1)	)
387 			)
388 			return ECHOSTATUS_BAD_FORMAT;
389 
390 		//
391 		// 16, 24, or 32 bit samples are required
392 		//
393 		if ( 	(32 != pAudioFormat->wBitsPerSample) &&
394 				(24 != pAudioFormat->wBitsPerSample) &&
395 				(16 != pAudioFormat->wBitsPerSample) )
396 			return ECHOSTATUS_BAD_FORMAT;
397 
398 
399 		//
400 		// Make sure that this interleave factor on this pipe
401 		// does not exceed the number of pipes for the card
402 		//
403 		WORD wMaxPipe;
404 
405 		if (wPipeIndex >= GetNumPipesOut())
406 		{
407 			wMaxPipe = GetNumPipesIn();
408 			wPipeIndex = wPipeIndex - GetNumPipesOut();
409 		}
410 		else
411 		{
412 			wMaxPipe = GetNumPipesOut();
413 		}
414 
415 		if ( (wPipeIndex + wInterleave) > wMaxPipe)
416 			return ECHOSTATUS_BAD_FORMAT;
417 
418 		return ECHOSTATUS_OK;
419 	}
420 
421 	//
422 	// Check the interleave
423 	//
424 	if ( 	(1 != pAudioFormat->wDataInterleave) &&
425 			(2 != pAudioFormat->wDataInterleave) )
426 
427 	{
428 		ECHO_DEBUGPRINTF(	("CEchoGals::QueryAudioFormat - interleave %d not allowed\n",
429 								pAudioFormat->wDataInterleave));
430 		return ECHOSTATUS_BAD_FORMAT;
431 	}
432 
433 	//
434 	//	If the big endian flag is set, the data must be mono or stereo interleave,
435 	// 32 bits wide, left justified data.  Only the upper 24 bits are used.
436 	//
437 	if (pAudioFormat->byDataAreBigEndian)
438 	{
439 		//
440 		// Must have 32 bits per sample
441 		//
442 		if (pAudioFormat->wBitsPerSample != 32)
443 		{
444 			ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Only 32 bits per"
445 									" sample supported for big-endian data\n"));
446 			return ECHOSTATUS_BAD_FORMAT;
447 		}
448 
449 		//
450 		// Mono or stereo only
451 		//
452 		switch (pAudioFormat->wDataInterleave)
453 		{
454 
455 #ifdef STEREO_BIG_ENDIAN32_SUPPORT
456 
457 			case 1 :
458 			case 2 :
459 				break;
460 #else
461 
462 			case 1 :
463 				break;
464 
465 #endif
466 
467 			default :
468 				ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Interleave of %d"
469 										" not allowed for big-endian data\n",
470 										pAudioFormat->wDataInterleave));
471 				return ECHOSTATUS_BAD_FORMAT;
472 		}
473 
474 		return ECHOSTATUS_OK;
475 	}
476 
477 	//
478 	// Check bits per sample
479 	//
480 	switch ( pAudioFormat->wBitsPerSample )
481 	{
482 		case 8 :
483 		case 16 :
484 		case 24 :
485 		case 32 :
486 			break;
487 
488 		default :
489 			ECHO_DEBUGPRINTF(
490 				("CEchoGals::QueryAudioFormat: No valid format "
491 				 "specified, bits per sample %d\n",
492 				 pAudioFormat->wBitsPerSample) );
493 			Status = ECHOSTATUS_BAD_FORMAT;
494 			break;
495 	}
496 
497 	return Status;
498 
499 }	// ECHOSTATUS CEchoGals::QueryAudioFormat
500 
501 
502 //===========================================================================
503 //
504 // SetAudioFormat sets the format of the audio data in host memory
505 // for this pipe.
506 //
507 //===========================================================================
508 
509 ECHOSTATUS CEchoGals::SetAudioFormat
510 (
511 	WORD							wPipeIndex,
512 	PECHOGALS_AUDIOFORMAT	pAudioFormat
513 )
514 {
515 	ECHOSTATUS	Status;
516 
517 	ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: "
518 							 "for pipe %d\n",
519 							 wPipeIndex) );
520 
521 	//
522 	// Make sure this pipe is open
523 	//
524 	Status = VerifyAudioOpen( wPipeIndex );
525 	if ( ECHOSTATUS_OK != Status )
526 		return Status;
527 
528 	//
529 	// Check the format
530 	//
531 	Status = QueryAudioFormat( wPipeIndex, pAudioFormat );
532 	if ( ECHOSTATUS_OK != Status )
533 		return Status;
534 
535 	//
536 	// Set the format
537 	//
538 	Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat );
539 	if (ECHOSTATUS_OK == Status)
540 	{
541 		WORD wBytesPerSample;
542 
543 		wBytesPerSample = pAudioFormat->wDataInterleave * (pAudioFormat->wBitsPerSample / 8);
544 		m_wBytesPerSample[ wPipeIndex ] = wBytesPerSample;
545 		m_Pipes[ wPipeIndex ].wInterleave = pAudioFormat->wDataInterleave;
546 	}
547 
548 	return Status;
549 
550 }	// ECHOSTATUS CEchoGals::SetAudioFormat - single pipe
551 
552 
553 //===========================================================================
554 //
555 // This call lets you set the audio format for several pipes at once.
556 //
557 //===========================================================================
558 
559 ECHOSTATUS CEchoGals::SetAudioFormat
560 (
561 	PCChannelMask				pChannelMask,
562 	PECHOGALS_AUDIOFORMAT	pAudioFormat
563 )
564 {
565 	WORD			wPipeIndex = 0xffff;
566 	ECHOSTATUS	Status;
567 
568 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
569 	{
570 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") );
571 		return ECHOSTATUS_DSP_DEAD;
572 	}
573 
574 	for ( ; ; )
575 	{
576 		WORD wBytesPerSample;
577 
578 		wPipeIndex = pChannelMask->GetIndexFromMask( ++wPipeIndex );
579 		if ( (WORD) ECHO_INVALID_CHANNEL == wPipeIndex )
580 			break;							// We be done!
581 
582 		//
583 		// See if this pipe is open
584 		//
585 		if ( !( m_cmAudioOpen.TestIndexInMask( wPipeIndex ) ) )
586 		{
587 			ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: "
588 									 "for pipe %d failed, pipe not open\n",
589 									 wPipeIndex) );
590 			return ECHOSTATUS_CHANNEL_NOT_OPEN;
591 		}
592 
593 		//
594 		// See if the format is OK
595 		//
596 		ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: "
597 								 "for pipe %d\n",
598 								 wPipeIndex) );
599 		Status = QueryAudioFormat( wPipeIndex, pAudioFormat );
600 		if ( ECHOSTATUS_OK != Status )
601 			return Status;
602 
603 		//
604 		// Set the format for this pipe
605 		//
606 		Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat );
607 		if ( ECHOSTATUS_OK != Status )
608 			return Status;
609 
610 		wBytesPerSample = pAudioFormat->wDataInterleave * (pAudioFormat->wBitsPerSample / 8);
611 		m_wBytesPerSample[ wPipeIndex ] = wBytesPerSample;
612 		m_Pipes[ wPipeIndex ].wInterleave = pAudioFormat->wDataInterleave;
613 	}
614 
615 	return ECHOSTATUS_OK;
616 
617 }	// ECHOSTATUS CEchoGals::SetAudioFormat - multiple pipes
618 
619 
620 
621 //===========================================================================
622 //
623 // GetAudioFormat returns the current audio format for a pipe.
624 //
625 //===========================================================================
626 
627 ECHOSTATUS CEchoGals::GetAudioFormat
628 (
629 	WORD							wPipeIndex,
630 	PECHOGALS_AUDIOFORMAT	pAudioFormat
631 )
632 {
633 	ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioFormat: "
634 							 "for pipe %d\n",
635 							 wPipeIndex) );
636 
637 	GetDspCommObject()->GetAudioFormat( wPipeIndex, pAudioFormat );
638 
639 	return ECHOSTATUS_OK;
640 
641 }	// ECHOSTATUS CEchoGals::GetAudioFormat
642 
643 
644 //===========================================================================
645 //
646 // This function does exactly what you think it does.
647 //
648 // Note that if the card is not set to internal clock (that is, the hardware
649 // is synced to word clock or some such), this call has no effect.
650 //
651 // Note that all of the inputs and outputs on a single card share the same
652 // clock.
653 //
654 //===========================================================================
655 
656 ECHOSTATUS CEchoGals::SetAudioSampleRate
657 (
658 	DWORD		dwSampleRate
659 )
660 {
661 	ECHOSTATUS	Status;
662 
663 	ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioSampleRate: "
664 							 "to %ld Hz\n",
665 							 dwSampleRate) );
666 
667 	//
668 	// Check to see if the sample rate is locked
669 	//
670 	if ( 0 != (m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED))
671 	{
672 			return ECHOSTATUS_OK;
673 	}
674 	else
675 	{
676 		Status = QueryAudioSampleRate( dwSampleRate );
677 		if ( ECHOSTATUS_OK != Status )
678 			return Status;
679 
680 		if ( dwSampleRate == GetDspCommObject()->SetSampleRate( dwSampleRate ) )
681 		{
682 			m_dwSampleRate = dwSampleRate;
683 			return ECHOSTATUS_OK;
684 		}
685 	}
686 	return ECHOSTATUS_BAD_FORMAT;
687 
688 }	// ECHOSTATUS CEchoGals::SetAudioSampleRate
689 
690 
691 //===========================================================================
692 //
693 // GetAudioSampleRate - retrieves the current sample rate for the hardware
694 //
695 //===========================================================================
696 
697 ECHOSTATUS CEchoGals::GetAudioSampleRate
698 (
699 	PDWORD	pdwSampleRate
700 )
701 {
702 	ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioSampleRate\n"));
703 
704 	*pdwSampleRate = m_dwSampleRate;
705 
706 	return ECHOSTATUS_OK;
707 
708 }	// ECHOSTATUS CEchoGals::GetAudioSampleRate
709 
710 
711 
712 
713 /******************************************************************************
714 
715  Functions related to the scatter-gather list
716 
717  ******************************************************************************/
718 
719 
720 //===========================================================================
721 //
722 // Use the given CDaffyDuck object as the scatter-gather list for this pipe
723 //
724 //===========================================================================
725 
726 ECHOSTATUS CEchoGals::SetDaffyDuck(WORD wPipeIndex, CDaffyDuck *pDuck)
727 {
728 	m_DaffyDucks[wPipeIndex] = pDuck;
729 
730 	return ECHOSTATUS_OK;
731 
732 }	// SetDaffyDuck
733 
734 
735 
736 
737 //===========================================================================
738 //
739 // This method returns a pointer to the daffy duck for a pipe; the caller
740 // can then have direct access to the daffy duck object.
741 //
742 //===========================================================================
743 
744 CDaffyDuck *CEchoGals::GetDaffyDuck(WORD wPipeIndex)
745 {
746 	ECHO_DEBUGPRINTF(("CEchoGals::GetDaffyDuck for pipe index %d\n",wPipeIndex));
747 
748 	if (wPipeIndex >= GetNumPipes())
749 		return NULL;
750 
751 	return m_DaffyDucks[wPipeIndex];
752 }
753 
754 
755 
756 /******************************************************************************
757 
758  Functions for starting and stopping transport
759 
760  ******************************************************************************/
761 
762 //===========================================================================
763 //
764 //	Start transport for a single pipe
765 //
766 //===========================================================================
767 
768 ECHOSTATUS CEchoGals::Start
769 (
770 	WORD	wPipeIndex
771 )
772 {
773 	CChannelMask	cmMask;
774 
775 	cmMask.SetIndexInMask( wPipeIndex );
776 	return Start( &cmMask );
777 
778 }	// ECHOSTATUS CEchoGals::Start
779 
780 
781 //===========================================================================
782 //
783 //	Start transport for a group of pipes
784 //
785 // This function includes logic to sync-start several pipes at once,
786 // according to the process ID specified when the pipe was opened.  This is
787 // included to work around a limitation of the Windows wave API so that
788 // programs could use multiple inputs and outputs and have them start at the
789 // same time.
790 //
791 // If you don't want to use this feature, call CEchoGals::ClearFlags
792 // with ECHOGALS_FLAG_SYNCH_WAVE and the pipes will start immediately.
793 //
794 //===========================================================================
795 
796 ECHOSTATUS CEchoGals::Start
797 (
798 	PCChannelMask	pChannelMask
799 )
800 {
801 	WORD				wPipe;
802 	DWORD				dwPhysStartAddr;
803 	CChannelMask	cmStart;
804 	PVOID				ProcessId = NULL;
805 	CDspCommObject *pDCO;
806 
807 	pDCO = GetDspCommObject();
808 	if ( NULL == pDCO || pDCO->IsBoardBad() )
809 		return ECHOSTATUS_DSP_DEAD;
810 
811 	//
812 	//	See if we are dealing with synchronized wave pipes.  If the sync
813 	// flag is set, get the process ID for this pipe to compare with
814 	// other pipes.
815 	//
816 	if ( GetFlags() & ECHOGALS_FLAG_SYNCH_WAVE )
817 	{
818 		wPipe = pChannelMask->GetIndexFromMask( 0 );
819 		ProcessId = m_ProcessId[ wPipe ];
820 	}
821 
822 	//--------------------------------------------------------
823 	// Process each pipe in the mask
824 	//--------------------------------------------------------
825 
826 	for (wPipe = 0; wPipe < GetNumPipes(); wPipe++)
827 	{
828 		PDWORD pdwDspCommPositions;
829 
830 		//
831 		// Skip this pipe if it's not in the mask
832 		//
833 		if (!pChannelMask->TestIndexInMask(wPipe))
834 			continue;
835 
836 		//
837 		// This pipe must have a CDaffyDuck object
838 		//
839 		if (NULL == m_DaffyDucks[ wPipe ])
840 		{
841 			ECHO_DEBUGPRINTF(("CDaffyDuck::Start - trying to start pipe index %d "
842 									"but there is no CDaffyDuck!\n",wPipe));
843 			return ECHOSTATUS_CHANNEL_NOT_OPEN;
844 		}
845 
846 		//
847 		// If this pipe was opened in cyclic mode, make sure that the corresponding
848 		// CDaffyDuck has been wrapped
849 		//
850 		if (	(0 != m_cmAudioCyclic.TestIndexInMask( wPipe ) ) &&
851 				(FALSE == m_DaffyDucks[wPipe]->Wrapped())
852 			)
853 		{
854 			ECHO_DEBUGPRINTF(("CEchoGals::Start called for pipe index %d - "
855 									"pipe was opened in cyclic mode, but the duck "
856 									"has not been wrapped\n",wPipe));
857 			return ECHOSTATUS_DUCK_NOT_WRAPPED;
858 		}
859 
860 		//
861 		// Set the physical start address for the duck for this pipe
862 		//
863 		dwPhysStartAddr = m_DaffyDucks[wPipe]->GetPhysStartAddr();
864 		pDCO->SetAudioDuckListPhys( wPipe, dwPhysStartAddr );
865 
866 
867 		//
868 		// Do different things to this pipe depending on the
869 		// state
870 		//
871 		switch (m_byPipeState[wPipe])
872 		{
873 			case PIPE_STATE_RESET :
874 				//
875 				// Clean up the DMA position stuff
876 				//
877 				pdwDspCommPositions = pDCO->GetAudioPositionPtr();
878 				pdwDspCommPositions[ wPipe ] = 0;
879 
880 				//
881 				// If this pipe isn't synced or is in a reset state,
882 				// start it up
883 				//
884 				if (NULL == ProcessId)
885 				{
886 					m_byPipeState[ wPipe ] = PIPE_STATE_STARTED;
887 					cmStart.SetIndexInMask( wPipe );
888 				}
889 				else
890 				{
891 					//
892 					// This pipe is synced; upgrade to PENDING
893 					//
894 					m_byPipeState[ wPipe ] = PIPE_STATE_PENDING;
895 				}
896 				break;
897 
898 
899 			case PIPE_STATE_STOPPED :
900 
901 				if (NULL == ProcessId)
902 				{
903 					//
904 					// Non-synced pipe; start 'er up!
905 					//
906 					m_byPipeState[ wPipe ] = PIPE_STATE_STARTED;
907 					cmStart.SetIndexInMask( wPipe );
908 				}
909 				else
910 				{
911 					//
912 					// Synced pipe; if this pipe is in STOP mode,
913 					// upgrade it to PENDING status
914 					//
915 					m_byPipeState[ wPipe ] = PIPE_STATE_PENDING;
916 				}
917 				break;
918 
919 
920 			case PIPE_STATE_PENDING :
921 			case PIPE_STATE_STARTED :
922 				break;
923 		}
924 	}
925 
926 	//-----------------------------------------------------------------
927 	// Start the pipes
928 	//-----------------------------------------------------------------
929 
930 	//
931 	// Don't go if all the synced pipes are not yet pending
932 	//
933 	BOOL	fAllReady = TRUE;
934 	for ( wPipe = 0; wPipe < GetNumPipes(); wPipe++ )
935 	{
936 		if ( 	( ProcessId == m_ProcessId[ wPipe ] ) &&
937 				( PIPE_STATE_STOPPED == m_byPipeState[wPipe]))
938 		{
939 			ECHO_DEBUGPRINTF(("CEchoGals::Start - can't start; pipe %d "
940 									"still set to state %d\n",
941 									wPipe,
942 									m_byPipeState[wPipe]));
943 			fAllReady = FALSE;
944 		}
945 	}
946 
947 	//
948 	// All synced pipes are pending; time to go!
949 	//
950 	if (fAllReady)
951 	{
952 		for (wPipe = 0; wPipe < GetNumPipes(); wPipe++)
953 		{
954 			if ( 	(ProcessId == m_ProcessId[ wPipe ]) &&
955 					(PIPE_STATE_PENDING == m_byPipeState[ wPipe ]))
956 			{
957 				m_byPipeState[wPipe] = PIPE_STATE_STARTED;
958 				cmStart.SetIndexInMask( wPipe );
959 				ECHO_DEBUGPRINTF(("CEchoGals::Start - setting pipe %d to start\n",
960 										wPipe));
961 			}
962 		}
963 	}
964 
965 	if ( cmStart.IsEmpty() )
966 		return ECHOSTATUS_OK;
967 
968 
969 	//-----------------------------------------------------------------
970 	// Time to go
971 	//-----------------------------------------------------------------
972 
973 	return pDCO->StartTransport( &cmStart );
974 
975 }	// ECHOSTATUS CEchoGals::Start
976 
977 
978 
979 //===========================================================================
980 //
981 // Stop a single pipe
982 //
983 //===========================================================================
984 
985 ECHOSTATUS CEchoGals::Stop
986 (
987 	WORD	wPipeIndex
988 )
989 {
990 	CChannelMask	cmMask;
991 
992 	cmMask.SetIndexInMask( wPipeIndex );
993 	return( Stop( &cmMask ) );
994 
995 }	// ECHOSTATUS CEchoGals::Stop
996 
997 
998 //===========================================================================
999 //
1000 // Stop several pipes simultaneously
1001 //
1002 //===========================================================================
1003 
1004 ECHOSTATUS CEchoGals::Stop
1005 (
1006 	PCChannelMask	pChannelMask
1007 )
1008 {
1009 	INT32			i;
1010 	ECHOSTATUS	Status;
1011 
1012 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1013 		return ECHOSTATUS_DSP_DEAD;
1014 
1015 	Status = GetDspCommObject()->StopTransport( pChannelMask );
1016 	if ( ECHOSTATUS_OK != Status )
1017 		return Status;
1018 
1019 	for ( i = 0; i < GetNumPipes(); i++ )
1020 	{
1021 		//
1022 		//	Skip channel if not in mask
1023 		//
1024 		if ( !pChannelMask->TestIndexInMask( (WORD) i ) )
1025 			continue;
1026 
1027 
1028 		//
1029 		// Don't bother if it's stopped already
1030 		//
1031 		if ( PIPE_STATE_STOPPED == m_byPipeState[ i ] )
1032 			continue;
1033 
1034 		//
1035 		// Muck with the DMA position
1036 		//
1037 		UpdateDmaPos( (WORD) i );
1038 
1039 		m_byPipeState[ i ] = PIPE_STATE_STOPPED;
1040 	}
1041 
1042 	return Status;
1043 
1044 }	// ECHOSTATUS CEchoGals::Stop
1045 
1046 
1047 //===========================================================================
1048 //
1049 // Reset transport for a single pipe
1050 //
1051 //===========================================================================
1052 
1053 ECHOSTATUS CEchoGals::Reset
1054 (
1055 	WORD	wPipeIndex
1056 )
1057 {
1058 	CChannelMask	cmMask;
1059 
1060 	cmMask.SetIndexInMask( wPipeIndex );
1061 	return Reset( &cmMask );
1062 
1063 }	// ECHOSTATUS CEchoGals::Reset
1064 
1065 
1066 //===========================================================================
1067 //
1068 // Reset transport for a group of pipes simultaneously
1069 //
1070 //===========================================================================
1071 
1072 ECHOSTATUS CEchoGals::Reset
1073 (
1074 	PCChannelMask	pChannelMask
1075 )
1076 {
1077 	WORD			i;
1078 	ECHOSTATUS	Status;
1079 
1080 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1081 		return ECHOSTATUS_DSP_DEAD;
1082 
1083 	Status = GetDspCommObject()->ResetTransport( pChannelMask );
1084 	if ( ECHOSTATUS_OK != Status )
1085 		return Status;
1086 
1087 	for ( i = 0; i < GetNumPipes(); i++ )
1088 	{
1089 		//
1090 		//	Skip channel if not in mask
1091 		//
1092 		if ( !pChannelMask->TestIndexInMask( (WORD) i ) )
1093 			continue;
1094 
1095 		if ( PIPE_STATE_RESET == m_byPipeState[ i ] )
1096 			continue;
1097 
1098 		//
1099 		// Muck with the DMA position
1100 		//
1101 		UpdateDmaPos( i );
1102 		m_dwLastDspPos[ i ] = 0;
1103 		GetDspCommObject()->ResetPipePosition(i);
1104 
1105 		m_byPipeState[ i ] = PIPE_STATE_RESET;
1106 	}
1107 
1108 	return Status;
1109 
1110 }	// ECHOSTATUS CEchoGals::Reset
1111 
1112 
1113 
1114 
1115 /******************************************************************************
1116 
1117  Functions for handling the current DMA position for pipes; the DMA position
1118  is the number of bytes transported by a pipe.
1119 
1120  ******************************************************************************/
1121 
1122 //===========================================================================
1123 //
1124 // The DSP sends back a 32 bit DMA counter for each pipe of the number of bytes
1125 // transported; this count is written by the DSP to the comm page without
1126 // the driver doing anything.
1127 //
1128 // The driver then maintains a 64 bit counter based off of the DSP's counter.
1129 //
1130 // Call UpdateDmaPos to cause the driver to update the internal 64 bit DMA
1131 // counter.
1132 //
1133 // The 64 bit DMA counter is in units of bytes, not samples.
1134 //
1135 //===========================================================================
1136 
1137 void CEchoGals::UpdateDmaPos( WORD wPipeIndex )
1138 {
1139 	DWORD dwDspPos;
1140 	DWORD dwDelta;
1141 
1142 	//
1143 	// Get the current DSP position and find out how much it
1144 	// has moved since last time.  This is necessary to avoid
1145 	// the 32 bit counter wrapping around.
1146 	//
1147 	dwDspPos = GetDspCommObject()->GetAudioPosition( wPipeIndex );
1148 	dwDelta = dwDspPos - m_dwLastDspPos[ wPipeIndex ];
1149 
1150 	//
1151 	// Adjust the 64 bit position
1152 	//
1153 	m_ullDmaPos[ wPipeIndex ] += dwDelta;
1154 	m_dwLastDspPos[ wPipeIndex ] = dwDspPos;
1155 
1156 } // UpdateDmaPos
1157 
1158 
1159 //===========================================================================
1160 //
1161 // ResetDmaPos resets the 64 bit DMA counter.
1162 //
1163 //===========================================================================
1164 
1165 void CEchoGals::ResetDmaPos(WORD wPipe)
1166 {
1167 	m_ullDmaPos[ wPipe ] = 0;
1168 	m_dwLastDspPos[ wPipe ] = 0;
1169 
1170 	//
1171 	// There may still be mappings in the daffy duck; if so,
1172 	// tell them to reset their DMA positions starting at zero
1173 	//
1174 	if (NULL != m_DaffyDucks[wPipe])
1175 		m_DaffyDucks[wPipe]->ResetStartPos();
1176 }
1177 
1178 
1179 //===========================================================================
1180 //
1181 // This is a very powerful feature; calling this function gives you a pointer
1182 // to the memory location where the DSP writes the 32 bit DMA position for
1183 // a pipe.  The DSP is constantly updating this value as it moves data.
1184 //
1185 // Since the DSP is constantly updating it, you can dereference this pointer
1186 // from anywhere and read the DMA position without calling the generic driver.
1187 // This means that with some adroit mapping, you could read the DMA position
1188 // from user mode without calling the kernel.
1189 //
1190 // Remember, Peter - with great power comes great responsibility; you should
1191 // only read this pointer and never write to it or to anywhere around it.  You
1192 // could easily lock up your computer.
1193 //
1194 // Note that the DSP writes the position in little endian format; if you are
1195 // on a big endian machine, you will need to byte swap the postion
1196 // before you use it.
1197 //
1198 //===========================================================================
1199 
1200 ECHOSTATUS CEchoGals::GetAudioPositionPtr
1201 (
1202 	WORD		wPipeIndex,
1203 	PDWORD &	pdwPosition
1204 )
1205 {
1206 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1207 		return ECHOSTATUS_DSP_DEAD;
1208 
1209 	if (wPipeIndex >= GetNumPipes())
1210 	{
1211 		pdwPosition = NULL;
1212 		return ECHOSTATUS_INVALID_CHANNEL;
1213 	}
1214 
1215 	PDWORD	pdwDspCommPos = GetDspCommObject()->GetAudioPositionPtr();
1216 
1217 	pdwPosition = pdwDspCommPos + wPipeIndex;
1218 
1219 	return ECHOSTATUS_OK;
1220 
1221 }	// ECHOSTATUS CEchoGals::GetAudioPositionPtr
1222 
1223