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
OpenAudio(PECHOGALS_OPENAUDIOPARAMETERS pOpenParameters,PWORD pwPipeIndex,BOOL fCheckHardware,CDaffyDuck * pDuck)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
CloseAudio(PECHOGALS_CLOSEAUDIOPARAMETERS pCloseParameters,BOOL fFreeDuck)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
VerifyAudioOpen(WORD wPipeIndex)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
GetActivePipes(PCChannelMask pChannelMask)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
GetOpenPipes(PCChannelMask pChannelMask)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
QueryAudioFormat(WORD wPipeIndex,PECHOGALS_AUDIOFORMAT pAudioFormat)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
SetAudioFormat(WORD wPipeIndex,PECHOGALS_AUDIOFORMAT pAudioFormat)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
SetAudioFormat(PCChannelMask pChannelMask,PECHOGALS_AUDIOFORMAT pAudioFormat)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
GetAudioFormat(WORD wPipeIndex,PECHOGALS_AUDIOFORMAT pAudioFormat)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
SetAudioSampleRate(DWORD dwSampleRate)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
GetAudioSampleRate(PDWORD pdwSampleRate)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
SetDaffyDuck(WORD wPipeIndex,CDaffyDuck * pDuck)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
GetDaffyDuck(WORD wPipeIndex)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
Start(WORD wPipeIndex)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
Start(PCChannelMask pChannelMask)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
Stop(WORD wPipeIndex)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
Stop(PCChannelMask pChannelMask)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
Reset(WORD wPipeIndex)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
Reset(PCChannelMask pChannelMask)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
UpdateDmaPos(WORD wPipeIndex)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
ResetDmaPos(WORD wPipe)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
GetAudioPositionPtr(WORD wPipeIndex,PDWORD & pdwPosition)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