7 * OpenAL based audio streaming
10 * Revision 1.2 2005/08/12 20:21:06 taylor
13 * Revision 1.1 2005/08/12 08:44:39 taylor
14 * import of FS2_Open audio code which is now *nix only, does not include windows or ogg support that FS2_Open has
16 * Revision 1.12 2005/06/24 19:36:49 taylor
17 * we only want to have m_data_offset be 0 for oggs since the seeking callback will account for the true offset
18 * only extern the one int we need for the -nosound speech fix rather than including the entire header
20 * Revision 1.11 2005/06/19 02:45:55 taylor
21 * OGG streaming fixes to get data reading right and avoid skipping
22 * properly handle seeking in OGG streams
23 * compiler warning fix in OpenAL builds
25 * Revision 1.10 2005/06/01 09:41:14 taylor
26 * bit of cleanup for audiostr-openal and fix a Windows-only enum error
27 * bunch of OGG related fixes for Linux and Windows (DirectSound and OpenAL), fixes audio related TBP 3.2 crashes
28 * gracefully handle OGG logical bitstream changes, shouldn't even load if there is more than 1
30 * Revision 1.9 2005/05/28 19:43:28 taylor
31 * debug message fixing
32 * a little bit of code clarity
34 * Revision 1.8 2005/05/24 03:11:38 taylor
35 * an extra bounds check in sound.cpp
36 * fix audiostr error when filename is !NULL but 0 in len might hit on SDL debug code
38 * Revision 1.7 2005/05/15 06:47:57 taylor
39 * don't let the ogg callbacks close the file handle on us, let us do it ourselves to keep things straight
41 * Revision 1.6 2005/05/13 23:09:28 taylor
42 * Ooops! Added the wrong version of the streaming patch from Jens
44 * Revision 1.5 2005/05/12 17:47:57 taylor
45 * use vm_malloc(), vm_free(), vm_realloc(), vm_strdup() rather than system named macros
46 * fixes various problems and is past time to make the switch
47 * fix a few streaming errors in OpenAL code (Jens Granseuer)
48 * temporary change to help deal with missing music in OpenAL Windows builds
49 * don't assert when si->data is NULL unless we really need to check (OpenAL only)
51 * Revision 1.4 2005/04/05 11:48:22 taylor
52 * remove acm-unix.cpp, replaced by acm-openal.cpp since it's properly cross-platform now
53 * better error handling for OpenAL functions
54 * Windows can now build properly with OpenAL
55 * extra check to make sure we don't try and use too many hardware bases sources
56 * fix memory error from OpenAL extension list in certain instances
58 * Revision 1.3 2005/04/01 07:33:08 taylor
59 * fix hanging on exit with OpenAL
60 * some better error handling on OpenAL init and make it more Windows friendly too
61 * basic 3d sound stuff for OpenAL, not working right yet
63 * Revision 1.2 2005/03/27 08:51:24 taylor
64 * this is what coding on an empty stomach will get you
66 * Revision 1.1 2005/03/27 05:48:58 taylor
67 * initial import of OpenAL streaming (many thanks to Pierre Willenbrock for the missing parts)
73 #ifdef PLAT_UNIX // to end of file...
95 #define MAX_STREAM_BUFFERS 4
101 #define MAX_AUDIO_STREAMS 30
105 #define SUCCESS TRUE // Error returns for all member functions
106 #define FAILURE FALSE
109 #define BIGBUF_SIZE 180000 // This can be reduced to 88200 once we don't use any stereo
110 //#define BIGBUF_SIZE 88300 // This can be reduced to 88200 once we don't use any stereo
111 ubyte *Wavedata_load_buffer = NULL; // buffer used for cueing audiostreams
112 ubyte *Wavedata_service_buffer = NULL; // buffer used for servicing audiostreams
114 CRITICAL_SECTION Global_service_lock;
116 typedef BOOL (*TIMERCALLBACK)(ptr_u);
118 #define COMPRESSED_BUFFER_SIZE 88300
119 ubyte *Compressed_buffer = NULL; // Used to load in compressed data during a cueing interval
120 ubyte *Compressed_service_buffer = NULL; // Used to read in compressed data during a service interval
122 #define AS_HIGHEST_MAX 999999999 // max uncompressed filesize supported is 999 meg
125 int Audiostream_inited = 0;
128 static int audiostr_read_word(SDL_RWops *rw, WORD *i)
130 int rc = SDL_RWread( rw, i, 1, sizeof(WORD) );
132 if (rc != sizeof(WORD))
135 *i = INTEL_SHORT(*i);
140 static int audiostr_read_dword(SDL_RWops *rw, DWORD *i)
142 int rc = SDL_RWread( rw, i, 1, sizeof(DWORD) );
144 if (rc != sizeof(DWORD))
155 void constructor(void);
156 void destructor(void);
157 BOOL Create (UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback);
159 static Uint32 CALLBACK TimeProc(Uint32 interval, void *dwUser);
160 TIMERCALLBACK m_pfnCallback;
164 SDL_TimerID m_nIDTimer;
172 BOOL Open (char *pszFilename);
174 int Read (ubyte *pbDest, uint cbSize, int service=1);
175 uint GetNumBytesRemaining (void) { return (m_nDataSize - m_nBytesPlayed); }
176 uint GetUncompressedAvgDataRate (void) { return (m_nUncompressedAvgDataRate); }
177 uint GetDataSize (void) { return (m_nDataSize); }
178 uint GetNumBytesPlayed (void) { return (m_nBytesPlayed); }
179 ubyte GetSilenceData (void);
180 WAVEFORMATEX m_wfmt; // format of wave file used by Direct Sound
181 WAVEFORMATEX *m_pwfmt_original; // foramt of wave file from actual wave source
182 uint m_total_uncompressed_bytes_read;
183 uint m_max_uncompressed_bytes_to_read;
184 uint m_bits_per_sample_uncompressed;
187 uint m_data_offset; // number of bytes to actual wave data
188 int m_data_bytes_left;
191 uint m_wave_format; // format of wave source (ie WAVE_FORMAT_PCM, WAVE_FORMAT_ADPCM)
192 uint m_nBlockAlign; // wave data block alignment spec
193 uint m_nUncompressedAvgDataRate; // average wave data rate
194 uint m_nDataSize; // size of data chunk
195 uint m_nBytesPlayed; // offset into data chunk
196 BOOL m_abort_next_read;
200 WAVEFORMATEX m_wfxDest;
208 BOOL Create (char *pszFilename);
210 void Play (long volume, int looping);
211 int Is_Playing(){ return(m_fPlaying); }
212 int Is_Paused(){ return(m_bIsPaused); }
213 int Is_Past_Limit() { return m_bPastLimit; }
214 void Stop (int paused=0);
215 void Stop_and_Rewind (void);
216 void Fade_and_Destroy (void);
217 void Fade_and_Stop(void);
218 void Set_Volume(long vol);
221 void Set_Byte_Cutoff(unsigned int num_bytes_cutoff);
222 void Set_Default_Volume(long converted_volume) { m_lDefaultVolume = converted_volume; }
223 long Get_Default_Volume() { return m_lDefaultVolume; }
224 uint Get_Bytes_Committed(void);
225 int Is_looping() { return m_bLooping; }
228 ushort m_bits_per_sample_uncompressed;
232 BOOL WriteWaveData (uint cbSize, uint* num_bytes_written,int service=1);
233 BOOL WriteSilence (uint cbSize);
234 DWORD GetMaxWriteSize (void);
235 BOOL ServiceBuffer (void);
236 static BOOL TimerCallback (ptr_u dwUser);
238 ALuint m_source_id; // name of openAL source
239 ALuint m_buffer_ids[MAX_STREAM_BUFFERS]; //names of buffers
240 int m_play_buffer_id;
242 Timer m_timer; // ptr to Timer object
243 WaveFile * m_pwavefile; // ptr to WaveFile object
244 BOOL m_fCued; // semaphore (stream cued)
245 BOOL m_fPlaying; // semaphore (stream playing)
246 long m_lInService; // reentrancy semaphore
247 uint m_cbBufOffset; // last write position
248 uint m_nBufLength; // length of sound buffer in msec
249 uint m_cbBufSize; // size of sound buffer in bytes
250 uint m_nBufService; // service interval in msec
251 uint m_nTimeStarted; // time (in system time) playback started
253 BOOL m_bLooping; // whether or not to loop playback
254 BOOL m_bFade; // fade out music
255 BOOL m_bDestroy_when_faded;
256 long m_lVolume; // volume of stream ( 0 -> -10 000 )
257 long m_lCutoffVolume;
258 BOOL m_bIsPaused; // stream is stopped, but not rewinded
259 ushort m_silence_written; // number of bytes of silence written to buffer
260 ushort m_bReadingDone; // no more bytes to be read from disk, still have remaining buffer to play
261 DWORD m_fade_timer_id; // timestamp so we know when to start fade
262 DWORD m_finished_id; // timestamp so we know when we've played #bytes required
263 BOOL m_bPastLimit; // flag to show we've played past the number of bytes requred
264 long m_lDefaultVolume;
267 CRITICAL_SECTION write_lock;
271 // Timer class implementation
273 ////////////////////////////////////////////////////////////
276 void Timer::constructor(void)
283 void Timer::destructor(void)
286 SDL_RemoveTimer(m_nIDTimer);
292 BOOL Timer::Create (UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback)
294 BOOL bRtn = SUCCESS; // assume success
297 Assert(nPeriod > 10);
298 Assert(nPeriod >= nRes);
303 m_pfnCallback = pfnCallback;
305 if ((m_nIDTimer = SDL_AddTimer (m_nPeriod, TimeProc, (void*) this)) == NULL) {
306 nprintf(("SOUND", "SOUND ==> Error, unable to create timer\n"));
314 // Timer proc for multimedia timer callback set with timeSetTime().
316 // Calls procedure specified when Timer object was created. The
317 // dwUser parameter contains "this" pointer for associated Timer object.
319 Uint32 CALLBACK Timer::TimeProc(Uint32 interval, void *dwUser)
321 // dwUser contains ptr to Timer object
322 Timer * ptimer = (Timer *) dwUser;
324 // Call user-specified callback and pass back user specified data
325 (ptimer->m_pfnCallback) (ptimer->m_dwUser);
327 if (ptimer->m_nPeriod) {
330 SDL_RemoveTimer(ptimer->m_nIDTimer);
331 ptimer->m_nIDTimer = NULL;
337 // WaveFile class implementation
339 ////////////////////////////////////////////////////////////
342 void WaveFile::Init(void)
347 m_pwfmt_original = NULL;
349 m_nUncompressedAvgDataRate = 0;
352 m_total_uncompressed_bytes_read = 0;
353 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
356 m_abort_next_read = FALSE;
360 void WaveFile::Close(void)
363 if (m_pwfmt_original) {
364 free(m_pwfmt_original);
365 m_pwfmt_original = NULL;
368 if ( m_hStream_open ) {
369 ACM_stream_close((void*)m_hStream);
382 BOOL WaveFile::Open (char *pszFilename)
386 BOOL fRtn = SUCCESS; // assume success
388 char fullpath[_MAX_PATH];
390 m_total_uncompressed_bytes_read = 0;
391 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
393 int FileSize, FileOffset;
395 if ( !cf_find_file_location(pszFilename, CF_TYPE_ANY, fullpath, &FileSize, &FileOffset )) {
399 cfp = SDL_RWFromFile(fullpath, "rb");
405 // Skip the "RIFF" tag and file size (8 bytes)
406 // Skip the "WAVE" tag (4 bytes)
407 SDL_RWseek( cfp, 12+FileOffset, SEEK_SET );
409 // Now read RIFF tags until the end of file
410 uint tag, size, next_chunk;
412 while(done == FALSE) {
413 if ( !audiostr_read_dword(cfp, &tag) )
416 if ( !audiostr_read_dword(cfp, &size) )
419 next_chunk = SDL_RWtell( cfp );
423 case 0x20746d66: // The 'fmt ' tag
424 audiostr_read_word(cfp, &pcmwf.wf.wFormatTag);
425 audiostr_read_word(cfp, &pcmwf.wf.nChannels);
426 audiostr_read_dword(cfp, &pcmwf.wf.nSamplesPerSec);
427 audiostr_read_dword(cfp, &pcmwf.wf.nAvgBytesPerSec);
428 audiostr_read_word(cfp, &pcmwf.wf.nBlockAlign);
429 audiostr_read_word(cfp, &pcmwf.wBitsPerSample);
431 if ( pcmwf.wf.wFormatTag != WAVE_FORMAT_PCM ) {
432 audiostr_read_word(cfp, &cbExtra);
435 // Allocate memory for WAVEFORMATEX structure + extra bytes
436 if ( (m_pwfmt_original = (WAVEFORMATEX *) malloc ( sizeof(WAVEFORMATEX)+cbExtra )) != NULL ){
437 Assert(m_pwfmt_original != NULL);
438 // Copy bytes from temporary format structure
439 memcpy (m_pwfmt_original, &pcmwf, sizeof(pcmwf));
440 m_pwfmt_original->cbSize = cbExtra;
442 // Read those extra bytes, append to WAVEFORMATEX structure
444 SDL_RWread( cfp, ((ubyte *)(m_pwfmt_original) + sizeof(WAVEFORMATEX)), 1, cbExtra );
448 Int3(); // malloc failed
453 case 0x61746164: // the 'data' tag
454 m_nDataSize = size; // This is size of data chunk. Compressed if ADPCM.
455 m_data_bytes_left = size;
456 m_data_offset = SDL_RWtell( cfp );
460 default: // unknown, skip it
464 SDL_RWseek( cfp, next_chunk, SEEK_SET );
467 // At this stage, examine source format, and set up WAVEFORATEX structure for DirectSound.
468 // Since DirectSound only supports PCM, force this structure to be PCM compliant. We will
469 // need to convert data on the fly later if our souce is not PCM
470 switch ( m_pwfmt_original->wFormatTag ) {
471 case WAVE_FORMAT_PCM:
472 m_wave_format = WAVE_FORMAT_PCM;
473 m_wfmt.wBitsPerSample = m_pwfmt_original->wBitsPerSample;
476 case WAVE_FORMAT_ADPCM:
477 m_wave_format = WAVE_FORMAT_ADPCM;
478 m_wfmt.wBitsPerSample = 16;
479 m_bits_per_sample_uncompressed = 16;
483 nprintf(("SOUND", "SOUND => Not supporting %d format for playing wave files\n", m_pwfmt_original->wFormatTag));
490 // Set up the WAVEFORMATEX structure to have the right PCM characteristics
491 m_wfmt.wFormatTag = WAVE_FORMAT_PCM;
492 m_wfmt.nChannels = m_pwfmt_original->nChannels;
493 m_wfmt.nSamplesPerSec = m_pwfmt_original->nSamplesPerSec;
495 m_wfmt.nBlockAlign = (ushort)(( m_wfmt.nChannels * m_wfmt.wBitsPerSample ) / 8);
496 m_wfmt.nAvgBytesPerSec = m_wfmt.nBlockAlign * m_wfmt.nSamplesPerSec;
498 // Init some member data from format chunk
499 m_nBlockAlign = m_pwfmt_original->nBlockAlign;
500 m_nUncompressedAvgDataRate = m_wfmt.nAvgBytesPerSec;
509 // Handle all errors here
510 nprintf(("SOUND","SOUND ==> Could not open wave file %s for streaming\n",pszFilename));
518 if (m_pwfmt_original)
520 free(m_pwfmt_original);
521 m_pwfmt_original = NULL;
530 // Set the file pointer to the start of wave data
532 BOOL WaveFile::Cue (void)
534 BOOL fRtn = SUCCESS; // assume success
537 m_total_uncompressed_bytes_read = 0;
538 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
540 rval = SDL_RWseek( cfp, m_data_offset, SEEK_SET );
546 m_data_bytes_left = m_nDataSize;
547 m_abort_next_read = FALSE;
555 // Returns number of bytes actually read.
557 // Returns -1 if there is nothing more to be read. This function can return 0, since
558 // sometimes the amount of bytes requested is too small for the ACM decompression to
559 // locate a suitable block
560 int WaveFile::Read(ubyte *pbDest, uint cbSize, int service)
562 void *dest_buf=NULL, *uncompressed_wave_data;
563 int rc, uncompressed_bytes_written;
564 uint src_bytes_used, convert_len, num_bytes_desired=0, num_bytes_read;
566 // nprintf(("Alan","Reqeusted: %d\n", cbSize));
569 uncompressed_wave_data = Wavedata_service_buffer;
571 uncompressed_wave_data = Wavedata_load_buffer;
574 switch ( m_wave_format ) {
575 case WAVE_FORMAT_PCM:
576 num_bytes_desired = cbSize;
580 case WAVE_FORMAT_ADPCM:
581 if ( !m_hStream_open ) {
582 if ( !ACM_stream_open(m_pwfmt_original, &m_wfxDest, (void**)&m_hStream, m_bits_per_sample_uncompressed) ) {
589 num_bytes_desired = cbSize;
592 dest_buf = Compressed_service_buffer;
594 dest_buf = Compressed_buffer;
597 if ( num_bytes_desired <= 0 ) {
598 num_bytes_desired = 0;
599 // nprintf(("Alan","No bytes required for ADPCM time interval\n"));
601 num_bytes_desired = ACM_query_source_size((void*)m_hStream, cbSize);
602 // nprintf(("Alan","Num bytes desired: %d\n", num_bytes_desired));
607 nprintf(("SOUND", "SOUND => Not supporting %d format for playing wave files\n"));
617 // read data from disk
618 if ( m_data_bytes_left <= 0 ) {
620 uncompressed_bytes_written = 0;
624 if ( (m_data_bytes_left > 0) && (num_bytes_desired > 0) ) {
627 if ( num_bytes_desired <= (uint)m_data_bytes_left ) {
628 num_bytes_read = num_bytes_desired;
631 num_bytes_read = m_data_bytes_left;
634 actual_read = SDL_RWread( cfp, dest_buf, 1, num_bytes_read );
636 if ( (actual_read <= 0) || (m_abort_next_read) ) {
638 uncompressed_bytes_written = 0;
642 if ( num_bytes_desired >= (uint)m_data_bytes_left ) {
643 m_abort_next_read = 1;
646 num_bytes_read = actual_read;
649 // convert data if necessary, to PCM
650 if ( m_wave_format == WAVE_FORMAT_ADPCM ) {
651 if ( num_bytes_read > 0 ) {
652 rc = ACM_convert((void*)m_hStream, (ubyte*)dest_buf, num_bytes_read, (ubyte*)uncompressed_wave_data, BIGBUF_SIZE, &convert_len, &src_bytes_used);
657 if ( convert_len == 0 ) {
662 Assert(src_bytes_used <= num_bytes_read);
663 if ( src_bytes_used < num_bytes_read ) {
664 // seek back file pointer to reposition before unused source data
665 SDL_RWseek( cfp, src_bytes_used - num_bytes_read, SEEK_CUR );
668 // Adjust number of bytes left
669 m_data_bytes_left -= src_bytes_used;
670 m_nBytesPlayed += src_bytes_used;
671 uncompressed_bytes_written = convert_len;
673 // Successful read, keep running total of number of data bytes read
677 // Successful read, keep running total of number of data bytes read
678 // Adjust number of bytes left
679 m_data_bytes_left -= num_bytes_read;
680 m_nBytesPlayed += num_bytes_read;
681 uncompressed_bytes_written = num_bytes_read;
687 uncompressed_bytes_written = 0;
690 m_total_uncompressed_bytes_read += uncompressed_bytes_written;
691 // nprintf(("Alan","Read: %d\n", uncompressed_bytes_written));
692 return (uncompressed_bytes_written);
698 // Returns 8 bits of data representing silence for the Wave file format.
700 // Since we are dealing only with PCM format, we can fudge a bit and take
701 // advantage of the fact that for all PCM formats, silence can be represented
702 // by a single byte, repeated to make up the proper word size. The actual size
703 // of a word of wave data depends on the format:
705 // PCM Format Word Size Silence Data
706 // 8-bit mono 1 byte 0x80
707 // 8-bit stereo 2 bytes 0x8080
708 // 16-bit mono 2 bytes 0x0000
709 // 16-bit stereo 4 bytes 0x00000000
711 ubyte WaveFile::GetSilenceData (void)
713 ubyte bSilenceData = 0;
715 // Silence data depends on format of Wave file
716 if (m_pwfmt_original) {
717 if (m_wfmt.wBitsPerSample == 8) {
718 // For 8-bit formats (unsigned, 0 to 255)
719 // Packed DWORD = 0x80808080;
721 } else if (m_wfmt.wBitsPerSample == 16) {
722 // For 16-bit formats (signed, -32768 to 32767)
723 // Packed DWORD = 0x00000000;
732 return (bSilenceData);
736 // AudioStream class implementation
738 ////////////////////////////////////////////////////////////
740 // The following constants are the defaults for our streaming buffer operation.
741 const ushort DefBufferLength = 2000; // default buffer length in msec
742 const ushort DefBufferServiceInterval = 250; // default buffer service interval in msec
745 AudioStream::AudioStream (void)
747 SDL_LockMutex(write_lock);
751 AudioStream::~AudioStream (void)
753 SDL_UnlockMutex(write_lock);
756 void AudioStream::Init_Data ()
762 m_bPastLimit = FALSE;
764 m_bDestroy_when_faded = FALSE;
766 m_lCutoffVolume = -10000;
768 m_silence_written = 0;
769 m_bReadingDone = FALSE;
772 m_fPlaying = m_fCued = FALSE;
773 m_lInService = FALSE;
775 m_nBufLength = DefBufferLength;
777 m_nBufService = DefBufferServiceInterval;
780 memset(m_buffer_ids, 0, sizeof(m_buffer_ids));
782 m_play_buffer_id = 0;
786 BOOL AudioStream::Create (char *pszFilename)
789 BOOL fRtn = SUCCESS; // assume success
796 // make 100% sure we got a good filename
797 if ( !strlen(pszFilename) )
800 // Create a new WaveFile object
801 m_pwavefile = (WaveFile *)malloc(sizeof(WaveFile));
808 m_pwavefile->m_bits_per_sample_uncompressed = m_bits_per_sample_uncompressed;
809 if (m_pwavefile->Open (pszFilename)) {
810 // Calculate sound buffer size in bytes
811 // Buffer size is average data rate times length of buffer
812 // No need for buffer to be larger than wave data though
813 m_cbBufSize = (m_nBufLength/1000) * (m_pwavefile->m_wfmt.wBitsPerSample/8) * m_pwavefile->m_wfmt.nChannels * m_pwavefile->m_wfmt.nSamplesPerSec;
814 m_cbBufSize /= MAX_STREAM_BUFFERS;
815 // if the requested buffer size is too big then cap it
816 m_cbBufSize = (m_cbBufSize > BIGBUF_SIZE) ? BIGBUF_SIZE : m_cbBufSize;
818 // nprintf(("SOUND", "SOUND => Stream buffer created using %d bytes\n", m_cbBufSize));
820 // Create sound buffer
821 OpenAL_ErrorCheck( alGenBuffers(MAX_STREAM_BUFFERS, m_buffer_ids), return FAILURE );
823 OpenAL_ErrorCheck( alGenSources(1, &m_source_id), return FAILURE );
825 OpenAL_ErrorPrint( alSourcef(m_source_id, AL_ROLLOFF_FACTOR, 0) );
827 OpenAL_ErrorPrint( alSourcei(m_source_id, AL_SOURCE_RELATIVE, AL_TRUE) );
829 ALfloat posv[] = { 0, 0, 0 };
830 OpenAL_ErrorPrint( alSourcefv(m_source_id, AL_POSITION, posv) );
832 OpenAL_ErrorPrint( alSourcef(m_source_id, AL_GAIN, 1) );
836 Snd_sram += m_cbBufSize;
839 // Error opening file
840 nprintf(("SOUND", "SOUND => Failed to open wave file: %s\n\r", pszFilename));
841 m_pwavefile->Close();
848 // Error, unable to create WaveFile object
849 nprintf(("Sound", "SOUND => Failed to create WaveFile object %s\n\r", pszFilename));
854 // Error, passed invalid parms
862 BOOL AudioStream::Destroy (void)
866 SDL_LockMutex(write_lock);
871 // Release sound buffer
872 OpenAL_ErrorPrint( alDeleteBuffers(MAX_STREAM_BUFFERS, m_buffer_ids) );
873 OpenAL_ErrorPrint( alDeleteSources(1, &m_source_id) );
874 Snd_sram -= m_cbBufSize;
876 // Delete WaveFile object
878 m_pwavefile->Close();
885 SDL_UnlockMutex(write_lock);
892 // Writes wave data to sound buffer. This is a helper method used by Create and
893 // ServiceBuffer; it's not exposed to users of the AudioStream class.
894 BOOL AudioStream::WriteWaveData (uint size, uint *num_bytes_written, int service)
897 ubyte *uncompressed_wave_data;
899 *num_bytes_written = 0;
901 if ( size == 0 || m_bReadingDone ) {
905 if ( (m_buffer_ids[0] == 0) || !m_pwavefile ) {
910 SDL_LockMutex(Global_service_lock);
914 uncompressed_wave_data = Wavedata_service_buffer;
916 uncompressed_wave_data = Wavedata_load_buffer;
919 int num_bytes_read = 0;
921 // Lock the sound buffer
922 num_bytes_read = m_pwavefile->Read(uncompressed_wave_data, m_cbBufSize, service);
924 if ( num_bytes_read == -1 ) {
925 // means nothing left to read!
930 if ( num_bytes_read > 0 ) {
931 // nprintf(("SOUND", "SOUND ==> Queueing %d bytes of Data\n", num_bytes_read));
933 // Lock the sound buffer
934 ALenum format = AL_FORMAT_MONO8;
936 if (m_pwavefile->m_wfmt.nChannels == 1) {
937 if (m_pwavefile->m_wfmt.wBitsPerSample == 8)
938 format = AL_FORMAT_MONO8;
939 else if (m_pwavefile->m_wfmt.wBitsPerSample == 16)
940 format = AL_FORMAT_MONO16;
941 } else if (m_pwavefile->m_wfmt.nChannels == 2) {
942 if (m_pwavefile->m_wfmt.wBitsPerSample == 8)
943 format = AL_FORMAT_STEREO8;
944 else if (m_pwavefile->m_wfmt.wBitsPerSample == 16)
945 format = AL_FORMAT_STEREO16;
948 // unqueue and recycle a processed buffer
952 OpenAL_ErrorPrint( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &p) );
955 OpenAL_ErrorPrint( alSourceUnqueueBuffers(m_source_id, 1, &bid) );
958 OpenAL_ErrorCheck( alBufferData(m_buffer_ids[m_play_buffer_id], format, uncompressed_wave_data, num_bytes_read, m_pwavefile->m_wfmt.nSamplesPerSec), return FAILURE );
960 OpenAL_ErrorCheck( alSourceQueueBuffers(m_source_id, 1, &m_buffer_ids[m_play_buffer_id]), return FAILURE );
964 if (m_play_buffer_id >= MAX_STREAM_BUFFERS)
965 m_play_buffer_id = 0;
969 SDL_UnlockMutex(Global_service_lock);
978 // Writes silence to sound buffer. This is a helper method used by
979 // ServiceBuffer; it's not exposed to users of the AudioStream class.
980 BOOL AudioStream::WriteSilence (uint size)
984 // not used currently with the OpenAL code
992 // Helper function to calculate max size of sound buffer write operation, i.e. how much
993 // free space there is in buffer.
994 DWORD AudioStream::GetMaxWriteSize (void)
996 DWORD dwMaxSize = m_cbBufSize;
999 OpenAL_ErrorCheck( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &n), return 0 );
1001 OpenAL_ErrorCheck( alGetSourcei(m_source_id, AL_BUFFERS_QUEUED, &q), return 0 );
1003 if (!n && (q >= MAX_STREAM_BUFFERS)) //all buffers queued
1006 // nprintf(("Alan","Max write size: %d\n", dwMaxSize));
1010 #define FADE_VOLUME_INTERVAL 400 // 100 == 1db
1011 #define VOLUME_ATTENUATION_BEFORE_CUTOFF 3000 // 12db
1012 BOOL AudioStream::ServiceBuffer (void)
1017 if ( status != ASF_USED )
1020 SDL_LockMutex(write_lock);
1022 // status may have changed, so lets check once again
1023 if ( status != ASF_USED ){
1024 SDL_UnlockMutex(write_lock);
1029 if ( m_bFade == TRUE ) {
1030 if ( m_lCutoffVolume == -10000 ) {
1032 // nprintf(("Alan","Volume is: %d\n",vol));
1033 m_lCutoffVolume = max(vol - VOLUME_ATTENUATION_BEFORE_CUTOFF, -10000);
1037 vol = vol - FADE_VOLUME_INTERVAL; // decrease by 1db
1038 // nprintf(("Alan","Volume is now: %d\n",vol));
1041 // nprintf(("Sound","SOUND => Volume for stream sound is %d\n",vol));
1042 // nprintf(("Alan","Cuttoff Volume is: %d\n",m_lCutoffVolume));
1043 if ( vol < m_lCutoffVolume ) {
1045 m_lCutoffVolume = -10000;
1046 if ( m_bDestroy_when_faded == TRUE ) {
1047 SDL_UnlockMutex(write_lock);
1050 // Reset reentrancy semaphore
1056 // Reset reentrancy semaphore
1057 SDL_UnlockMutex(write_lock);
1064 // All of sound not played yet, send more data to buffer
1065 DWORD dwFreeSpace = GetMaxWriteSize ();
1067 // Determine free space in sound buffer
1070 // Some wave data remains, but not enough to fill free space
1071 // Send wave data to buffer, fill remainder of free space with silence
1072 uint num_bytes_written;
1074 if (WriteWaveData (dwFreeSpace, &num_bytes_written) == SUCCESS) {
1075 // nprintf(("Alan","Num bytes written: %d\n", num_bytes_written));
1077 if ( m_pwavefile->m_total_uncompressed_bytes_read >= m_pwavefile->m_max_uncompressed_bytes_to_read ) {
1078 m_fade_timer_id = timer_get_milliseconds() + 1700; // start fading 1.7 seconds from now
1079 m_finished_id = timer_get_milliseconds() + 2000; // 2 seconds left to play out buffer
1080 m_pwavefile->m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
1083 if ( (m_fade_timer_id>0) && ((uint)timer_get_milliseconds() > m_fade_timer_id) ) {
1084 m_fade_timer_id = 0;
1088 if ( (m_finished_id>0) && ((uint)timer_get_milliseconds() > m_finished_id) ) {
1090 m_bPastLimit = TRUE;
1094 // get the number of buffers processed to see if we're done
1095 OpenAL_ErrorCheck( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &n), return FALSE );
1097 if ( m_bReadingDone && (n == MAX_STREAM_BUFFERS) ) {
1098 if ( m_bDestroy_when_faded == TRUE ) {
1099 SDL_UnlockMutex(write_lock);
1102 // Reset reentrancy semaphore
1106 // All of sound has played, stop playback or loop again
1107 if ( m_bLooping && !m_bFade) {
1108 Play(m_lVolume, m_bLooping);
1116 // Error writing wave data
1123 SDL_UnlockMutex(write_lock);
1129 void AudioStream::Cue (void)
1131 uint num_bytes_written;
1135 m_fade_timer_id = 0;
1137 m_bPastLimit = FALSE;
1139 m_lCutoffVolume = -10000;
1141 m_bDestroy_when_faded = FALSE;
1146 // Reset file ptr, etc
1147 m_pwavefile->Cue ();
1149 // Unqueue all buffers
1151 OpenAL_ErrorPrint( alGetSourcei(m_source_id, AL_BUFFERS_PROCESSED, &p) );
1152 OpenAL_ErrorPrint( alSourceUnqueueBuffers(m_source_id, p, m_buffer_ids) );
1154 // Fill buffer with wave data
1155 WriteWaveData (m_cbBufSize, &num_bytes_written, 0);
1162 void AudioStream::Play (long volume, int looping)
1164 if (m_buffer_ids[0] != 0) {
1167 if ( m_bIsPaused == FALSE)
1171 // Cue for playback if necessary
1181 OpenAL_ErrorPrint( alSourcePlay(m_source_id) );
1183 m_nTimeStarted = timer_get_milliseconds();
1186 // Kick off timer to service buffer
1187 m_timer.constructor();
1189 m_timer.Create (m_nBufService, m_nBufService, ptr_u (this), TimerCallback);
1191 // Playback begun, no longer cued
1193 m_bIsPaused = FALSE;
1197 // Timer callback for Timer object created by ::Play method.
1198 BOOL AudioStream::TimerCallback (ptr_u dwUser)
1200 // dwUser contains ptr to AudioStream object
1201 AudioStream * pas = (AudioStream *) dwUser;
1203 return (pas->ServiceBuffer ());
1206 void AudioStream::Set_Byte_Cutoff(unsigned int byte_cutoff)
1208 if ( m_pwavefile == NULL )
1211 m_pwavefile->m_max_uncompressed_bytes_to_read = byte_cutoff;
1214 unsigned int AudioStream::Get_Bytes_Committed(void)
1216 if ( m_pwavefile == NULL )
1219 return m_pwavefile->m_total_uncompressed_bytes_read;
1224 void AudioStream::Fade_and_Destroy (void)
1227 m_bDestroy_when_faded = TRUE;
1231 void AudioStream::Fade_and_Stop (void)
1234 m_bDestroy_when_faded = FALSE;
1239 void AudioStream::Stop(int paused)
1243 OpenAL_ErrorPrint( alSourcePause(m_source_id) );
1245 OpenAL_ErrorPrint( alSourceStop(m_source_id) );
1249 m_bIsPaused = paused;
1251 // Delete Timer object
1252 m_timer.destructor();
1257 void AudioStream::Stop_and_Rewind (void)
1261 OpenAL_ErrorPrint( alSourceStop(m_source_id) );
1263 // Delete Timer object
1264 m_timer.destructor();
1269 m_fCued = FALSE; // this will cause wave file to start from beginning
1270 m_bReadingDone = FALSE;
1274 void AudioStream::Set_Volume(long vol)
1282 Assert( vol >= -10000 && vol <= 0 );
1284 ALfloat alvol = (vol != -10000) ? powf(10.0f, (float)vol / (-600.0f / log10f(.5f))): 0.0f;
1286 OpenAL_ErrorPrint( alSourcef(m_source_id, AL_GAIN, alvol) );
1289 if ( h_result != 0 )
1290 nprintf(("Sound","SOUND => SetVolume() failed with code '%s'\n", get_DSERR_text(h_result) ));
1295 long AudioStream::Get_Volume()
1302 AudioStream Audio_streams[MAX_AUDIO_STREAMS];
1305 void audiostream_init()
1309 if ( Audiostream_inited == 1 )
1312 if ( !ACM_is_inited() ) {
1316 // Allocate memory for the buffer which holds the uncompressed wave data that is streamed from the
1317 // disk during a load/cue
1318 if ( Wavedata_load_buffer == NULL ) {
1319 Wavedata_load_buffer = (ubyte*)malloc(BIGBUF_SIZE);
1320 Assert(Wavedata_load_buffer != NULL);
1323 // Allocate memory for the buffer which holds the uncompressed wave data that is streamed from the
1324 // disk during a service interval
1325 if ( Wavedata_service_buffer == NULL ) {
1326 Wavedata_service_buffer = (ubyte*)malloc(BIGBUF_SIZE);
1327 Assert(Wavedata_service_buffer != NULL);
1330 // Allocate memory for the buffer which holds the compressed wave data that is read from the hard disk
1331 if ( Compressed_buffer == NULL ) {
1332 Compressed_buffer = (ubyte*)malloc(COMPRESSED_BUFFER_SIZE);
1333 Assert(Compressed_buffer != NULL);
1336 if ( Compressed_service_buffer == NULL ) {
1337 Compressed_service_buffer = (ubyte*)malloc(COMPRESSED_BUFFER_SIZE);
1338 Assert(Compressed_service_buffer != NULL);
1341 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1342 Audio_streams[i].Init_Data();
1343 Audio_streams[i].status = ASF_FREE;
1344 Audio_streams[i].type = ASF_NONE;
1347 SDL_InitSubSystem(SDL_INIT_TIMER);
1349 Global_service_lock = SDL_CreateMutex();
1351 Audiostream_inited = 1;
1354 // Close down the audiostream system. Must call audiostream_init() before any audiostream functions can
1356 void audiostream_close()
1358 if ( Audiostream_inited == 0 )
1363 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1364 if ( Audio_streams[i].status == ASF_USED ) {
1365 Audio_streams[i].status = ASF_FREE;
1366 Audio_streams[i].Destroy();
1370 // free global buffers
1371 if ( Wavedata_load_buffer ) {
1372 free(Wavedata_load_buffer);
1373 Wavedata_load_buffer = NULL;
1376 if ( Wavedata_service_buffer ) {
1377 free(Wavedata_service_buffer);
1378 Wavedata_service_buffer = NULL;
1381 if ( Compressed_buffer ) {
1382 free(Compressed_buffer);
1383 Compressed_buffer = NULL;
1386 if ( Compressed_service_buffer ) {
1387 free(Compressed_service_buffer);
1388 Compressed_service_buffer = NULL;
1391 SDL_DestroyMutex( Global_service_lock );
1393 Audiostream_inited = 0;
1397 // Open a digital sound file for streaming
1399 // input: filename => disk filename of sound file
1400 // type => what type of audio stream do we want to open:
1405 // returns: success => handle to identify streaming sound
1407 int audiostream_open( char *filename, int type )
1411 if (!Audiostream_inited || !snd_is_inited())
1414 for ( i=0; i<MAX_AUDIO_STREAMS; i++ ) {
1415 if ( Audio_streams[i].status == ASF_FREE ) {
1416 Audio_streams[i].status = ASF_USED;
1417 Audio_streams[i].type = type;
1422 if ( i == MAX_AUDIO_STREAMS ) {
1423 nprintf(("Sound", "SOUND => No more audio streams available!\n"));
1430 Audio_streams[i].m_bits_per_sample_uncompressed = 8;
1431 case ASF_EVENTMUSIC:
1432 Audio_streams[i].m_bits_per_sample_uncompressed = 16;
1439 rc = Audio_streams[i].Create(filename);
1442 Audio_streams[i].status = ASF_FREE;
1449 void audiostream_close_file(int i, int fade)
1451 if (!Audiostream_inited)
1457 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1459 if ( Audio_streams[i].status == ASF_USED ) {
1460 if ( fade == TRUE ) {
1461 Audio_streams[i].Fade_and_Destroy();
1463 Audio_streams[i].Destroy();
1469 void audiostream_close_all(int fade)
1473 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1474 if ( Audio_streams[i].status == ASF_FREE )
1477 audiostream_close_file(i, fade);
1481 void audiostream_play(int i, float volume, int looping)
1483 if (!Audiostream_inited)
1489 Assert(looping >= 0);
1490 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1492 // convert from 0->1 to -10000->0 for volume
1493 int converted_volume;
1494 if ( volume == -1 ) {
1495 converted_volume = Audio_streams[i].Get_Default_Volume();
1498 Assert(volume >= 0.0f && volume <= 1.0f );
1499 converted_volume = ds_convert_volume(volume);
1502 Assert( Audio_streams[i].status == ASF_USED );
1503 Audio_streams[i].Set_Default_Volume(converted_volume);
1504 Audio_streams[i].Play(converted_volume, looping);
1507 // use as buffer service function
1508 int audiostream_is_playing(int i)
1513 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1514 if ( Audio_streams[i].status != ASF_USED )
1517 return Audio_streams[i].Is_Playing();
1520 void audiostream_stop(int i, int rewind, int paused)
1522 if (!Audiostream_inited)
1528 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1529 Assert( Audio_streams[i].status == ASF_USED );
1532 Audio_streams[i].Stop_and_Rewind();
1534 Audio_streams[i].Stop(paused);
1537 void audiostream_set_volume_all(float volume, int type)
1541 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1542 if ( Audio_streams[i].status == ASF_FREE )
1545 if ( Audio_streams[i].type == type ) {
1546 int converted_volume;
1547 converted_volume = ds_convert_volume(volume);
1548 Audio_streams[i].Set_Volume(converted_volume);
1553 void audiostream_set_volume(int i, float volume)
1558 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1559 Assert( volume >= 0 && volume <= 1);
1561 if ( Audio_streams[i].status == ASF_FREE )
1564 int converted_volume;
1565 converted_volume = ds_convert_volume(volume);
1566 Audio_streams[i].Set_Volume(converted_volume);
1569 int audiostream_is_paused(int i)
1574 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1575 if ( Audio_streams[i].status == ASF_FREE )
1579 is_paused = Audio_streams[i].Is_Paused();
1584 void audiostream_set_byte_cutoff(int i, unsigned int cutoff)
1589 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1590 Assert( cutoff > 0 );
1592 if ( Audio_streams[i].status == ASF_FREE )
1595 Audio_streams[i].Set_Byte_Cutoff(cutoff);
1598 uint audiostream_get_bytes_committed(int i)
1603 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1605 if ( Audio_streams[i].status == ASF_FREE )
1608 uint num_bytes_committed;
1609 num_bytes_committed = Audio_streams[i].Get_Bytes_Committed();
1611 return num_bytes_committed;
1614 int audiostream_done_reading(int i)
1619 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1621 if ( Audio_streams[i].status == ASF_FREE )
1625 done_reading = Audio_streams[i].Is_Past_Limit();
1627 return done_reading;
1630 int audiostream_is_inited()
1632 return Audiostream_inited;
1635 void audiostream_pause(int i)
1640 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1641 if ( Audio_streams[i].status == ASF_FREE )
1644 if ( audiostream_is_playing(i) == TRUE ) {
1645 audiostream_stop(i, 0, 1);
1649 void audiostream_pause_all()
1653 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1654 if ( Audio_streams[i].status == ASF_FREE )
1657 audiostream_pause(i);
1661 void audiostream_unpause(int i)
1668 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1669 if ( Audio_streams[i].status == ASF_FREE )
1672 if ( audiostream_is_paused(i) == TRUE ) {
1673 is_looping = Audio_streams[i].Is_looping();
1674 audiostream_play(i, -1.0f, is_looping);
1678 void audiostream_unpause_all()
1682 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1683 if ( Audio_streams[i].status == ASF_FREE )
1686 audiostream_unpause(i);
1690 void audiostream_set_sample_cutoff(int i, uint cutoff)
1695 uint audiostream_get_samples_committed(int i)