352 lines
12 KiB
C
352 lines
12 KiB
C
/**
|
|
* =========================================================================
|
|
* File : snd_mgr.h
|
|
* Project : 0 A.D.
|
|
* Description : OpenAL sound engine. handles sound I/O, buffer
|
|
* : suballocation and voice management/prioritization.
|
|
*
|
|
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
|
* =========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2004-2005 Jan Wassenberg
|
|
*
|
|
* Redistribution and/or modification are also permitted under the
|
|
* terms of the GNU General Public License as published by the
|
|
* Free Software Foundation (version 2 or later, at your option).
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef SND_MGR_H__
|
|
#define SND_MGR_H__
|
|
|
|
#include "lib/res/handle.h"
|
|
|
|
/**
|
|
|
|
overview
|
|
--------
|
|
|
|
this module provides a moderately high-level sound interface. basic usage
|
|
is opening a sound and requesting it be played; it is closed automatically
|
|
when playback has finished (fire and forget).
|
|
any number of sound play requests may be issued; the most 'important' ones
|
|
are actually played (necessary due to limited hardware mixing capacity).
|
|
3d positional sounds (heard to emanate from a given spot) are supported.
|
|
active sound instances are referenced by Handles, so changing volume etc.
|
|
during playback is possible (useful for fadeout).
|
|
|
|
|
|
sound setup
|
|
-----------
|
|
|
|
OpenAL provides portable access to the underlying sound hardware, and
|
|
falls back to software mixing if no acceleration is provided.
|
|
we allow the user to specify the device to use (in case the default
|
|
has problems) and maximum number of sources (to reduce mixing cost).
|
|
|
|
|
|
performance
|
|
-----------
|
|
much effort has been invested in efficiency: all sound data is cached,
|
|
so every open() after the first is effectively free. large sound files are
|
|
streamed from disk to reduce load time and memory usage. hardware mixing
|
|
resources are suballocated to avoid delays when starting to play.
|
|
therefore, the user can confidently fire off hundreds of sound requests.
|
|
finally, lengthy initialization steps are delayed until the sound engine
|
|
is actually needed (i.e. upon first open()). perceived startup time is
|
|
therefore reduced - the user sees e.g. our main menu earlier.
|
|
|
|
|
|
terminology
|
|
-----------
|
|
|
|
"hardware voice" refers to mixing resources on the DSP. strictly
|
|
speaking, we mean 'OpenAL source', but this term is more clear.
|
|
voice ~= source, unless expensive effects (e.g. EAX) are enabled.
|
|
note: software mixing usually doesn't have a fixed 'source' cap.
|
|
"gain" is quantified volume. 1 is unattenuated, 0.5 corresponds to -6 dB,
|
|
and 0 is silence. this can be set per-source as well as globally.
|
|
"position" of a sound is within the app's coordinate system,
|
|
the orientation of which is passed to snd_update.
|
|
"importance" of a sound derives from the app-assigned priority
|
|
(e.g. voiceover must not be skipped in favor of seagulls) and
|
|
distance from the listener. it's calculated by our prioritizer.
|
|
"virtual source" denotes a sound play request issued by the app.
|
|
this is in contrast to an actual AL source, which will be mixed
|
|
into the output channel. the most important VSrc receive an al_src.
|
|
"sound instances" store playback parameters (e.g. position), and
|
|
reference the (centrally cached) "sound data" that will be played.
|
|
|
|
**/
|
|
|
|
|
|
//
|
|
// device enumeration
|
|
//
|
|
|
|
/**
|
|
* prepare to enumerate all device names (this resets the list returned by
|
|
* snd_dev_next).
|
|
* may be called each time the device list is needed.
|
|
*
|
|
* @return LibError; fails iff the requisite OpenAL extension isn't available.
|
|
* in that case, a "cannot enum device" message should be displayed, but
|
|
* snd_dev_set need not be called; OpenAL will use its default device.
|
|
**/
|
|
extern LibError snd_dev_prepare_enum();
|
|
|
|
/**
|
|
* get next device name in list.
|
|
*
|
|
* do not call unless snd_dev_prepare_enum succeeded!
|
|
* not thread-safe! (static data from snd_dev_prepare_enum is used)
|
|
*
|
|
* @return device name string, or 0 if all have been returned.
|
|
**/
|
|
extern const char* snd_dev_next();
|
|
|
|
|
|
//
|
|
// sound system setup
|
|
//
|
|
|
|
/**
|
|
* tell OpenAL to use the specified device in future.
|
|
*
|
|
* @param alc_new_dev_name device name string. if 0, revert to
|
|
* OpenAL's default choice, which will also be used if
|
|
* this routine is never called.
|
|
* the device name is typically taken from a config file at init-time;
|
|
* the snd_dev* enumeration routines above are used to present a list
|
|
* of choices to the user in the options screen.
|
|
*
|
|
* if OpenAL hasn't yet been initialized (i.e. no sounds have been opened),
|
|
* this just stores the device name for use when init does occur.
|
|
* note: we can't check now if it's invalid (if so, init will fail).
|
|
* otherwise, we shut OpenAL down (thereby stopping all sounds) and
|
|
* re-initialize with the new device. that's fairly time-consuming,
|
|
* so preferably call this routine before sounds are loaded.
|
|
*
|
|
* @return LibError (the status returned by OpenAL re-init)
|
|
**/
|
|
extern LibError snd_dev_set(const char* alc_new_dev_name);
|
|
|
|
/**
|
|
* set maximum number of voices to play simultaneously;
|
|
* this can be used to reduce mixing cost on low-end systems.
|
|
*
|
|
* @param cap maximum number of voices. ignored if higher than
|
|
* an implementation-defined limit anyway.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_set_max_voices(uint cap);
|
|
|
|
/**
|
|
* set amplitude modifier, which is effectively applied to all sounds.
|
|
* this is akin to a global "volume" control.
|
|
*
|
|
* @param gain amplitude modifier. must be non-negative;
|
|
* 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_set_master_gain(float gain);
|
|
|
|
|
|
//
|
|
// sound instance
|
|
//
|
|
|
|
/**
|
|
* open and return a handle to a sound instance.
|
|
* this loads the sound data and makes it ready for other snd_* APIs.
|
|
*
|
|
* @param snd_fn input filename. if a text file (extension "txt"), it is
|
|
* assumed to be a definition file containing the sound file name and
|
|
* its gain (0.0 .. 1.0).
|
|
* otherwise, it is taken to be the sound file name and
|
|
* gain is set to the default of 1.0 (no attenuation).
|
|
*
|
|
* @param is_stream (default false) forces the sound to be opened as a
|
|
* stream: opening is faster, it won't be kept in memory, but
|
|
* only one instance can be open at a time.
|
|
* @return Handle or LibError
|
|
**/
|
|
extern Handle snd_open(const char* snd_fn, bool stream = false);
|
|
|
|
/**
|
|
* close the sound instance. if it was playing, it will be stopped.
|
|
*
|
|
* rationale: sounds are already closed automatically when done playing;
|
|
* this API is provided for completeness only.
|
|
*
|
|
* @param hs Handle to sound instance. zeroed afterwards.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_free(Handle& hs);
|
|
|
|
/**
|
|
* start playing the sound.
|
|
*
|
|
* Notes:
|
|
* <UL>
|
|
* <LI> once done playing, the sound is automatically closed (allows
|
|
* fire-and-forget play code).
|
|
* <LI> if no hardware voice is available, this sound may not be
|
|
* played at all, or in the case of looped sounds, start later.
|
|
* </UL>
|
|
*
|
|
* @param priority (min 0 .. max 1, default 0) indicates which sounds are
|
|
* considered more important (i.e. will override others when no hardware
|
|
* voices are available). the static priority is attenuated by
|
|
* distance to the listener; see snd_update.
|
|
*
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_play(Handle hs, float priority = 0.0f);
|
|
|
|
/**
|
|
* change 3d position of the sound source.
|
|
*
|
|
* may be called at any time; fails with invalid handle return if
|
|
* the sound has already been closed (e.g. it never played).
|
|
*
|
|
* @param relative treat (x,y,z) as relative to the listener;
|
|
* if false (the default), it is the position in world coordinates.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_set_pos(Handle hs, float x, float y, float z, bool relative = false);
|
|
|
|
/**
|
|
* change gain (amplitude modifier) of the sound source.
|
|
*
|
|
* should not be called during a fade (see note in implementation);
|
|
* fails with invalid handle return if the sound has already been
|
|
* closed (e.g. it never played).
|
|
*
|
|
* @param gain amplitude modifier. must be non-negative;
|
|
* 1 -> unattenuated, 0.5 -> -6 dB, 0 -> silence.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_set_gain(Handle hs, float gain);
|
|
|
|
/**
|
|
* change pitch shift of the sound source.
|
|
*
|
|
* may be called at any time; fails with invalid handle return if
|
|
* the sound has already been closed (e.g. it never played).
|
|
*
|
|
* @param pitch 1.0 means no change; each reduction by 50% equals a
|
|
* pitch shift of -12 semitones (one octave). zero is invalid.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_set_pitch(Handle hs, float pitch);
|
|
|
|
/**
|
|
* enable/disable looping on the sound source.
|
|
* used to implement variable-length sounds (e.g. while building).
|
|
*
|
|
* may be called at any time; fails with invalid handle return if
|
|
* the sound has already been closed (e.g. it never played).
|
|
*
|
|
* Notes:
|
|
* <UL>
|
|
* <LI> looping sounds are not discarded if they cannot be played for
|
|
* lack of a hardware voice at the moment play was requested.
|
|
* <LI> once looping is again disabled and the sound has reached its end,
|
|
* the sound instance is freed automatically (as if never looped).
|
|
* </UL>
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_set_loop(Handle hs, bool loop);
|
|
|
|
/// types of fade in/out operations
|
|
enum FadeType
|
|
{
|
|
FT_NONE, /// currently no fade in progres
|
|
FT_LINEAR, /// f(t) = t
|
|
FT_EXPONENTIAL, /// f(t) = t**3
|
|
FT_S_CURVE, /// cosine curve
|
|
|
|
FT_ABORT /// abort and mark pending fade as complete
|
|
};
|
|
|
|
/**
|
|
* fade the sound source in or out over time.
|
|
*
|
|
* may be called at any time; fails with invalid handle return if
|
|
* the sound has already been closed (e.g. it never played).
|
|
*
|
|
* gain starts at <initial_gain> (immediately) and is moved toward
|
|
* <final_gain> over <length> seconds.
|
|
* @param type of fade curve: linear, exponential or S-curve.
|
|
* for guidance on which to use, see
|
|
* http://www.transom.org/tools/editing_mixing/200309.stupidfadetricks.html
|
|
* you can also pass FT_ABORT to stop fading (if in progress) and
|
|
* set gain to the current <final_gain> parameter.
|
|
* special cases:
|
|
* - if <initial_gain> < 0 (an otherwise illegal value), the sound's
|
|
* current gain is used as the start value (useful for fading out).
|
|
* - if <final_gain> is 0, the sound is freed when the fade completes or
|
|
* is aborted, thus allowing fire-and-forget fadeouts. no cases are
|
|
* foreseen where this is undesirable, and it is easier to implement
|
|
* than an extra set-free-after-fade-flag function.
|
|
*
|
|
* note that this function doesn't busy-wait until the fade is complete;
|
|
* any number of fades may be active at a time (allows cross-fading).
|
|
* each snd_update calculates a new gain value for all pending fades.
|
|
* it is safe to start another fade on the same sound source while
|
|
* one is already in progress; the old one will be discarded.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_fade(Handle hvs, float initial_gain, float final_gain,
|
|
float length, FadeType type);
|
|
|
|
|
|
//
|
|
// sound engine
|
|
//
|
|
|
|
/**
|
|
* (temporarily) disable all sound output.
|
|
*
|
|
* because it causes future snd_open calls to immediately abort before they
|
|
* demand-initialize OpenAL, startup is sped up considerably (500..1000ms).
|
|
* therefore, this must be called before the first snd_open to have
|
|
* any effect; otherwise, the cat will already be out of the bag and
|
|
* we debug_warn of it.
|
|
*
|
|
* rationale: this is a quick'n dirty way of speeding up startup during
|
|
* development without having to change the game's sound code.
|
|
*
|
|
* can later be called to reactivate sound; all settings ever changed
|
|
* will be applied and subsequent sound load / play requests will work.
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_disable(bool disabled);
|
|
|
|
/**
|
|
* perform housekeeping (e.g. streaming); call once a frame.
|
|
*
|
|
* all parameters are expressed in world coordinates. they can all be NULL
|
|
* to avoid updating the listener data; this is useful when the game world
|
|
* has not been initialized yet.
|
|
* @param pos listener's position
|
|
* @param dir listener view direction
|
|
* @param up listener's local up vector
|
|
* @return LibError
|
|
**/
|
|
extern LibError snd_update(const float* pos, const float* dir, const float* up);
|
|
|
|
/**
|
|
* free all resources and shut down the sound system.
|
|
* call before h_mgr_shutdown.
|
|
**/
|
|
extern void snd_shutdown();
|
|
|
|
#endif // #ifndef SND_MGR_H__
|