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