123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- /*
- Copyright (C) 2005 Michael Liebscher
- 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 2
- 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, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- /*
- * sound_stream.c: Sound Stream manager.
- *
- * Author: Michael Liebscher <johnnycanuck@users.sourceforge.net>
- * Date: 2004
- *
- * Acknowledgement:
- * Portion of this code was derived from Quake II Evolved.
- *
- */
- #include "../wolfiphone.h"
- typedef struct
- {
- char introName[ MAX_GAMEPATH ];
- char loopName[ MAX_GAMEPATH ];
- _boolean looping;
- filehandle_t *hFile;
- int start;
- int rate;
- unsigned format;
- void *vorbisFile;
- } musicTrack_t;
- // anything greater than 1<<13 caused crashes on iphone OS 2.1 (on a 3G iphone)
- #define BUFFER_SIZE (1<<13)
- PRIVATE musicTrack_t bgTrack;
- PRIVATE channel_t *s_streamingChannel;
- extern void Sound_StopBGTrack( void );
- /*
- -----------------------------------------------------------------------------
- Function: ovc_read -OGG read Callback. Reads data from a stream.
-
- Parameters:
- ptr -[in] Storage location for data.
- size -[in] Item size in bytes.
- nmemb -[in] Maximum number of items to be read.
- datasource -[in] music track data structure.
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PRIVATE size_t ovc_read( void *ptr, size_t size, size_t nmemb, void *datasource )
- {
- musicTrack_t *track = (musicTrack_t *)datasource;
-
- if( ! size || ! nmemb )
- {
- return 0;
- }
-
- return FS_ReadFile( ptr, size, nmemb, track->hFile );
- }
- /*
- -----------------------------------------------------------------------------
- Function: ovc_read -OGG seek Callback. Moves the file pointer to a specified
- location.
-
- Parameters:
- datasource -[in] music track data structure.
- offset -[in] Number of bytes from whence.
- whence -[in] Initial position.
-
- Returns:
- If successful, fseek returns 0. Otherwise, it returns a nonzero
- value.
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PRIVATE int ovc_seek( void *datasource, ogg_int64_t offset, int whence )
- {
- musicTrack_t *track = (musicTrack_t *)datasource;
- return FS_FileSeek( track->hFile, offset, whence );
- }
- /*
- -----------------------------------------------------------------------------
- Function: ovc_close -OGG close Callback. Closes a stream.
-
- Parameters: datasource -[in] music track data structure.
-
- Returns: 0 if the stream is successfully closed, otherwise nonzero.
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PRIVATE int ovc_close( void *datasource )
- {
- return 0;
- }
- /*
- -----------------------------------------------------------------------------
- Function: ovc_tell -OGG tell Callback. Gets the current position of a file
- pointer.
-
- Parameters: datasource -[in] music track data structure.
-
- Returns: The current file position.
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PRIVATE long ovc_tell( void *datasource )
- {
- musicTrack_t *track = (musicTrack_t *)datasource;
- return FS_FileTell( track->hFile );
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_OpenBGTrack -OGG read Callback.
-
- Parameters:
- name -[in] File name to open.
- track -[in/out] Music track data structure.
-
- Returns: False on error, otherwise true.
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PRIVATE _boolean Sound_OpenBGTrack( const char *name, musicTrack_t *track )
- {
- OggVorbis_File *vorbisFile;
- vorbis_info *vorbisInfo;
- ov_callbacks vorbisCallbacks = {ovc_read, ovc_seek, ovc_close, ovc_tell};
- int ret;
- extern cvar_t *music;
-
- if ( music->value == 0 ) {
- return 0;
- }
-
- track->hFile = FS_OpenFile( name, 0 );
- if( ! track->hFile )
- {
- return false;
- }
- track->vorbisFile = vorbisFile = Z_Malloc( sizeof( OggVorbis_File ) );
- if( (ret = ov_open_callbacks( track, vorbisFile, NULL, 0, vorbisCallbacks )) < 0 )
- {
- switch( ret )
- {
- case OV_EREAD:
- Com_DPrintf( "A read from media returned an error.(%s)\n", name );
- break;
- case OV_ENOTVORBIS:
- Com_DPrintf( "Bitstream is not Vorbis data.(%s)\n", name );
- break;
- case OV_EVERSION:
- Com_DPrintf( "Vorbis version mismatch.(%s)\n", name );
- break;
- case OV_EBADHEADER:
- Com_DPrintf( "Invalid Vorbis bitstream header.(%s)\n", name );
- break;
- case OV_EFAULT:
- Com_DPrintf( "Internal logic fault; indicates a bug or heap/stack corruption.(%s)\n", name );
- break;
- }
- Com_DPrintf( "Could not open OGG stream (%s)\n", name );
-
- return false;
- }
- vorbisInfo = ov_info( vorbisFile, -1 );
- if( vorbisInfo->channels != 1 && vorbisInfo->channels != 2 )
- {
- Com_DPrintf( "Only mono and stereo OGG files supported (%s)\n", name );
-
- return false;
- }
- track->start = ov_raw_tell( vorbisFile );
- track->rate = vorbisInfo->rate;
- track->format = (vorbisInfo->channels == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
- return true;
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_CloseBGTrack -Close out background music track.
-
- Parameters: track -[in] Music track to close.
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PRIVATE void Sound_CloseBGTrack( musicTrack_t *track )
- {
- if( track->vorbisFile )
- {
- ov_clear( track->vorbisFile );
- Z_Free( track->vorbisFile );
- track->vorbisFile = NULL;
- }
- if( track->hFile )
- {
- FS_CloseFile( track->hFile );
- }
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_StreamBGTrack -Called each frame to update streaming music
- track.
-
- Parameters: Nothing
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PUBLIC void Sound_StreamBGTrack( void )
- {
- W8 data[BUFFER_SIZE];
- int processed, queued, state;
- int size, read, dummy;
- unsigned buffer;
- if( ! s_musicVolume->value )
- {
- return;
- }
- if( ! s_streamingChannel )
- {
- return;
- }
- // Unqueue and delete any processed buffers
- pfalGetSourcei( s_streamingChannel->sourceName, AL_BUFFERS_PROCESSED, &processed );
- if( processed > 0 )
- {
- while (processed--)
- {
- pfalSourceUnqueueBuffers( s_streamingChannel->sourceName, 1, &buffer );
- pfalDeleteBuffers( 1, &buffer );
- }
- }
- // Make sure we always have at least 4 buffers in the queue
- pfalGetSourcei( s_streamingChannel->sourceName, AL_BUFFERS_QUEUED, &queued );
- while( queued < 4 )
- {
- size = 0;
- // Stream from disk
- while( size < BUFFER_SIZE )
- {
- read = ov_read( bgTrack.vorbisFile, (char *)data + size, BUFFER_SIZE - size, &dummy );
- if( read == 0 )
- {
- // End of file
- if( ! bgTrack.looping)
- {
- // Close the intro track
- Sound_CloseBGTrack( &bgTrack );
- // Open the loop track
- if( ! Sound_OpenBGTrack( bgTrack.loopName, &bgTrack ) )
- {
- Sound_StopBGTrack();
- return;
- }
- bgTrack.looping = true;
- }
- // Restart the track, skipping over the header
- ov_raw_seek( bgTrack.vorbisFile, (ogg_int64_t)bgTrack.start );
- // Try streaming again
- read = ov_read( bgTrack.vorbisFile, (char *)data + size, BUFFER_SIZE - size, &dummy );
- }
- if( read <= 0 )
- {
- // An error occurred
- Sound_StopBGTrack();
- return;
- }
- size += read;
- }
- // Upload and queue the new buffer
- pfalGenBuffers( 1, &buffer );
- pfalBufferData( buffer, bgTrack.format, data, size, bgTrack.rate );
- pfalSourceQueueBuffers( s_streamingChannel->sourceName, 1, &buffer );
- queued++;
- }
- // Update volume
- pfalSourcef( s_streamingChannel->sourceName, AL_GAIN, s_musicVolume->value );
- // If not playing, then do so
- pfalGetSourcei( s_streamingChannel->sourceName, AL_SOURCE_STATE, &state );
- if( state != AL_PLAYING )
- {
- pfalSourcePlay(s_streamingChannel->sourceName);
- }
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_StartStreaming -Start streaming background music track.
-
- Parameters: Nothing
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PUBLIC void Sound_StartStreaming( void )
- {
- if( ! sound_initialized )
- {
- return;
- }
- if( s_streamingChannel )
- {
- return;
- }
- s_streamingChannel = Sound_PickChannel( 0, 0 );
- if( ! s_streamingChannel )
- {
- return;
- }
- s_streamingChannel->streaming = true;
- // hmmm...
- pfalDeleteSources( 1, &s_streamingChannel->sourceName );
- pfalGenSources( 1, &s_streamingChannel->sourceName );
- // Set up the source
- pfalSourcei( s_streamingChannel->sourceName, AL_BUFFER, 0 );
- pfalSourcei( s_streamingChannel->sourceName, AL_LOOPING, AL_FALSE );
- pfalSourcei( s_streamingChannel->sourceName, AL_SOURCE_RELATIVE, AL_TRUE );
- pfalSourcefv( s_streamingChannel->sourceName, AL_POSITION, vec3_origin );
- pfalSourcefv( s_streamingChannel->sourceName, AL_VELOCITY, vec3_origin );
- pfalSourcef( s_streamingChannel->sourceName, AL_REFERENCE_DISTANCE, 1.0 );
- pfalSourcef( s_streamingChannel->sourceName, AL_MAX_DISTANCE, 1.0 );
- pfalSourcef( s_streamingChannel->sourceName, AL_ROLLOFF_FACTOR, 0.0 );
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_StopStreaming -Stop playing streaming music track.
-
- Parameters: Nothing
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PUBLIC void Sound_StopStreaming( void )
- {
- int processed;
- unsigned buffer;
- if( ! sound_initialized )
- {
- return;
- }
- if( ! s_streamingChannel )
- {
- return;
- }
- s_streamingChannel->streaming = false;
- pfalSourceStop( s_streamingChannel->sourceName );
- pfalGetSourcei( s_streamingChannel->sourceName, AL_BUFFERS_PROCESSED, &processed );
- if( processed > 0 )
- {
- while( processed-- )
- {
- pfalSourceUnqueueBuffers( s_streamingChannel->sourceName, 1, &buffer );
- pfalDeleteBuffers( 1, &buffer );
- }
- }
- pfalSourcei( s_streamingChannel->sourceName, AL_BUFFER, 0 );
- // hmmm...
- pfalDeleteSources( 1, &s_streamingChannel->sourceName );
- pfalGenSources( 1, &s_streamingChannel->sourceName );
- s_streamingChannel = NULL;
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_StartBGTrack -Play background music track.
-
- Parameters:
- introTrack -[in] File name of intro track.
- loopTrack -[in] File name of loop track.
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PUBLIC void Sound_StartBGTrack( const char *introTrack, const char *loopTrack )
- {
- if( ! sound_initialized )
- {
- return;
- }
- Sound_StopBGTrack();
- my_strlcpy( bgTrack.introName, introTrack, sizeof( bgTrack.introName ) );
- my_strlcpy( bgTrack.loopName, loopTrack, sizeof( bgTrack.loopName) );
- Sound_StartStreaming();
- if( ! Sound_OpenBGTrack( bgTrack.introName, &bgTrack ) )
- {
- Sound_StopBGTrack();
- return;
- }
- Sound_StreamBGTrack();
- }
- /*
- -----------------------------------------------------------------------------
- Function: Sound_StopBGTrack -Stop playing background track.
-
- Parameters: Nothing
-
- Returns: Nothing
-
- Notes:
- -----------------------------------------------------------------------------
- */
- PUBLIC void Sound_StopBGTrack( void )
- {
- if( ! sound_initialized )
- {
- return;
- }
- Sound_StopStreaming();
- Sound_CloseBGTrack( &bgTrack );
- memset( &bgTrack, 0, sizeof( musicTrack_t ) );
- }
|