#include #include #include "gamemem.h" #include "vectors.h" #include "entities.h" #include "config.h" #include "gamesound.h" #include "gameinitexit.h" #include "carphysics.h" #include "roads.h" #include "environment.h" #include "random.h" #include "gameframe.h" #include "sky.h" typedef struct{ STSoundRef engine,skid,turbo,noise,hall,wind,horn; tFileRef skidSound; // int enginePlaying,skidPlaying,turboPlaying,noisePlaying,hallPlaying,windPlaying,hornPlaying; } tSoundSource; int gSoundLoopRunnig=false; STSoundRef gSoundLoop; int gThunder; void SoundPause() { ST_SetAppVolume(0); } void SoundInit() { if(gConfig->soundEnable) ST_Open(16,ST_DEFAULT_SAMPLE_RATE); ST_SetAppVolume(gConfig->soundVolume*stMaxVolume); } void SoundReInit() { ST_SetAppVolume(gConfig->soundVolume*stMaxVolume); } #define kReferenceDistance 7 #define kRollOfFactor 0.6 #define kDopplerFactor 1 #define kDopplerVelocity 331.45 //Callback to Loop Sounds void SoundLoopCallback(long theMessage, STSoundCookie soundCookie,struct STSoundSpec *soundSpec) { if(theMessage==stSoundFinished) ST_PlaySoundParam(soundSpec); else if(theMessage==stSoundBumped) soundSpec->refCon=(void*)gFrameCount; } //calls ST_AdjustSound to setup 3d placement and custom volume/pitch of sound void SetupSound(STSoundCookie s,tVector3 pos,tVector3 velo,float vol,float pitch,float prio) { if(!s) return; tVector3 dir=pos-gCameraEntity->pos; if(gConfig->carsOnSpeed) dir=dir+Vector(30*sin(gFrameCount*0.01),10*sin(gFrameCount*0.1),20*sin(gFrameCount*0.008)); float distance=~dir; float panning=0; if(distance) { dir=dir*1/distance; float distanceFactor=kReferenceDistance/(kReferenceDistance+kRollOfFactor*(distance-kReferenceDistance)); vol*=distanceFactor; float listenerSpeed=-dir*gCameraEntity->velo; if(gConfig->carsOnSpeed) listenerSpeed+=90*sin(gFrameCount*0.001); float sourceSpeed=dir*velo; pitch=pitch+pitch*kDopplerFactor*((kDopplerVelocity-listenerSpeed)/(kDopplerVelocity+sourceSpeed)-1); tMatrix3 m; MatrixTranspose(gCameraEntity->dir,m); dir=dir*m; panning=dir.x; } STSoundSpec spec; ST_GetSoundSpec(s,&spec); prio*=vol; if(prio>1)prio=1; spec.priority=prio*0xff; spec.leftVol=0.1*ST_MAX_VOLUME*vol*(1+panning);//*gConfig->soundVolume; spec.rightVol=0.1*ST_MAX_VOLUME*vol*(1-panning);//*gConfig->soundVolume; spec.pitchAdjustment=ST_NORMAL_PITCH/pitch; ST_AdjustSound(s,&spec); } void LoadSound(tFileRef f) { void *data=FileGetDataPtr(f); void **soundData=IndirectInitialize(data,MemoryBlockSize(data)); gFileTable[f].parsedData=MemoryAllocateBlock(sizeof(STSoundRef)); ST_LoadIndirect(soundData,(STSoundRef*)gFileTable[f].parsedData); IndirectDeallocate(soundData); gFileTable[f].parsed=true; } void LoadAllSounds() { for(int i=0;isoundEnable&&!gBackgroundReplay) { if(!gFileTable[f].parsed) LoadSound(f); STSoundSpec spec; spec.sndCookie=NULL; spec.theSound=*((STSoundRef*)gFileTable[f].parsedData); spec.pitchAdjustment=ST_NORMAL_PITCH; spec.STCallBack=loop?SoundLoopCallback:NULL; if(silentStart) { spec.priority=0x00; spec.leftVol=0;//*gConfig->soundVolume; spec.rightVol=0;//*gConfig->soundVolume; } else { spec.priority=0x7f; spec.leftVol=ST_MAX_VOLUME;//*gConfig->soundVolume; spec.rightVol=ST_MAX_VOLUME;//*gConfig->soundVolume; } spec.soundSampleSize=16; spec.refCon=(void*)-1L; return ST_PlaySoundParam(&spec); } else return NULL; } #define kMinSkidGainSlideVelo 0.9 #define kMaxSkidGainSlideVelo 4.0 #define kMinSkidPitchSlideVelo 1.2 #define kMaxSkidPitchSlideVelo 15.0 #define kMaxSoundDistance 120 #define kEchoFadeTime 0.3 #include "text.h" void CarSoundEntity(int i) { if(gBackgroundReplay) return; tGameEntity *carEntity=gCarEntities[i]; tCarPhysics *phys=(tCarPhysics*)carEntity->physics; tCarDefinition *car=&(phys->car); //initialise Sound info, if it doesn't exist. if(!carEntity->soundSource) carEntity->soundSource=MemoryAllocateZeroedBlock(sizeof(tSoundSource)); //if car is far enough away from camera, silence all sound if(sqr(carEntity->pos-gCameraEntity->pos)>sqr(kMaxSoundDistance)) { if(((tSoundSource*)carEntity->soundSource)->skid) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->skid); ((tSoundSource*)carEntity->soundSource)->skid=NULL; } if(((tSoundSource*)carEntity->soundSource)->engine) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->engine); ((tSoundSource*)carEntity->soundSource)->engine=NULL; } if(((tSoundSource*)carEntity->soundSource)->hall) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->hall); ((tSoundSource*)carEntity->soundSource)->hall=NULL; } if(((tSoundSource*)carEntity->soundSource)->wind) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->wind); ((tSoundSource*)carEntity->soundSource)->wind=NULL; } if(((tSoundSource*)carEntity->soundSource)->turbo) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->turbo); ((tSoundSource*)carEntity->soundSource)->turbo=NULL; } if(((tSoundSource*)carEntity->soundSource)->horn) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->horn); ((tSoundSource*)carEntity->soundSource)->horn=NULL; } return; } //officially stop bumped sounds STSoundSpec sp; if(((tSoundSource*)carEntity->soundSource)->skid) { ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->skid,&sp); if(sp.refCon!=(void*)-1) if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) ((tSoundSource*)carEntity->soundSource)->skid=NULL; } if(((tSoundSource*)carEntity->soundSource)->engine) { ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->engine,&sp); if(sp.refCon!=(void*)-1) if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) ((tSoundSource*)carEntity->soundSource)->engine=NULL; } if(((tSoundSource*)carEntity->soundSource)->hall) { ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->hall,&sp); if(sp.refCon!=(void*)-1) if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) ((tSoundSource*)carEntity->soundSource)->hall=NULL; } if(((tSoundSource*)carEntity->soundSource)->wind) { ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->wind,&sp); if(sp.refCon!=(void*)-1) if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) ((tSoundSource*)carEntity->soundSource)->wind=NULL; } if(((tSoundSource*)carEntity->soundSource)->turbo) { ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->turbo,&sp); if(sp.refCon!=(void*)-1) if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) ((tSoundSource*)carEntity->soundSource)->turbo=NULL; } if(((tSoundSource*)carEntity->soundSource)->horn) { ST_GetSoundSpec(((tSoundSource*)carEntity->soundSource)->horn,&sp); if(sp.refCon!=(void*)-1) if(sp.refCon<(void*)(gFrameCount-5*(int)kFPS)) ((tSoundSource*)carEntity->soundSource)->horn=NULL; } //engine sounds: //is the car in a tunnel? int echo=gSurfaceTypes->types[phys->wheels[0].surfaceType].soundEcho; //fade echo status if(phys->echoecho+=kFrameTime*(1/kEchoFadeTime); if(phys->echo>echo) phys->echo=echo; } if(phys->echo>echo) { phys->echo-=kFrameTime*(1/kEchoFadeTime); if(phys->echoecho=echo; } //if engine sound isn't running and echo<1, start engine sound if(!((tSoundSource*)carEntity->soundSource)->engine&&phys->echo<1.0) ((tSoundSource*)carEntity->soundSource)->engine=PlaySound(car->engineSample,true,true); //if halled engine sound isn't running and echo<1, start engine sound if(!((tSoundSource*)carEntity->soundSource)->hall&&phys->echo>0.0) ((tSoundSource*)carEntity->soundSource)->hall=PlaySound(car->hallSample,true,true); if(((tSoundSource*)carEntity->soundSource)->engine&&phys->echo>=1.0) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->engine); ((tSoundSource*)carEntity->soundSource)->engine=NULL; } if(((tSoundSource*)carEntity->soundSource)->hall&&phys->echo<=0.0) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->hall); ((tSoundSource*)carEntity->soundSource)->hall=false; } //Setup sound pitch/volume float rpm=(phys->rpm-car->jerkRPM)/(car->maxRPM-car->jerkRPM); float rpmGain=(car->zeroRPMSoundGain+rpm*(car->fullRPMSoundGain-car->zeroRPMSoundGain)); float throttleGain=(car->zeroThrottleSoundGain+phys->throttle*(car->fullThrottleSoundGain-car->zeroThrottleSoundGain)); float rpmPitch=(car->zeroRPMSoundPitch+rpm*(car->fullRPMSoundPitch-car->zeroRPMSoundPitch)); float throttlePitch=(car->zeroThrottleSoundPitch+phys->throttle*(car->fullThrottleSoundPitch-car->zeroThrottleSoundPitch)); if(((tSoundSource*)carEntity->soundSource)->engine) SetupSound(((tSoundSource*)carEntity->soundSource)->engine,carEntity->pos,carEntity->velo,1.5*rpmGain*throttleGain*(1.0-phys->echo),rpmPitch*throttlePitch,1); if(((tSoundSource*)carEntity->soundSource)->hall) SetupSound(((tSoundSource*)carEntity->soundSource)->hall,carEntity->pos,carEntity->velo,1.5*rpmGain*throttleGain*(phys->echo),rpmPitch*throttlePitch,1); //skid float slideVelo=0; for(int i=0;inumWheels;i++) slideVelo+=gSurfaceTypes->types[phys->wheels[i].surfaceType].soundEnable?phys->wheels[i].slipVelo:0; slideVelo/=car->numWheels; float skidGain=(slideVelo-kMinSkidGainSlideVelo)/(kMaxSkidGainSlideVelo-kMinSkidGainSlideVelo); if(!gSurfaceTypes->types[phys->wheels[0].surfaceType].soundEnable) skidGain=0; if(((tSoundSource*)carEntity->soundSource)->skid&&((tSoundSource*)carEntity->soundSource)->skidSound!=gSurfaceTypes->types[phys->wheels[0].surfaceType].skidSound) skidGain=0; if(skidGain<=0){ if(((tSoundSource*)carEntity->soundSource)->skid) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->skid); ((tSoundSource*)carEntity->soundSource)->skid=NULL; } } else if(!((tSoundSource*)carEntity->soundSource)->skid) { ((tSoundSource*)carEntity->soundSource)->skid=PlaySound(gSurfaceTypes->types[phys->wheels[0].surfaceType].skidSound,true,true); ((tSoundSource*)carEntity->soundSource)->skidSound=gSurfaceTypes->types[phys->wheels[0].surfaceType].skidSound; } if(((tSoundSource*)carEntity->soundSource)->skid) { if(skidGain<0)skidGain=0; if(skidGain>1)skidGain=1; float skidPitch=(slideVelo-kMinSkidPitchSlideVelo)/(kMaxSkidPitchSlideVelo-kMinSkidPitchSlideVelo); if(skidPitch<0)skidPitch=0; if(skidPitch>1)skidPitch=1; SetupSound(((tSoundSource*)carEntity->soundSource)->skid,carEntity->pos,carEntity->velo,skidGain*2,(0.85+0.4*skidPitch),1); } //wind sounds (player Car only) if(carEntity!=gViewedEntity) { if(((tSoundSource*)carEntity->soundSource)->wind) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->wind); ((tSoundSource*)carEntity->soundSource)->wind=NULL; } } else { if(!((tSoundSource*)carEntity->soundSource)->wind) ((tSoundSource*)carEntity->soundSource)->wind=PlaySound(FileGetReference("wind.wav"),true,true); float velo=~carEntity->velo; float windGain=velo/60.0; float windPitch=velo/100; if(windGain>1)windGain=1; windGain*=1-CalcDraftFactor(carEntity); if(windPitch>1)windPitch=1; SetupSound(((tSoundSource*)carEntity->soundSource)->wind,carEntity->pos,carEntity->velo,windGain*2.5,1.0+0.6*windPitch,1); } //turbo sounds if(car->turboGain) { if(!((tSoundSource*)carEntity->soundSource)->turbo) ((tSoundSource*)carEntity->soundSource)->turbo=PlaySound(car->turboSample,true,true); float turboAccel=(phys->rpm/car->maxRPM)*(0.3*phys->throttle+0.7)-0.1; phys->turboRPM+=turboAccel*kFrameTime*3; if(phys->turboRPM<0)phys->turboRPM=0; float maxTurbo=(phys->rpm/car->maxRPM)*(0.5*phys->throttle+0.5); if(phys->turboRPM>maxTurbo)phys->turboRPM-=(turboAccel>0?turboAccel*kFrameTime*3:0)+0.5*kFrameTime; float turboGain=phys->turboRPM; float turboPitch=phys->turboRPM; if(turboGain>1)turboGain=1; if(turboPitch>1)turboPitch=1; SetupSound(((tSoundSource*)carEntity->soundSource)->turbo,carEntity->pos,carEntity->velo,turboGain*car->turboGain*1.5,0.3+0.3*turboPitch,1); } //horn if(!(phys->lightFlags&kLightFlagHorn)){ if(((tSoundSource*)carEntity->soundSource)->horn) { ST_HaltSound(((tSoundSource*)carEntity->soundSource)->horn); ((tSoundSource*)carEntity->soundSource)->horn=NULL; } } else if(!((tSoundSource*)carEntity->soundSource)->horn) { tFileRef hornRef=car->hornSample; if(!hornRef) hornRef=FileGetReference("horn.wav"); ((tSoundSource*)carEntity->soundSource)->horn=PlaySound(hornRef,true,true); } if(((tSoundSource*)carEntity->soundSource)->horn) SetupSound(((tSoundSource*)carEntity->soundSource)->horn,carEntity->pos,carEntity->velo,2,car->hornPitch+1,10); } void SoundFrame() { if(gConfig->soundEnable) { //for each car process car's sound for(int i=0;inumPlayers;i++) CarSoundEntity(i); //environmental sounds (rain, thunder) if(gEnvironment->soundLoopEnable) { if(!gSoundLoopRunnig) { gSoundLoopRunnig=true; gSoundLoop=PlaySound(gEnvironment->soundLoop,true,false); } SetupSound(gSoundLoop,gCameraEntity->pos,gCameraEntity->velo,0.5+0.2*sin(gFrameCount*kFrameTime*0.5),1,10); } if(gEnvironment->soundRandomEnable) { if(gLightning) gThunder=gFrameCount; if(gFrameCount==gThunder+4*kFPS) SetupSound(PlaySound(FileGetIndexedReference(gEnvironment->soundRandom,RandomInt(0,3)),false,false),gCameraEntity->pos,gCameraEntity->velo,RandomFl(0.5,1.5),1,1); } } } //silence all sounds void SoundSilence() { if(gConfig->soundEnable&&!gBackgroundReplay) ST_HaltSound(nil); gSoundLoopRunnig=false; } void SoundFreeEntitySources(tGameEntity *entity) { } //plays a sound from a car's location void CarPlayCrashNoise(tGameEntity *carEntity,tFileRef sound,float volume) { if(gConfig->soundEnable&&!gBackgroundReplay&&carEntity->soundSource) { int isSoundPlaying=true; if(!((tSoundSource*)carEntity->soundSource)->noise) isSoundPlaying=false; else if(!ST_IsSoundPlaying(((tSoundSource*)carEntity->soundSource)->noise)) isSoundPlaying=false; if(!isSoundPlaying) { ((tSoundSource*)carEntity->soundSource)->noise=PlaySound(sound,false,false); SetupSound(((tSoundSource*)carEntity->soundSource)->noise,carEntity->pos,carEntity->velo,volume,1,2); } } } //simple sound playback for user interface. void PlayInterfaceSound(tFileRef sound) { if(gConfig->interfaceSounds) PlaySound(sound,false,false); }