02061d74c2
(as received from Jonas Echterhoff)
467 lines
14 KiB
C++
467 lines
14 KiB
C++
#include <sound_tool.h>
|
|
#include <math.h>
|
|
#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;i<gFileTableSize;i++)
|
|
if(char *extension=FileGetExtension(i))
|
|
// if(!_stricmp(extension,kFileTypeWAV))
|
|
if(!_stricmp(extension,".ima"))
|
|
LoadSound(i);
|
|
}
|
|
|
|
//start playing a sound. if loop is set, use SoundLoopCallback() to loop sound.
|
|
STSoundCookie PlaySound(tFileRef f,int loop,int silentStart)
|
|
{
|
|
if(gConfig->soundEnable&&!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->echo<echo)
|
|
{
|
|
phys->echo+=kFrameTime*(1/kEchoFadeTime);
|
|
if(phys->echo>echo)
|
|
phys->echo=echo;
|
|
}
|
|
if(phys->echo>echo)
|
|
{
|
|
phys->echo-=kFrameTime*(1/kEchoFadeTime);
|
|
if(phys->echo<echo)
|
|
phys->echo=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;i<car->numWheels;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;i<gGameInfo->numPlayers;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);
|
|
}
|
|
|