2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Sound/AudioStr.cpp $
15 * Routines to stream large WAV files from disk
18 * Revision 1.6 2003/08/03 16:10:30 taylor
19 * cleanup; compile warning fixes
21 * Revision 1.5 2002/06/09 04:41:26 relnev
22 * added copyright header
24 * Revision 1.4 2002/06/05 04:03:33 relnev
25 * finished cfilesystem.
27 * removed some old code.
29 * fixed mouse save off-by-one.
33 * Revision 1.3 2002/05/27 18:42:50 theoddone33
34 * Fix missing audiostr_* symbols
36 * Revision 1.2 2002/05/07 03:16:52 theoddone33
37 * The Great Newline Fix
39 * Revision 1.1.1.1 2002/05/03 03:28:10 root
43 * 5 9/14/99 1:32a Jimb
44 * Commented out Int3() that was hanging Jim's machine. Happens before
45 * sm2-07 command brief.
47 * 4 7/14/99 12:09p Jefff
48 * Make sure we're not servicing a bogus audiostream. Check for "used"
49 * after the critical section lock.
51 * 3 12/17/98 4:01p Andsager
52 * up wavedata buffer size to 180000 to allow stereo 16b/22KHz streaming
54 * 2 10/07/98 10:53a Dave
57 * 1 10/07/98 10:51a Dave
59 * 53 6/28/98 6:35p Lawrance
60 * move re-entrancy semaphore into audiostream class
62 * 52 5/24/98 4:42p Dan
63 * AL: Fix several bugs related to pausing and enabling/disabling event
66 * 51 5/21/98 11:57a Lawrance
67 * fix potential bug with transitions for music when in packfiles
69 * 50 5/15/98 9:09p Lawrance
70 * The last of the multi-threading fixes
72 * 49 5/15/98 7:57p Duncan
73 * AL: Fix race condition with music streaming
75 * 48 5/15/98 10:13a Lawrance
76 * remove unused audiostream member
78 * 47 5/14/98 5:45p Lawrance2
79 * Put critical section around audiostream destroying
81 * 46 5/12/98 5:40p Lawrance
82 * Add critical section code to the service buffer call.. since it is
83 * possible to release buffers while in this call
85 * 45 5/10/98 3:49p Sandeep
86 * Fix problem with having the audio streaming while trying to close down
89 * 44 4/30/98 4:53p John
90 * Restructured and cleaned up cfile code. Added capability to read off
91 * of CD-ROM drive and out of multiple pack files.
93 * 43 4/26/98 3:30a Lawrance
94 * Fix a couple of potential bugs
96 * 42 4/21/98 10:18a Dan
98 * 41 4/17/98 6:59a John
99 * Changed code the used 'new' and 'delete' to use 'malloc' and 'free'
100 * instead. Had to manually can constructors/destructors.
102 * 40 4/13/98 10:18a John
105 * 39 4/13/98 10:16a John
106 * Switched gettime back to timer_get_milliseconds, which is now thread
109 * 38 4/12/98 11:08p Lawrance
110 * switch back to using gettime() in separate threads
112 * 37 4/12/98 5:31p Lawrance
113 * use timer_get_milliseconds() instead of gettime()
115 * 36 4/06/98 12:36a Lawrance
116 * Ensure all non-music ADPCM files get decompressed to 8 bit.
118 * 35 4/03/98 4:56p Lawrance
119 * Upu the max audio streams to 30
121 * 34 3/31/98 4:50p Dan
122 * AL: Clean up all audio streams if necessary in
123 * event_music_level_close()
125 * 33 3/23/98 4:12p Lawrance
126 * Fix subtle bug with looping and fading out songs
128 * 32 2/18/98 5:49p Lawrance
129 * Even if the ADPCM codec is unavailable, allow game to continue.
131 * 31 2/15/98 4:43p Lawrance
132 * work on real-time voice
134 * 30 1/19/98 11:37p Lawrance
135 * Fixing Optimization build warnings
137 * 29 1/17/98 4:41p Lawrance
138 * Fix problem with multiple audio streams using the same buffers
140 * 28 1/16/98 11:49a Lawrance
141 * Use own internal timer for fading.
143 * 27 12/28/97 12:43p John
144 * Put in support for reading archive files; Made missionload use the
145 * cf_get_file_list function. Moved demos directory out of data tree.
147 * 26 12/27/97 8:08p Lawrance
148 * If an audiostream doesn't exists, it can't be playing
150 * 25 12/18/97 3:30p Lawrance
151 * Fix bug that sometimes caused music with no volume to not get stopped
154 * 24 12/17/97 10:17p Allender
155 * redid streadming code to use mmio* functions instead of cf* functions.
156 * Our functions are not reentrant!
158 * 23 12/10/97 10:04p Lawrance
159 * modify what happens in Audio_stream constructor
161 * 22 12/09/97 6:14p Lawrance
164 * 21 12/08/97 6:21p Lawrance
165 * fix problems with signaling that end-of-file has been reached
167 * 20 12/05/97 10:50a Lawrance
168 * improve how silence bytes are written on transitions
170 * 19 12/04/97 5:35p Lawrance
171 * fix bug that may have caused errors when writing silence
173 * 18 11/28/97 2:09p Lawrance
174 * Overhaul how ADPCM conversion works... use much less memory... safer
177 * 17 10/03/97 8:24a Lawrance
178 * When unpausing, be sure to retain looping status
180 * 16 9/24/97 5:30p Lawrance
181 * fix bug that was messing up streaming of 8 bit audio
183 * 15 9/18/97 10:31p Lawrance
184 * add functions to pause and unpause all audio streams
186 * 14 9/09/97 3:39p Sandeep
187 * warning level 4 bugs
199 #include <mmsystem.h>
204 #include "audiostr.h"
205 #include "cfile.h" // needed for cf_get_path
207 #include "sound.h" /* for Snd_sram */
213 #define SUCCESS TRUE // Error returns for all member functions
214 #define FAILURE FALSE
219 typedef BOOL (*TIMERCALLBACK)(DWORD);
221 #define BIGBUF_SIZE 180000 // This can be reduced to 88200 once we don't use any stereo
222 //#define BIGBUF_SIZE 88300 // This can be reduced to 88200 once we don't use any stereo
223 unsigned char *Wavedata_load_buffer = NULL; // buffer used for cueing audiostreams
224 unsigned char *Wavedata_service_buffer = NULL; // buffer used for servicing audiostreams
226 CRITICAL_SECTION Global_service_lock;
228 #define COMPRESSED_BUFFER_SIZE 88300
229 unsigned char *Compressed_buffer = NULL; // Used to load in compressed data during a cueing interval
230 unsigned char *Compressed_service_buffer = NULL; // Used to read in compressed data during a service interval
232 #define AS_HIGHEST_MAX 999999999 // max uncompressed filesize supported is 999 meg
238 // Wrapper class for Windows multimedia timer services. Provides
239 // both periodic and one-shot events. User must supply callback
240 // for periodic events.
246 void constructor(void);
247 void destructor(void);
248 BOOL Create (UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback);
250 static void CALLBACK TimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
251 TIMERCALLBACK m_pfnCallback;
263 // WAV file class (read-only).
276 BOOL Open (LPSTR pszFilename);
278 int Read (BYTE * pbDest, UINT cbSize, int service=1);
279 UINT GetNumBytesRemaining (void) { return (m_nDataSize - m_nBytesPlayed); }
280 UINT GetUncompressedAvgDataRate (void) { return (m_nUncompressedAvgDataRate); }
281 UINT GetDataSize (void) { return (m_nDataSize); }
282 UINT GetNumBytesPlayed (void) { return (m_nBytesPlayed); }
283 BYTE GetSilenceData (void);
284 WAVEFORMATEX m_wfmt; // format of wave file used by Direct Sound
285 WAVEFORMATEX * m_pwfmt_original; // foramt of wave file from actual wave source
286 UINT m_total_uncompressed_bytes_read;
287 UINT m_max_uncompressed_bytes_to_read;
288 UINT m_bits_per_sample_uncompressed;
291 UINT m_data_offset; // number of bytes to actual wave data
292 int m_data_bytes_left;
295 UINT m_wave_format; // format of wave source (ie WAVE_FORMAT_PCM, WAVE_FORMAT_ADPCM)
296 UINT m_nBlockAlign; // wave data block alignment spec
297 UINT m_nUncompressedAvgDataRate; // average wave data rate
298 UINT m_nDataSize; // size of data chunk
299 UINT m_nBytesPlayed; // offset into data chunk
300 BOOL m_abort_next_read;
302 HACMSTREAM m_hStream;
304 WAVEFORMATEX m_wfxDest;
309 // AudioStreamServices
311 // DirectSound apportions services on a per-window basis to allow
312 // sound from background windows to be muted. The AudioStreamServices
313 // class encapsulates the initialization of DirectSound services.
315 // Each window that wants to create AudioStream objects must
316 // first create and initialize an AudioStreamServices object.
317 // All AudioStream objects must be destroyed before the associated
318 // AudioStreamServices object is destroyed.
319 class AudioStreamServices
322 void Constructor(void);
324 LPDIRECTSOUND GetPDS (void) { return m_pds; }
332 // Audio stream interface class for playing WAV files using DirectSound.
333 // Users of this class must create AudioStreamServices object before
334 // creating an AudioStream object.
350 BOOL Create (LPSTR pszFilename, AudioStreamServices * pass);
352 void Play (long volume, int looping);
353 int Is_Playing(){ return(m_fPlaying); }
354 int Is_Paused(){ return(m_bIsPaused); }
355 int Is_Past_Limit() { return m_bPastLimit; }
356 void Stop (int paused=0);
357 void Stop_and_Rewind (void);
358 void Fade_and_Destroy (void);
359 void Fade_and_Stop(void);
360 void Set_Volume(long vol);
363 void Set_Byte_Cutoff(unsigned int num_bytes_cutoff);
364 void Set_Default_Volume(long converted_volume) { m_lDefaultVolume = converted_volume; }
365 long Get_Default_Volume() { return m_lDefaultVolume; }
366 unsigned int Get_Bytes_Committed(void);
367 int Is_looping() { return m_bLooping; }
370 UINT m_bits_per_sample_uncompressed;
374 BOOL WriteWaveData (UINT cbSize, UINT* num_bytes_written,int service=1);
375 BOOL WriteSilence (UINT cbSize);
376 DWORD GetMaxWriteSize (void);
377 BOOL ServiceBuffer (void);
378 static BOOL TimerCallback (DWORD dwUser);
380 AudioStreamServices * m_pass; // ptr to AudioStreamServices object
381 LPDIRECTSOUNDBUFFER m_pdsb; // ptr to Direct Sound buffer
382 WaveFile * m_pwavefile; // ptr to WaveFile object
383 Timer m_timer; // ptr to Timer object
384 BOOL m_fCued; // semaphore (stream cued)
385 BOOL m_fPlaying; // semaphore (stream playing)
386 DSBUFFERDESC m_dsbd; // Direct Sound buffer description
387 LONG m_lInService; // reentrancy semaphore
388 UINT m_cbBufOffset; // last write position
389 UINT m_nBufLength; // length of sound buffer in msec
390 UINT m_cbBufSize; // size of sound buffer in bytes
391 UINT m_nBufService; // service interval in msec
392 UINT m_nTimeStarted; // time (in system time) playback started
394 BOOL m_bLooping; // whether or not to loop playback
395 BOOL m_bFade; // fade out music
396 BOOL m_bDestroy_when_faded;
397 LONG m_lVolume; // volume of stream ( 0 -> -10 000 )
398 LONG m_lCutoffVolume;
399 BOOL m_bIsPaused; // stream is stopped, but not rewinded
400 UINT m_silence_written; // number of bytes of silence written to buffer
401 UINT m_bReadingDone; // no more bytes to be read from disk, still have remaining buffer to play
402 DWORD m_fade_timer_id; // timestamp so we know when to start fade
403 DWORD m_finished_id; // timestamp so we know when we've played #bytes required
404 BOOL m_bPastLimit; // flag to show we've played past the number of bytes requred
405 LONG m_lDefaultVolume;
408 CRITICAL_SECTION write_lock;
412 // AudioStreamServices class implementation
414 ////////////////////////////////////////////////////////////
417 void AudioStreamServices::Constructor(void)
419 // Initialize member data
422 // It would seem to make sense to initialize DirectSound here,
423 // but because there could be an error, it's best done in a
424 // separate member function, ::Initialize.
428 extern LPDIRECTSOUND pDirectSound; // From Sound.cpp
432 BOOL AudioStreamServices::Initialize ()
435 BOOL fRtn = SUCCESS; // assume success
438 m_pds = pDirectSound;
447 // AudioStream class implementation
449 ////////////////////////////////////////////////////////////
451 // The following constants are the defaults for our streaming buffer operation.
452 const UINT DefBufferLength = 2000; // default buffer length in msec
453 const UINT DefBufferServiceInterval = 250; // default buffer service interval in msec
456 AudioStream::AudioStream (void)
458 InitializeCriticalSection( &write_lock );
463 AudioStream::~AudioStream (void)
465 DeleteCriticalSection( &write_lock );
469 void AudioStream::Init_Data ()
475 m_bPastLimit = FALSE;
477 m_bDestroy_when_faded = FALSE;
479 m_lCutoffVolume = -10000;
481 m_silence_written = 0;
482 m_bReadingDone = FALSE;
486 m_fPlaying = m_fCued = FALSE;
487 m_lInService = FALSE;
489 m_nBufLength = DefBufferLength;
491 m_nBufService = DefBufferServiceInterval;
496 BOOL AudioStream::Create (LPSTR pszFilename, AudioStreamServices * pass)
498 BOOL fRtn = SUCCESS; // assume success
506 if (pszFilename && m_pass) {
507 // Create a new WaveFile object
509 m_pwavefile = (WaveFile *)malloc(sizeof(WaveFile));
516 m_pwavefile->m_bits_per_sample_uncompressed = m_bits_per_sample_uncompressed;
517 if (m_pwavefile->Open (pszFilename)) {
518 // Calculate sound buffer size in bytes
519 // Buffer size is average data rate times length of buffer
520 // No need for buffer to be larger than wave data though
521 m_cbBufSize = (m_pwavefile->GetUncompressedAvgDataRate () * m_nBufLength) / 1000;
522 nprintf(("SOUND", "SOUND => Stream buffer created using %d bytes\n", m_cbBufSize));
523 // m_cbBufSize = (m_cbBufSize > m_pwavefile->GetDataSize ()) ? m_pwavefile->GetDataSize () : m_cbBufSize;
525 //nprintf(("Sound", "SOUND => average data rate = %d\n\r", m_pwavefile->GetUncompressedAvgDataRate ()));
526 //nprintf(("Sound", "SOUND => m_cbBufSize = %d\n\r", m_cbBufSize));
528 // Create sound buffer
530 memset (&m_dsbd, 0, sizeof (DSBUFFERDESC));
531 m_dsbd.dwSize = sizeof (DSBUFFERDESC);
532 m_dsbd.dwBufferBytes = m_cbBufSize;
533 m_dsbd.lpwfxFormat = &m_pwavefile->m_wfmt;
534 m_dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_LOCSOFTWARE;
536 hr = (m_pass->GetPDS ())->CreateSoundBuffer (&m_dsbd, &m_pdsb, NULL);
540 Snd_sram += m_cbBufSize;
543 // Error, unable to create DirectSound buffer
544 nprintf(("Sound", "SOUND => Error, unable to create DirectSound buffer\n\r"));
545 if (hr == DSERR_BADFORMAT) {
546 nprintf(("Sound", "SOUND => Bad format (probably ADPCM)\n\r"));
553 // Error opening file
554 nprintf(("SOUND", "SOUND => Failed to open wave file: %s\n\r", pszFilename));
555 m_pwavefile->Close();
562 // Error, unable to create WaveFile object
563 nprintf(("Sound", "SOUND => Failed to create WaveFile object %s\n\r", pszFilename));
568 // Error, passed invalid parms
577 BOOL AudioStream::Destroy (void)
581 EnterCriticalSection(&write_lock);
586 // Release DirectSound buffer
590 Snd_sram -= m_cbBufSize;
593 // Delete WaveFile object
595 m_pwavefile->Close();
602 LeaveCriticalSection(&write_lock);
609 // Writes wave data to sound buffer. This is a helper method used by Create and
610 // ServiceBuffer; it's not exposed to users of the AudioStream class.
611 BOOL AudioStream::WriteWaveData (UINT size, UINT *num_bytes_written, int service)
614 LPBYTE lpbuf1 = NULL;
615 LPBYTE lpbuf2 = NULL;
618 DWORD dwbyteswritten1 = 0;
619 DWORD dwbyteswritten2 = 0;
621 unsigned char *uncompressed_wave_data;
623 *num_bytes_written = 0;
625 if ( size == 0 || m_bReadingDone ) {
629 if ( !m_pdsb || !m_pwavefile ) {
634 EnterCriticalSection(&Global_service_lock);
638 uncompressed_wave_data = Wavedata_service_buffer;
640 uncompressed_wave_data = Wavedata_load_buffer;
643 int num_bytes_read = 0;
645 // Lock the sound buffer
646 hr = m_pdsb->Lock (m_cbBufOffset, size, (void**)(&lpbuf1), &dwsize1, (void**)(&lpbuf2), &dwsize2, 0);
648 // Write data to sound buffer. Because the sound buffer is circular, we may have to
649 // do two write operations if locked portion of buffer wraps around to start of buffer.
652 num_bytes_read = m_pwavefile->Read(uncompressed_wave_data, dwsize1+dwsize2,service);
653 if ( num_bytes_read == -1 ) {
654 // means nothing left to read!
659 if ( num_bytes_read > 0 ) {
660 if ( (unsigned int)num_bytes_read > dwsize1 ) {
661 dwbyteswritten1 = dwsize1;
662 dwbyteswritten2 = num_bytes_read - dwsize1;
664 memcpy(lpbuf1, uncompressed_wave_data, dwsize1);
666 memcpy(lpbuf2, uncompressed_wave_data+dwsize1, num_bytes_read-dwsize1);
668 dwbyteswritten1 = num_bytes_read;
670 memcpy(lpbuf1, uncompressed_wave_data, num_bytes_read);
674 // Update our buffer offset and unlock sound buffer
675 m_cbBufOffset = (m_cbBufOffset + dwbyteswritten1 + dwbyteswritten2) % m_cbBufSize;
676 *num_bytes_written = dwbyteswritten1 + dwbyteswritten2;
677 m_pdsb->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2);
680 // Error locking sound buffer
681 nprintf(("SOUND", "SOUND ==> Error, unable to lock sound buffer in AudioStr\n"));
686 LeaveCriticalSection(&Global_service_lock);
695 // Writes silence to sound buffer. This is a helper method used by
696 // ServiceBuffer; it's not exposed to users of the AudioStream class.
697 BOOL AudioStream::WriteSilence (UINT size)
700 LPBYTE lpbuf1 = NULL;
701 LPBYTE lpbuf2 = NULL;
704 DWORD dwbyteswritten1 = 0;
705 DWORD dwbyteswritten2 = 0;
708 // Lock the sound buffer
709 hr = m_pdsb->Lock (m_cbBufOffset, size, (void**)(&lpbuf1), &dwsize1, (void**)(&lpbuf2), &dwsize2, 0);
712 // Get silence data for this file format. Although word sizes vary for different
713 // wave file formats, ::Lock will always return pointers on word boundaries.
714 // Because silence data for 16-bit PCM formats is 0x0000 or 0x00000000, we can
715 // get away with writing bytes and ignoring word size here.
716 BYTE bSilence = m_pwavefile->GetSilenceData ();
718 // Write silence to sound buffer. Because the sound buffer is circular, we may have to
719 // do two write operations if locked portion of buffer wraps around to start of buffer.
720 memset (lpbuf1, bSilence, dwsize1);
721 dwbyteswritten1 = dwsize1;
723 // Second write required?
725 memset (lpbuf2, bSilence, dwsize2);
726 dwbyteswritten2 = dwsize2;
729 // Update our buffer offset and unlock sound buffer
730 m_cbBufOffset = (m_cbBufOffset + dwbyteswritten1 + dwbyteswritten2) % m_cbBufSize;
731 // m_pdsb->Unlock (lpbuf1, dwbyteswritten1, lpbuf2, dwbyteswritten2);
732 m_pdsb->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2);
735 // Error locking sound buffer
736 nprintf(("SOUND", "SOUND ==> Error, unable to lock sound buffer in AudioStr\n"));
746 // Helper function to calculate max size of sound buffer write operation, i.e. how much
747 // free space there is in buffer.
748 DWORD AudioStream::GetMaxWriteSize (void)
750 DWORD dwWriteCursor, dwPlayCursor, dwMaxSize;
752 // Get current play position
753 if (m_pdsb->GetCurrentPosition (&dwPlayCursor, &dwWriteCursor) == DS_OK) {
754 if (m_cbBufOffset <= dwPlayCursor) {
755 // Our write position trails play cursor
756 dwMaxSize = dwPlayCursor - m_cbBufOffset;
759 else {// (m_cbBufOffset > dw7Cursor)
760 // Play cursor has wrapped
761 dwMaxSize = m_cbBufSize - m_cbBufOffset + dwPlayCursor;
765 // GetCurrentPosition call failed
770 // nprintf(("Alan","Max write size: %d\n", dwMaxSize));
777 // Routine to service buffer requests initiated by periodic timer.
779 // Returns TRUE if buffer serviced normally; otherwise returns FALSE.
780 #define FADE_VOLUME_INTERVAL 400 // 100 == 1db
781 #define VOLUME_ATTENUATION_BEFORE_CUTOFF 3000 // 12db
782 BOOL AudioStream::ServiceBuffer (void)
787 if ( status != ASF_USED )
790 EnterCriticalSection(&write_lock);
792 // status may have changed, so lets check once again
793 if ( status != ASF_USED ){
794 LeaveCriticalSection(&write_lock);
798 // Check for reentrance
799 if (InterlockedExchange (&m_lInService, TRUE) == FALSE) {
800 if ( m_bFade == TRUE ) {
801 if ( m_lCutoffVolume == -10000 ) {
803 // nprintf(("Alan","Volume is: %d\n",vol));
804 m_lCutoffVolume = max(vol - VOLUME_ATTENUATION_BEFORE_CUTOFF, -10000);
808 vol = vol - FADE_VOLUME_INTERVAL; // decrease by 1db
809 // nprintf(("Alan","Volume is now: %d\n",vol));
812 // nprintf(("Sound","SOUND => Volume for stream sound is %d\n",vol));
813 // nprintf(("Alan","Cuttoff Volume is: %d\n",m_lCutoffVolume));
814 if ( vol < m_lCutoffVolume ) {
816 m_lCutoffVolume = -10000;
817 if ( m_bDestroy_when_faded == TRUE ) {
818 LeaveCriticalSection(&write_lock);
820 // Reset reentrancy semaphore
821 InterlockedExchange (&m_lInService, FALSE);
826 // Reset reentrancy semaphore
827 LeaveCriticalSection(&write_lock);
828 InterlockedExchange (&m_lInService, FALSE);
834 // All of sound not played yet, send more data to buffer
835 DWORD dwFreeSpace = GetMaxWriteSize ();
837 // Determine free space in sound buffer
840 // Some wave data remains, but not enough to fill free space
841 // Send wave data to buffer, fill remainder of free space with silence
842 uint num_bytes_written;
844 if (WriteWaveData (dwFreeSpace, &num_bytes_written) == SUCCESS) {
845 // nprintf(("Alan","Num bytes written: %d\n", num_bytes_written));
847 if ( m_pwavefile->m_total_uncompressed_bytes_read >= m_pwavefile->m_max_uncompressed_bytes_to_read ) {
848 m_fade_timer_id = timer_get_milliseconds() + 1700; // start fading 1.7 seconds from now
849 m_finished_id = timer_get_milliseconds() + 2000; // 2 seconds left to play out buffer
850 m_pwavefile->m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
853 if ( (m_fade_timer_id>0) && ((uint)timer_get_milliseconds() > m_fade_timer_id) ) {
858 if ( (m_finished_id>0) && ((uint)timer_get_milliseconds() > m_finished_id) ) {
863 if ( (num_bytes_written < dwFreeSpace) && m_bReadingDone ) {
864 int num_bytes_silence;
865 num_bytes_silence = dwFreeSpace - num_bytes_written;
867 if ( num_bytes_silence > 0 ) {
869 m_silence_written += num_bytes_silence;
870 if (WriteSilence (num_bytes_silence) == FAILURE) {
875 if ( m_silence_written >= m_cbBufSize ) {
876 m_silence_written = 0;
878 if ( m_bDestroy_when_faded == TRUE ) {
879 LeaveCriticalSection(&write_lock);
881 // Reset reentrancy semaphore
882 InterlockedExchange (&m_lInService, FALSE);
886 // All of sound has played, stop playback or loop again
887 if ( m_bLooping && !m_bFade) {
888 Play(m_lVolume, m_bLooping);
898 // Error writing wave data
904 // Reset reentrancy semaphore
905 InterlockedExchange (&m_lInService, FALSE);
907 // Service routine reentered. Do nothing, just return
911 LeaveCriticalSection(&write_lock);
916 void AudioStream::Cue (void)
918 UINT num_bytes_written;
924 m_bPastLimit = FALSE;
926 m_lCutoffVolume = -10000;
928 m_bDestroy_when_faded = FALSE;
933 // Reset file ptr, etc
936 // Reset DirectSound buffer
937 m_pdsb->SetCurrentPosition (0);
939 // Fill buffer with wave data
940 WriteWaveData (m_cbBufSize, &num_bytes_written,0);
948 void AudioStream::Play (long volume, int looping)
953 if ( m_bIsPaused == FALSE)
957 // Cue for playback if necessary
967 // Begin DirectSound playback
968 HRESULT hr = m_pdsb->Play (0, 0, DSBPLAY_LOOPING);
970 m_nTimeStarted = timer_get_milliseconds();
972 // Kick off timer to service buffer
973 m_timer.constructor();
975 m_timer.Create (m_nBufService, m_nBufService, DWORD (this), TimerCallback);
977 // Playback begun, no longer cued
982 // If the buffer was lost, try to restore it
983 if ( hr == DSERR_BUFFERLOST ) {
984 hr = m_pdsb->Restore();
986 hr = m_pdsb->Play (0, 0, DSBPLAY_LOOPING);
989 nprintf(("Sound", "Sound => Lost a buffer, tried restoring but got %s\n", get_DSERR_text(hr) ));
990 Int3(); // get Alan, he wants to see this
995 nprintf(("Sound", "Sound => Play failed with return value %s\n", get_DSERR_text(hr) ));
1001 // Timer callback for Timer object created by ::Play method.
1002 BOOL AudioStream::TimerCallback (DWORD dwUser)
1004 // dwUser contains ptr to AudioStream object
1005 AudioStream * pas = (AudioStream *) dwUser;
1007 return (pas->ServiceBuffer ());
1010 void AudioStream::Set_Byte_Cutoff(unsigned int byte_cutoff)
1012 if ( m_pwavefile == NULL )
1015 m_pwavefile->m_max_uncompressed_bytes_to_read = byte_cutoff;
1018 unsigned int AudioStream::Get_Bytes_Committed(void)
1020 if ( m_pwavefile == NULL )
1023 return m_pwavefile->m_total_uncompressed_bytes_read;
1028 void AudioStream::Fade_and_Destroy (void)
1031 m_bDestroy_when_faded = TRUE;
1035 void AudioStream::Fade_and_Stop (void)
1038 m_bDestroy_when_faded = FALSE;
1043 void AudioStream::Stop(int paused)
1046 // Stop DirectSound playback
1049 m_bIsPaused = paused;
1051 // Delete Timer object
1052 m_timer.destructor();
1057 void AudioStream::Stop_and_Rewind (void)
1060 // Stop DirectSound playback
1063 // Delete Timer object
1064 m_timer.destructor();
1069 m_fCued = FALSE; // this will cause wave file to start from beginning
1070 m_bReadingDone = FALSE;
1074 void AudioStream::Set_Volume(long vol)
1082 Assert( vol >= -10000 && vol <= 0 );
1083 h_result = m_pdsb->SetVolume(vol);
1085 if ( h_result != DS_OK )
1086 nprintf(("Sound","SOUND => SetVolume() failed with code '%s'\n", get_DSERR_text(h_result) ));
1091 long AudioStream::Get_Volume()
1097 void Timer::constructor(void)
1104 void Timer::destructor(void)
1107 timeKillEvent (m_nIDTimer);
1114 BOOL Timer::Create (UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback)
1116 BOOL bRtn = SUCCESS; // assume success
1118 Assert(pfnCallback);
1119 Assert(nPeriod > 10);
1120 Assert(nPeriod >= nRes);
1122 m_nPeriod = nPeriod;
1125 m_pfnCallback = pfnCallback;
1127 if ((m_nIDTimer = timeSetEvent (m_nPeriod, m_nRes, TimeProc, (DWORD) this, TIME_PERIODIC)) == NULL) {
1135 // Timer proc for multimedia timer callback set with timeSetTime().
1137 // Calls procedure specified when Timer object was created. The
1138 // dwUser parameter contains "this" pointer for associated Timer object.
1140 void CALLBACK Timer::TimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
1142 // dwUser contains ptr to Timer object
1143 Timer * ptimer = (Timer *) dwUser;
1145 // Call user-specified callback and pass back user specified data
1146 (ptimer->m_pfnCallback) (ptimer->m_dwUser);
1150 // WaveFile class implementation
1152 ////////////////////////////////////////////////////////////
1155 void WaveFile::Init(void)
1157 // Init data members
1160 m_pwfmt_original = NULL;
1162 m_nUncompressedAvgDataRate = 0;
1165 m_total_uncompressed_bytes_read = 0;
1166 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
1169 m_abort_next_read = FALSE;
1173 void WaveFile::Close(void)
1176 if (m_pwfmt_original) {
1177 free(m_pwfmt_original);
1178 m_pwfmt_original = NULL;
1181 if ( m_hStream_open ) {
1182 ACM_stream_close((void*)m_hStream);
1189 mmioClose( cfp, 0 );
1196 BOOL WaveFile::Open (LPSTR pszFilename)
1200 BOOL fRtn = SUCCESS; // assume success
1201 PCMWAVEFORMAT pcmwf;
1202 char fullpath[_MAX_PATH];
1204 m_total_uncompressed_bytes_read = 0;
1205 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
1207 int FileSize, FileOffset;
1209 if ( !cf_find_file_location(pszFilename, CF_TYPE_ANY, fullpath, &FileSize, &FileOffset )) {
1213 cfp = mmioOpen(fullpath, NULL, MMIO_ALLOCBUF | MMIO_READ);
1214 if ( cfp == NULL ) {
1218 // Skip the "RIFF" tag and file size (8 bytes)
1219 // Skip the "WAVE" tag (4 bytes)
1220 mmioSeek( cfp, 12+FileOffset, SEEK_SET );
1222 // Now read RIFF tags until the end of file
1223 uint tag, size, next_chunk;
1225 while(done == FALSE) {
1226 if ( mmioRead(cfp, (char *)&tag, sizeof(uint)) != sizeof(uint) )
1229 if ( mmioRead(cfp, (char *)&size, sizeof(uint)) != sizeof(uint) )
1232 next_chunk = mmioSeek( cfp, 0, SEEK_CUR );
1236 case 0x20746d66: // The 'fmt ' tag
1237 mmioRead( cfp, (char *)&pcmwf, sizeof(PCMWAVEFORMAT) );
1238 if ( pcmwf.wf.wFormatTag != WAVE_FORMAT_PCM ) {
1239 mmioRead( cfp, (char *)&cbExtra, sizeof(short) );
1242 // Allocate memory for WAVEFORMATEX structure + extra bytes
1243 if ( (m_pwfmt_original = (WAVEFORMATEX *) malloc ( sizeof(WAVEFORMATEX)+cbExtra )) != NULL ){
1244 Assert(m_pwfmt_original != NULL);
1245 // Copy bytes from temporary format structure
1246 memcpy (m_pwfmt_original, &pcmwf, sizeof(pcmwf));
1247 m_pwfmt_original->cbSize = cbExtra;
1249 // Read those extra bytes, append to WAVEFORMATEX structure
1251 mmioRead( cfp, (char *)((ubyte *)(m_pwfmt_original) + sizeof(WAVEFORMATEX)), cbExtra );
1255 Int3(); // malloc failed
1260 case 0x61746164: // the 'data' tag
1261 m_nDataSize = size; // This is size of data chunk. Compressed if ADPCM.
1262 m_data_bytes_left = size;
1263 m_data_offset = mmioSeek( cfp, 0, SEEK_CUR);
1267 default: // unknown, skip it
1271 mmioSeek( cfp, next_chunk, SEEK_SET );
1274 // At this stage, examine source format, and set up WAVEFORATEX structure for DirectSound.
1275 // Since DirectSound only supports PCM, force this structure to be PCM compliant. We will
1276 // need to convert data on the fly later if our souce is not PCM
1277 switch ( m_pwfmt_original->wFormatTag ) {
1278 case WAVE_FORMAT_PCM:
1279 m_wave_format = WAVE_FORMAT_PCM;
1280 m_wfmt.wBitsPerSample = m_pwfmt_original->wBitsPerSample;
1283 case WAVE_FORMAT_ADPCM:
1284 m_wave_format = WAVE_FORMAT_ADPCM;
1285 m_wfmt.wBitsPerSample = 16;
1289 nprintf(("SOUND", "SOUND => Not supporting %d format for playing wave files\n"));
1296 // Set up the WAVEFORMATEX structure to have the right PCM characteristics
1297 m_wfmt.wFormatTag = WAVE_FORMAT_PCM;
1298 m_wfmt.nChannels = m_pwfmt_original->nChannels;
1299 m_wfmt.nSamplesPerSec = m_pwfmt_original->nSamplesPerSec;
1301 m_wfmt.nBlockAlign = (unsigned short)(( m_wfmt.nChannels * m_wfmt.wBitsPerSample ) / 8);
1302 m_wfmt.nAvgBytesPerSec = m_wfmt.nBlockAlign * m_wfmt.nSamplesPerSec;
1304 // Init some member data from format chunk
1305 m_nBlockAlign = m_pwfmt_original->nBlockAlign;
1306 m_nUncompressedAvgDataRate = m_wfmt.nAvgBytesPerSec;
1308 // Cue for streaming
1315 // Handle all errors here
1316 nprintf(("SOUND","SOUND ==> Could not open wave file %s for streaming\n",pszFilename));
1321 mmioClose( cfp, 0 );
1324 if (m_pwfmt_original)
1326 free(m_pwfmt_original);
1327 m_pwfmt_original = NULL;
1337 // Set the file pointer to the start of wave data
1339 BOOL WaveFile::Cue (void)
1341 BOOL fRtn = SUCCESS; // assume success
1344 m_total_uncompressed_bytes_read = 0;
1345 m_max_uncompressed_bytes_to_read = AS_HIGHEST_MAX;
1347 rval = mmioSeek( cfp, m_data_offset, SEEK_SET );
1352 m_data_bytes_left = m_nDataSize;
1353 m_abort_next_read = FALSE;
1361 // Returns number of bytes actually read.
1363 // Returns -1 if there is nothing more to be read. This function can return 0, since
1364 // sometimes the amount of bytes requested is too small for the ACM decompression to
1365 // locate a suitable block
1366 int WaveFile::Read(BYTE *pbDest, UINT cbSize, int service)
1368 unsigned char *dest_buf=NULL, *uncompressed_wave_data;
1369 int rc, uncompressed_bytes_written;
1370 unsigned int src_bytes_used, convert_len, num_bytes_desired=0, num_bytes_read;
1372 // nprintf(("Alan","Reqeusted: %d\n", cbSize));
1376 uncompressed_wave_data = Wavedata_service_buffer;
1378 uncompressed_wave_data = Wavedata_load_buffer;
1381 switch ( m_wave_format ) {
1382 case WAVE_FORMAT_PCM:
1383 num_bytes_desired = cbSize;
1387 case WAVE_FORMAT_ADPCM:
1388 if ( !m_hStream_open ) {
1389 if ( !ACM_stream_open(m_pwfmt_original, &m_wfxDest, (void**)&m_hStream), m_bits_per_sample_uncompressed ) {
1396 num_bytes_desired = cbSize;
1399 dest_buf = Compressed_service_buffer;
1401 dest_buf = Compressed_buffer;
1404 if ( num_bytes_desired <= 0 ) {
1405 num_bytes_desired = 0;
1406 // nprintf(("Alan","No bytes required for ADPCM time interval\n"));
1408 num_bytes_desired = ACM_query_source_size((void*)m_hStream, cbSize);
1409 // nprintf(("Alan","Num bytes desired: %d\n", num_bytes_desired));
1414 nprintf(("SOUND", "SOUND => Not supporting %d format for playing wave files\n"));
1424 // read data from disk
1425 if ( m_data_bytes_left <= 0 ) {
1427 uncompressed_bytes_written = 0;
1431 if ( m_data_bytes_left > 0 && num_bytes_desired > 0 ) {
1434 if ( num_bytes_desired <= (unsigned int)m_data_bytes_left ) {
1435 num_bytes_read = num_bytes_desired;
1438 num_bytes_read = m_data_bytes_left;
1441 actual_read = mmioRead( cfp, (char *)dest_buf, num_bytes_read );
1442 if ( (actual_read <= 0) || (m_abort_next_read) ) {
1444 uncompressed_bytes_written = 0;
1448 if ( num_bytes_desired >= (unsigned int)m_data_bytes_left ) {
1449 m_abort_next_read = 1;
1452 num_bytes_read = actual_read;
1455 // convert data if necessary, to PCM
1456 if ( m_wave_format == WAVE_FORMAT_ADPCM ) {
1457 if ( num_bytes_read > 0 ) {
1458 rc = ACM_convert((void*)m_hStream, dest_buf, num_bytes_read, uncompressed_wave_data, BIGBUF_SIZE, &convert_len, &src_bytes_used);
1462 if ( convert_len == 0 ) {
1467 Assert(src_bytes_used <= num_bytes_read);
1468 if ( src_bytes_used < num_bytes_read ) {
1469 // seek back file pointer to reposition before unused source data
1470 mmioSeek(cfp, src_bytes_used - num_bytes_read, SEEK_CUR);
1473 // Adjust number of bytes left
1474 m_data_bytes_left -= src_bytes_used;
1475 m_nBytesPlayed += src_bytes_used;
1476 uncompressed_bytes_written = convert_len;
1478 // Successful read, keep running total of number of data bytes read
1482 // Successful read, keep running total of number of data bytes read
1483 // Adjust number of bytes left
1484 m_data_bytes_left -= num_bytes_read;
1485 m_nBytesPlayed += num_bytes_read;
1486 uncompressed_bytes_written = num_bytes_read;
1492 uncompressed_bytes_written = 0;
1495 m_total_uncompressed_bytes_read += uncompressed_bytes_written;
1496 // nprintf(("Alan","Read: %d\n", uncompressed_bytes_written));
1497 return (uncompressed_bytes_written);
1503 // Returns 8 bits of data representing silence for the Wave file format.
1505 // Since we are dealing only with PCM format, we can fudge a bit and take
1506 // advantage of the fact that for all PCM formats, silence can be represented
1507 // by a single byte, repeated to make up the proper word size. The actual size
1508 // of a word of wave data depends on the format:
1510 // PCM Format Word Size Silence Data
1511 // 8-bit mono 1 byte 0x80
1512 // 8-bit stereo 2 bytes 0x8080
1513 // 16-bit mono 2 bytes 0x0000
1514 // 16-bit stereo 4 bytes 0x00000000
1516 BYTE WaveFile::GetSilenceData (void)
1518 BYTE bSilenceData = 0;
1520 // Silence data depends on format of Wave file
1521 if (m_pwfmt_original) {
1522 if (m_wfmt.wBitsPerSample == 8) {
1523 // For 8-bit formats (unsigned, 0 to 255)
1524 // Packed DWORD = 0x80808080;
1525 bSilenceData = 0x80;
1527 else if (m_wfmt.wBitsPerSample == 16) {
1528 // For 16-bit formats (signed, -32768 to 32767)
1529 // Packed DWORD = 0x00000000;
1530 bSilenceData = 0x00;
1540 return (bSilenceData);
1543 AudioStreamServices * m_pass = NULL; // ptr to AudioStreamServices object
1545 #define MAX_AUDIO_STREAMS 30
1546 AudioStream Audio_streams[MAX_AUDIO_STREAMS];
1547 #endif // !PLAT_UNIX
1549 int Audiostream_inited = 0;
1551 void audiostream_init()
1553 if ( Audiostream_inited == 1 )
1561 if ( !ACM_is_inited() ) {
1565 // Create and initialize AudioStreamServices object.
1566 // This must be done once and only once for each window that uses
1567 // streaming services.
1568 m_pass = (AudioStreamServices *)malloc(sizeof(AudioStreamServices));
1571 m_pass->Constructor();
1572 m_pass->Initialize();
1574 if ( !pDirectSound ) {
1579 // Allocate memory for the buffer which holds the uncompressed wave data that is streamed from the
1580 // disk during a load/cue
1581 if ( Wavedata_load_buffer == NULL ) {
1582 Wavedata_load_buffer = (unsigned char*)malloc(BIGBUF_SIZE);
1583 Assert(Wavedata_load_buffer != NULL);
1586 // Allocate memory for the buffer which holds the uncompressed wave data that is streamed from the
1587 // disk during a service interval
1588 if ( Wavedata_service_buffer == NULL ) {
1589 Wavedata_service_buffer = (unsigned char*)malloc(BIGBUF_SIZE);
1590 Assert(Wavedata_service_buffer != NULL);
1593 // Allocate memory for the buffer which holds the compressed wave data that is read from the hard disk
1594 if ( Compressed_buffer == NULL ) {
1595 Compressed_buffer = (unsigned char*)malloc(COMPRESSED_BUFFER_SIZE);
1596 Assert(Compressed_buffer != NULL);
1599 if ( Compressed_service_buffer == NULL ) {
1600 Compressed_service_buffer = (unsigned char*)malloc(COMPRESSED_BUFFER_SIZE);
1601 Assert(Compressed_service_buffer != NULL);
1604 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1605 Audio_streams[i].Init_Data();
1606 Audio_streams[i].status = ASF_FREE;
1607 Audio_streams[i].type = ASF_NONE;
1610 InitializeCriticalSection( &Global_service_lock );
1613 Audiostream_inited = 1;
1616 // Close down the audiostream system. Must call audiostream_init() before any audiostream functions can
1618 void audiostream_close()
1620 if ( Audiostream_inited == 0 )
1628 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1629 if ( Audio_streams[i].status == ASF_USED ) {
1630 Audio_streams[i].status = ASF_FREE;
1631 Audio_streams[i].Destroy();
1635 // Destroy AudioStreamServices object
1641 // free global buffers
1642 if ( Wavedata_load_buffer ) {
1643 free(Wavedata_load_buffer);
1644 Wavedata_load_buffer = NULL;
1647 if ( Wavedata_service_buffer ) {
1648 free(Wavedata_service_buffer);
1649 Wavedata_service_buffer = NULL;
1652 if ( Compressed_buffer ) {
1653 free(Compressed_buffer);
1654 Compressed_buffer = NULL;
1657 if ( Compressed_service_buffer ) {
1658 free(Compressed_service_buffer);
1659 Compressed_service_buffer = NULL;
1662 DeleteCriticalSection( &Global_service_lock );
1664 Audiostream_inited = 0;
1667 // Open a digital sound file for streaming
1669 // input: filename => disk filename of sound file
1670 // type => what type of audio stream do we want to open:
1675 // returns: success => handle to identify streaming sound
1677 int audiostream_open( char * filename, int type )
1684 if (!Audiostream_inited || !snd_is_inited())
1687 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1688 if ( Audio_streams[i].status == ASF_FREE ) {
1689 Audio_streams[i].status = ASF_USED;
1690 Audio_streams[i].type = type;
1695 if ( i == MAX_AUDIO_STREAMS ) {
1696 nprintf(("Sound", "SOUND => No more audio streams available!\n"));
1703 Audio_streams[i].m_bits_per_sample_uncompressed = 8;
1705 case ASF_EVENTMUSIC:
1706 Audio_streams[i].m_bits_per_sample_uncompressed = 16;
1713 rc = Audio_streams[i].Create(filename, m_pass);
1715 Audio_streams[i].status = ASF_FREE;
1724 void audiostream_close_file(int i, int fade)
1726 if (!Audiostream_inited)
1735 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1737 if ( Audio_streams[i].status == ASF_USED ) {
1738 if ( fade == TRUE ) {
1739 Audio_streams[i].Fade_and_Destroy();
1742 Audio_streams[i].Destroy();
1748 void audiostream_close_all(int fade)
1755 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1756 if ( Audio_streams[i].status == ASF_FREE )
1759 audiostream_close_file(i, fade);
1764 extern int ds_convert_volume(float volume);
1766 void audiostream_play(int i, float volume, int looping)
1768 if (!Audiostream_inited)
1777 Assert(looping >= 0);
1778 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1780 // convert from 0->1 to -10000->0 for volume
1781 int converted_volume;
1782 if ( volume == -1 ) {
1783 converted_volume = Audio_streams[i].Get_Default_Volume();
1786 Assert(volume >= 0.0f && volume <= 1.0f );
1787 converted_volume = ds_convert_volume(volume);
1790 Assert( Audio_streams[i].status == ASF_USED );
1791 Audio_streams[i].Set_Default_Volume(converted_volume);
1792 Audio_streams[i].Play(converted_volume, looping);
1796 void audiostream_stop(int i, int rewind, int paused)
1798 if (!Audiostream_inited) return;
1806 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1807 Assert( Audio_streams[i].status == ASF_USED );
1810 Audio_streams[i].Stop_and_Rewind();
1812 Audio_streams[i].Stop(paused);
1816 int audiostream_is_playing(int i)
1825 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1826 if ( Audio_streams[i].status != ASF_USED )
1829 return Audio_streams[i].Is_Playing();
1834 void audiostream_set_volume_all(float volume, int type)
1841 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1842 if ( Audio_streams[i].status == ASF_FREE )
1845 if ( Audio_streams[i].type == type ) {
1846 int converted_volume;
1847 converted_volume = ds_convert_volume(volume);
1848 Audio_streams[i].Set_Volume(converted_volume);
1855 void audiostream_set_volume(int i, float volume)
1863 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1864 Assert( volume >= 0 && volume <= 1);
1866 if ( Audio_streams[i].status == ASF_FREE )
1869 int converted_volume;
1870 converted_volume = ds_convert_volume(volume);
1871 Audio_streams[i].Set_Volume(converted_volume);
1876 int audiostream_is_paused(int i)
1885 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1886 if ( Audio_streams[i].status == ASF_FREE )
1890 is_paused = Audio_streams[i].Is_Paused();
1896 void audiostream_set_byte_cutoff(int i, unsigned int cutoff)
1904 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1905 Assert( cutoff > 0 );
1907 if ( Audio_streams[i].status == ASF_FREE )
1910 Audio_streams[i].Set_Byte_Cutoff(cutoff);
1915 unsigned int audiostream_get_bytes_committed(int i)
1924 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1926 if ( Audio_streams[i].status == ASF_FREE )
1929 unsigned int num_bytes_committed;
1930 num_bytes_committed = Audio_streams[i].Get_Bytes_Committed();
1931 return num_bytes_committed;
1935 int audiostream_done_reading(int i)
1944 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1946 if ( Audio_streams[i].status == ASF_FREE )
1950 done_reading = Audio_streams[i].Is_Past_Limit();
1951 return done_reading;
1956 int audiostream_is_inited()
1958 return Audiostream_inited;
1961 // pause a single audio stream, indentified by handle i.
1962 void audiostream_pause(int i)
1970 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
1971 if ( Audio_streams[i].status == ASF_FREE )
1974 if ( audiostream_is_playing(i) == TRUE ) {
1975 audiostream_stop(i, 0, 1);
1980 // pause all audio streams that are currently playing.
1981 void audiostream_pause_all()
1988 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
1989 if ( Audio_streams[i].status == ASF_FREE )
1992 audiostream_pause(i);
1997 // unpause the audio stream identified by handle i.
1998 void audiostream_unpause(int i)
2008 Assert( i >= 0 && i < MAX_AUDIO_STREAMS );
2009 if ( Audio_streams[i].status == ASF_FREE )
2012 if ( audiostream_is_paused(i) == TRUE ) {
2013 is_looping = Audio_streams[i].Is_looping();
2014 audiostream_play(i, -1.0f, is_looping);
2019 // unpause all audio streams that are currently paused
2020 void audiostream_unpause_all()
2027 for ( i = 0; i < MAX_AUDIO_STREAMS; i++ ) {
2028 if ( Audio_streams[i].status == ASF_FREE )
2031 audiostream_unpause(i);