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
52ECHOSTATUS 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
199ECHOSTATUS 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
258ECHOSTATUS 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
291ECHOSTATUS 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
311ECHOSTATUS 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
342ECHOSTATUS 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
509ECHOSTATUS 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
551ECHOSTATUS 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
613ECHOSTATUS 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
642ECHOSTATUS 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
683ECHOSTATUS 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
712ECHOSTATUS 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
730CDaffyDuck *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
754ECHOSTATUS 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
782ECHOSTATUS 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
971ECHOSTATUS 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
990ECHOSTATUS 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
1039ECHOSTATUS 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
1058ECHOSTATUS 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
1123void 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
1151void 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
1186ECHOSTATUS 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