238 lines
9.6 KiB
C++
238 lines
9.6 KiB
C++
/*
|
|
* Copyright (C) 2002,2003,2004 Daniel Heck
|
|
* Copyright (C) 2007 Andreas Lochmann
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
#ifndef SOUND_HH
|
|
#define SOUND_HH
|
|
|
|
#include "ecl_math.hh"
|
|
#include "oxydlib/OxydVersion.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
/** ----------- Survey of the formal data structure -----------
|
|
*
|
|
* The wav-files are not to be accessed directly. There are several layers
|
|
* between the wav-data and the final EmitSoundEvent-function, to allow for the
|
|
* following uses:
|
|
* (a) Sound Sets, possibly user defined,
|
|
* (b) Access to oxyd's sound data,
|
|
* (c) Sound damping for loud objects with user defined data,
|
|
* (d) A "Default" mode which switches the sound set between level packs.
|
|
* (e) A "silence string" that can be written instead of playing the sound.
|
|
* For this, six layers of sound information exist.
|
|
* Note: A sound effect is given by a wav-file and several data how to play it.
|
|
* An object may choose a sound effect to play. Then it becomes a sound
|
|
* event (= sound effect + position etc.).
|
|
*
|
|
* (1) SoundEngine
|
|
* -> Providing global information and methods for using SDL.
|
|
* (2) SoundData
|
|
* -> Holding technical details about how to play the wav-files.
|
|
* (3) SoundEffect
|
|
* -> Holding game-related information about how to play a sound
|
|
* effect. This includes the effect's filename, default volume,
|
|
* if it's looped, if it's played on a global scale by default,
|
|
* the default damping information for this event, etc.
|
|
* (4) SoundSet
|
|
* -> Holding the sound set key of a sound set (see below), and if
|
|
* it is connected to Oxyd. It also provides methods to
|
|
* activate a sound set.
|
|
* (5) SoundEvent
|
|
* -> A struct to hold information about a sound event, like the
|
|
* effect's name, position, priority, volume etc.
|
|
* (6) SoundDamping
|
|
* -> Each time an object makes noise, a SoundDamping-record
|
|
* is created with an entry of the objects address (as void *).
|
|
* This class modulates the volume with which the next effect
|
|
* connected to this object is played.
|
|
*
|
|
* Sound events are normally invoked by "EmitSoundEvent(EFFECTNAME, ...)".
|
|
* EFFECTNAME can then be e.g. "pickup" or "laseron". This effect name does not
|
|
* yet define the sound effect completely. Instead, the actually activated sound
|
|
* set is looked up. It holds a string called sound set key (e.g. "Enigma" or
|
|
* "Oxyd*"), and together with the effect name they form the effect key, here
|
|
* "Enigma#pickup" or "Oxyd*#laseron". This effect key is looked up in the sound
|
|
* effect repository and results in a SoundEffect-entry, which is then played.
|
|
*
|
|
*/
|
|
|
|
/** -------------- About the sound damping system --------------
|
|
*
|
|
* The sound damping is not automatically used. Instead, it is called through
|
|
* the "World::getVolume"-function:
|
|
* sound::EmitSoundEvent (NAME, POS, getVolume(NAME, OBJECT_CALLING, DEF_VOLUME))
|
|
* OBJECT_CALLING need not be a real address, it is never dereferenced. It just
|
|
* holds as a representative of the calling object. The SoundDamping-records
|
|
* are evaluated and finally erased by World::tick_sound_dampings.
|
|
*
|
|
* The values used in the sound damping systes can be user defined. In the
|
|
* following, we use the defaults:
|
|
*
|
|
* tick_sound_dampings is only to be evaluated every 10th tick (0.1s).
|
|
* Each damping factor (the ivar connected to the noisy object and its sound
|
|
* effect name) is reduced by 0.9. This is less than the 10th root of 0.5 (0.933),
|
|
* so after 1s the damping factor is reduced by more than one half. If the factor
|
|
* shrinks under 0.5, it is considered 0.
|
|
*
|
|
* Examples:
|
|
* 1) Frequency less than one sound event per 0.6 seconds.
|
|
* Then there is no damping at all.
|
|
* 2) N events per second. For each event, factor (F) is raised by
|
|
* one. And each 0.1 seconds it is multiplied with 0.9. We now
|
|
* have N/10 events per 0.1 seconds, hence in equilibrium f
|
|
* oscillates between
|
|
* f = (f + N/10) * 0.9 => f = N
|
|
* and f + N/10 = N * 1.1 (geometric series!).
|
|
* In particular, for large enough N, f is approximately proportional
|
|
* to N with half-life of less than a second. This is then evaluated
|
|
* in getVolume.
|
|
*
|
|
* World::getVolume returns the volume, if object OBJECT_CALLING wants
|
|
* to play sound effect NAME with default volume DEF_VOLUME. Often played
|
|
* sounds from always the same object are damped to reduce noise-level.
|
|
* Note that OBJECT_CALLING == NULL is explicitly allowed and used e.g.
|
|
* for all laser-sounds. The damping factor is increased by 1.0 for each
|
|
* event, and multiplied with 0.9 each 0.1 seconds, thereby approximately
|
|
* equals the average number of events per second.
|
|
*
|
|
*/
|
|
|
|
/** -------------- Compatibility with 1.00 --------------
|
|
*
|
|
* Enigma 1.01 uses a new option "SoundSetName", which replaces "SoundSet".
|
|
* To enable Enigma 1.00 to run on the same system, the "SoundSet" variable
|
|
* still exists. It is set to 0 (= "Default") for all user sound sets:
|
|
* When exiting 1.01 with a user sound set and starting 1.00, the default
|
|
* sound set will be activated for 1.00. Yet, as "SoundSetName" still shows
|
|
* the old value, 1.01 will use it to find its user sound set. Even if
|
|
* you change the sound set in 1.00, this will have no effect on the
|
|
* chosen sound set for 1.01. In contrast to this, if you change the 1.01
|
|
* sound set, the 1.00 "SoundSet"-variable will be adapted. Except for this
|
|
* "restriction", you can use two different sound sets for 1.00 and 1.01.
|
|
*
|
|
*/
|
|
|
|
namespace sound
|
|
{
|
|
/* -------------------- Data types -------------------- */
|
|
|
|
typedef std::string SoundName;
|
|
|
|
typedef std::vector <unsigned char> ByteVec;
|
|
|
|
struct SoundData {
|
|
ByteVec buf;
|
|
unsigned freq;
|
|
size_t samplesize;
|
|
bool signedp;
|
|
int nchannels;
|
|
};
|
|
|
|
/* -------------------- SoundDampingList ---------------- */
|
|
|
|
/*! This class stores object addresses and assigns volumes to
|
|
sound events, based on the frequency of the event. */
|
|
|
|
struct DampingData {
|
|
double incr;
|
|
double maxi;
|
|
double mult;
|
|
double mini;
|
|
double tick;
|
|
};
|
|
|
|
class SoundDamping {
|
|
private:
|
|
std::string effect_name;
|
|
const void *origin;
|
|
float factor;
|
|
DampingData damp;
|
|
|
|
public:
|
|
SoundDamping(std::string effect_name_, const void *origin_);
|
|
bool is_equal(std::string name2, const void *origin2) {
|
|
return (origin2 == origin) && (effect_name == name2);
|
|
}
|
|
float get_volume(float def_volume);
|
|
bool tick(); // returns true if this entry should be erased
|
|
};
|
|
|
|
/* -------------------- Functions -------------------- */
|
|
|
|
void Init(bool withMusic =true, bool withSound =true);
|
|
void Shutdown();
|
|
|
|
void Tick (double dtime);
|
|
|
|
void TempDisableSound();
|
|
void TempReEnableSound();
|
|
|
|
void SetListenerPosition (const ecl::V2 &pos);
|
|
bool PlaySound (const SoundName &, const ecl::V2 &pos,
|
|
double relative_volume = 1.0, int priority=0);
|
|
bool PlaySoundGlobal (const SoundName &, double relative_volume = 1.0, int priority=0);
|
|
|
|
void PlayMusic (const std::string &name);
|
|
void FadeoutMusic();
|
|
|
|
/*! Stop any music currently playing. */
|
|
void StopMusic();
|
|
|
|
/*! Stop music only if it has the specified name, otherwise
|
|
continue playing. */
|
|
void StopMusic (const std::string &name);
|
|
|
|
void ClearCache();
|
|
void DefineSound (const SoundName &, const SoundData &);
|
|
void SetSoundVolume (double vol);
|
|
void SetMusicVolume (double vol);
|
|
|
|
/*! Helper function for oxyd.cc */
|
|
std::string GetOxydSoundSet(OxydLib::OxydVersion oxyd_ver);
|
|
|
|
/*! Sound set handling */
|
|
void InitSoundSets();
|
|
void SetActiveSoundSet(std::string soundset_name);
|
|
void SetDefaultSoundSet(std::string soundset_name);
|
|
|
|
/*! Define a new sound event. */
|
|
void DefineSoundEffect(std::string soundset_key, std::string name, std::string filename,
|
|
double volume, bool loop, bool global, int priority,
|
|
double damp_max, double damp_inc, double damp_mult,
|
|
double damp_min, double damp_tick, std::string silence_string);
|
|
|
|
/*! Trigger a sound event. Return whether the event was handled. */
|
|
bool EmitSoundEvent (const std::string &eventname,
|
|
const ecl::V2 &pos = ecl::V2 (),
|
|
double volume = 1.0, bool force_global = false);
|
|
bool EmitSoundEventGlobal (const std::string &eventname, double volume = 1.0);
|
|
|
|
/*! Send the silence string of a sound effect to command line. */
|
|
void WriteSilenceString (const std::string &eventname);
|
|
|
|
/*! Helper functions for options menu */
|
|
int GetOptionSoundSetCount();
|
|
int GetOptionSoundSet();
|
|
void SetOptionSoundSet(int value);
|
|
std::string GetOptionSoundSetText(int value);
|
|
}
|
|
|
|
#endif
|