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, ¤tCmd, 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, ¤tCmd, 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, ¤tCmd );
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, ¤tCmd );
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