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
54ECHOSTATUS 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
201ECHOSTATUS 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
261ECHOSTATUS 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
294ECHOSTATUS 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
314ECHOSTATUS 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
345ECHOSTATUS 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
512ECHOSTATUS 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
554ECHOSTATUS 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
616ECHOSTATUS 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
645ECHOSTATUS 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
686ECHOSTATUS 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
715ECHOSTATUS 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
733CDaffyDuck *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
757ECHOSTATUS 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
785ECHOSTATUS 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
974ECHOSTATUS 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
993ECHOSTATUS 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
1042ECHOSTATUS 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
1061ECHOSTATUS 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
1126void 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
1154void 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
1189ECHOSTATUS 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