/** * ========================================================================= * 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: *