/* ************************************************************************* ArmageTron -- Just another Tron Lightcycle Game in 3D. Copyright (C) 2000 Manuel Moos (manuel@moosnet.de) ************************************************************************** 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. *************************************************************************** */ #include "config.h" #include "tMemManager.h" #include "tDirectories.h" #include "eSound.h" #include "tError.h" #include #include "tConfiguration.h" #include "uMenu.h" #include "eCamera.h" //#include "tList.h" #include #include #include "eGrid.h" //eGrid* eSoundPlayer::S_Grid = NULL; #ifdef WIN32 #define HAVE_LIBSDL_MIXER 1 #endif #ifndef DEDICATED #ifdef HAVE_LIBSDL_MIXER #include static Mix_Music* music = NULL; #endif static SDL_AudioSpec audio; static bool sound_is_there=false; static bool uses_sdl_mixer=false; #endif // sound quality #define SOUND_OFF 0 #define SOUND_LOW 1 #define SOUND_MED 2 #define SOUND_HIGH 3 #ifdef WIN32 static int buffer_shift=1; #else static int buffer_shift=0; #endif static tConfItem bs("SOUND_BUFFER_SHIFT",buffer_shift); static int sound_quality=SOUND_MED; static tConfItem sq("SOUND_QUALITY",sound_quality); static int sound_sources=10; static tConfItem ss("SOUND_SOURCES",sound_sources); static REAL loudness_thresh=0; static int real_sound_sources=0; static tList se_globalPlayers; void fill_audio(void *udata, Uint8 *stream, int len) { #ifndef DEDICATED real_sound_sources=0; int i; if (eGrid::CurrentGrid()) for(i=eGrid::CurrentGrid()->Cameras().Len()-1;i>=0;i--) eGrid::CurrentGrid()->Cameras()(i)->SoundMix(stream,len); for(i=se_globalPlayers.Len()-1;i>=0;i--) se_globalPlayers(i)->Mix(stream,len,0,1,1); if (real_sound_sources>sound_sources+4) loudness_thresh+=.01; if (real_sound_sources>sound_sources+1) loudness_thresh+=.001; if (real_sound_sources> (6-buffer_shift)) desired.samples <<= 1; desired.channels = 2; desired.callback = fill_audio; desired.userdata = NULL; // save configuration file with sound disabled int sound_quality_back = sound_quality; if ( st_FirstUse ) { sound_quality = SOUND_OFF; st_SaveConfig(); con << tOutput("$sound_firstinit"); } #ifdef HAVE_LIBSDL_MIXER uses_sdl_mixer=true; // init using SDL_Mixer sound_is_there=(Mix_OpenAudio(desired.freq, desired.format, desired.channels, desired.samples)>=0); if ( sound_is_there ) { // query actual sound info audio = desired; int channels; Mix_QuerySpec( &audio.freq, &audio.format, &channels ); audio.channels = channels; // register callback Mix_SetPostMix( &fill_audio, NULL ); const tPath& vpath = tDirectories::Data(); tString musFile = vpath.GetReadPath( "music/fire.xm" ); music = Mix_LoadMUS( musFile ); if ( music ) Mix_FadeInMusic( music, -1, 2000 ); } #else // just use SDL to init sound uses_sdl_mixer=false; sound_is_there=(SDL_OpenAudio(&desired,&audio)>=0); #endif // restore sound settings if ( sound_quality != sound_quality_back ) { sound_quality = sound_quality_back; st_SaveConfig(); } if (sound_is_there && (audio.format!=AUDIO_S16SYS || audio.channels!=2)) { uses_sdl_mixer=false; se_SoundExit(); // force emulation of 16 bit stereo; sadly, this cannot use SDL_Mixer :-( audio.format=AUDIO_S16SYS; audio.channels=2; sound_is_there=(SDL_OpenAudio(&audio,NULL)>=0); con << tOutput("$sound_error_no16bit"); } if (!sound_is_there) con << tOutput("$sound_error_initfailed"); else { //for(int i=wavs.Len()-1;i>=0;i--) //wavs(i)->Init(); tOutput o; o.SetTemplateParameter(1,audio.freq); o.SetTemplateParameter(2,audio.samples); o << "$sound_inited"; con << o; } } #endif } void se_SoundExit(){ #ifndef DEDICATED se_SoundLock(); eWavData::UnloadAll(); se_SoundPause(true); se_SoundUnlock(); if (sound_is_there){ #ifdef DEBUG con << tOutput("$sound_disabling"); #endif // se_SoundPause(false); // for(int i=wavs.Len()-1;i>=0;i--) //wavs(i)->Exit(); #ifdef HAVE_LIBSDL_MIXER if ( music ) { if( Mix_PlayingMusic() ) { Mix_FadeOutMusic(100); SDL_Delay(100); } Mix_FreeMusic( music ); music = NULL; } se_SoundPause(true); if ( uses_sdl_mixer ) Mix_CloseAudio(); else #endif SDL_CloseAudio(); #ifdef DEBUG con << tOutput("$sound_disabling_done"); #endif } sound_is_there=false; #endif } #ifndef DEDICATED static unsigned int locks; #endif void se_SoundLock(){ #ifndef DEDICATED if (!locks) SDL_LockAudio(); locks++; #endif } void se_SoundUnlock(){ #ifndef DEDICATED locks--; if (!locks) SDL_UnlockAudio(); #endif } void se_SoundPause(bool p){ #ifndef DEDICATED SDL_PauseAudio(p); #endif } // *********************************************************** eWavData* eWavData::s_anchor = NULL; eWavData::eWavData(const char * fileName,const char *alternative) :tListItem(s_anchor),data(NULL),len(0),freeData(false){ //wavs.Add(this,id); filename = fileName; filename_alt = alternative; } void eWavData::Load(){ //wavs.Add(this,id); if (data) return; #ifndef DEDICATED freeData = false; alt=false; const tPath& path = tDirectories::Data(); SDL_AudioSpec *result=SDL_LoadWAV( path.GetReadPath( filename ) ,&spec,&data,&len); if (result!=&spec || !data){ if (filename_alt.Len()>1){ result=SDL_LoadWAV( path.GetReadPath( filename_alt ),&spec,&data,&len); if (result!=&spec || !data) { tOutput err; err.SetTemplateParameter(1, filename); err << "$sound_error_filenotfound"; tERR_ERROR(err); } else alt=true; } else{ result=SDL_LoadWAV( path.GetReadPath( "sound/expl.wav" ) ,&spec,&data,&len); if (result!=&spec || !data) { tOutput err; err.SetTemplateParameter(1, "sound/expl.waw"); err << "$sound_error_filenotfount"; tERR_ERROR(err); } else len=0; } /* tERR_ERROR("Sound file " << fileName << " not found. Have you called " "Armagetron from the right directory?"); */ } if (spec.format==AUDIO_S16SYS) samples=len>>1; // else if(spec.format==AUDIO_U8) // samples=len; else { // prepare error message tOutput err; err.SetTemplateParameter(1, filename); err << "$sound_error_unsupported"; // convert to 16 bit system format SDL_AudioCVT cvt; if ( -1 == SDL_BuildAudioCVT( &cvt, spec.format, spec.channels, spec.freq, AUDIO_S16SYS, spec.channels, spec.freq ) ) { tERR_ERROR(err); } cvt.buf=reinterpret_cast( malloc( len * cvt.len_mult ) ); cvt.len=len; memcpy(cvt.buf, data, len); freeData = true; if ( -1 == SDL_ConvertAudio( &cvt ) ) { tERR_ERROR(err); } SDL_FreeWAV( data ); data = cvt.buf; spec.format = AUDIO_S16SYS; len = len * cvt.len_mult; samples = len >> 1; } samples/=spec.channels; #ifdef DEBUG #ifdef LINUX con << "Sound file " << filename << " loaded: "; switch (spec.format){ case AUDIO_S16SYS: con << "16 bit "; break; case AUDIO_U8: con << "8 bit "; break; default: con << "unknown "; break; } if (spec.channels==2) con << "stereo "; else con << "mono "; con << "at " << spec.freq << " Hz,\n"; con << samples << " samples in " << len << " bytes.\n"; #endif #endif #endif } void eWavData::Unload(){ #ifndef DEDICATED //wavs.Add(this,id); if (data){ se_SoundLock(); if ( freeData ) { free(data); } else { SDL_FreeWAV(data); } data=NULL; len=0; se_SoundUnlock(); } #endif } void eWavData::UnloadAll(){ //wavs.Add(this,id); eWavData* wav = s_anchor; while ( wav ) { wav->Unload(); wav = wav->Next(); } } eWavData::~eWavData(){ #ifndef DEDICATED Unload(); #endif } bool eWavData::Mix(Uint8 *dest,Uint32 playlen,eAudioPos &pos, REAL Rvol,REAL Lvol,REAL Speed,bool loop){ #ifndef DEDICATED if ( !data ) { return false; } playlen/=4; // Rvol *= 4; // Lvol *= 4; const REAL thresh = .25; if ( Rvol > thresh ) { Rvol = thresh; } if ( Lvol > thresh ) { Lvol = thresh; } #define SPEED_FRACTION (1<<20) #define VOL_SHIFT 16 #define VOL_FRACTION (1<0 && pos.pos> (VOL_SHIFT-8); l += (lvol*(data[(pos.pos<<1)+1]-128)) >> (VOL_SHIFT-8); if (r>MAX_VAL) r=MAX_VAL; if (l>MAX_VAL) l=MAX_VAL; if (r=SPEED_FRACTION){ pos.fraction-=SPEED_FRACTION; pos.pos++; } playlen--; } else{ while (playlen>0 && pos.pos> VOL_SHIFT; l += (lvol*(((short *)data)[(pos.pos<<1)+1])) >> VOL_SHIFT; if (r>MAX_VAL) r=MAX_VAL; if (l>MAX_VAL) l=MAX_VAL; if (r=SPEED_FRACTION){ pos.fraction-=SPEED_FRACTION; pos.pos++; } playlen--; } } } else{ if (spec.format==AUDIO_U8){ while (playlen>0 && pos.pos> (VOL_SHIFT-8); r += (rvol*d) >> (VOL_SHIFT-8); if (r>MAX_VAL) r=MAX_VAL; if (l>MAX_VAL) l=MAX_VAL; if (r=SPEED_FRACTION){ pos.fraction-=SPEED_FRACTION; pos.pos++; } playlen--; } } else while (playlen>0 && pos.pos> VOL_SHIFT; r += (rvol*d) >> VOL_SHIFT; if (r>MAX_VAL) r=MAX_VAL; if (l>MAX_VAL) l=MAX_VAL; if (r=SPEED_FRACTION){ pos.fraction-=SPEED_FRACTION; pos.pos++; } playlen--; } } if (loop && pos.pos>=samples) pos.pos-=samples; else goon=false; } #endif return (playlen>0); } void eWavData::Loop(){ #ifndef DEDICATED Uint8 *buff2=tNEW(Uint8) [len]; if (buff2){ memcpy(buff2,data,len); Uint32 samples; if (spec.format==AUDIO_U8){ samples=len; for(int i=samples-1;i>=0;i--){ Uint32 j=i+((len>>2)<<1); if (j>=len) j-=len; REAL a=fabs(100*(j/REAL(samples)-.5)); if (a>1) a=1; REAL b=1-a; data[i]=int(a*buff2[i]+b*buff2[j]); } } else if (spec.format==AUDIO_S16SYS){ samples=len>>1; for(int i=samples-1;i>=0;i--){ /* REAL a=2*i/REAL(samples); if (a>1) a=2-a; REAL b=1-a; */ Uint32 j=i+((samples>>2)<<1); while (j>=samples) j-=samples; REAL a=fabs(100*(j/REAL(samples)-.5)); if (a>1) a=1; REAL b=1-a; ((short *)data)[i]=int(a*((short *)buff2)[i]+b*((short *)buff2)[j]); } } delete[] buff2; } #endif } // ****************************************************************** void eAudioPos::Reset(int randomize){ #ifndef DEDICATED if (randomize){ fraction=int(SPEED_FRACTION*(rand()/float(RAND_MAX))); pos=int(randomize*(rand()/float(RAND_MAX))); } else fraction=pos=0; #endif } eSoundPlayer::eSoundPlayer(eWavData &w,bool l) :id(-1),wav(&w),loop(l){ if (l) wav->Load(); for(int i=MAX_VIEWERS-1;i>=0;i--) goon[i]=true; } eSoundPlayer::~eSoundPlayer(){} bool eSoundPlayer::Mix(Uint8 *dest, Uint32 len, int viewer, REAL rvol, REAL lvol, REAL speed){ if (goon[viewer]){ if (rvol+lvol>loudness_thresh){ real_sound_sources++; return goon[viewer]=!wav->Mix(dest,len,pos[viewer],rvol,lvol,speed,loop); } else return true; } else return false; } void eSoundPlayer::Reset(int randomize){ wav->Load(); for(int i=MAX_VIEWERS-1;i>=0;i--){ pos[i].Reset(randomize); goon[i]=true; } } void eSoundPlayer::End(){ for(int i=MAX_VIEWERS-1;i>=0;i--){ goon[i]=false; } } void eSoundPlayer::MakeGlobal(){ wav->Load(); se_SoundLock(); se_globalPlayers.Add(this,id); se_SoundUnlock(); } // *************************************************************** uMenu Sound_menu("$sound_menu_text"); static uMenuItemInt sources_men (&Sound_menu,"$sound_menu_sources_text", "$sound_menu_sources_help", sound_sources,2,20,2); static uMenuItemSelection sq_men (&Sound_menu,"$sound_menu_quality_text", "$sound_menu_quality_help", sound_quality); static uSelectEntry a(sq_men, "$sound_menu_quality_off_text", "$sound_menu_quality_off_help", SOUND_OFF); static uSelectEntry b(sq_men, "$sound_menu_quality_low_text", "$sound_menu_quality_low_help", SOUND_LOW); static uSelectEntry c(sq_men, "$sound_menu_quality_medium_text", "$sound_menu_quality_medium_help", SOUND_MED); static uSelectEntry d(sq_men, "$sound_menu_quality_high_text", "$sound_menu_quality_high_help", SOUND_HIGH); static uMenuItemSelection bm_men (&Sound_menu, "$sound_menu_buffer_text", "$sound_menu_buffer_help", buffer_shift); static uSelectEntry ba(bm_men, "$sound_menu_buffer_vsmall_text", "$sound_menu_buffer_vsmall_help", -2); static uSelectEntry bb(bm_men, "$sound_menu_buffer_small_text", "$sound_menu_buffer_small_help", -1); static uSelectEntry bc(bm_men, "$sound_menu_buffer_med_text", "$sound_menu_buffer_med_help", 0); static uSelectEntry bd(bm_men, "$sound_menu_buffer_high_text", "$sound_menu_buffer_high_help", 1); static uSelectEntry be(bm_men, "$sound_menu_buffer_vhigh_text", "$sound_menu_buffer_vhigh_help", 2); void se_SoundMenu(){ // se_SoundPause(true); // se_SoundLock(); int oldsettings=sound_quality; int oldshift=buffer_shift; Sound_menu.Enter(); if (oldsettings!=sound_quality || oldshift!=buffer_shift){ se_SoundExit(); se_SoundInit(); } // se_SoundUnlock(); // se_SoundPause(false); }