7 * OpenAL based audio streaming
10 * Revision 1.1 2005/08/12 08:44:39 taylor
11 * import of FS2_Open audio code which is now *nix only, does not include windows or ogg support that FS2_Open has
13 * Revision 1.12 2005/06/24 19:36:49 taylor
14 * we only want to have m_data_offset be 0 for oggs since the seeking callback will account for the true offset
15 * only extern the one int we need for the -nosound speech fix rather than including the entire header
17 * Revision 1.11 2005/06/19 02:45:55 taylor
18 * OGG streaming fixes to get data reading right and avoid skipping
19 * properly handle seeking in OGG streams
20 * compiler warning fix in OpenAL builds
22 * Revision 1.10 2005/06/01 09:41:14 taylor
23 * bit of cleanup for audiostr-openal and fix a Windows-only enum error
24 * bunch of OGG related fixes for Linux and Windows (DirectSound and OpenAL), fixes audio related TBP 3.2 crashes
25 * gracefully handle OGG logical bitstream changes, shouldn't even load if there is more than 1
27 * Revision 1.9 2005/05/28 19:43:28 taylor
28 * debug message fixing
29 * a little bit of code clarity
31 * Revision 1.8 2005/05/24 03:11:38 taylor
32 * an extra bounds check in sound.cpp
33 * fix audiostr error when filename is !NULL but 0 in len might hit on SDL debug code
35 * Revision 1.7 2005/05/15 06:47:57 taylor
36 * don't let the ogg callbacks close the file handle on us, let us do it ourselves to keep things straight
38 * Revision 1.6 2005/05/13 23:09:28 taylor
39 * Ooops! Added the wrong version of the streaming patch from Jens
41 * Revision 1.5 2005/05/12 17:47:57 taylor
42 * use vm_malloc(), vm_free(), vm_realloc(), vm_strdup() rather than system named macros
43 * fixes various problems and is past time to make the switch
44 * fix a few streaming errors in OpenAL code (Jens Granseuer)
45 * temporary change to help deal with missing music in OpenAL Windows builds
46 * don't assert when si->data is NULL unless we really need to check (OpenAL only)
48 * Revision 1.4 2005/04/05 11:48:22 taylor
49 * remove acm-unix.cpp, replaced by acm-openal.cpp since it's properly cross-platform now
50 * better error handling for OpenAL functions
51 * Windows can now build properly with OpenAL
52 * extra check to make sure we don't try and use too many hardware bases sources
53 * fix memory error from OpenAL extension list in certain instances
55 * Revision 1.3 2005/04/01 07:33:08 taylor
56 * fix hanging on exit with OpenAL
57 * some better error handling on OpenAL init and make it more Windows friendly too
58 * basic 3d sound stuff for OpenAL, not working right yet
60 * Revision 1.2 2005/03/27 08:51:24 taylor
61 * this is what coding on an empty stomach will get you
63 * Revision 1.1 2005/03/27 05:48:58 taylor
64 * initial import of OpenAL streaming (many thanks to Pierre Willenbrock for the missing parts)
70 #ifdef PLAT_UNIX // to end of file...
92 #define MAX_STREAM_BUFFERS 4
98 #define MAX_AUDIO_STREAMS 30
102 #define SUCCESS TRUE // Error returns for all member functions
103 #define FAILURE FALSE
106 #define BIGBUF_SIZE 180000 // This can be reduced to 88200 once we don't use any stereo
107 //#define BIGBUF_SIZE 88300 // This can be reduced to 88200 once we don't use any stereo
108 ubyte *Wavedata_load_buffer = NULL; // buffer used for cueing audiostreams
109 ubyte *Wavedata_service_buffer = NULL; // buffer used for servicing audiostreams
111 CRITICAL_SECTION Global_service_lock;
113 typedef BOOL (*TIMERCALLBACK)(ptr_u);
115 #define COMPRESSED_BUFFER_SIZE 88300
116 ubyte *Compressed_buffer = NULL; // Used to load in compressed data during a cueing interval
117 ubyte *Compressed_service_buffer = NULL; // Used to read in compressed data during a service interval
119 #define AS_HIGHEST_MAX 999999999 // max uncompressed filesize supported is 999 meg
122 int Audiostream_inited = 0;
125 static int audiostr_read_word(SDL_RWops *rw, WORD *i)
127 int rc = SDL_RWread( rw, i, 1, sizeof(WORD) );
129 if (rc != sizeof(WORD))
132 *i = INTEL_SHORT(*i);
137 static int audiostr_read_dword(SDL_RWops *rw, DWORD *i)
139 int rc = SDL_RWread( rw, i, 1, sizeof(DWORD) );
141 if (rc != sizeof(DWORD))
152 void constructor(void);
153 void destructor(void);
154 BOOL Create (UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback);
156 static Uint32 CALLBACK TimeProc(Uint32 interval, void *dwUser);
157 TIMERCALLBACK m_pfnCallback;
161 SDL_TimerID m_nIDTimer;
169 BOOL Open (char *pszFilename);
171 int Read (ubyte *pbDest, uint cbSize, int service=1);
172 uint GetNumBytesRemaining (void) { return (m_nDataSize - m_nBytesPlayed); }
173 uint GetUncompressedAvgDataRate (void) { return (m_nUncompressedAvgDataRate); }
174 uint GetDataSize (void) { return (m_nDataSize); }
175 uint GetNumBytesPlayed (void) { return (m_nBytesPlayed); }
176 ubyte GetSilenceData (void);
177 WAVEFORMATEX m_wfmt; // format of wave file used by Direct Sound
178 WAVEFORMATEX *m_pwfmt_original; // foramt of wave file from actual wave source
179 uint m_total_uncompressed_bytes_read;
180 uint m_max_uncompressed_bytes_to_read;
181 uint m_bits_per_sample_uncompressed;
184 uint m_data_offset; // number of bytes to actual wave data
185 int m_data_bytes_left;
188 uint m_wave_format; // format of wave source (ie WAVE_FORMAT_PCM, WAVE_FORMAT_ADPCM)
189 uint m_nBlockAlign; // wave data block alignment spec
190 uint m_nUncompressedAvgDataRate; // average wave data rate
191 uint m_nDataSize; // size of data chunk
192 uint m_nBytesPlayed; // offset into data chunk
193 BOOL m_abort_next_read;
197 WAVEFORMATEX m_wfxDest;
205 BOOL Create (char *pszFilename);
207 void Play (long volume, int looping);
208 int Is_Playing(){ return(m_fPlaying); }
209 int Is_Paused(){ return(m_bIsPaused); }
210 int Is_Past_Limit() { return m_bPastLimit; }
211 void Stop (int paused=0);
212 void Stop_and_Rewind (void);
213 void Fade_and_Destroy (void);
214 void Fade_and_Stop(void);
215 void Set_Volume(long vol);
218 void Set_Byte_Cutoff(unsigned int num_bytes_cutoff);
219 void Set_Default_Volume(long converted_volume) { m_lDefaultVolume = converted_volume; }
220 long Get_Default_Volume() { return m_lDefaultVolume; }
221 uint Get_Bytes_Committed(void);
222 int Is_looping() { return m_bLooping; }
225 ushort m_bits_per_sample_uncompressed;
229 BOOL WriteWaveData (uint cbSize, uint* num_bytes_written,int service=1);
230 BOOL WriteSilence (uint cbSize);
231 DWORD GetMaxWriteSize (void);
232 BOOL ServiceBuffer (void);
233 static BOOL TimerCallback (ptr_u dwUser);
235 ALuint m_source_id; // name of openAL source
236 ALuint m_buffer_ids[MAX_STREAM_BUFFERS]; //names of buffers
237 int m_play_buffer_id;
239 Timer m_timer; // ptr to Timer object
240 WaveFile * m_pwavefile; // ptr to WaveFile object
241 BOOL m_fCued; // semaphore (stream cued)
242 BOOL m_fPlaying; // semaphore (stream playing)
243 long m_lInService; // reentrancy semaphore
244 uint m_cbBufOffset; // last write position
245 uint m_nBufLength; // length of sound buffer in msec
246 uint m_cbBufSize; // size of sound buffer in bytes
247 uint m_nBufService; // service interval in msec
248 uint m_nTimeStarted; // time (in system time) playback started
250 BOOL m_bLooping; // whether or not to loop playback
251 BOOL m_bFade; // fade out music
252 BOOL m_bDestroy_when_faded;
253 long m_lVolume; // volume of stream ( 0 -> -10 000 )
254 long m_lCutoffVolume;
255 BOOL m_bIsPaused; // stream is stopped, but not rewinded
256 ushort m_silence_written; // number of bytes of silence written to buffer
257 ushort m_bReadingDone; // no more bytes to be read from disk, still have remaining buffer to play
258 DWORD m_fade_timer_id; // timestamp so we know when to start fade
259 DWORD m_finished_id; // timestamp so we know when we've played #bytes required
260 BOOL m_bPastLimit; // flag to show we've played past the number of bytes requred
261 long m_lDefaultVolume;
264 CRITICAL_SECTION write_lock;
268 // Timer class implementation
270 ////////////////////////////////////////////////////////////
273 void Timer::constructor(void)
280 void Timer::destructor(void)
283 SDL_RemoveTimer(m_nIDTimer);
289 BOOL Timer::Create (UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback)
291 BOOL bRtn = SUCCESS; // assume success
294 Assert(nPeriod > 10);
295 Assert(nPeriod >= nRes);
300 m_pfnCallback = pfnCallback;
302 if ((m_nIDTimer = SDL_AddTimer (m_nPeriod, TimeProc, (void*) this)) == NULL) {
303 nprintf(("SOUND", "SOUND ==> Error, unable to create timer\n"));
311 // Timer proc for multimedia timer callback set with timeSetTime().
313 // Calls procedure specified when Timer object was created. The
314 // dwUser parameter contains "this" pointer for associated Timer object.
316 Uint32 CALLBACK Timer::TimeProc(Uint32 interval, void *dwUser)
318 // dwUser contains ptr to Timer object
319 Timer * ptimer = (Timer *) dwUser;
321 // Call user-specified callback and pass back user specified data
322 (ptimer->m_pfnCallback) (ptimer->m_dwUser);
324 if (ptimer->m_nPeriod) {
327 SDL_RemoveTimer(ptimer->m_nIDTimer);
328 ptimer->m_nIDTimer = NULL;
334 // WaveFile class implementation
336 ////////////////////////////////////////////////////////////
339 void WaveFile::Init(void)
344 m_pwfmt_original = NULL;
346 m_nUncompressedAvgDataRate = 0;
349 m_total_uncompressed_bytes_read = 0;
350 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
353 m_abort_next_read = FALSE;
357 void WaveFile::Close(void)
360 if (m_pwfmt_original) {
361 free(m_pwfmt_original);
362 m_pwfmt_original = NULL;
365 if ( m_hStream_open ) {
366 ACM_stream_close((void*)m_hStream);
379 BOOL WaveFile::Open (char *pszFilename)
383 BOOL fRtn = SUCCESS; // assume success
385 char fullpath[_MAX_PATH];
387 m_total_uncompressed_bytes_read = 0;
388 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
390 int FileSize, FileOffset;
392 if ( !cf_find_file_location(pszFilename, CF_TYPE_ANY, fullpath, &FileSize, &FileOffset )) {
396 cfp = SDL_RWFromFile(fullpath, "rb");
402 // Skip the "RIFF" tag and file size (8 bytes)
403 // Skip the "WAVE" tag (4 bytes)
404 SDL_RWseek( cfp, 12+FileOffset, SEEK_SET );
406 // Now read RIFF tags until the end of file
407 uint tag, size, next_chunk;
409 while(done == FALSE) {
410 if ( !audiostr_read_dword(cfp, &tag) )
413 if ( !audiostr_read_dword(cfp, &size) )
416 next_chunk = SDL_RWtell( cfp );
420 case 0x20746d66: // The 'fmt ' tag
421 audiostr_read_word(cfp, &pcmwf.wf.wFormatTag);
422 audiostr_read_word(cfp, &pcmwf.wf.nChannels);
423 audiostr_read_dword(cfp, &pcmwf.wf.nSamplesPerSec);
424 audiostr_read_dword(cfp, &pcmwf.wf.nAvgBytesPerSec);
425 audiostr_read_word(cfp, &pcmwf.wf.nBlockAlign);
426 audiostr_read_word(cfp, &pcmwf.wBitsPerSample);
428 if ( pcmwf.wf.wFormatTag != WAVE_FORMAT_PCM ) {
429 audiostr_read_word(cfp, &cbExtra);
432 // Allocate memory for WAVEFORMATEX structure + extra bytes
433 if ( (m_pwfmt_original = (WAVEFORMATEX *) malloc ( sizeof(WAVEFORMATEX)+cbExtra )) != NULL ){
434 Assert(m_pwfmt_original != NULL);
435 // Copy bytes from temporary format structure
436 memcpy (m_pwfmt_original, &pcmwf, sizeof(pcmwf));
437 m_pwfmt_original->cbSize = cbExtra;
439 // Read those extra bytes, append to WAVEFORMATEX structure
441 SDL_RWread( cfp, ((ubyte *)(m_pwfmt_original) + sizeof(WAVEFORMATEX)), 1, cbExtra );
445 Int3(); // malloc failed
450 case 0x61746164: // the 'data' tag
451 m_nDataSize = size; // This is size of data chunk. Compressed if ADPCM.
452 m_data_bytes_left = size;
453 m_data_offset = SDL_RWtell( cfp );
457 default: // unknown, skip it
461 SDL_RWseek( cfp, next_chunk, SEEK_SET );
464 // At this stage, examine source format, and set up WAVEFORATEX structure for DirectSound.
465 // Since DirectSound only supports PCM, force this structure to be PCM compliant. We will
466 // need to convert data on the fly later if our souce is not PCM
467 switch ( m_pwfmt_original->wFormatTag ) {
468 case WAVE_FORMAT_PCM:
469 m_wave_format = WAVE_FORMAT_PCM;
470 m_wfmt.wBitsPerSample = m_pwfmt_original->wBitsPerSample;
473 case WAVE_FORMAT_ADPCM:
474 m_wave_format = WAVE_FORMAT_ADPCM;
475 m_wfmt.wBitsPerSample = 16;
476 m_bits_per_sample_uncompressed = 16;
480 nprintf(("SOUND", "SOUND => Not supporting %d format for playing wave files\n", m_pwfmt_original->wFormatTag));
487 // Set up the WAVEFORMATEX structure to have the right PCM characteristics
488 m_wfmt.wFormatTag = WAVE_FORMAT_PCM;
489 m_wfmt.nChannels = m_pwfmt_original->nChannels;
490 m_wfmt.nSamplesPerSec = m_pwfmt_original->nSamplesPerSec;
492 m_wfmt.nBlockAlign = (ushort)(( m_wfmt.nChannels * m_wfmt.wBitsPerSample ) / 8);
493 m_wfmt.nAvgBytesPerSec = m_wfmt.nBlockAlign * m_wfmt.nSamplesPerSec;
495 // Init some member data from format chunk
496 m_nBlockAlign = m_pwfmt_original->nBlockAlign;
497 m_nUncompressedAvgDataRate = m_wfmt.nAvgBytesPerSec;
506 // Handle all errors here
507 nprintf(("SOUND","SOUND ==> Could not open wave file %s for streaming\n",pszFilename));
515 if (m_pwfmt_original)
517 free(m_pwfmt_original);
518 m_pwfmt_original = NULL;
527 // Set the file pointer to the start of wave data
529 BOOL WaveFile::Cue (void)
531 BOOL fRtn = SUCCESS; // assume success
534 m_total_uncompressed_bytes_read = 0;
535 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
537 rval = SDL_RWseek( cfp, m_data_offset, SEEK_SET );
543 m_data_bytes_left = m_nDataSize;
544 m_abort_next_read = FALSE;
552 // Returns number of bytes actually read.
554 // Returns -1 if there is nothing more to be read. This function can return 0, since
555 // sometimes the amount of bytes requested is too small for the ACM decompression to
556 // locate a suitable block
557 int WaveFile::Read(ubyte *pbDest, uint cbSize, int service)
559 void *dest_buf=NULL, *uncompressed_wave_data;
560 int rc, uncompressed_bytes_written;
561 uint src_bytes_used, convert_len, num_bytes_desired=0, num_bytes_read;
563 // nprintf(("Alan","Reqeusted: %d\n", cbSize));
565 #if BYTE_ORDER == BIG_ENDIAN
570 uncompressed_wave_data = Wavedata_service_buffer;
572 uncompressed_wave_data = Wavedata_load_buffer;
575 switch ( m_wave_format ) {
576 case WAVE_FORMAT_PCM:
577 num_bytes_desired = cbSize;
581 case WAVE_FORMAT_ADPCM:
582 if ( !m_hStream_open ) {
583 if ( !ACM_stream_open(m_pwfmt_original, &m_wfxDest, (void**)&m_hStream, m_bits_per_sample_uncompressed) ) {
590 num_bytes_desired = cbSize;
593 dest_buf = Compressed_service_buffer;
595 dest_buf = Compressed_buffer;
598 if ( num_bytes_desired <= 0 ) {
599 num_bytes_desired = 0;
600 // nprintf(("Alan","No bytes required for ADPCM time interval\n"));
602 num_bytes_desired = ACM_query_source_size((void*)m_hStream, cbSize);
603 // nprintf(("Alan","Num bytes desired: %d\n", num_bytes_desired));
608 nprintf(("SOUND", "SOUND => Not supporting %d format for playing wave files\n"));
618 // read data from disk
619 if ( m_data_bytes_left <= 0 ) {
621 uncompressed_bytes_written = 0;
625 if ( (m_data_bytes_left > 0) && (num_bytes_desired > 0) ) {
628 if ( num_bytes_desired <= (uint)m_data_bytes_left ) {
629 num_bytes_read = num_bytes_desired;
632 num_bytes_read = m_data_bytes_left;
635 actual_read = SDL_RWread( cfp, dest_buf, 1, num_bytes_read );
637 if ( (actual_read <= 0) || (m_abort_next_read) ) {
639 uncompressed_bytes_written = 0;
643 if ( num_bytes_desired >= (uint)m_data_bytes_left ) {
644 m_abort_next_read = 1;
647 num_bytes_read = actual_read;
650 // convert data if necessary, to PCM
651 if ( m_wave_format == WAVE_FORMAT_ADPCM ) {
652 if ( num_bytes_read > 0 ) {
653 rc = ACM_convert((void*)m_hStream, (ubyte*)dest_buf, num_bytes_read, (ubyte*)uncompressed_wave_data, BIGBUF_SIZE, &convert_len, &src_bytes_used);
658 if ( convert_len == 0 ) {
663 Assert(src_bytes_used <= num_bytes_read);
664 if ( src_bytes_used < num_bytes_read ) {
665 // seek back file pointer to reposition before unused source data
666 SDL_RWseek( cfp, src_bytes_used - num_bytes_read, SEEK_CUR );
669 // Adjust number of bytes left
670 m_data_bytes_left -= src_bytes_used;
671 m_nBytesPlayed += src_bytes_used;
672 uncompressed_bytes_written = convert_len;
674 // Successful read, keep running total of number of data bytes read
678 // Successful read, keep running total of number of data bytes read
679 // Adjust number of bytes left
680 m_data_bytes_left -= num_bytes_read;
681 m_nBytesPlayed += num_bytes_read;
682 uncompressed_bytes_written = num_bytes_read;
688 uncompressed_bytes_written = 0;
691 m_total_uncompressed_bytes_read += uncompressed_bytes_written;
692 // nprintf(("Alan","Read: %d\n", uncompressed_bytes_written));
693 return (uncompressed_bytes_written);
699 // Returns 8 bits of data representing silence for the Wave file format.
701 // Since we are dealing only with PCM format, we can fudge a bit and take
702 // advantage of the fact that for all PCM formats, silence can be represented
703 // by a single byte, repeated to make up the proper word size. The actual size
704 // of a word of wave data depends on the format:
706 // PCM Format Word Size Silence Data
707 // 8-bit mono 1 byte 0x80
708 // 8-bit stereo 2 bytes 0x8080
709 // 16-bit mono 2 bytes 0x0000
710 // 16-bit stereo 4 bytes 0x00000000
712 ubyte WaveFile::GetSilenceData (void)
714 ubyte bSilenceData = 0;
716 // Silence data depends on format of Wave file
717 if (m_pwfmt_original) {
718 if (m_wfmt.wBitsPerSample == 8) {
719 // For 8-bit formats (unsigned, 0 to 255)
720 // Packed DWORD = 0x80808080;
722 } else if (m_wfmt.wBitsPerSample == 16) {
723 // For 16-bit formats (signed, -32768 to 32767)
724 // Packed DWORD = 0x00000000;
733 return (bSilenceData);
737 // AudioStream class implementation
739 ////////////////////////////////////////////////////////////
741 // The following constants are the defaults for our streaming buffer operation.
742 const ushort DefBufferLength = 2000; // default buffer length in msec
743 const ushort DefBufferServiceInterval = 250; // default buffer service interval in msec
746 AudioStream::AudioStream (void)
748 SDL_LockMutex(write_lock);
752 AudioStream::~AudioStream (void)
754 SDL_UnlockMutex(write_lock);
757 void AudioStream::Init_Data ()
763 m_bPastLimit = FALSE;
765 m_bDestroy_when_faded = FALSE;
767 m_lCutoffVolume = -10000;
769 m_silence_written = 0;
770 m_bReadingDone = FALSE;
773 m_fPlaying = m_fCued = FALSE;
774 m_lInService = FALSE;
776 m_nBufLength = DefBufferLength;
778 m_nBufService = DefBufferServiceInterval;
781 memset(m_buffer_ids, 0, sizeof(m_buffer_ids));
783 m_play_buffer_id = 0;
787 BOOL AudioStream::Create (char *pszFilename)
790 BOOL fRtn = SUCCESS; // assume success
797 // make 100% sure we got a good filename
798 if ( !strlen(pszFilename) )
801 // Create a new WaveFile object
802 m_pwavefile = (WaveFile *)malloc(sizeof(WaveFile));
809 m_pwavefile->m_bits_per_sample_uncompressed = m_bits_per_sample_uncompressed;
810 if (m_pwavefile->Open (pszFilename)) {
811 // Calculate sound buffer size in bytes
812 // Buffer size is average data rate times length of buffer
813 // No need for buffer to be larger than wave data though
814 m_cbBufSize = (m_nBufLength/1000) * (m_pwavefile->m_wfmt.wBitsPerSample/8) * m_pwavefile->m_wfmt.nChannels * m_pwavefile->m_wfmt.nSamplesPerSec;
815 m_cbBufSize /= MAX_STREAM_BUFFERS;
816 // if the requested buffer size is too big then cap it
817 m_cbBufSize = (m_cbBufSize > BIGBUF_SIZE) ? BIGBUF_SIZE : m_cbBufSize;
819 // nprintf(("SOUND", "SOUND => Stream buffer created using %d bytes\n", m_cbBufSize));
821 // Create sound buffer
822 OpenAL_ErrorCheck( alGenBuffers(MAX_STREAM_BUFFERS, m_buffer_ids), return FAILURE );
824 OpenAL_ErrorCheck( alGenSources(1, &m_source_id), return FAILURE );
826 OpenAL_ErrorPrint( alSourcef(m_source_id, AL_ROLLOFF_FACTOR, 0) );
828 OpenAL_ErrorPrint( alSourcei(m_source_id, AL_SOURCE_RELATIVE, AL_TRUE) );
830 ALfloat posv[] = { 0, 0, 0 };
831 OpenAL_ErrorPrint( alSourcefv(m_source_id, AL_POSITION, posv) );
833 OpenAL_ErrorPrint( alSourcef(m_source_id, AL_GAIN, 1) );
837 Snd_sram += m_cbBufSize;
840 // Error opening file
841 nprintf(("SOUND", "SOUND => Failed to open wave file: %s\n\r", pszFilename));
842 m_pwavefile->Close();
849 // Error, unable to create WaveFile object
850 nprintf(("Sound", "SOUND => Failed to create WaveFile object %s\n\r", pszFilename));
855 // Error, passed invalid parms
863 BOOL AudioStream::Destroy (void)
867 SDL_LockMutex(write_lock);
872 // Release sound buffer
873 OpenAL_ErrorPrint( alDeleteBuffers(MAX_STREAM_BUFFERS, m_buffer_ids) );
874 OpenAL_ErrorPrint( alDeleteSources(1, &m_source_id) );
875 Snd_sram -= m_cbBufSize;
877 // Delete WaveFile object
879 m_pwavefile->Close();
886 SDL_UnlockMutex(write_lock);
893 // Writes wave data to sound buffer. This is a helper method used by Create and
894 // ServiceBuffer; it's not exposed to users of the AudioStream class.
895 BOOL AudioStream::WriteWaveData (uint size, uint *num_bytes_written, int service)
898 ubyte *uncompressed_wave_data;
900 *num_bytes_written = 0;
902 if ( size == 0 || m_bReadingDone ) {
906 if ( (m_buffer_ids[0] == 0) || !m_pwavefile ) {
911 SDL_LockMutex(Global_service_lock);
915 uncompressed_wave_data = Wavedata_service_buffer;
917 uncompressed_wave_data = Wavedata_load_buffer;
920 int num_bytes_read = 0;
922 // Lock the sound buffer
923 num_bytes_read = m_pwavefile->Read(uncompressed_wave_data, m_cbBufSize, service);
925 if ( num_bytes_read == -1 ) {
926 // means nothing left to read!
931 if ( num_bytes_read > 0 ) {
932 // nprintf(("SOUND", "SOUND ==> Queueing %d bytes of Data\n", num_bytes_read));
934 // Lock the sound buffer
935 ALenum format = AL_FORMAT_MONO8;
937 if (m_pwavefile->m_wfmt.nChannels == 1) {
938 if (m_pwavefile->m_wfmt.wBitsPerSample == 8)
939 format = AL_FORMAT_MONO8;
940 else if (m_pwavefile->m_wfmt.wBitsPerSample == 16)
941 format = AL_FORMAT_MONO16;
942 } else if (m_pwavefile->m_wfmt.nChannels == 2) {
943 if (m_pwavefile->m_wfmt.wBitsPerSample == 8)
944 format = AL_FORMAT_STEREO8;
945 else if (m_pwavefile->m_wfmt.wBitsPerSample == 16)
946 format = AL_FORMAT_STEREO16;
949 // unqueue and recycle a processed buffer
953 OpenAL_ErrorPrint( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &p) );
956 OpenAL_ErrorPrint( alSourceUnqueueBuffers(m_source_id, 1, &bid) );
959 OpenAL_ErrorCheck( alBufferData(m_buffer_ids[m_play_buffer_id], format, uncompressed_wave_data, num_bytes_read, m_pwavefile->m_wfmt.nSamplesPerSec), return FAILURE );
961 OpenAL_ErrorCheck( alSourceQueueBuffers(m_source_id, 1, &m_buffer_ids[m_play_buffer_id]), return FAILURE );
965 if (m_play_buffer_id >= MAX_STREAM_BUFFERS)
966 m_play_buffer_id = 0;
970 SDL_UnlockMutex(Global_service_lock);
979 // Writes silence to sound buffer. This is a helper method used by
980 // ServiceBuffer; it's not exposed to users of the AudioStream class.
981 BOOL AudioStream::WriteSilence (uint size)
985 // not used currently with the OpenAL code
993 // Helper function to calculate max size of sound buffer write operation, i.e. how much
994 // free space there is in buffer.
995 DWORD AudioStream::GetMaxWriteSize (void)
997 DWORD dwMaxSize = m_cbBufSize;
1000 OpenAL_ErrorCheck( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &n), return 0 );
1002 OpenAL_ErrorCheck( alGetSourcei(m_source_id, AL_BUFFERS_QUEUED, &q), return 0 );
1004 if (!n && (q >= MAX_STREAM_BUFFERS)) //all buffers queued
1007 // nprintf(("Alan","Max write size: %d\n", dwMaxSize));
1011 #define FADE_VOLUME_INTERVAL 400 // 100 == 1db
1012 #define VOLUME_ATTENUATION_BEFORE_CUTOFF 3000 // 12db
1013 BOOL AudioStream::ServiceBuffer (void)
1018 if ( status != ASF_USED )
1021 SDL_LockMutex(write_lock);
1023 // status may have changed, so lets check once again
1024 if ( status != ASF_USED ){
1025 SDL_UnlockMutex(write_lock);
1030 if ( m_bFade == TRUE ) {
1031 if ( m_lCutoffVolume == -10000 ) {
1033 // nprintf(("Alan","Volume is: %d\n",vol));
1034 m_lCutoffVolume = max(vol - VOLUME_ATTENUATION_BEFORE_CUTOFF, -10000);
1038 vol = vol - FADE_VOLUME_INTERVAL; // decrease by 1db
1039 // nprintf(("Alan","Volume is now: %d\n",vol));
1042 // nprintf(("Sound","SOUND => Volume for stream sound is %d\n",vol));
1043 // nprintf(("Alan","Cuttoff Volume is: %d\n",m_lCutoffVolume));
1044 if ( vol < m_lCutoffVolume ) {
1046 m_lCutoffVolume = -10000;
1047 if ( m_bDestroy_when_faded == TRUE ) {
1048 SDL_UnlockMutex(write_lock);
1051 // Reset reentrancy semaphore
1057 // Reset reentrancy semaphore
1058 SDL_UnlockMutex(write_lock);
1065 // All of sound not played yet, send more data to buffer
1066 DWORD dwFreeSpace = GetMaxWriteSize ();
1068 // Determine free space in sound buffer
1071 // Some wave data remains, but not enough to fill free space
1072 // Send wave data to buffer, fill remainder of free space with silence
1073 uint num_bytes_written;
1075 if (WriteWaveData (dwFreeSpace, &num_bytes_written) == SUCCESS) {
1076 // nprintf(("Alan","Num bytes written: %d\n", num_bytes_written));
1078 if ( m_pwavefile->m_total_uncompressed_bytes_read >= m_pwavefile->m_max_uncompressed_bytes_to_read ) {
1079 m_fade_timer_id = timer_get_milliseconds() + 1700; // start fading 1.7 seconds from now
1080 m_finished_id = timer_get_milliseconds() + 2000; // 2 seconds left to play out buffer
1081 m_pwavefile->m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
1084 if ( (m_fade_timer_id>0) && ((uint)timer_get_milliseconds() > m_fade_timer_id) ) {
1085 m_fade_timer_id = 0;
1089 if ( (m_finished_id>0) && ((uint)timer_get_milliseconds() > m_finished_id) ) {
1091 m_bPastLimit = TRUE;
1095 // get the number of buffers processed to see if we're done
1096 OpenAL_ErrorCheck( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &n), return FALSE );
1098 if ( m_bReadingDone && (n == MAX_STREAM_BUFFERS) ) {
1099 if ( m_bDestroy_when_faded == TRUE ) {
1100 SDL_UnlockMutex(write_lock);
1103 // Reset reentrancy semaphore
1107 // All of sound has played, stop playback or loop again
1108 if ( m_bLooping && !m_bFade) {
1109 Play(m_lVolume, m_bLooping);
1117 // Error writing wave data
1124 SDL_UnlockMutex(write_lock);
1130 void AudioStream::Cue (void)
1132 uint num_bytes_written;
1136 m_fade_timer_id = 0;
1138 m_bPastLimit = FALSE;
1140 m_lCutoffVolume = -10000;
1142 m_bDestroy_when_faded = FALSE;
1147 // Reset file ptr, etc
1148 m_pwavefile->Cue ();
1150 // Unqueue all buffers
1152 OpenAL_ErrorPrint( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &p) );
1153 OpenAL_ErrorPrint( alSourceUnqueueBuffers(m_source_id, p, m_buffer_ids) );
1155 // Fill buffer with wave data
1156 WriteWaveData (m_cbBufSize, &num_bytes_written, 0);
1163 void AudioStream::Play (long volume, int looping)
1165 if (m_buffer_ids[0] != 0) {
1168 if ( m_bIsPaused == FALSE)
1172 // Cue for playback if necessary
1182 OpenAL_ErrorPrint( alSourcePlay(m_source_id) );
1184 m_nTimeStarted = timer_get_milliseconds();
1187 // Kick off timer to service buffer
1188 m_timer.constructor();
1190 m_timer.Create (m_nBufService, m_nBufService, ptr_u (this), TimerCallback);
1192 // Playback begun, no longer cued
1194 m_bIsPaused = FALSE;
1198 // Timer callback for Timer object created by ::Play method.
1199 BOOL AudioStream::TimerCallback (ptr_u dwUser)
1201 // dwUser contains ptr to AudioStream object
1202 AudioStream * pas = (AudioStream *) dwUser;
1204 return (pas->ServiceBuffer ());
1207 void AudioStream::Set_Byte_Cutoff(unsigned int byte_cutoff)
1209 if ( m_pwavefile == NULL )
1212 m_pwavefile->m_max_uncompressed_bytes_to_read = byte_cutoff;
1215 unsigned int AudioStream::Get_Bytes_Committed(void)
1217 if ( m_pwavefile == NULL )
1220 return m_pwavefile->m_total_uncompressed_bytes_read;
1225 void AudioStream::Fade_and_Destroy (void)
1228 m_bDestroy_when_faded = TRUE;
1232 void AudioStream::Fade_and_Stop (void)
1235 m_bDestroy_when_faded = FALSE;
1240 void AudioStream::Stop(int paused)
1244 OpenAL_ErrorPrint( alSourcePause(m_source_id) );
1246 OpenAL_ErrorPrint( alSourceStop(m_source_id) );
1250 m_bIsPaused = paused;
1252 // Delete Timer object
1253 m_timer.destructor();
1258 void AudioStream::Stop_and_Rewind (void)
1262 OpenAL_ErrorPrint( alSourceStop(m_source_id) );
1264 // Delete Timer object
1265 m_timer.destructor();
1270 m_fCued = FALSE; // this will cause wave file to start from beginning
1271 m_bReadingDone = FALSE;
1275 void AudioStream::Set_Volume(long vol)
1283 Assert( vol >= -10000 && vol <= 0 );
1285 ALfloat alvol = (vol != -10000) ? powf(10.0f, (float)vol / (-600.0f / log10f(.5f))): 0.0f;
1287 OpenAL_ErrorPrint( alSourcef(m_source_id, AL_GAIN, alvol) );
1290 if ( h_result != 0 )
1291 nprintf(("Sound","SOUND => SetVolume() failed with code '%s'\n", get_DSERR_text(h_result) ));
1296 long AudioStream::Get_Volume()
1303 AudioStream Audio_streams[MAX_AUDIO_STREAMS];
1306 void audiostream_init()
1310 if ( Audiostream_inited == 1 )
1313 if ( !ACM_is_inited() ) {
1317 // Allocate memory for the buffer which holds the uncompressed wave data that is streamed from the
1318 // disk during a load/cue
1319 if ( Wavedata_load_buffer == NULL ) {
1320 Wavedata_load_buffer = (ubyte*)malloc(BIGBUF_SIZE);
1321 Assert(Wavedata_load_buffer != NULL);
1324 // Allocate memory for the buffer which holds the uncompressed wave data that is streamed from the
1325 // disk during a service interval
1326 if ( Wavedata_service_buffer == NULL ) {
1327 Wavedata_service_buffer = (ubyte*)malloc(BIGBUF_SIZE);
1328 Assert(Wavedata_service_buffer != NULL);
1331 // Allocate memory for the buffer which holds the compressed wave data that is read from the hard disk
1332 if ( Compressed_buffer == NULL ) {
1333 Compressed_buffer = (ubyte*)malloc(COMPRESSED_BUFFER_SIZE);
1334 Assert(Compressed_buffer != NULL);
1337 if ( Compressed_service_buffer == NULL ) {
1338 Compressed_service_buffer = (ubyte*)malloc(COMPRESSED_BUFFER_SIZE);
1339 Assert(Compressed_service_buffer != NULL);
1342 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1343 Audio_streams[i].Init_Data();
1344 Audio_streams[i].status = ASF_FREE;
1345 Audio_streams[i].type = ASF_NONE;
1348 SDL_InitSubSystem(SDL_INIT_TIMER);
1350 Global_service_lock = SDL_CreateMutex();
1352 Audiostream_inited = 1;
1355 // Close down the audiostream system. Must call audiostream_init() before any audiostream functions can
1357 void audiostream_close()
1359 if ( Audiostream_inited == 0 )
1364 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1365 if ( Audio_streams[i].status == ASF_USED ) {
1366 Audio_streams[i].status = ASF_FREE;
1367 Audio_streams[i].Destroy();
1371 // free global buffers
1372 if ( Wavedata_load_buffer ) {
1373 free(Wavedata_load_buffer);
1374 Wavedata_load_buffer = NULL;
1377 if ( Wavedata_service_buffer ) {
1378 free(Wavedata_service_buffer);
1379 Wavedata_service_buffer = NULL;
1382 if ( Compressed_buffer ) {
1383 free(Compressed_buffer);
1384 Compressed_buffer = NULL;
1387 if ( Compressed_service_buffer ) {
1388 free(Compressed_service_buffer);
1389 Compressed_service_buffer = NULL;
1392 SDL_DestroyMutex( Global_service_lock );
1394 Audiostream_inited = 0;
1398 // Open a digital sound file for streaming
1400 // input: filename => disk filename of sound file
1401 // type => what type of audio stream do we want to open:
1406 // returns: success => handle to identify streaming sound
1408 int audiostream_open( char *filename, int type )
1412 if (!Audiostream_inited || !snd_is_inited())
1415 for ( i=0; i<MAX_AUDIO_STREAMS; i++ ) {
1416 if ( Audio_streams[i].status == ASF_FREE ) {
1417 Audio_streams[i].status = ASF_USED;
1418 Audio_streams[i].type = type;
1423 if ( i == MAX_AUDIO_STREAMS ) {
1424 nprintf(("Sound", "SOUND => No more audio streams available!\n"));
1431 Audio_streams[i].m_bits_per_sample_uncompressed = 8;
1432 case ASF_EVENTMUSIC:
1433 Audio_streams[i].m_bits_per_sample_uncompressed = 16;
1440 rc = Audio_streams[i].Create(filename);
1443 Audio_streams[i].status = ASF_FREE;
1450 void audiostream_close_file(int i, int fade)
1452 if (!Audiostream_inited)
1458 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1460 if ( Audio_streams[i].status == ASF_USED ) {
1461 if ( fade == TRUE ) {
1462 Audio_streams[i].Fade_and_Destroy();
1464 Audio_streams[i].Destroy();
1470 void audiostream_close_all(int fade)
1474 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1475 if ( Audio_streams[i].status == ASF_FREE )
1478 audiostream_close_file(i, fade);
1482 void audiostream_play(int i, float volume, int looping)
1484 if (!Audiostream_inited)
1490 Assert(looping >= 0);
1491 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1493 // convert from 0->1 to -10000->0 for volume
1494 int converted_volume;
1495 if ( volume == -1 ) {
1496 converted_volume = Audio_streams[i].Get_Default_Volume();
1499 Assert(volume >= 0.0f && volume <= 1.0f );
1500 converted_volume = ds_convert_volume(volume);
1503 Assert( Audio_streams[i].status == ASF_USED );
1504 Audio_streams[i].Set_Default_Volume(converted_volume);
1505 Audio_streams[i].Play(converted_volume, looping);
1508 // use as buffer service function
1509 int audiostream_is_playing(int i)
1514 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1515 if ( Audio_streams[i].status != ASF_USED )
1518 return Audio_streams[i].Is_Playing();
1521 void audiostream_stop(int i, int rewind, int paused)
1523 if (!Audiostream_inited)
1529 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1530 Assert( Audio_streams[i].status == ASF_USED );
1533 Audio_streams[i].Stop_and_Rewind();
1535 Audio_streams[i].Stop(paused);
1538 void audiostream_set_volume_all(float volume, int type)
1542 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1543 if ( Audio_streams[i].status == ASF_FREE )
1546 if ( Audio_streams[i].type == type ) {
1547 int converted_volume;
1548 converted_volume = ds_convert_volume(volume);
1549 Audio_streams[i].Set_Volume(converted_volume);
1554 void audiostream_set_volume(int i, float volume)
1559 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1560 Assert( volume >= 0 && volume <= 1);
1562 if ( Audio_streams[i].status == ASF_FREE )
1565 int converted_volume;
1566 converted_volume = ds_convert_volume(volume);
1567 Audio_streams[i].Set_Volume(converted_volume);
1570 int audiostream_is_paused(int i)
1575 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1576 if ( Audio_streams[i].status == ASF_FREE )
1580 is_paused = Audio_streams[i].Is_Paused();
1585 void audiostream_set_byte_cutoff(int i, unsigned int cutoff)
1590 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1591 Assert( cutoff > 0 );
1593 if ( Audio_streams[i].status == ASF_FREE )
1596 Audio_streams[i].Set_Byte_Cutoff(cutoff);
1599 uint audiostream_get_bytes_committed(int i)
1604 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1606 if ( Audio_streams[i].status == ASF_FREE )
1609 uint num_bytes_committed;
1610 num_bytes_committed = Audio_streams[i].Get_Bytes_Committed();
1612 return num_bytes_committed;
1615 int audiostream_done_reading(int i)
1620 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1622 if ( Audio_streams[i].status == ASF_FREE )
1626 done_reading = Audio_streams[i].Is_Past_Limit();
1628 return done_reading;
1631 int audiostream_is_inited()
1633 return Audiostream_inited;
1636 void audiostream_pause(int i)
1641 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1642 if ( Audio_streams[i].status == ASF_FREE )
1645 if ( audiostream_is_playing(i) == TRUE ) {
1646 audiostream_stop(i, 0, 1);
1650 void audiostream_pause_all()
1654 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1655 if ( Audio_streams[i].status == ASF_FREE )
1658 audiostream_pause(i);
1662 void audiostream_unpause(int i)
1669 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1670 if ( Audio_streams[i].status == ASF_FREE )
1673 if ( audiostream_is_paused(i) == TRUE ) {
1674 is_looping = Audio_streams[i].Is_looping();
1675 audiostream_play(i, -1.0f, is_looping);
1679 void audiostream_unpause_all()
1683 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1684 if ( Audio_streams[i].status == ASF_FREE )
1687 audiostream_unpause(i);
1691 void audiostream_set_sample_cutoff(int i, uint cutoff)
1696 uint audiostream_get_samples_committed(int i)