Redline/source/sound_ST.cpp

467 lines
14 KiB
C++
Raw Normal View History

#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);
}