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