1 /*
2      PLIB - A Suite of Portable Game Libraries
3      Copyright (C) 1998,2002  Steve Baker
4 
5      This library is free software; you can redistribute it and/or
6      modify it under the terms of the GNU Library General Public
7      License as published by the Free Software Foundation; either
8      version 2 of the License, or (at your option) any later version.
9 
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Library General Public License for more details.
14 
15      You should have received a copy of the GNU Library General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 
19      For further information visit http://plib.sourceforge.net
20 
21      $Id: slDSP.cxx 1775 2003-07-04 14:33:03Z puggles $
22 */
23 
24 
25 #include "sl.h"
26 #include <errno.h>
27 
28 static int init_bytes ;
29 
30 #ifdef SL_USING_OSS_AUDIO
31 
32 /* ------------------------------------------------------------ */
33 /* OSSAUDIO - Linux, FreeBSD, etc                               */
34 /* ------------------------------------------------------------ */
35 
open(const char * device,int _rate,int _stereo,int _bps)36 void slDSP::open ( const char *device, int _rate, int _stereo, int _bps )
37 {
38   fd = ::open ( device, O_WRONLY | O_NONBLOCK ) ;
39 
40   if ( fd < 0 )
41   {
42     perror ( "slDSP: open" ) ;
43     error = SL_TRUE ;
44 
45     stereo     = SL_FALSE ;
46     bps        =    1     ;
47     rate       =  8000    ;
48     init_bytes =    0     ;
49   }
50   else
51   {
52     error = SL_FALSE ;
53 
54     /* Set up a driver fragment size of 1024 (ie 2^10) */
55 
56 errno = 0 ;
57     ioctl ( SNDCTL_DSP_SETFRAGMENT, 0x7FFF000A ) ;
58 
59     stereo = ioctl ( SOUND_PCM_WRITE_CHANNELS, _stereo ? 2 : 1 ) >= 2 ;
60     bps    = ioctl ( SOUND_PCM_WRITE_BITS, _bps ) ;
61     rate   = ioctl ( SOUND_PCM_WRITE_RATE, _rate ) ;
62 
63 if ( errno != 0 ) perror ( "slDSP: ioctl" ) ;
64 
65     getBufferInfo () ;
66     init_bytes = buff_info.bytes ;
67   }
68 }
69 
70 
close()71 void slDSP::close ()
72 {
73   if ( fd >= 0 )
74     ::close ( fd ) ;
75 }
76 
77 
getDriverBufferSize()78 int slDSP::getDriverBufferSize ()
79 {
80   if ( error )
81     return 0 ;
82 
83   getBufferInfo () ;
84   return buff_info.fragsize ;
85 }
86 
getBufferInfo()87 void slDSP::getBufferInfo ()
88 {
89   if ( error )
90     return ;
91 
92   if ( ::ioctl ( fd, SNDCTL_DSP_GETOSPACE, & buff_info ) < 0 )
93   {
94     perror ( "slDSP: getBufferInfo" ) ;
95     error = SL_TRUE ;
96     return ;
97   }
98 }
99 
100 
write(void * buffer,size_t length)101 void slDSP::write ( void *buffer, size_t length )
102 {
103   if ( error || (int)length <= 0 )
104     return ;
105 
106   size_t nwritten = ::write ( fd, (const char *) buffer, length ) ;
107 
108   if ( (int)nwritten < 0 )
109     perror ( "slDSP: write" ) ;
110   else
111   if ( nwritten != length )
112     perror ( "slDSP: short write" ) ;
113 }
114 
115 
secondsRemaining()116 float slDSP::secondsRemaining ()
117 {
118   if ( error )
119     return 0.0f ;
120 
121   getBufferInfo () ;
122 
123   int samples_left = buff_info.fragments * buff_info.fragsize ;
124 
125   if (   stereo  ) samples_left /= 2 ;
126   if ( bps == 16 ) samples_left /= 2 ;
127   return   (float) samples_left / (float) rate ;
128 }
129 
130 
secondsUsed()131 float slDSP::secondsUsed ()
132 {
133   if ( error )
134     return 0.0f ;
135 
136   getBufferInfo () ;
137 
138   int samples_used = init_bytes - buff_info.bytes ;
139 
140   if (  stereo   ) samples_used /= 2 ;
141   if ( bps == 16 ) samples_used /= 2 ;
142   return   (float) samples_used / (float) rate ;
143 }
144 
145 
sync()146 void slDSP::sync ()
147 {
148    if ( !error) ::ioctl ( fd, SOUND_PCM_SYNC , 0 ) ;
149 }
150 
stop()151 void slDSP::stop ()
152 {
153    if ( !error) ::ioctl ( fd, SOUND_PCM_RESET, 0 ) ;
154 }
155 
156 #endif
157 
158 #ifdef UL_WIN32
159 
160 /* ------------------------------------------------------------ */
161 /* win32                                                        */
162 /* ------------------------------------------------------------ */
163 
wperror(MMRESULT num)164 static	void	wperror(MMRESULT num)
165 {
166    char	buffer[0xff];  // yes, this is hardcoded :-)
167 
168    waveOutGetErrorText( num, buffer, sizeof(buffer)-1);
169 
170    ulSetError ( UL_WARNING, "SlDSP: %s (%d)", buffer, num );
171 }
172 
173 
174 
waveOutProc(HWAVEOUT hwo,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2)175 void CALLBACK waveOutProc( HWAVEOUT hwo, UINT uMsg,
176       DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
177 {
178    slDSP *pDsp = (slDSP *)dwInstance;
179    switch( uMsg )
180    {
181    case    WOM_CLOSE:
182       break;
183 
184    case    WOM_OPEN:
185       break;
186 
187    case    WOM_DONE:
188       pDsp->counter--;
189       break;
190    }
191 }
192 
193 
open(const char * device,int _rate,int _stereo,int _bps)194 void slDSP::open ( const char *device, int _rate, int _stereo, int _bps )
195 {
196    MMRESULT	result;
197    int i;
198 
199    hWaveOut   	= NULL;
200    curr_header	= 0;
201    counter      = 0;
202    written      = 0;
203    for ( i = 0; i < BUFFER_COUNT; i++ ) wavehdr[i] = NULL;
204 
205    Format.wFormatTag       = WAVE_FORMAT_PCM;
206    Format.nChannels        = _stereo ? 2 : 1;
207    Format.nSamplesPerSec   = _rate;
208    Format.wBitsPerSample   = _bps;
209    Format.nBlockAlign      = 1;
210    Format.nAvgBytesPerSec  = _rate * Format.nChannels;
211    Format.cbSize           = 0;
212 
213    result = waveOutOpen( & hWaveOut, WAVE_MAPPER, & Format, 0,
214       0L, WAVE_FORMAT_QUERY );
215 
216    if ( result != MMSYSERR_NOERROR )
217    {
218       wperror( result);
219 
220       error      = SL_TRUE  ;
221       stereo     = SL_FALSE ;
222       bps        = _bps     ;
223       rate       = _rate    ;
224       init_bytes =    0     ;
225 
226       return;
227    }
228 
229 #if 0
230    ulSetError ( UL_DEBUG, "Request: stereo=%d bps=%d rate=%d",
231       _stereo, _bps, _rate );
232    ulSetError ( UL_DEBUG, "Result: channels=%d bps=%d rate=%d",
233       Format.nChannels, Format.wBitsPerSample,
234       Format.nSamplesPerSec );
235 #endif
236 
237    // Now the hwaveouthandle "should" be valid
238 
239    if ( ( result = waveOutOpen( & hWaveOut, WAVE_MAPPER,
240          (WAVEFORMATEX *)& Format, (DWORD)waveOutProc,
241          (DWORD)this, CALLBACK_FUNCTION )) != MMSYSERR_NOERROR )
242    {
243       wperror( result);
244 
245       error      = SL_TRUE ;
246       stereo     = SL_FALSE ;
247       bps        = _bps     ;
248       rate       = _rate    ;
249       init_bytes =    0     ;
250       return;
251    }
252    else
253    {
254       error  = SL_FALSE ;
255       stereo = _stereo;
256       bps    = _bps;
257       rate   = _rate;
258 
259       /* hmm ?! */
260 
261       init_bytes = BUFFER_SIZE;
262 
263       for ( i = 0; i < BUFFER_COUNT; i++ )
264       {
265          Uchar *p = new Uchar[sizeof(WAVEHDR) + BUFFER_SIZE];
266 
267          wavehdr[i] = (WAVEHDR*) p;
268          wavehdr[i]->lpData          = (LPSTR) p + sizeof(WAVEHDR);
269          wavehdr[i]->dwBufferLength  = (DWORD) BUFFER_SIZE;
270          wavehdr[i]->dwBytesRecorded = 0L;
271          wavehdr[i]->dwUser          = 0;
272          wavehdr[i]->dwFlags         = 0;
273          wavehdr[i]->dwLoops         = 0;
274          wavehdr[i]->lpNext          = 0;
275          wavehdr[i]->reserved        = 0;
276 
277          result = waveOutPrepareHeader( hWaveOut,
278             wavehdr[i], sizeof(WAVEHDR) );
279 
280          if ( result != MMSYSERR_NOERROR )
281          {
282             wperror ( result );
283             error = SL_TRUE;
284             return;
285          }
286       }
287    }
288 }
289 
290 
close()291 void slDSP::close ()
292 {
293    if ( hWaveOut != NULL )
294    {
295       waveOutReset( hWaveOut );
296 
297       for ( int i = 0; i < BUFFER_COUNT; i++ )
298       {
299          waveOutUnprepareHeader( hWaveOut, wavehdr[i], sizeof(WAVEHDR) );
300 
301          delete[] (Uchar*) wavehdr[i];
302       }
303 
304       waveOutClose( hWaveOut );
305       hWaveOut = NULL;
306    }
307 }
308 
getDriverBufferSize()309 int slDSP::getDriverBufferSize ()
310 {
311    if ( error )
312       return 0 ;
313 
314    /* hmm ?! */
315 
316    return    BUFFER_SIZE;
317 }
318 
getBufferInfo()319 void slDSP::getBufferInfo ()
320 {
321     return ;
322 }
323 
324 
write(void * buffer,size_t length)325 void slDSP::write ( void *buffer, size_t length )
326 {
327    MMRESULT	result;
328 
329    if ( error || (int)length <= 0 )
330       return ;
331 
332 #if 0
333    ulSetError ( UL_DEBUG, "written=%ld counter=%d curr_header=%d",
334       written, counter, curr_header );
335 #endif
336 
337    memcpy(wavehdr[curr_header]->lpData, buffer, length);
338    wavehdr[curr_header]->dwBufferLength  = (DWORD) length;
339 
340    result = waveOutWrite(hWaveOut, wavehdr[curr_header], sizeof(WAVEHDR));
341 
342    if ( result != MMSYSERR_NOERROR )
343    {
344       wperror ( result );
345       error = SL_TRUE;
346    }
347 
348    counter ++;
349    written += (DWORD) BUFFER_SIZE;
350 
351    curr_header = ( curr_header + 1 ) % BUFFER_COUNT;
352 }
353 
354 
secondsRemaining()355 float slDSP::secondsRemaining ()
356 {
357    if ( error )
358       return 0.0f ;
359 
360    return 10.0f ;
361 }
362 
363 
secondsUsed()364 float slDSP::secondsUsed ()
365 {
366    DWORD    samples_used;
367    MMRESULT	result;
368    float    samp_time;
369 
370    if ( error )
371       return 0.0f ;
372 
373    mmt.wType = TIME_BYTES;
374 
375    result = waveOutGetPosition( hWaveOut, &mmt, sizeof( mmt ));
376 
377    if ( mmt.u.cb == 0 || counter == 0)
378       return    (float)0.0;
379 
380    if ( counter < BUFFER_COUNT )
381       samples_used = written - mmt.u.cb ;
382    else
383       samples_used = BUFFER_COUNT * BUFFER_SIZE ;
384 
385    if (  stereo   ) samples_used /= 2 ;
386    if ( bps == 16 ) samples_used /= 2 ;
387 
388    samp_time  = (float) samples_used / (float) rate ;
389 
390 #if 0
391    ulSetError ( UL_DEBUG, "%0.2f written packets=%ld stereo=%d bps=%d rate=%d",
392       samp_time, counter, stereo, bps, rate );
393 #endif
394 
395    return   samp_time;
396 }
397 
398 
sync()399 void slDSP::sync ()
400 {
401 }
402 
stop()403 void slDSP::stop ()
404 {
405    if ( error )
406      return ;
407 
408    waveOutReset( hWaveOut );
409    written = 0 ;
410 }
411 
412 /* ------------------------------------------------------------ */
413 /* NetBSD/OpenBSD 2.3 this should be very close to SUN Audio    */
414 /* ------------------------------------------------------------ */
415 
416 #elif (defined(UL_BSD) && !defined(__FreeBSD__) && !defined(__DragonFly__)) || defined(UL_SOLARIS)
open(const char * device,int _rate,int _stereo,int _bps)417 void slDSP::open ( const char *device, int _rate, int _stereo, int _bps )
418 {
419 
420   counter = 0;
421 
422   fd = ::open ( device, O_RDWR | O_NONBLOCK ) ;
423 
424   if ( fd < 0 )
425   {
426     perror ( "slDSP: open" ) ;
427     error = SL_TRUE ;
428   }
429   else
430   {
431 
432     if( ::ioctl( fd, AUDIO_GETINFO, &ainfo) == -1)
433     {
434       perror("slDSP: open - getinfo");
435       stereo     = SL_FALSE ;
436       bps        =    8     ;
437       rate       =  8000    ;
438       init_bytes =    0     ;
439 
440       return;
441     }
442 
443 #ifdef UL_SOLARIS
444 	AUDIO_INITINFO(&ainfo);
445 #endif
446 
447     ainfo.play.sample_rate  = _rate;
448     ainfo.play.precision    = _bps;
449 #ifdef UL_SOLARIS
450 	if ( ainfo.play.port == AUDIO_SPEAKER )
451 		ainfo.play.channels = 1;
452 	else
453 		ainfo.play.channels     = _stereo ? 2 : 1;
454 
455 	ainfo.play.encoding = AUDIO_ENCODING_ALAW;
456 
457 //	if ( _bps < 16 )
458 //	{
459 //		ainfo.play.encoding	    = AUDIO_ENCODING_LINEAR8;
460 //	}
461 //	else
462 //	{
463 //		ainfo.play.encoding	= AUDIO_ENCODING_LINEAR;
464 //	}
465 #else
466     ainfo.play.encoding     = AUDIO_ENCODING_ULINEAR;
467 #endif
468 
469     if( :: ioctl(fd, AUDIO_SETINFO, &ainfo) == -1)
470     {
471       perror("slDSP: open - setinfo");
472       stereo     = SL_FALSE ;
473       bps        =    8     ;
474       rate       =  8000    ;
475       init_bytes =    0     ;
476       return;
477     }
478 
479     rate    = _rate;
480     stereo  = _stereo;
481     bps     = _bps;
482 
483     error = SL_FALSE ;
484 
485     getBufferInfo ();
486 
487     // I could not change the size,
488     // so let's try this ...
489 
490     init_bytes = 1024 * 8;
491   }
492 }
493 
494 
close()495 void slDSP::close ()
496 {
497   if ( fd >= 0 )
498     ::close ( fd ) ;
499 }
500 
501 
getDriverBufferSize()502 int slDSP::getDriverBufferSize ()
503 {
504   if ( error )
505     return 0 ;
506 
507   getBufferInfo () ;
508 
509   // HW buffer is 0xffff on my box
510 
511 #ifdef UL_SOLARIS
512   return ainfo.play.buffer_size;
513 #else
514   return  1024 * 8;
515 #endif
516 
517 }
518 
getBufferInfo()519 void slDSP::getBufferInfo ()
520 {
521   if ( error )
522     return ;
523 
524   if( ::ioctl( fd, AUDIO_GETINFO, &ainfo) < 0)
525   {
526     perror ( "slDSP: getBufferInfo" ) ;
527     error = SL_TRUE ;
528     return ;
529   }
530 
531 #ifndef UL_SOLARIS
532   if( ::ioctl( fd, AUDIO_GETOOFFS, &audio_offset ) < 0)
533   {
534     perror ( "slDSP: getBufferInfo" ) ;
535     error = SL_TRUE ;
536     return ;
537   }
538 #endif
539 }
540 
541 
write(void * buffer,size_t length)542 void slDSP::write ( void *buffer, size_t length )
543 {
544   if ( error || (int)length <= 0 )
545     return ;
546 
547   size_t nwritten = ::write ( fd, (const char *) buffer, length ) ;
548 
549   if ( (int)nwritten < 0 )
550     perror ( "slDSP: write" ) ;
551   else if ( nwritten != length )
552     perror ( "slDSP: short write" ) ;
553 
554   counter ++; /* hmmm */
555 }
556 
557 
secondsRemaining()558 float slDSP::secondsRemaining ()
559 {
560     return 10.0f ;
561 }
562 
563 
secondsUsed()564 float slDSP::secondsUsed ()
565 {
566   /*
567    * original formula from Steve:
568    * -----------------------------
569    *
570    * int samples_used = init_bytes - buff_info.bytes ;
571    *                    |            |
572    *                    |            +--- current available
573    *                    |                 space in bytes !
574    *                    +---------------- available space
575    *                                      when empty;
576    *
577    * sample_used contains the number of bytes which are
578    * "used" or in the DSP "pipeline".
579    */
580 
581 
582   int samples_used;
583 
584   if ( error )
585     return 0.0f ;
586 
587   getBufferInfo () ;
588 
589   //This is wrong: this is the hw queue in the kernel !
590   //samples_used   = ainfo.play.buffer_size - audio_offset.offset ;
591 
592   // This is: all data written minus where we are now in the queue
593 
594   if ( counter == 0 )
595       return 0.0;
596 
597 #ifdef UL_SOLARIS
598   samples_used = ( counter * init_bytes ) - ainfo.play.samples;
599 #else
600   samples_used = ( counter * init_bytes ) - audio_offset.samples;
601 #endif
602 
603   if (  stereo   ) samples_used /= 2 ;
604   if ( bps == 16 ) samples_used /= 2 ;
605 
606   return   (float) samples_used / (float) rate ;
607 }
608 
609 
sync()610 void slDSP::sync ()
611 {
612 #ifdef UL_SOLARIS
613    if ( !error) ::ioctl ( fd, I_FLUSH, FLUSHRW ) ;
614 #else
615    if ( !error) ::ioctl ( fd, AUDIO_FLUSH , 0 ) ;
616 #endif
617 }
618 
stop()619 void slDSP::stop ()
620 {
621    // nothing found yet
622 }
623 
624 /* ------------------------------------------------------------ */
625 /* SGI IRIX audio                                               */
626 /* ------------------------------------------------------------ */
627 
628 #elif defined(UL_IRIX)
629 
open(const char * device,int _rate,int _stereo,int _bps)630 void slDSP::open ( const char *device, int _rate, int _stereo, int _bps )
631 {
632   if ( _bps != 8 )
633   {
634     perror ( "slDSP: supports only 8bit audio for sgi" ) ;
635     error = SL_TRUE;
636     return;
637   }
638 
639   init_bytes = 1024 * 4 ;
640 
641   config  = ALnewconfig();
642 
643   ALsetchannels (  config, _stereo ? AL_STEREO : AL_MONO );
644   ALsetwidth    (  config, _bps == 8 ? AL_SAMPLE_8 : AL_SAMPLE_16 );
645   ALsetqueuesize(  config, init_bytes * 2 );
646 
647   port = ALopenport( device, "w", config );
648 
649   if ( port == NULL )
650   {
651     perror ( "slDSP: open" ) ;
652     error = SL_TRUE ;
653   }
654   else
655   {
656     long params[2] = {AL_OUTPUT_RATE, 0 };
657 
658     params[1] = _rate;
659 
660     if ( ALsetparams(AL_DEFAULT_DEVICE, params, 2) != 0 )
661         {
662        perror ( "slDSP: open - ALsetparams" ) ;
663        error = SL_TRUE ;
664        return;
665         }
666 
667     rate    = _rate;
668     stereo  = _stereo;
669     bps     = _bps;
670 
671     error = SL_FALSE ;
672 
673   }
674 }
675 
676 
close()677 void slDSP::close ()
678 {
679   if ( port != NULL )
680   {
681      ALcloseport ( port   );
682      ALfreeconfig( config );
683      port = NULL;
684   }
685 }
686 
687 
getDriverBufferSize()688 int slDSP::getDriverBufferSize ()
689 {
690   if ( error )
691     return 0 ;
692 
693   return  ALgetqueuesize( config ) / 2 ;
694 }
695 
getBufferInfo()696 void slDSP::getBufferInfo ()
697 {
698   if ( error )
699     return ;
700 }
701 
702 
write(void * buffer,size_t length)703 void slDSP::write ( void *buffer, size_t length )
704 {
705   char *buf = (char *)buffer;
706 
707   if ( error || (int)length <= 0 )
708     return ;
709 
710   for ( int i = 0; i < (int)length; i++ )
711     buf[i] = buf[i] >> 1;
712 
713   ALwritesamps(port, (void *)buf, length );
714 }
715 
716 
secondsRemaining()717 float slDSP::secondsRemaining ()
718 {
719   int   samples_remain;
720 
721   if ( error )
722     return 0.0f ;
723 
724   samples_remain = ALgetfillable(port) ;
725 
726   return   (float) samples_remain / (float) rate ;
727 }
728 
729 
secondsUsed()730 float slDSP::secondsUsed ()
731 {
732   int   samples_used;
733 
734   if ( error )
735     return 0.0f ;
736 
737   samples_used = ALgetfilled(port) * 2 ;
738 
739   return   (float) samples_used / (float) rate ;
740 }
741 
742 
sync()743 void slDSP::sync ()
744 {
745    if ( error )
746      return ;
747 
748   /* found this in the header file - but no description
749    * or example for the long parameter.
750    */
751 
752   // ALflush(ALport, long);
753 }
754 
stop()755 void slDSP::stop ()
756 {
757 }
758 
759 
760 #endif
761 
762 
763 #if defined(UL_MACINTOSH) || defined(UL_MAC_OSX)
764 
765 
766 // Print out debugging info when secondsUsed is called,
767 // track useful information, and used extended error checking.
768 //#define SL_MAC_DEBUG
769 
770 pascal void sndCallbackProc ( SndChannelPtr theChan, SndCommand *theCmd );
771        void doError         ( OSErr theError );
772 
773 int   bytesUsed;
774 bool  playing;
775 
776 //SCStatus sndChanStatus;
777 
778 #ifdef SL_MAC_DEBUG
779   int   underWrites;
780   int   queued;
781   int   writes;
782   int   callBacks;
783   float roughTime;
784   int   qLength;
785   int   qHead;
786   int   qTail;
787 #endif
788 
doError(OSErr theError)789 void doError( OSErr theError ) {
790 
791   const char* msg = 0 ;
792 
793   switch( theError ) {
794 
795   case 0:
796     msg = "No Error." ;
797     break;
798   case notEnoughHardwareErr:
799     msg = "Insufficient hardware available." ;
800     break;
801   case badChannel:
802     msg = "Channel is corrupt or unusable." ;
803     break;
804   case badFormat:
805     msg = "Resource is corrupt or unusable." ;
806     break;
807   case queueFull:
808     msg = "No room in the queue." ;
809     break;
810   case channelBusy:
811     msg = "Channel is busy." ;
812     break;
813   case siInvalidCompression:
814     msg = "Invalid compression type." ;
815     break;
816   case notEnoughBufferSpace:
817     msg = "Insufficient memory available." ;
818     break;
819   case buffersTooSmall:
820     msg = "Buffer is too small." ;
821     break;
822   case paramErr:
823     msg = "Invalid parameter specified." ;
824     break;
825   }
826 
827   if ( msg != 0 )
828     ulSetError ( UL_WARNING, "OSErr : %s", msg ) ;
829   else
830     ulSetError ( UL_WARNING, "OSErr : Unknown Error : %d.", theError );
831 }
832 
sndCallbackProc(SndChannelPtr chan,SndCommand * cmd)833 pascal void sndCallbackProc ( SndChannelPtr chan, SndCommand *cmd )
834 {
835 
836   //SndChannelStatus is a BIG waste of cpu time, but I don't know
837   //any other way to remedy this. I will leave it out for now.
838   //SndChannelStatus(chan, sizeof(SCStatus), &sndChanStatus);
839   //playing = sndChanStatus.scChannelBusy ? true : false;
840 
841   bytesUsed -= BUFFER_SIZE ;
842 
843 #ifdef SL_MAC_DEBUG
844   callBacks++;
845   queued--;
846   qLength = chan->qLength;
847   qTail   = chan->qTail;
848   qHead   = chan->qHead;
849 #endif
850 }
851 
open(const char * device,int _rate,int _stereo,int _bps)852 void slDSP::open ( const char *device, int _rate, int _stereo, int _bps )
853 {
854 
855   // Check for valid ranges on inputs
856   if ( _rate > 65535 )
857   {
858     ulSetError ( UL_WARNING,
859       "slDsp : Sample rate out of bounds! Setting to 44100hz.");
860     _rate = 44100;
861   }
862 
863   error  = SL_FALSE;
864   stereo = _stereo;
865   rate   = _rate;
866   bps    = _bps;
867   osErr  = noErr;
868 
869   bytesPerSample   = (stereo ? 2:1) * (bps/8);
870   bytesPerSecond   = bytesPerSample * rate;
871   secondsPerPacket = BUFFER_SIZE / bytesPerSecond;
872   secUsed = 0;
873   secLeft = 0;
874   playing = false;
875 
876   long initOptions = 0;             // Channel init options
877   if ( stereo )
878     initOptions += initStereo;
879   else
880     initOptions += initMono;
881 
882   // Extra init options available
883   //initOptions += initNoInterp;     // No linear interpolation
884   //initOptions += initNoDrop;       // No drop-sample conversion
885   //initOptions += initChanLeft;     // Left stereo channel
886   //initOptions += initChanRight;    // Right stereo channel
887 
888   // Define a call-back routine - invoked via callBackCmd
889   SndCallBackUPP callBackRoutine = NewSndCallBackUPP ( sndCallbackProc );
890 
891   // Allocate a sound channel
892   sndChannel = new SndChannel;
893   sndChannel -> userInfo = 0;
894   sndChannel -> qLength  = 128;  // Queue as many as 128 packets at a time;
895   osErr = SndNewChannel( &sndChannel, sampledSynth,
896                           initOptions, callBackRoutine );
897 
898   if ( osErr != noErr )
899   {
900     SndDisposeChannel( sndChannel, true );
901     ulSetError ( UL_WARNING,
902       "slDSP::open() Problem creating sound channel" );
903     doError (osErr);
904       error = SL_TRUE;
905   }
906 
907   // Format sound header structure
908   extSndHeader.numChannels   = stereo ? 2 : 1;
909   extSndHeader.sampleRate    = rate << 16;
910   extSndHeader.encode        = extSH;
911   extSndHeader.sampleSize    = bps;
912 
913   // Allocate the sound buffer
914   buf = new char [VIRTUAL_BUFFER_SIZE];
915   if ( !buf )
916   {
917     ulSetError ( UL_WARNING,
918       "slDSP::open() Not enough memory to allocate sound buffer." );
919     SndDisposeChannel( sndChannel, true );
920     error = SL_TRUE;
921   }
922   rpos = buf;
923   wpos = buf;
924 }
925 
close()926 void slDSP::close ()
927 {
928   SndDisposeChannel( sndChannel, true );
929   delete [] buf;
930 }
931 
write(void * buffer,size_t length)932 void slDSP::write ( void *buffer, size_t length )
933 {
934 
935   if ( error || (int)length <= 0 || length > VIRTUAL_BUFFER_SIZE  )
936     return;
937 
938   // Make sure data will fit into available space
939   if ( length >= VIRTUAL_BUFFER_SIZE - (rpos - buf) )
940     wpos = buf;
941 
942   // Copy sound data into buffer
943   rpos = wpos;
944   ptr  = (char*)buffer;
945   for ( int i = 0; i < length; i++ )
946     *wpos++ = *ptr++;
947 
948   // Format the sound header
949   extSndHeader.samplePtr = rpos;
950   extSndHeader.numFrames = length / bytesPerSample;
951 
952   // Format the buffer command
953   currentCmd.cmd = bufferCmd;
954   currentCmd.param2 = (long)&extSndHeader;
955 
956   // Do the sound command
957   osErr = SndDoCommand (sndChannel, &currentCmd, false);
958 
959 #ifdef SL_MAC_DEBUG
960   if ( osErr != noErr )
961   {
962       ulSetError ( UL_WARNING, "Error slDsp::write - bufferCmd() : " );
963       doError( osErr );
964       error = SL_TRUE;
965   }
966 #endif
967 
968   // Issue a callBack command when the bufferCmd has finished
969   currentCmd.cmd = callBackCmd;
970   osErr = SndDoCommand (sndChannel, &currentCmd, false);
971 
972 #ifdef SL_MAC_DEBUG
973   if ( osErr != noErr )
974   {
975       ulSetError ( UL_WARNING, "Error slDsp::write - callBackCmd() : " );
976       doError( osErr );
977       error = SL_TRUE;
978   }
979 #endif
980 
981   // Add on time for this packet
982   secUsed += length / bytesPerSecond;
983 
984   // Add on bytes used
985   bytesUsed += BUFFER_SIZE;
986 
987   // Reset the timing for the buffer
988   if (!playing)
989   {
990     Microseconds(&lastTime);
991     playing = true;
992   }
993 
994 #ifdef SL_MAC_DEBUG
995   queued++;
996   writes++;
997 #endif
998 
999 }
1000 
sync()1001 void slDSP::sync ()
1002 {
1003   if ( error ) return;
1004 
1005   // flushCmd will remove all queued commands in a channel,
1006   // and the command in progress is not affected.
1007   // This is not exactly what sync() is supposed to do!
1008   currentCmd.cmd    = flushCmd;
1009   osErr = SndDoImmediate ( sndChannel, &currentCmd );
1010 
1011 #ifdef SL_MAC_DEBUG
1012   if ( osErr != noErr )
1013   {
1014     ulSetError ( UL_WARNING, "Error slDsp::sync() : " );
1015     doError( osErr );
1016     error = SL_TRUE;
1017   }
1018 #endif
1019 }
1020 
stop()1021 void slDSP::stop ()
1022 {
1023   if ( error ) return;
1024 
1025   currentCmd.cmd = quietCmd;
1026   osErr = SndDoImmediate ( sndChannel, &currentCmd );
1027   playing = false;
1028 
1029 #ifdef SL_MAC_DEBUG
1030   if ( osErr != noErr )
1031   {
1032     ulSetError ( UL_WARNING, "Error slDsp::stop() : " );
1033     doError( osErr );
1034     error = SL_TRUE;
1035   }
1036 #endif
1037 }
1038 
getBufferInfo()1039 void slDSP::getBufferInfo ()
1040 {
1041   return;
1042 }
1043 
getDriverBufferSize()1044 int slDSP::getDriverBufferSize ()
1045 {
1046   return BUFFER_SIZE;
1047 }
1048 
secondsRemaining()1049 float slDSP::secondsRemaining ()
1050 {
1051   if ( error )
1052     return 0.0f;
1053 
1054   // sl doesn't use this, so I didn't write it!
1055   return 10.0f;
1056 }
1057 
secondsUsed()1058 float slDSP::secondsUsed ()
1059 {
1060   if ( error || secUsed <= 0 )
1061     return secUsed = 0;
1062 
1063   Microseconds( &currTime );
1064   secUsed  -= (currTime.lo - lastTime.lo) / 1000000.0;
1065   Microseconds( &lastTime );
1066 
1067   // This fixes inaccuracy with Microseconds
1068   if ( secUsed > bytesUsed / bytesPerSecond + secondsPerPacket )
1069     secUsed -= secondsPerPacket;
1070 
1071 #ifdef SL_MAC_DEBUG
1072   if (queued == 0)
1073     underWrites++;
1074   roughTime = bytesUsed / bytesPerSecond;
1075   ulSetError ( UL_DEBUG, "%d\t%d\t%f\t%d\t%d\t%f\t%d\t%d\t%d\t%d",
1076     bytesUsed, queued, secUsed, callBacks,
1077     writes, roughTime, underWrites, qLength, qHead, qTail );
1078 #endif
1079 
1080   return secUsed;
1081 }
1082 
1083 #endif
1084 
1085 
1086