void S_StopBackgroundTrack( void ) { bgTrack_t *next; if( source ) qalSourceStop( source ); if( alloced_buffers ) { qalSourceUnqueueBuffers( source, MUSIC_BUFFERS, buffers ); qalDeleteBuffers( MUSIC_BUFFERS, buffers ); alloced_buffers = queued_buffers = qfalse; } music_source_free(); qalGetError(); while( s_bgTrackHead ) { next = s_bgTrackHead->anext; S_CloseMusicTrack( s_bgTrackHead ); S_Free( s_bgTrackHead ); s_bgTrackHead = next; } s_bgTrack = NULL; s_bgTrackHead = NULL; s_bgTrackBuffering = qfalse; s_bgTrackPaused = qfalse; }
/* * S_StartBackgroundTrack */ void S_StartBackgroundTrack( const char *intro, const char *loop, int mode ) { const char *ext; bgTrack_t *introTrack, *loopTrack; bgTrack_t *firstTrack = NULL; S_StopBackgroundTrack(); if( !intro || !intro[0] ) { return; } s_bgTrackMuted = false; s_bgTrackPaused = false; ext = COM_FileExtension( intro ); if( ext && !Q_stricmp( ext, ".m3u" ) ) { // mode bits: // 1 - shuffle // 2 - loop the selected track // 4 - stream (even if muted) firstTrack = S_ReadPlaylistFile( intro, mode & 1 ? true : false, mode & 2 ? true : false ); if( firstTrack ) { goto start_playback; } } // the intro track loops unless another loop track has been specified introTrack = S_AllocTrack( intro ); introTrack->loop = true; introTrack->next = introTrack->prev = introTrack; introTrack->muteOnPause = introTrack->isUrl || mode & 4 ? true : false; if( loop && loop[0] && Q_stricmp( intro, loop ) ) { loopTrack = S_AllocTrack( loop ); if( S_OpenMusicTrack( loopTrack, NULL ) ) { S_CloseMusicTrack( loopTrack ); introTrack->next = introTrack->prev = loopTrack; introTrack->loop = false; loopTrack->loop = true; loopTrack->muteOnPause = loopTrack->isUrl || mode & 4 ? true : false; loopTrack->next = loopTrack->prev = loopTrack; } } firstTrack = introTrack; start_playback: if( !firstTrack || firstTrack->ignore ) { S_StopBackgroundTrack(); return; } S_OpenBackgroundTrackTask( firstTrack ); }
/* * S_OpenMusicTrack */ static qboolean S_OpenMusicTrack( bgTrack_t *track ) { const char *filename = track->filename; if( track->ignore ) return qfalse; mark0: s_bgTrackBuffering = qfalse; if( !track->stream ) { qboolean delay = qfalse; track->stream = S_OpenStream( filename, &delay ); if( track->stream && delay ) { // let the background track buffer for a while Com_Printf( "S_OpenMusicTrack: buffering %s...\n", track->filename ); s_bgTrackBuffering = qtrue; } } else { if( !S_ResetStream( track->stream ) ) { // if seeking failed for whatever reason (stream?), try reopening again S_CloseMusicTrack( track ); goto mark0; } } if( !S_ValidMusicFile( track ) ) { S_CloseMusicTrack( track ); // mark as permanently invalid track->ignore = qtrue; Com_Printf( "Invalid music file %s\n", filename ); return qfalse; } return qtrue; }
/* * S_OpenMusicTrack */ static bool S_OpenMusicTrack( bgTrack_t *track, bool *buffering ) { if( track->ignore ) return false; mark0: if( buffering ) *buffering = false; if( !track->file ) { bool opened, delay = false; memset( &track->info, 0, sizeof( track->info ) ); // try ogg track->open = SNDOGG_OpenTrack; opened = track->open( track, &delay ); // try wav if( !opened ) { track->open = S_BackgroundTrack_OpenWav; opened = track->open( track, &delay ); } if( opened && delay ) { // let the background track buffer for a while // Com_Printf( "S_OpenMusicTrack: buffering %s...\n", track->filename ); if( buffering ) *buffering = true; } } else { int seek; if( track->seek ) seek = track->seek( track, 0 ); else seek = trap_FS_Seek( track->file, track->info.dataofs, FS_SEEK_SET ); // if seeking failed for whatever reason (stream?), try reopening again if( seek ) { S_CloseMusicTrack( track ); goto mark0; } } return true; }
static qboolean music_process( ALuint b ) { int l = 0; ALuint format; ALenum error; snd_stream_t *music_stream; start: if( s_bgTrackBuffering ) return qtrue; music_stream = s_bgTrack->stream; if( music_stream ) { l = S_ReadStream( music_stream, MUSIC_BUFFER_SIZE, decode_buffer ); } else { l = 0; } if( !l ) { bgTrack_t *cur; cur = s_bgTrack; if( !S_AdvanceBackgroundTrack( 1 ) ) { if( !S_ValidMusicFile( s_bgTrack ) ) return qfalse; } else { // we've advanced to the next track, close this one S_CloseMusicTrack( cur ); goto start; } if( !S_ResetStream( music_stream ) ) { // if failed, close the track? return qfalse; } goto start; } format = S_SoundFormat( music_stream->info.width, music_stream->info.channels ); qalBufferData( b, format, decode_buffer, l, music_stream->info.rate ); if( ( error = qalGetError() ) != AL_NO_ERROR ) return qfalse; return qtrue; }
/* * S_PauseBackgroundTrack */ void S_PauseBackgroundTrack( void ) { if( !s_bgTrack ) { return; } // in case of a streaming URL, reset the stream if( s_bgTrack->isUrl ) { if( s_bgTrackPaused ) { S_OpenMusicTrack( s_bgTrack ); } else { S_CloseMusicTrack( s_bgTrack ); } } s_bgTrackPaused = !s_bgTrackPaused; }
/* * S_AdvanceBackgroundTrack */ static bool S_AdvanceBackgroundTrack( int n ) { bgTrack_t *track; if( n < 0 ) { track = S_PrevMusicTrack( s_bgTrack ); } else { track = S_NextMusicTrack( s_bgTrack ); } if( track && track != s_bgTrack ) { S_CloseBackgroundTrackTask(); S_CloseMusicTrack( s_bgTrack ); S_OpenBackgroundTrackTask( track ); return true; } return false; }
/* * S_AdvanceBackgroundTrack */ static qboolean S_AdvanceBackgroundTrack( int n ) { bgTrack_t *track; if( n < 0 ) track = S_PrevMusicTrack( s_bgTrack ); else track = S_NextMusicTrack( s_bgTrack ); if( track && track != s_bgTrack ) { if( s_bgTrack->isUrl ) S_CloseMusicTrack( s_bgTrack ); s_bgTrack = track; return qtrue; } return qfalse; }
/* * S_StopBackgroundTrack */ void S_StopBackgroundTrack( void ) { bgTrack_t *next; S_CloseBackgroundTrackTask(); while( s_bgTrackHead ) { next = s_bgTrackHead->anext; S_CloseMusicTrack( s_bgTrackHead ); S_Free( s_bgTrackHead ); s_bgTrackHead = next; } s_bgTrack = NULL; s_bgTrackHead = NULL; s_bgTrackMuted = false; s_bgTrackPaused = false; }
void S_StartBackgroundTrack( const char *intro, const char *loop ) { int count; const char *ext; bgTrack_t *t, f; bgTrack_t *introTrack, *loopTrack; ALenum error; int mode = 0; // Stop any existing music that might be playing S_StopBackgroundTrack(); if( !intro || !intro[0] ) return; s_bgTrackPaused = qfalse; ext = COM_FileExtension( intro ); if( ext && !Q_stricmp( ext, ".m3u" ) ) { // mode bits: // 1 - shuffle // 2 - loop the selected track if( loop && loop[0] ) mode = atoi( loop ); if( S_ReadPlaylistFile( intro, mode & 1 ? qtrue : qfalse ) ) goto start_playback; } // the intro track loops unless another loop track has been specified introTrack = S_AllocTrack( intro ); introTrack->next = introTrack->prev = introTrack; if( loop && loop[0] && Q_stricmp( intro, loop ) ) { loopTrack = S_AllocTrack( loop ); if( S_OpenMusicTrack( loopTrack ) ) { S_CloseMusicTrack( loopTrack ); loopTrack->next = introTrack->next = introTrack->prev = loopTrack; loopTrack->prev = introTrack; } } s_bgTrack = introTrack; start_playback: // this effectively precaches the first 15 scheduled tracks in the playlist for( count = 0, t = s_bgTrack; count < 15 && t && t != s_bgTrack; count++ ) { if( !t->isUrl ) { S_OpenMusicTrack( t ); if( t->next == t || t->next == s_bgTrack ) break; // break on an endless loop or full cycle if( !t->ignore && ( mode & 2 ) ) { // no point in precaching the whole playlist when we're only going // to loop one single track break; } } t = t->next; } // start playback with the first valid track if( count > 1 ) { memset( &f, 0, sizeof( f ) ); f.next = s_bgTrack; s_bgTrack = S_NextMusicTrack( &f ); } else { S_OpenMusicTrack( s_bgTrack ); } if( !s_bgTrack || s_bgTrack->ignore ) { S_StopBackgroundTrack(); return; } if( mode & 2 ) { // loop the same track over and over s_bgTrack->next = s_bgTrack->prev = s_bgTrack; } music_source_get(); if( !src ) { Com_Printf( "Error couldn't get source for music\n" ); S_StopBackgroundTrack(); return; } alloced_buffers = qfalse; queued_buffers = qfalse; qalGenBuffers( MUSIC_BUFFERS, buffers ); if( ( error = qalGetError() ) != AL_NO_ERROR ) { Com_Printf( "Error couldn't generate music buffers (%s)\n", S_ErrorMessage( error ) ); S_StopBackgroundTrack(); return; } alloced_buffers = qtrue; }
void S_UpdateMusic( void ) { int i, processed; ALint state; ALenum error; ALuint b; int num_processed_buffers; ALuint processed_buffers[MUSIC_BUFFERS]; if( !s_bgTrack ) return; if( !s_musicvolume->value && !s_bgTrack->isUrl ) return; if( s_bgTrackPaused || s_bgTrackLocked ) return; if( s_bgTrackBuffering ) { if( S_EoStream( s_bgTrack->stream ) ) { // we should now advance to the next track S_CloseMusicTrack( s_bgTrack ); } else { if( S_FTellSteam( s_bgTrack->stream ) < BACKGROUND_TRACK_BUFFERING_SIZE ) return; // in case we delayed openening to let the stream be cached for a while, // start actually reading from it now if( !S_ContOpenStream( s_bgTrack->stream ) ) { // let music_process do the dirty job of advancing to the next track S_CloseMusicTrack( s_bgTrack ); s_bgTrack->ignore = qtrue; } } s_bgTrackBuffering = qfalse; } if( !queued_buffers ) { // if we haven't queued any buffers yet, do it now num_processed_buffers = MUSIC_BUFFERS; memcpy( processed_buffers, buffers, sizeof( buffers ) ); } else { num_processed_buffers = 0; processed = 0; qalGetSourcei( source, AL_BUFFERS_PROCESSED, &processed ); while( processed-- ) { qalSourceUnqueueBuffers( source, 1, &b ); processed_buffers[num_processed_buffers++] = b; } } for( i = 0; i < num_processed_buffers; i++ ) { b = processed_buffers[i]; if( !music_process( b ) ) { Com_Printf( "Error processing music data\n" ); S_StopBackgroundTrack(); return; } qalSourceQueueBuffers( source, 1, &b ); if( ( error = qalGetError() ) != AL_NO_ERROR ) { Com_Printf( "Couldn't queue music data (%s)\n", S_ErrorMessage( error ) ); S_StopBackgroundTrack(); return; } } // If it's not still playing, give it a kick qalGetSourcei( source, AL_SOURCE_STATE, &state ); if( !queued_buffers || state != AL_PLAYING ) { queued_buffers = qtrue; qalSourcePlay( source ); } if( s_musicvolume->modified ) qalSourcef( source, AL_GAIN, s_musicvolume->value ); }