/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/streamaudio/streamaudio.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2000/04/18 00:00:49 $ * $Author: icculus $ * * Interface for streaming audio, compressed or not * * $Log: streamaudio.cpp,v $ * Revision 1.1.1.1 2000/04/18 00:00:49 icculus * initial checkin * * * 44 9/29/99 5:13p Jeff * Linux now has real stream audio * * 43 7/28/99 2:04p Kevin * Macintosh Changes * * 41 5/17/99 1:56p Ardussi * changed to compile on Mac * * 40 5/10/99 9:22p Samir * added code to allow for certain streams to be specified to start on * next sound frame. * * 39 4/29/99 3:03p Samir * took out mprintfs and if we're opening an opened stream, close it and * continue. * * 38 4/17/99 5:58a Jeff * Changes for linux * * 37 4/14/99 1:48a Jeff * fixed case mismatched #includes * * 36 4/10/99 5:09p Samir * beginning to add prioritization of sounds code. currently non * functional, but allows me to change all calls to Play3dSound so that I * can test later. * * 35 3/22/99 5:12p Samir * on shutdown, CLOSE any open streams. * * 34 3/17/99 4:22p Samir * made pause and resume for streams work. * * 33 3/09/99 6:40p Jeff * put try/catch around StreamPlay * * 32 3/04/99 6:38p Samir * Saved stream state per frame. * * 31 3/03/99 5:08p Samir * added slew of debug code: hopefully the library will be revamped after * OEM. * * 30 3/01/99 8:11p Samir * fixed bug when audio stream was playing while sound system shutdown. * * 29 2/28/99 6:35p Samir * fixed streaming bugs for very small samples. * * 28 2/28/99 5:11p Nate * added playcount. * * 27 2/27/99 6:52p Samir * added function to get current loop count * * 26 2/26/99 8:34p Samir * may have fixed problem with really small streams. * * 25 2/26/99 5:31p Samir * * 24 2/25/99 9:20p Kevin * Semi fix for music in DS * * 23 2/17/99 8:35p Samir * use m_streamdone to determine whether play should reset the stream or * not. * * 22 12/13/98 6:50p Samir * fixed problems with looping and next.switching sync. * * 21 12/11/98 5:25p Samir * took out debug messages. * * 20 12/11/98 5:20p Samir * perhaps prevent unused buffers from playing. * * 19 12/11/98 3:27p Samir * add debug code. * * 18 12/10/98 7:12p Samir * fixed some bugs in file/playback buffer sequencing with new system. * * 17 12/10/98 10:11a Samir * newer streaming audio library. * * 11 11/20/98 5:20p Samir * correcting a lot of errors in AudioStream::Next * * 10 11/13/98 4:03p Nate * took out volume stuff from high level sound lib * * 9 11/13/98 2:25p Samir * added 'next' stream processing. * * 9 11/13/98 10:48a Samir * added 'next' stream capability. * * 8 10/23/98 7:04p Samir * * 7 10/23/98 6:45p Matt * Scale the streaming audio volume by the master volume. * * 6 8/10/98 5:54p Samir * added looping streams and soft measure stopping. * * 5 7/30/98 4:35p Dan * more error checking. * * 4 7/30/98 10:57a Sean * if no sound, then don't do anything. * * 3 7/24/98 5:18p Samir * use OSFArchive for stream file operations now. * * 2 7/09/98 8:36p Samir * fully implemented redo of Stream interface. * * 1 7/09/98 6:48p Samir * * MOVED FROM MAIN PROJECT * * 10 6/29/98 4:15p Chris * Streaming audio works * * 9 6/24/98 5:00p Jeff * set filehandle to null on close, short circuit read data if file handle * is NULL * * 8 6/24/98 4:41p Jeff * will kill the stream is the sound is killed * * * $NoKeywords: $ */ #include #include #include "adecode.h" #include "cfile.h" #include "ddio.h" #include "log.h" #include "Macros.h" #include "mem.h" #include "pserror.h" #include "streamaudio.h" // this stream is for everyone (used by the StreamPlay interface) static AudioStream User_audio_stream; llsSystem *AudioStream::m_ll_sndsys = NULL; AudioStream *AudioStream::m_streams[STRM_LIMIT]; int AudioStream::m_thisid = -1; #define SAMPLES_PER_STREAM_SHORT_BUF 22050 #define SAMPLES_PER_STREAM_BUF 44100 // sets the low-level sound object void AudioStream::InitSystem(llsSystem *sndsys) { int i; AudioStream::m_ll_sndsys = sndsys; // reserve sound slots for streams, they should never get replaced, and always should be played. for (i = 0; i < STRM_LIMIT; i++) { AudioStream::m_streams[i] = NULL; } } // shutdsown void AudioStream::Shutdown() { if (AudioStream::m_ll_sndsys) { int i; // stop all streams. for (i = 0; i < STRM_LIMIT; i++) { if (AudioStream::m_streams[i]) { AudioStream::m_streams[i]->Close(); AudioStream::m_streams[i] = NULL; } } AudioStream::m_ll_sndsys = NULL; } } // allocates a stream slot for a stream bool AudioStream::ActivateStream(AudioStream *stream) { int i; for (i = 0; i < STRM_LIMIT; i++) { if (AudioStream::m_streams[i] == NULL) { AudioStream::m_streams[i] = stream; stream->m_streamindex = i; return true; } } LOG_DEBUG << "AudioStream queue filled!"; return false; } void AudioStream::DeactivateStream(AudioStream *stream) { if (stream->m_streamindex > -1) { AudioStream::m_streams[stream->m_streamindex] = NULL; stream->m_streamindex = -1; } } // called by application to allow stream playback void AudioStream::Frame() { int t; float time = timer_GetTime(); if (!m_ll_sndsys) return; for (t = 0; t < STRM_LIMIT; t++) { if (AudioStream::m_streams[t]) { AudioStream *strm = AudioStream::m_streams[t]; if (strm->m_laststate != strm->m_state) { LOG_DEBUG.printf("STRM[%d]:frame_state=%d", strm->m_curid, strm->m_state); } if (strm->m_state == STRM_PLAYING) { // see if the sound is still playing if it isn't then kill it (also if no more loops) strm->m_measure_timer += (time - strm->m_last_frametime); if (strm->m_measure_timer >= strm->m_measure_time) { strm->m_curmeasure++; } strm->m_last_frametime = time; if (strm->m_state == STRM_PLAYING) strm->UpdateData(); } else if (strm->m_state == STRM_STOPPING) { if (!AudioStream::m_ll_sndsys->IsSoundInstancePlaying(strm->m_llshandle)) { strm->Stop(); if (CHECK_FLAG(strm->m_flags, STRM_OPNF_ONETIME)) { strm->Close(); } strm->m_readahead = false; } } else if (strm->m_state == STRM_INVALID) { strm->DeactivateStream(strm); } else if (strm->m_start_on_frame) { strm->m_llshandle = m_ll_sndsys->PlayStream(&strm->m_playinfo); if (strm->m_llshandle > -1) { // for one buffer samples, prepare to stop now so that the one and only one buffer gets called. // looped one buffer samples dont do this though... if (strm->m_nbufs == 1 && !strm->m_start_on_frame_looped) { strm->m_state = STRM_STOPPING; } else { strm->m_state = STRM_PLAYING; } } strm->m_start_on_frame = false; strm->m_start_on_frame_looped = false; } strm->m_laststate = strm->m_state; } } } // called to pause all streams. void AudioStream::PauseAll() { int t; for (t = 0; t < STRM_LIMIT; t++) { if (AudioStream::m_streams[t]) { AudioStream::m_streams[t]->Pause(); } } } void AudioStream::ResumeAll() { int t; for (t = 0; t < STRM_LIMIT; t++) { if (AudioStream::m_streams[t]) { AudioStream::m_streams[t]->Resume(); } } } // AudioStream object Interface // allows dynamic playing of streams AudioStream::AudioStream() { m_decoder = NULL; // audio decomp object m_llshandle = -1; // snd system handle m_sbufidx = 0; // stream position markers for synching. m_fbufidx = 0; m_flags = 0; m_state = STRM_INVALID; m_streamindex = -1; m_curmeasure = -1; m_stopnextmeasure = false; m_stopflag = NULL; m_bytesleft = 0; m_readahead_finished_loop = false; m_playcount = 0; m_curid = -1; m_nbufs = 0; m_start_on_frame = false; } AudioStream::~AudioStream() { AudioStream::Close(); } // sets flags for playback (STRM_OPNF_XXX) void AudioStream::SetFlags(int flags) { m_flags = flags; } void AudioStream::SetLoopCount(int loop_count) { // if loop_count = 0, then m_loopcount= -1, infinite; // if loop_count = 1, then m_loopcount=0, no looping, etc. m_loopcount = loop_count - 1; } int AudioStream::GetLoopCount() const { // if loop_count = 0, then m_loopcount= -1, infinite; // if loop_count = 1, then m_loopcount=0, no looping, etc. return m_loopcount + 1; } void AudioStream::SetVolume(float vol) { m_volume = vol; if (m_ll_sndsys) { if (m_llshandle < 0) return; m_ll_sndsys->AdjustSound(m_llshandle, vol, 0.0f, m_playinfo.samples_per_22khz_sample * 22050); } } float AudioStream::GetVolume() { return m_volume; } // flags specify what type of stream it is. bool AudioStream::Open(const char *filename, int open_flags) { // don't open a stream that's already open, or bogus filename if (m_state != STRM_INVALID) { AudioStream::Close(); // Int3(); // return false; } if (m_archive.Opened() || !filename) return false; if (!m_ll_sndsys) return false; m_state = STRM_INVALID; m_streamindex = -1; m_flags = open_flags; m_curmeasure = 0; m_volume = 1.0f; m_bufsize = SAMPLES_PER_STREAM_BUF * 4; m_measure_timer = 0.0f; m_measure_time = 0.0f; m_last_frametime = 0.0f; m_readahead_finished_loop = false; m_curid = -1; m_nbufs = 0; m_start_on_frame = m_start_on_frame_looped = false; // open up stream file if (m_archive.Open(filename)) { int i; int nbufs; ASSERT(m_archive.StreamType() == OSF_DIGITAL_STRM); m_thisid++; m_curid = m_thisid; for (i = 0; i < STRM_BUFCOUNT; i++) { m_buffer[i].flags = 0; m_buffer[i].nbytes = 0; m_buffer[i].data = NULL; m_buffer[i].id = -1; // marks stream buffer to be allocated. } // gradual streams don't readahead yet. nbufs = CHECK_FLAG(m_flags, STRM_OPNF_GRADUAL) ? 1 : STRM_BUFCOUNT; if (!AudioStream::ReopenDigitalStream(0, nbufs)) { return false; } AudioStream::SetLoopCount(1); m_laststate = m_state; m_state = STRM_STOPPED; return true; } return false; } // deallocates all stream buffers and decoder. void AudioStream::Close() { int i; if (m_archive.Opened()) { // stop the stream, close the archive, close the decoder. AudioStream::Stop(); m_archive.Close(); m_curid = -1; // free streaming buffers and decoder if we need to. for (i = 0; i < STRM_BUFCOUNT; i++) { if (m_buffer[i].data) { mem_free(m_buffer[i].data); m_buffer[i].data = NULL; m_buffer[i].nbytes = 0; m_buffer[i].flags = 0; m_buffer[i].id = -1; } } if (m_decoder) { delete m_decoder; m_decoder = NULL; } } m_playcount = 0; m_nbufs = 0; m_state = STRM_INVALID; } // has stream finished with its readahead of current loop? bool AudioStream::ReadAheadFinishedLoop() { if (m_readahead_finished_loop) { m_readahead_finished_loop = false; return true; } return false; } // are we still reading from disk? bool AudioStream::ReadAhead() { return m_readahead; } bool AudioStream::IsReady() { if (m_state == STRM_STOPPED) { if (!m_readahead || m_fbufidx == (STRM_BUFCOUNT - 1)) { return true; } } return false; } ////////////////////////////////////////////////////////////////////////////// bool AudioStream::ReopenDigitalStream(uint8_t fbufidx, int nbufs) { const tOSFDigiHdr *digihdr = (const tOSFDigiHdr *)m_archive.StreamHeader(); m_bytesleft = m_archive.StreamLength(); m_readahead = false; // free current decoder. if (m_decoder) { delete m_decoder; m_decoder = NULL; } // instatiate decompression facility or use raw source data uint32_t sample_count = 0; uint32_t channels = 0; if (m_archive.StreamComp() == OSF_DIGIACM_STRM) { uint32_t sample_rate; m_decoder = AudioDecoder::CreateDecoder(ADecodeFileRead, this, channels, sample_rate, sample_count); if (!m_decoder) { delete m_decoder; m_decoder = NULL; return false; } } else { return false; } // convert bufsize to true bufsize (bufsize = samples per measure for now.) int bytesize, granularity; switch (m_archive.StreamFormat()) { case SAF_8BIT_M: granularity = 1; break; case SAF_8BIT_S: granularity = 2; bytesize = digihdr->measure << 1; break; case SAF_16BIT_M: granularity = 2; bytesize = digihdr->measure << 1; break; case SAF_16BIT_S: granularity = 4; bytesize = digihdr->measure << 2; break; } // bufsize is bytes per measure. now scale according to memory requirements. // clear out buffer list if (channels == 0 || channels > 2) { // weird, faulty osf LOG_WARNING.printf("STRM[%d]: Illegal OSF (no channels?): %d.", m_curid, channels); return false; } int32_t bytes_per_buf = (SAMPLES_PER_STREAM_BUF * granularity); int32_t filelen = (sample_count / channels) * granularity; int nbuffers = filelen / bytes_per_buf; if (nbuffers >= 0 && nbuffers <= 1) { if (filelen > 0) { bytes_per_buf = (SAMPLES_PER_STREAM_SHORT_BUF * granularity); nbuffers = filelen / bytes_per_buf; if (nbuffers == 0) nbuffers = 1; } else { LOG_WARNING.printf("STRM[%d]: Bad stream length %d", m_curid, filelen); return false; } } m_playbytestotal = filelen; m_playbytesleft = filelen; m_fbufidx = fbufidx; m_sbufidx = 0; // Get the file size, and figure out an ideal buffer size // so our looping sounds don't have an odd silence in the middle m_measure_time = (digihdr->measure / 22050.0f); m_bufsize = (filelen / nbuffers); // figure out if we still need to be 4-byte aligned. granularity = granularity >> 1; if (granularity) { if (m_bufsize % 4) { m_bufsize = m_bufsize + (4 - (m_bufsize % 4)); if (m_bufsize % 4) { Int3(); } } } // mark stream as not done. m_readahead = true; m_readahead_finished_loop = false; if (!nbufs) { // dont set m_readahead, because readahead only told this function whether it was going to do // the read of buffers now, or let UpdateData do it later. return true; } // fill buffers. nbufs--; while (!CHECK_FLAG(m_buffer[m_fbufidx].flags, STRM_BUFF_USED) && nbufs >= 0 && m_readahead) { // if our stream's current id does not match the streaming buffer's id, then we need to reallocate // the stream buffer with the new memory size if (m_buffer[m_fbufidx].id != (int)m_curid) { if (m_buffer[m_fbufidx].data) { mem_free(m_buffer[m_fbufidx].data); } m_buffer[m_fbufidx].data = mem_rmalloc(m_bufsize); } m_buffer[m_fbufidx].nbytes = AudioStream::ReadFileData(m_fbufidx, m_bufsize); m_buffer[m_fbufidx].flags = 0; m_buffer[m_fbufidx].flags |= STRM_BUFF_USED; m_buffer[m_fbufidx].id = m_thisid; m_playbytesleft -= m_buffer[m_fbufidx].nbytes; if (m_playbytesleft <= (m_bufsize / 4)) { if (m_buffer[m_fbufidx].nbytes == 0) { memset(m_buffer[m_fbufidx].data, 0, 4); m_buffer[m_fbufidx].nbytes = 4; } m_buffer[m_fbufidx].flags |= STRM_BUFF_TERMINAL; m_readahead = false; m_readahead_finished_loop = true; } m_nbufs++; //@@ if (m_buffer[m_fbufidx].nbytes < m_bufsize || m_bytesleft == 0) { //@@ if (m_buffer[m_fbufidx].nbytes == 0) { //@@ memset(m_buffer[m_fbufidx].data, 0, 4); //@@ m_buffer[m_fbufidx].nbytes = 4; //@@ } //@@ m_buffer[m_fbufidx].flags |= STRM_BUFF_TERMINAL; //@@ m_readahead = false; //@@ m_readahead_finished_loop = true; //@@ } m_fbufidx = (m_fbufidx + 1) % STRM_BUFCOUNT; } // readjust file buffer index down so that it matches the CURRENT file index, not the next one. if (m_fbufidx == 0) m_fbufidx = STRM_BUFCOUNT - 1; else m_fbufidx--; return true; } /////////////////////////////////////////////////////////////////////////////// // plays a stream bool AudioStream::Play(bool start_on_frame) { // call low level stream manager. - samir int sflag = SIF_STREAMING_16_M, i; bool looped = false; if (m_state == STRM_INVALID) { #ifdef WIN32 Int3(); #endif return false; } // play stream if there's room to. if (!ActivateStream(this)) return false; switch (m_archive.StreamFormat()) { case SAF_8BIT_M: sflag = SIF_STREAMING_8_M; break; case SAF_8BIT_S: sflag = SIF_STREAMING_8_S; break; case SAF_16BIT_M: sflag = SIF_STREAMING_16_M; break; case SAF_16BIT_S: sflag = SIF_STREAMING_16_S; break; default: Int3(); return false; } // reset soft stopping. m_stopnextmeasure = false; m_curmeasure = 0; m_stopflag = NULL; if (m_state != STRM_STOPPED) { AudioStream::Stop(); } if (m_playcount > 0) { AudioStream::Reset(); } m_playcount++; m_measure_timer = 0.0f; m_last_frametime = timer_GetTime(); // check for terminal and if loopcount != 0 then specify terminal as looping if (m_loopcount != 0) { for (i = 0; i < STRM_BUFCOUNT; i++) { if (CHECK_FLAG(m_buffer[i].flags, STRM_BUFF_USED)) { if (CHECK_FLAG(m_buffer[i].flags, STRM_BUFF_TERMINAL)) { if (m_loopcount == -1) { m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; looped = true; AudioStream::Reset(); } else if (m_loopcount > 0) { m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; looped = true; AudioStream::Reset(); m_loopcount--; } m_readahead_finished_loop = true; } } } } // if (m_curid == 3) Int3(); // invoke low level sound system. memset(&m_playinfo, 0, sizeof(m_playinfo)); m_playinfo.samples_per_22khz_sample = 1.0; m_playinfo.sample_skip_interval = 0; m_playinfo.m_stream_cback = AudioStreamCB; m_playinfo.m_stream_data = m_buffer[m_sbufidx].data; m_playinfo.m_stream_format = sflag; m_playinfo.m_stream_size = m_buffer[m_sbufidx].nbytes; m_playinfo.m_stream_handle = m_streamindex; m_playinfo.m_stream_bufsize = m_bufsize; m_playinfo.user_data = this; m_playinfo.left_volume = m_playinfo.right_volume = m_volume; m_playinfo.priority = SND_PRIORITY_CRITICAL; // this stream must play. m_start_on_frame = start_on_frame; m_start_on_frame_looped = looped; if (!m_start_on_frame) { m_llshandle = m_ll_sndsys->PlayStream(&m_playinfo); if (m_llshandle > -1) { // for one buffer samples, prepare to stop now so that the one and only one buffer gets called. // looped one buffer samples dont do this though... if (m_nbufs == 1 && !looped) { m_state = STRM_STOPPING; } else { m_state = STRM_PLAYING; } return true; } else { LOG_ERROR << "STRMAUD:Eh? this should be reserved??"; Int3(); } } return true; } // pauses a stream void AudioStream::Pause() { if (m_state != STRM_PLAYING) { return; } m_state = STRM_PAUSED; m_ll_sndsys->PauseSound(m_llshandle); // mprintf(0, "Paused stream (%d)\n", m_llshandle); } // pauses a stream void AudioStream::Resume() { if (m_state != STRM_PAUSED) { return; } m_ll_sndsys->ResumeSound(m_llshandle); m_state = STRM_PLAYING; // mprintf(0, "Resumed stream (%d)\n", m_llshandle); } // causes a rewind to start of stream, if on_measure is true, stop occurs when measure ends void AudioStream::Stop(bool on_measure, int *stop_flag) { if (!m_ll_sndsys) { return; } m_start_on_frame = false; if (m_state == STRM_INVALID) { if (stop_flag) { *stop_flag = true; } return; } if (m_state != STRM_STOPPED) { if (!on_measure) { if (m_state == STRM_PLAYING || m_state == STRM_PAUSED || m_state == STRM_STOPPING) { m_ll_sndsys->StopSound(m_llshandle, SKT_STOP_IMMEDIATELY); m_state = STRM_STOPPED; m_readahead = m_readahead_finished_loop = true; DeactivateStream(this); m_llshandle = -1; m_curmeasure = 0; } } else { // handle soft stopping. m_stopflag = stop_flag; if (m_stopflag) { *m_stopflag = false; } m_stopnextmeasure = on_measure; } } else { DeactivateStream(this); m_llshandle = -1; m_curmeasure = 0; if (m_stopflag) { *m_stopflag = true; } } } // performs a clean stop and play when switching to next stream void AudioStream::Reset() { m_archive.Rewind(); m_bytesleft = m_archive.StreamLength(); m_curmeasure = 0; m_readahead = true; m_playbytesleft = m_playbytestotal; if (m_decoder) { delete m_decoder; uint32_t channels; uint32_t sample_rate; uint32_t sample_count; m_decoder = AudioDecoder::CreateDecoder(ADecodeFileRead, this, channels, sample_rate, sample_count); } } ////////////////////////////////////////////////////////////////////////////// // invoked by AudioStreamCB. #pragma optimize("", off) void *AudioStream::StreamCallback(int *size) { uint8_t nextbuffer = (m_sbufidx + 1) % STRM_BUFCOUNT; void *data = NULL; // we're not done yet. // adjust sound buffer to the next buffer if (m_state == STRM_STOPPING || m_state == STRM_STOPPED) { if (m_state == STRM_STOPPED) { LOG_DEBUG.printf("STRM[%d]: Callback entered while stopping! Force stop.", m_curid); } else { LOG_DEBUG.printf("STRM[%d]: Stopping softly", m_curid); } // mark played buffer as unused. m_buffer[m_sbufidx].flags = 0; m_buffer[m_sbufidx].nbytes = 0; *size = 0; return NULL; } if (!CHECK_FLAG(m_buffer[nextbuffer].flags, STRM_BUFF_USED)) { LOG_DEBUG.printf("STRM[%d]: Playing onetime buffer?", m_curid); m_state = STRM_STOPPED; *size = 0; return NULL; //@@ mprintf(0, "STRMAUD: Repeating last buffer.\n"); //@@ data = (void *)m_buffer[m_sbufidx].data; //@@ *size = m_buffer[m_sbufidx].nbytes; //@@ return data; } // mark played buffer as unused. m_buffer[m_sbufidx].flags = 0; m_buffer[m_sbufidx].nbytes = 0; m_sbufidx = nextbuffer; // ASSERT(CHECK_FLAG(m_buffer[m_sbufidx].flags, STRM_BUFF_USED)); // mprintf(0,"%c",m_sbufidx+'A'); // increment measure count if we've entered a new measure. // if stop measure has been flagged, don't return new valid data. // make sure and lock mutex, because we're modifying these contents when the caller could // do the same. // if we reached the last buffer in an opened stream, and we're not switching prepare to stop // if we are switching, release the switch lock and continue // otherwise just continue. data = (void *)m_buffer[m_sbufidx].data; *size = m_buffer[m_sbufidx].nbytes; // ASSERT((*size) > 0); if (CHECK_FLAG(m_buffer[m_sbufidx].flags, STRM_BUFF_TERMINAL)) { // mprintf(0, "STRMAUD: reached end of stream (%d bytes).\n", (*size)); if (!CHECK_FLAG(m_buffer[m_sbufidx].flags, STRM_BUFF_LOOPEND)) { m_state = STRM_STOPPING; } if (m_stopnextmeasure) { m_state = STRM_STOPPING; } LOG_DEBUG.printf("STRM[%d]: Start stopping softly", m_curid); // *done = true; } if ((*size) == 0) { // *done = true; LOG_DEBUG.printf("STRM[%d]: Used buffer has 0 bytes!", m_curid); m_state = STRM_STOPPING; data = NULL; } return data; } #pragma optimize("", on) // reads in decompressed raw data. int AudioStream::ReadFileData(int buf, int len) { if (!m_archive.Opened()) return 0; if (m_decoder) { // We have a compressed stream return m_decoder->Read(m_buffer[buf].data, len); } // We have a non-compressed stream if (len > m_bytesleft) { len = m_bytesleft; m_bytesleft = 0; } else { m_bytesleft -= len; } return m_archive.Read(m_buffer[buf].data, len); } // TURN OFF OPTIMIZATIONS HERE. Placement of instructions is VERY IMPORTANT here. synched with StreamCallback, // which also has optimizations off. #pragma optimize("", off) // updates file buffers void AudioStream::UpdateData() { int nextbuffer = ((m_fbufidx + 1) % STRM_BUFCOUNT); // check if are on a measure boundary for current stream. if so, then check if we have a next request pending if (CHECK_FLAG(m_buffer[nextbuffer].flags, STRM_BUFF_USED)) { return; } // quit out if we can. if (nextbuffer == m_sbufidx) { return; } // do read! // READ DATA INTO BUFFER. UPDATE BYTES LEFT PER MEASURE, ETC. if (m_readahead) { // ok update the next buffer with data m_fbufidx = nextbuffer; // mprintf(0,"%c",m_fbufidx+'a'); // if our stream's current id does not match the streaming buffer's id, then we need to reallocate // the stream buffer with the new memory size if (m_buffer[m_fbufidx].id != (int)m_curid) { if (m_buffer[m_fbufidx].data) { mem_free(m_buffer[m_fbufidx].data); } m_buffer[m_fbufidx].data = mem_rmalloc(m_bufsize); } m_buffer[m_fbufidx].nbytes = AudioStream::ReadFileData(m_fbufidx, m_bufsize); m_buffer[m_fbufidx].flags = 0; m_buffer[m_fbufidx].flags |= STRM_BUFF_USED; m_buffer[m_fbufidx].id = (int)m_curid; m_playbytesleft -= m_buffer[m_fbufidx].nbytes; if (m_playbytesleft <= (m_bufsize / 4)) { if (m_buffer[m_fbufidx].nbytes == 0) { memset(m_buffer[m_fbufidx].data, 0, 4); m_buffer[m_fbufidx].nbytes = 4; } m_buffer[m_fbufidx].flags |= STRM_BUFF_TERMINAL; m_readahead = false; m_readahead_finished_loop = true; } // looping? if (CHECK_FLAG(m_buffer[m_fbufidx].flags, STRM_BUFF_TERMINAL)) { if (m_loopcount == -1) { m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; AudioStream::Reset(); } else if (m_loopcount > 0) { m_buffer[m_fbufidx].flags |= STRM_BUFF_LOOPEND; AudioStream::Reset(); m_loopcount--; } m_readahead_finished_loop = true; } } } #pragma optimize("", on) /////////////////////////////////////////////////////////////////////////////// // decoder int ADecodeFileRead(void *data, void *buf, uint32_t qty) { AudioStream *stream = (AudioStream *)data; int iqty = (int)qty; if (iqty > stream->m_bytesleft) { iqty = stream->m_bytesleft; stream->m_bytesleft = 0; } else { stream->m_bytesleft -= iqty; } return stream->m_archive.Read((uint8_t *)buf, iqty); } // Router for stream callbacks. // Invoked by sound system void *AudioStreamCB(void *user_data, int handle, int *size) { AudioStream *stream = (AudioStream *)user_data; ASSERT(stream); ASSERT(stream->m_streamindex == handle); return stream->StreamCallback(size); } static float Stream_volume = 1.0f; static float Stream_master_volume = 1.0f; void StreamVolume(float master_volume) { Stream_master_volume = master_volume; User_audio_stream.SetVolume(Stream_volume * master_volume); } // these functions are the 'simplified' stream interface from Jeff (most of this code is from Jeff) int StreamPlay(const char *filename, float volume, int flags) { int retval = -1; try { flags = 0; User_audio_stream.Close(); User_audio_stream.Open(filename, STRM_OPNF_ONETIME); Stream_volume = volume; User_audio_stream.SetVolume(Stream_volume * Stream_master_volume); User_audio_stream.Play(true); // start this stream on the next frame. retval = User_audio_stream.m_streamindex; } catch (...) { return -1; } return retval; } void StreamStop(int handle) { if (handle > -1) { if (AudioStream::m_streams[handle]) AudioStream::m_streams[handle]->Stop(); } User_audio_stream.Close(); } int StreamGetSoundHandle(int handle) { if (handle > -1) { if (AudioStream::m_streams[handle]) return AudioStream::m_streams[handle]->GetSoundHandle(); else return -1; } return -1; }