/* s_stream.c - sound streaming Copyright (C) 2009 Uncle Mike 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. */ #include "common.h" #include "sound.h" #include "client.h" static bg_track_t s_bgTrack; static musicfade_t musicfade; // controlled by game dlls /* ================= S_PrintBackgroundTrackState ================= */ void S_PrintBackgroundTrackState( void ) { if( s_bgTrack.current[0] && s_bgTrack.loopName[0] ) Msg( "BackgroundTrack: intro %s, loop %s\n", s_bgTrack.current, s_bgTrack.loopName ); else if( s_bgTrack.current[0] ) Msg( "BackgroundTrack: %s\n", s_bgTrack.current ); else if( s_bgTrack.loopName[0] ) Msg( "BackgroundTrack: %s [loop]\n", s_bgTrack.loopName ); } /* ================= S_FadeMusicVolume ================= */ void S_FadeMusicVolume( float fadePercent ) { musicfade.percent = bound( 0.0f, fadePercent, 100.0f ); } /* ================= S_GetMusicVolume ================= */ float S_GetMusicVolume( void ) { float scale = 1.0f; if( !s_listener.inmenu && musicfade.percent != 0 ) { scale = bound( 0.0f, musicfade.percent / 100.0f, 1.0f ); scale = 1.0f - scale; } return s_musicvolume->value * scale; } /* ================= S_StartBackgroundTrack ================= */ void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, long position ) { S_StopBackgroundTrack(); if( !dma.initialized ) return; // check for special symbols if( introTrack && *introTrack == '*' ) introTrack = NULL; if( mainTrack && *mainTrack == '*' ) mainTrack = NULL; if(( !introTrack || !*introTrack ) && ( !mainTrack || !*mainTrack )) return; if( !introTrack ) introTrack = mainTrack; if( !*introTrack ) return; if( !mainTrack || !*mainTrack ) s_bgTrack.loopName[0] = '\0'; else Q_strncpy( s_bgTrack.loopName, mainTrack, sizeof( s_bgTrack.loopName )); // open stream s_bgTrack.stream = FS_OpenStream( va( "media/%s", introTrack )); Q_strncpy( s_bgTrack.current, introTrack, sizeof( s_bgTrack.current )); memset( &musicfade, 0, sizeof( musicfade )); // clear any soundfade s_bgTrack.source = cls.key_dest; if( position != 0 ) { // restore message, update song position FS_SetStreamPos( s_bgTrack.stream, position ); } } /* ================= S_StopBackgroundTrack ================= */ void S_StopBackgroundTrack( void ) { s_listener.stream_paused = false; if( !dma.initialized ) return; if( !s_bgTrack.stream ) return; FS_FreeStream( s_bgTrack.stream ); memset( &s_bgTrack, 0, sizeof( bg_track_t )); memset( &musicfade, 0, sizeof( musicfade )); } /* ================= S_StreamSetPause ================= */ void S_StreamSetPause( int pause ) { s_listener.stream_paused = pause; } /* ================= S_StreamGetCurrentState save\restore code ================= */ qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position ) { if( !s_bgTrack.stream ) return false; // not active if( currentTrack ) { if( s_bgTrack.current[0] ) Q_strncpy( currentTrack, s_bgTrack.current, MAX_STRING ); else Q_strncpy( currentTrack, "*", MAX_STRING ); // no track } if( loopTrack ) { if( s_bgTrack.loopName[0] ) Q_strncpy( loopTrack, s_bgTrack.loopName, MAX_STRING ); else Q_strncpy( loopTrack, "*", MAX_STRING ); // no track } if( position ) *position = FS_GetStreamPos( s_bgTrack.stream ); return true; } /* ================= S_StreamBackgroundTrack ================= */ void S_StreamBackgroundTrack( void ) { int bufferSamples; int fileSamples; byte raw[MAX_RAW_SAMPLES]; int r, fileBytes; rawchan_t *ch = NULL; if( !dma.initialized || !s_bgTrack.stream || s_listener.streaming ) return; // don't bother playing anything if musicvolume is 0 if( !s_musicvolume->value || s_listener.paused || s_listener.stream_paused ) return; if( !cl.background ) { // pause music by source type if( s_bgTrack.source == key_game && cls.key_dest == key_menu ) return; if( s_bgTrack.source == key_menu && cls.key_dest != key_menu ) return; } else if( cls.key_dest == key_console ) return; ch = S_FindRawChannel( S_RAW_SOUND_BACKGROUNDTRACK, true ); Assert( ch != NULL ); // see how many samples should be copied into the raw buffer if( ch->s_rawend < soundtime ) ch->s_rawend = soundtime; while( ch->s_rawend < soundtime + ch->max_samples ) { wavdata_t *info = FS_StreamInfo( s_bgTrack.stream ); bufferSamples = ch->max_samples - (ch->s_rawend - soundtime); // decide how much data needs to be read from the file fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED ); if( fileSamples <= 1 ) return; // no more samples need // our max buffer size fileBytes = fileSamples * ( info->width * info->channels ); if( fileBytes > sizeof( raw )) { fileBytes = sizeof( raw ); fileSamples = fileBytes / ( info->width * info->channels ); } // read r = FS_ReadStream( s_bgTrack.stream, fileBytes, raw ); if( r < fileBytes ) { fileBytes = r; fileSamples = r / ( info->width * info->channels ); } if( r > 0 ) { // add to raw buffer S_RawSamples( fileSamples, info->rate, info->width, info->channels, raw, S_RAW_SOUND_BACKGROUNDTRACK ); } else { // loop if( s_bgTrack.loopName[0] ) { FS_FreeStream( s_bgTrack.stream ); s_bgTrack.stream = FS_OpenStream( va( "media/%s", s_bgTrack.loopName )); Q_strncpy( s_bgTrack.current, s_bgTrack.loopName, sizeof( s_bgTrack.current )); if( !s_bgTrack.stream ) return; } else { S_StopBackgroundTrack(); return; } } } } /* ================= S_StartStreaming ================= */ void S_StartStreaming( void ) { if( !dma.initialized ) return; // begin streaming movie soundtrack s_listener.streaming = true; } /* ================= S_StopStreaming ================= */ void S_StopStreaming( void ) { if( !dma.initialized ) return; s_listener.streaming = false; } /* ================= S_StreamSoundTrack ================= */ void S_StreamSoundTrack( void ) { int bufferSamples; int fileSamples; byte raw[MAX_RAW_SAMPLES]; int r, fileBytes; rawchan_t *ch = NULL; if( !dma.initialized || !s_listener.streaming || s_listener.paused ) return; ch = S_FindRawChannel( S_RAW_SOUND_SOUNDTRACK, true ); Assert( ch != NULL ); // see how many samples should be copied into the raw buffer if( ch->s_rawend < soundtime ) ch->s_rawend = soundtime; while( ch->s_rawend < soundtime + ch->max_samples ) { wavdata_t *info = SCR_GetMovieInfo(); if( !info ) break; // bad soundtrack? bufferSamples = ch->max_samples - (ch->s_rawend - soundtime); // decide how much data needs to be read from the file fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED ); if( fileSamples <= 1 ) return; // no more samples need // our max buffer size fileBytes = fileSamples * ( info->width * info->channels ); if( fileBytes > sizeof( raw )) { fileBytes = sizeof( raw ); fileSamples = fileBytes / ( info->width * info->channels ); } // read audio stream r = SCR_GetAudioChunk( raw, fileBytes ); if( r < fileBytes ) { fileBytes = r; fileSamples = r / ( info->width * info->channels ); } if( r > 0 ) { // add to raw buffer S_RawSamples( fileSamples, info->rate, info->width, info->channels, raw, S_RAW_SOUND_SOUNDTRACK ); } else break; // no more samples for this frame } }