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