#ifndef __SOUND__ #define __SOUND__ // Resolution of sound shakes in fps #define SHAKE_FPS 30 /* =============================================================================== SOUND WORLD IDS =============================================================================== */ #define SOUNDWORLD_ANY -1 #define SOUNDWORLD_NONE 0 #define SOUNDWORLD_GAME 1 #define SOUNDWORLD_MENU 2 #define SOUNDWORLD_EDITOR 3 #define SOUNDWORLD_MAX 4 /* =============================================================================== SOUND SHADER DECL =============================================================================== */ // RAVEN BEGIN class rvCommonSample { public: rvCommonSample( void ); virtual ~rvCommonSample( void ) {} virtual const byte *GetSampleData( void ) const { return( NULL ); } virtual int GetNumChannels( void ) const { return( 0 ); } virtual int GetNumSamples( void ) const { return( 0 ); } virtual int GetSampleRate( void ) const { return( 0 ); } virtual int GetMemoryUsed( void ) const { return( 0 ); } virtual int GetDurationMS( void ) const { return( 0 ); } virtual float GetDuration( void ) const { return( 0.0f ); } virtual void Load( int langIndex = -1 ) {} virtual void PurgeSoundSample( void ) {} virtual bool IsOgg( void ) const { return false; } // is false for an expanded ogg virtual void Expand( bool force ) {} // expand oggs to pcm void SetReferencedThisLevel( void ) { levelLoadReferenced = true; } idStr name; // name of the sample file unsigned int timestamp; // the most recent of all images used in creation, for reloadImages command bool defaultSound; // error during loading, now a beep bool purged; bool levelLoadReferenced; // so we can tell which samples aren't needed any more // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation #if defined(_RV_MEM_SYS_SUPPORT) bool referencedOutsideLevelLoad; #endif // RAVEN END }; // RAVEN END // sound shader flags static const int SSF_PRIVATE_SOUND = BIT(0); // only plays for the current listenerId static const int SSF_ANTI_PRIVATE_SOUND =BIT(1); // plays for everyone but the current listenerId static const int SSF_NO_OCCLUSION = BIT(2); // don't flow through portals, only use straight line static const int SSF_GLOBAL = BIT(3); // play full volume to all speakers and all listeners static const int SSF_OMNIDIRECTIONAL = BIT(4); // fall off with distance, but play same volume in all speakers static const int SSF_LOOPING = BIT(5); // repeat the sound continuously static const int SSF_PLAY_ONCE = BIT(6); // never restart if already playing on any channel of a given emitter static const int SSF_UNCLAMPED = BIT(7); // don't clamp calculated volumes at 1.0 static const int SSF_NO_FLICKER = BIT(8); // always return 1.0 for volume queries static const int SSF_NO_DUPS = BIT(9); // try not to play the same sound twice in a row // RAVEN BEGIN static const int SSF_USEDOPPLER = BIT(10); // allow doppler pitch shifting effects static const int SSF_NO_RANDOMSTART = BIT(11); // don't offset the start position for looping sounds static const int SSF_VO_FOR_PLAYER = BIT(12); // Notifies a funcRadioChatter that this shader is directed at the player static const int SSF_IS_VO = BIT(13); // this sound is VO static const int SSF_CAUSE_RUMBLE = BIT(14); // causes joystick rumble static const int SSF_CENTER = BIT(15); // sound through center channel only static const int SSF_HILITE = BIT(16); // display debug info for this emitter // RAVEN END // these options can be overriden from sound shader defaults on a per-emitter and per-channel basis typedef struct { float minDistance; float maxDistance; float volume; float attenuatedVolume; float shakes; int soundShaderFlags; // SSF_* bit flags int soundClass; // for global fading of sounds // RAVEN BEGIN // bdube: frequency shift float frequencyShift; float wetLevel; float dryLevel; // RAVEN END } soundShaderParms_t; const int SOUND_MAX_LIST_WAVS = 32; // sound classes are used to fade most sounds down inside cinematics, leaving dialog // flagged with a non-zero class full volume const int SOUND_CLASS_MUSICAL = 3; const int SOUND_MAX_CLASSES = 4; // it is somewhat tempting to make this a virtual class to hide the private // details here, but that doesn't fit easily with the decl manager at the moment. // RAVEN BEGIN // jsinger: added to support serialization/deserialization of binary decls #ifdef RV_BINARYDECLS class idSoundShader : public idDecl, public Serializable<'ISS '> { public: virtual void Write( SerialOutputStream &stream ) const; virtual void AddReferences() const; idSoundShader( SerialInputStream &stream ); #else class idSoundShader : public idDecl { #endif public: idSoundShader( void ) { Init(); } virtual ~idSoundShader( void ) {} virtual size_t Size( void ) const; virtual bool SetDefaultText( void ); virtual const char * DefaultDefinition( void ) const; virtual bool Parse( const char *text, const int textLength, bool noCaching ); virtual void FreeData( void ); // RAVEN BEGIN virtual void SetReferencedThisLevel( void ); // RAVEN END virtual void List( void ) const; virtual const char * GetDescription( void ) const { return( desc ); } // so the editor can draw correct default sound spheres // this is currently defined as meters, which sucks, IMHO. virtual float GetMinDistance( void ) const { return( parms.minDistance ); } virtual float GetMaxDistance( void ) const { return( parms.maxDistance ); } // RAVEN BEGIN // scork: for detailed error-reporting virtual bool Validate( const char *psText, int iTextLength, idStr &strReportTo ) const; // jscott: implemented id's code virtual bool RebuildTextSource( void ); // jscott: required access functions bool IsPrivateSound( void ) const { return( !!( parms.soundShaderFlags & SSF_PRIVATE_SOUND ) ); } bool IsAntiPrivateSound( void ) const { return( !!( parms.soundShaderFlags & SSF_ANTI_PRIVATE_SOUND ) ); } bool IsNoOcclusion( void ) const { return( !!( parms.soundShaderFlags & SSF_NO_OCCLUSION ) ); } bool IsGlobal( void ) const { return( !!( parms.soundShaderFlags & SSF_GLOBAL ) ); } bool IsOmnidirectional( void ) const { return( !!( parms.soundShaderFlags & SSF_OMNIDIRECTIONAL ) ); } bool IsLooping( void ) const { return( !!( parms.soundShaderFlags & SSF_LOOPING ) ); } bool IsPlayOnce( void ) const { return( !!( parms.soundShaderFlags & SSF_PLAY_ONCE ) ); } bool IsUnclamped( void ) const { return( !!( parms.soundShaderFlags & SSF_UNCLAMPED ) ); } bool IsNoFlicker( void ) const { return( !!( parms.soundShaderFlags & SSF_NO_FLICKER ) ); } bool IsNoDupes( void ) const { return( !!( parms.soundShaderFlags & SSF_NO_DUPS ) ); } bool IsDoppler( void ) const { return( !!( parms.soundShaderFlags & SSF_USEDOPPLER ) ); } bool IsNoRandomStart( void ) const { return( !!( parms.soundShaderFlags & SSF_NO_RANDOMSTART ) ); } bool IsVO_ForPlayer( void ) const { return( !!( parms.soundShaderFlags & SSF_VO_FOR_PLAYER ) ); } bool IsVO( void ) const { return( !!( parms.soundShaderFlags & SSF_IS_VO ) ); } bool IsCauseRumble( void ) const { return( !!( parms.soundShaderFlags & SSF_CAUSE_RUMBLE ) ); } bool IsCenter( void ) const { return( !!( parms.soundShaderFlags & SSF_CENTER ) ); } float GetVolume( void ) const { return( parms.volume ); } float GetShakes( void ) const { return( parms.shakes ); } float GetTimeLength( void ) const; void GetParms( soundShaderParms_t *out ) const { *out = parms; } void SetNoShakes( bool in ) { noShakes = in; } bool GetNoShakes( void ) const { return( noShakes ); } void IncPlayCount( void ) { playCount++; } int GetPlayCount( void ) const { return( playCount ); } // RAVEN END float GetLeadinVolume( void ) const { return( leadinVolume ); } int GetNumEntries( void ) const { return( numEntries ); } rvCommonSample *GetLeadin( int index ) const { return( leadins[index] ); } rvCommonSample *GetEntry( int index ) const { return( entries[index] ); } const char *GetShakeData( int index ) const; void SetShakeData( int index, const char *ampData ); void Purge( bool freeBaseBlocks ); void LoadSampleData( int langIndex = -1 ); const char * GetSampleName( int index ) const; int GetSamplesPerSec( int index ) const; int GetNumChannels( int index ) const; int GetNumSamples( int index ) const; int GetMemorySize( int index ) const; const byte * GetNonCacheData( int index ) const; void ExpandSmallOggs( bool force ); // RAVEN END // returns NULL if an AltSound isn't defined in the shader. // we use this for pairing a specific broken light sound with a normal light sound virtual const idSoundShader *GetAltSound( void ) const { return( altSound ); } virtual bool HasDefaultSound( void ) const; virtual const soundShaderParms_t *GetParms( void ) const { return( &parms ); } virtual int GetNumSounds( void ) const; virtual const char * GetSound( int index ) const; private: friend class idSoundEmitterLocal; friend class idSoundChannel; friend class idSoundCache; // options from sound shader text soundShaderParms_t parms; // can be overriden on a per-channel basis const idSoundShader * altSound; idStr desc; // description bool errorDuringParse; // RAVEN BEGIN bool noShakes; // Don't generate shake data bool frequentlyUsed; // Expand this to pcm data no matter how long it is // RAVEN END float leadinVolume; // allows light breaking leadin sounds to be much louder than the broken loop // RAVEN BEGIN rvCommonSample * leadins[SOUND_MAX_LIST_WAVS]; // RAVEN END int numLeadins; // RAVEN BEGIN rvCommonSample * entries[SOUND_MAX_LIST_WAVS]; idStrList shakes; // RAVEN END int numEntries; // RAVEN BEGIN // bdube: frequency shift code from splash float minFrequencyShift; float maxFrequencyShift; int playCount; // For profiling // RAVEN END private: void Init( void ); bool ParseShader( idLexer &src ); }; class rvSoundShaderEdit { public: virtual ~rvSoundShaderEdit() {} virtual const char * GetSampleName( const idSoundShader *sound, int index ) const = 0; virtual int GetSamplesPerSec( const idSoundShader *sound, int index ) const = 0; virtual int GetNumChannels( const idSoundShader *sound, int index ) const = 0; virtual int GetNumSamples( const idSoundShader *sound, int index ) const = 0; virtual int GetMemorySize( const idSoundShader *sound, int index ) const = 0; virtual const byte * GetNonCacheData( const idSoundShader *sound, int index ) const = 0; virtual void LoadSampleData( idSoundShader *sound, int langIndex = -1 ) = 0; virtual void ExpandSmallOggs( idSoundShader *sound, bool force ) = 0; virtual const char *GetShakeData( idSoundShader *sound, int index ) = 0; virtual void SetShakeData( idSoundShader *sound, int index, const char *ampData ) = 0; virtual void Purge( idSoundShader *sound, bool freeBaseBlocks ) = 0; }; extern rvSoundShaderEdit *soundShaderEdit; /* =============================================================================== SOUND EMITTER =============================================================================== */ // sound channels static const int SCHANNEL_ANY = 0; // used in queries and commands to effect every channel at once, in // startSound to have it not override any other channel static const int SCHANNEL_ONE = 1; // any following integer can be used as a channel number typedef int s_channelType; // the game uses its own series of enums, and we don't want to require casts class idSoundEmitter { public: virtual ~idSoundEmitter( void ) {} // the parms specified will be the default overrides for all sounds started on this emitter. // NULL is acceptable for parms virtual void UpdateEmitter( const idVec3 &origin, const idVec3 &velocity, int listenerId, const soundShaderParms_t *parms ) = 0; // returns the length of the started sound in msec virtual int StartSound( const idSoundShader *shader, const s_channelType channel, float diversity = 0.0f, int shaderFlags = 0 ) = 0; // pass SCHANNEL_ANY to effect all channels virtual void ModifySound( const s_channelType channel, const soundShaderParms_t *parms ) = 0; virtual void StopSound( const s_channelType channel ) = 0; // to is in Db (sigh), over is in seconds virtual void FadeSound( const s_channelType channel, float to, float over ) = 0; // returns true if there are any sounds playing from this emitter. There is some conservative // slop at the end to remove inconsistent race conditions with the sound thread updates. // FIXME: network game: on a dedicated server, this will always be false virtual bool CurrentlyPlaying( const s_channelType channel = SCHANNEL_ANY ) const = 0; // returns a 0.0 to 1.0 value based on the current sound amplitude, allowing // graphic effects to be modified in time with the audio. // just samples the raw wav file, it doesn't account for volume overrides in the virtual float CurrentAmplitude( int channelFlags = -1, bool factorDistance = false ) = 0; // Returns true if the emitter is in the passed in world virtual bool AttachedToWorld( int id ) const = 0; // for save games. Index will always be > 0 virtual int Handle( void ) const = 0; }; /* =============================================================================== SOUND SYSTEM =============================================================================== */ typedef struct { idStr name; idStr format; int numChannels; int numSamplesPerSecond; int num44kHzSamples; int numBytes; bool looping; float lastVolume; int start44kHzTime; int current44kHzTime; } soundDecoderInfo_t; typedef struct soundPortalTrace_s { int portalArea; const struct soundPortalTrace_s *prevStack; } soundPortalTrace_t; class idSoundSystem { public: virtual ~idSoundSystem( void ) {} // all non-hardware initialization virtual void Init( void ) = 0; // shutdown routine virtual void Shutdown( void ) = 0; // call ClearBuffer if there is a chance that the AsyncUpdate won't get called // for 20+ msec, which would cause a stuttering repeat of the current // buffer contents virtual void ClearBuffer( void ) = 0; // sound is attached to the window, and must be recreated when the window is changed virtual bool InitHW( void ) = 0; virtual void InitVoiceComms( void ) = 0; virtual bool ShutdownHW( void ) = 0; virtual void ShutdownVoiceComms( void ) = 0; // Called once per common frame to check on changes to the sound system virtual void Frame( void ) = 0; // Service the sound system virtual void ForegroundUpdate( void ) = 0; // asyn loop, called at 60Hz virtual int AsyncUpdate( int time ) = 0; // direct mixing for OSes that support it virtual int AsyncMix( int soundTime, float *mixBuffer ) = 0; // async loop, when the sound driver uses a write strategy virtual int AsyncUpdateWrite( int time ) = 0; // it is a good idea to mute everything when starting a new level, // because sounds may be started before a valid listener origin // is specified virtual void SetMute( bool mute ) = 0; // for the sound level meter window virtual cinData_t ImageForTime( const int milliseconds, const bool waveform ) = 0; // get sound decoder info virtual int GetSoundDecoderInfo( int index, soundDecoderInfo_t &decoderInfo ) = 0; // Mark all soundSamples as currently unused, // but don't free anything. virtual void BeginLevelLoad( const char *mapName ) = 0; // Free all soundSamples marked as unused // We might want to defer the loading of new sounds to this point, // as we do with images, to avoid having a union in memory at one time. virtual void EndLevelLoad( const char *mapName ) = 0; // RAVEN BEGIN // jnewquist: Free all sounds #ifdef _XENON virtual void FlushLevelSoundSamples( void ) = 0; #endif // RAVEN END // Frees the empty base blocks in the appropriate soundCache virtual void CleanCache( void ) = 0; // prints memory info virtual void PrintMemInfo( MemInfo *mi ) = 0; #ifdef _USE_OPENAL // is EAX support present - -1: disabled at compile time. 0: no suitable hardwre 1: ok virtual int IsEAXAvailable( void ) = 0; virtual const char * GetDeviceName( int index ) = 0; virtual const char * GetDefaultDeviceName( void ) = 0; #endif // SoundWorld stuff // call at each map start virtual void SetRenderWorld( idRenderWorld *rw ) = 0; virtual void StopAllSounds( int worldId ) = 0; // RAVEN BEGIN // dissociate all virtual channels from hardware virtual void DisableAllSounds( void ) = 0; // get a new emitter that can play sounds in this world virtual int AllocSoundEmitter( int worldId ) = 0; virtual void FreeSoundEmitter( int worldId, int handle, bool immediate ) = 0; // RAVEN END // for load games, index 0 will return NULL virtual idSoundEmitter *EmitterForIndex( int worldId, int index ) = 0; virtual int GetNumEmitters( void ) const = 0; // query sound samples from all emitters reaching a given position virtual float CurrentShakeAmplitudeForPosition( int worldId, const int time, const idVec3 &listenerPosition ) = 0; // where is the camera/microphone // listenerId allows listener-private and antiPrivate sounds to be filtered // gameTime is in msec, and is used to time sound queries and removals so that they are independent // of any race conditions with the async update virtual void PlaceListener( const idVec3 &origin, const idMat3 &axis, const int listenerId, const int gameTime, const idStr &areaName ) = 0; // reset the listener portal to invalid during level transitions virtual void ResetListener( void ) = 0; // fade all sounds in the world with a given shader soundClass // to is in Db (sigh), over is in seconds virtual void FadeSoundClasses( int worldId, const int soundClass, float to, const float over ) = 0; // background music virtual void PlayShaderDirectly( int worldId, const char *name, int channel = -1 ) = 0; // dumps the current state and begins archiving commands virtual void StartWritingDemo( int worldId, idDemoFile *demo ) = 0; virtual void StopWritingDemo( int worldId ) = 0; // read a sound command from a demo file virtual void ProcessDemoCommand( int worldId, idDemoFile *demo ) = 0; virtual int GetHardwareTime( void ) const = 0; // unpauses the selected soundworld, pauses all others virtual int SetActiveSoundWorld( bool on ) = 0; // RAVEN BEGIN // jnewquist: Accessor for active sound world virtual int GetActiveSoundWorld( void ) = 0; // RAVEN END // Write the sound output to multiple wav files. Note that this does not use the // work done by AsyncUpdate, it mixes explicitly in the foreground every PlaceOrigin(), // under the assumption that we are rendering out screenshots and the gameTime is going // much slower than real time. // path should not include an extension, and the generated filenames will be: // _left.raw, _right.raw, or _51left.raw, _51right.raw, // _51center.raw, _51lfe.raw, _51backleft.raw, _51backright.raw, // If only two channel mixing is enabled, the left and right .raw files will also be // combined into a stereo .wav file. virtual void AVIOpen( int worldId, const char *path, const char *name ) = 0; virtual void AVIClose( int worldId ) = 0; // SaveGame / demo Support virtual void WriteToSaveGame( int worldId, idFile *savefile ) = 0; virtual void ReadFromSaveGame( int worldId, idFile *savefile ) = 0; // RAVEN BEGIN // rjohnson: added list active sounds virtual void ListActiveSounds( int worldId ) = 0; // RAVEN END // End SoundWorld stuff // RAVEN BEGIN // jscott: added virtual size_t ListSoundSummary( void ) = 0; virtual bool HasCache( void ) const = 0; virtual rvCommonSample *FindSample( const idStr &filename ) = 0; virtual int SamplesToMilliseconds( int samples ) const = 0; virtual void * AllocSoundSample( int size ) = 0; virtual void FreeSoundSample( const byte *address ) = 0; virtual bool GetInsideLevelLoad( void ) const = 0; virtual bool ValidateSoundShader( idSoundShader *shader ) = 0; // jscott: voice comm support virtual bool EnableRecording( bool enable, bool test, float &micLevel ) = 0; virtual int GetVoiceData( byte *buffer, int maxSize ) = 0; virtual void PlayVoiceData( int clientNum, const byte *buffer, int bytes ) = 0; virtual void BufferVoiceData( void ) = 0; virtual void MixVoiceData( float *finalMixBuffer, int numSpeakers, int newTime ) = 0; // ddynerman: voice comm utility virtual int GetCommClientNum( int channel ) const = 0; virtual int GetNumVoiceChannels( void ) const = 0; // jscott: reverb editor support virtual const char *GetReverbName( int reverb ) = 0; virtual int GetNumAreas( void ) = 0; virtual int GetReverb( int area ) = 0; virtual bool SetReverb( int area, const char *reverbName, const char *fileName ) = 0; virtual void EndCinematic() = 0; // RAVEN END }; extern idSoundSystem *soundSystem; void S_InitSoundSystem( void ); #endif /* !__SOUND__ */