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