New sound system implementation by stwf, refs #1223, #961.

Enables audio on OS X, refs #685, #931.
Fixes music not playing after a game, refs #946.

This was SVN commit r12428.
This commit is contained in:
historic_bruno 2012-08-15 00:10:44 +00:00
parent 2c260660e6
commit 011ba8255e
46 changed files with 2525 additions and 3329 deletions

View File

@ -96,7 +96,12 @@ skycolor = "0 0 0"
; GENERAL PREFERENCES:
sound.mastergain = 0.5
sound.mastergain = 0.9
sound.musicgain = 0.2
sound.ambientgain = 0.6
sound.actiongain = 0.7
sound.bufferCount = 50
sound.bufferSize = 65536
; Camera control settings
view.scroll.speed = 120.0

View File

@ -8,6 +8,7 @@
<ConeOuter>360</ConeOuter>
<Looping>0</Looping>
<RandOrder>1</RandOrder>
<Distanceless>1</Distanceless>
<RandGain>1</RandGain>
<GainUpper>1</GainUpper>
<GainLower>0.8</GainLower>

View File

@ -8,6 +8,7 @@
<ConeOuter>360</ConeOuter>
<Looping>0</Looping>
<RandOrder>1</RandOrder>
<Distanceless>1</Distanceless>
<RandGain>1</RandGain>
<GainUpper>1</GainUpper>
<GainLower>0.8</GainLower>

View File

@ -8,6 +8,7 @@
<ConeOuter>360</ConeOuter>
<Looping>0</Looping>
<RandOrder>1</RandOrder>
<Distanceless>1</Distanceless>
<RandGain>1</RandGain>
<GainUpper>1</GainUpper>
<GainLower>0.8</GainLower>

View File

@ -66,6 +66,20 @@ function newRandomSound(soundType, soundSubType, soundPrePath)
//console.write("Playing " + randomSoundPath + " ...");
switch (soundType)
{
case "music":
return new MusicSound(randomSoundPath);
break;
case "ambient":
return new AmbientSound(randomSoundPath);
break;
case "effect":
console.write("am loading effect '*"+randomSoundPath+"*'");
break;
default:
break;
}
return new Sound(randomSoundPath);
}

View File

@ -78,8 +78,8 @@ Music.prototype.updateState = function()
case this.states.OFF:
if (this.isPlaying())
{
this.currentMusic.fade(-1, 0.0, 3.0);
this.currentMusic = null;
var thePlayer = SoundPlayer();
thePlayer.stopMusic();
}
break;
@ -146,13 +146,7 @@ Music.prototype.getRandomTrack = function(tracks)
Music.prototype.switchMusic = function(track, fadeInPeriod, isLooping)
{
if (this.currentMusic)
{
this.currentMusic.fade(-1, 0.0, 5.0);
this.currentMusic = null;
}
this.currentMusic = new Sound(this.RELATIVE_MUSIC_PATH + track);
this.currentMusic = new MusicSound(this.RELATIVE_MUSIC_PATH + track);
if (this.currentMusic)
{
@ -160,9 +154,6 @@ Music.prototype.switchMusic = function(track, fadeInPeriod, isLooping)
this.currentMusic.loop();
else
this.currentMusic.play();
if (fadeInPeriod)
this.currentMusic.fade(0.0, this.musicGain, fadeInPeriod);
}
};
@ -180,6 +171,8 @@ Music.prototype.isPlaying = function()
Music.prototype.start = function()
{
var thePlayer = SoundPlayer();
thePlayer.startMusic();
this.setState(this.states.PEACE);
};

View File

@ -297,7 +297,8 @@ function checkPlayerState()
if (playerState.state == "defeated")
{
g_GameEnded = true;
global.music.setState(global.music.states.DEFEAT_CUE);
// TODO: DEFEAT_CUE is missing?
global.music.setState(global.music.states.DEFEAT);
closeMenu();
closeOpenDialogs();
@ -551,12 +552,11 @@ function playRandomAmbient(type)
// currentAmbient = newRandomSound("ambient", "temperate_", "dayscape");
const AMBIENT = "audio/ambient/dayscape/day_temperate_gen_03.ogg";
currentAmbient = new Sound(AMBIENT);
currentAmbient = new AmbientSound(AMBIENT);
if (currentAmbient)
{
currentAmbient.loop();
currentAmbient.setGain(0.8);
}
break;
@ -571,7 +571,7 @@ function stopAmbient()
{
if (currentAmbient)
{
currentAmbient.fade(-1, 0.0, 5.0);
currentAmbient.free();
currentAmbient = null;
}
}

View File

@ -212,13 +212,6 @@ function RunDetection(settings)
dialog_warnings.push("You are using 260.19.* series NVIDIA drivers, which may crash the game. Please upgrade to 260.19.21 or later.");
}
// http://trac.wildfiregames.com/ticket/685
if (os_macosx)
{
warnings.push("Audio has been disabled, due to problems with OpenAL on OS X.");
disable_audio = true;
}
// http://trac.wildfiregames.com/ticket/684
// https://bugs.freedesktop.org/show_bug.cgi?id=24047
// R600 drivers will advertise support for S3TC but not actually support it,

View File

@ -535,7 +535,10 @@ function setup_all_libs ()
"ps/Network",
"ps/GameSetup",
"ps/XML",
"sound",
"soundmanager",
"soundmanager/data",
"soundmanager/items",
"soundmanager/js",
"scripting",
"maths",
"maths/scripting",
@ -548,8 +551,14 @@ function setup_all_libs ()
"zlib",
"boost",
"enet",
"libcurl",
"libcurl"
}
if not _OPTIONS["without-audio"] then
table.insert(extern_libs, "openal")
table.insert(extern_libs, "vorbis")
end
setup_static_lib_project("engine", source_dirs, extern_libs, {})
@ -569,7 +578,6 @@ function setup_all_libs ()
end
setup_static_lib_project("graphics", source_dirs, extern_libs, {})
source_dirs = {
"tools/atlas/GameInterface",
"tools/atlas/GameInterface/Handlers"
@ -610,7 +618,6 @@ function setup_all_libs ()
"lib/posix",
"lib/res",
"lib/res/graphics",
"lib/res/sound",
"lib/sysdep",
"lib/tex"
}
@ -625,11 +632,6 @@ function setup_all_libs ()
"cxxtest",
}
if not _OPTIONS["without-audio"] then
table.insert(extern_libs, "openal")
table.insert(extern_libs, "vorbis")
end
-- CPU architecture-specific
if arch == "amd64" then
table.insert(source_dirs, "lib/sysdep/arch/amd64");

View File

@ -1,287 +0,0 @@
#include "precompiled.h"
#include "ogg.h"
#if CONFIG2_AUDIO
#include "lib/external_libraries/openal.h"
#include "lib/external_libraries/vorbis.h"
#include "lib/byte_order.h"
#include "lib/file/io/io.h"
#include "lib/file/file_system.h"
static Status LibErrorFromVorbis(int err)
{
switch(err)
{
case 0:
return INFO::OK;
case OV_HOLE:
return ERR::AGAIN;
case OV_EREAD:
return ERR::IO;
case OV_EFAULT:
return ERR::LOGIC;
case OV_EIMPL:
return ERR::NOT_SUPPORTED;
case OV_EINVAL:
return ERR::INVALID_PARAM;
case OV_ENOTVORBIS:
return ERR::NOT_SUPPORTED;
case OV_EBADHEADER:
return ERR::CORRUPTED;
case OV_EVERSION:
return ERR::INVALID_VERSION;
case OV_ENOTAUDIO:
return ERR::_1;
case OV_EBADPACKET:
return ERR::_2;
case OV_EBADLINK:
return ERR::_3;
case OV_ENOSEEK:
return ERR::_4;
default:
return ERR::FAIL;
}
}
//-----------------------------------------------------------------------------
class VorbisFileAdapter
{
public:
VorbisFileAdapter(const PFile& openedFile)
: file(openedFile)
, size(FileSize(openedFile->Pathname()))
, offset(0)
{
}
static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
{
VorbisFileAdapter* adapter = (VorbisFileAdapter*)context;
const off_t sizeRequested = numItems*itemSize;
const off_t sizeRemaining = adapter->size - adapter->offset;
const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
io::Operation op(*adapter->file.get(), bufferToFill, sizeToRead, adapter->offset);
if(io::Run(op) == INFO::OK)
{
adapter->offset += sizeToRead;
return sizeToRead;
}
errno = EIO;
return 0;
}
static int Seek(void* context, ogg_int64_t offset, int whence)
{
VorbisFileAdapter* adapter = (VorbisFileAdapter*)context;
off_t origin = 0;
switch(whence)
{
case SEEK_SET:
origin = 0;
break;
case SEEK_CUR:
origin = adapter->offset;
break;
case SEEK_END:
origin = adapter->size+1;
break;
NODEFAULT;
}
adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
return 0;
}
static int Close(void* context)
{
VorbisFileAdapter* adapter = (VorbisFileAdapter*)context;
adapter->file.reset();
return 0; // return value is ignored
}
static long Tell(void* context)
{
VorbisFileAdapter* adapter = (VorbisFileAdapter*)context;
return adapter->offset;
}
private:
PFile file;
off_t size;
off_t offset;
};
//-----------------------------------------------------------------------------
class VorbisBufferAdapter
{
public:
VorbisBufferAdapter(const shared_ptr<u8>& buffer, size_t size)
: buffer(buffer)
, size(size)
, offset(0)
{
}
static size_t Read(void* bufferToFill, size_t itemSize, size_t numItems, void* context)
{
VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context;
const off_t sizeRequested = numItems*itemSize;
const off_t sizeRemaining = adapter->size - adapter->offset;
const size_t sizeToRead = (size_t)std::min(sizeRequested, sizeRemaining);
memcpy(bufferToFill, adapter->buffer.get() + adapter->offset, sizeToRead);
adapter->offset += sizeToRead;
return sizeToRead;
}
static int Seek(void* context, ogg_int64_t offset, int whence)
{
VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context;
off_t origin = 0;
switch(whence)
{
case SEEK_SET:
origin = 0;
break;
case SEEK_CUR:
origin = adapter->offset;
break;
case SEEK_END:
origin = adapter->size+1;
break;
NODEFAULT;
}
adapter->offset = Clamp(off_t(origin+offset), off_t(0), adapter->size);
return 0;
}
static int Close(void* context)
{
VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context;
adapter->buffer.reset();
return 0; // return value is ignored
}
static long Tell(void* context)
{
VorbisBufferAdapter* adapter = (VorbisBufferAdapter*)context;
return adapter->offset;
}
private:
shared_ptr<u8> buffer;
off_t size;
off_t offset;
};
//-----------------------------------------------------------------------------
template <typename Adapter>
class OggStreamImpl : public OggStream
{
public:
OggStreamImpl(const Adapter& adapter)
: adapter(adapter)
{
}
Status Open()
{
ov_callbacks callbacks;
callbacks.read_func = Adapter::Read;
callbacks.close_func = Adapter::Close;
callbacks.seek_func = Adapter::Seek;
callbacks.tell_func = Adapter::Tell;
const int ret = ov_open_callbacks(&adapter, &vf, 0, 0, callbacks);
if(ret != 0)
WARN_RETURN(LibErrorFromVorbis(ret));
const int link = -1; // retrieve info for current bitstream
info = ov_info(&vf, link);
if(!info)
WARN_RETURN(ERR::INVALID_HANDLE);
return INFO::OK;
}
virtual ALenum Format()
{
return (info->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
}
virtual ALsizei SamplingRate()
{
return info->rate;
}
virtual Status GetNextChunk(u8* buffer, size_t size)
{
// we may have to call ov_read multiple times because it
// treats the buffer size "as a limit and not a request"
size_t bytesRead = 0;
for(;;)
{
const int isBigEndian = (BYTE_ORDER == BIG_ENDIAN);
const int wordSize = sizeof(i16);
const int isSigned = 1;
int bitstream; // unused
const int ret = ov_read(&vf, (char*)buffer+bytesRead, int(size-bytesRead), isBigEndian, wordSize, isSigned, &bitstream);
if(ret == 0) // EOF
return (Status)bytesRead;
else if(ret < 0)
WARN_RETURN(LibErrorFromVorbis(ret));
else // success
{
bytesRead += ret;
if(bytesRead == size)
return (Status)bytesRead;
}
}
}
private:
Adapter adapter;
OggVorbis_File vf;
vorbis_info* info;
};
//-----------------------------------------------------------------------------
Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream)
{
PFile file(new File);
RETURN_STATUS_IF_ERR(file->Open(pathname, L'r'));
shared_ptr<OggStreamImpl<VorbisFileAdapter> > tmp(new OggStreamImpl<VorbisFileAdapter>(VorbisFileAdapter(file)));
RETURN_STATUS_IF_ERR(tmp->Open());
stream = tmp;
return INFO::OK;
}
Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream)
{
shared_ptr<u8> contents;
size_t size;
RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, contents, size));
shared_ptr<OggStreamImpl<VorbisBufferAdapter> > tmp(new OggStreamImpl<VorbisBufferAdapter>(VorbisBufferAdapter(contents, size)));
RETURN_STATUS_IF_ERR(tmp->Open());
stream = tmp;
return INFO::OK;
}
#endif // CONFIG2_AUDIO

View File

@ -1,36 +0,0 @@
#ifndef INCLUDED_OGG
#define INCLUDED_OGG
#include "lib/config2.h"
#if CONFIG2_AUDIO
#include "lib/external_libraries/openal.h"
#include "lib/file/vfs/vfs.h"
class OggStream
{
public:
virtual ~OggStream() { }
virtual ALenum Format() = 0;
virtual ALsizei SamplingRate() = 0;
/**
* @return bytes read (<= size) or a (negative) Status
**/
virtual Status GetNextChunk(u8* buffer, size_t size) = 0;
};
typedef shared_ptr<OggStream> OggStreamPtr;
extern Status OpenOggStream(const OsPath& pathname, OggStreamPtr& stream);
/**
* A non-streaming OggStream (reading the whole file in advance)
* that can cope with archived/compressed files.
*/
extern Status OpenOggNonstream(const PIVFS& vfs, const VfsPath& pathname, OggStreamPtr& stream);
#endif // CONFIG2_AUDIO
#endif // INCLUDED_OGG

File diff suppressed because it is too large Load Diff

View File

@ -1,373 +0,0 @@
/* Copyright (c) 2010 Wildfire Games
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* OpenAL sound engine. handles sound I/O, buffer suballocation and
* voice management/prioritization.
*/
#ifndef INCLUDED_SND_MGR
#define INCLUDED_SND_MGR
#include "lib/res/handle.h"
#include "lib/file/vfs/vfs.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 Status; 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 Status 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 Status (the status returned by OpenAL re-init)
**/
extern Status 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 limit Maximum number of voices. Ignored if higher than
* an implementation-defined limit anyway.
* @return Status
**/
extern Status snd_set_max_voices(size_t limit);
/**
* 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 Status
**/
extern Status 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 vfs
* @param pathname 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).
* @return Handle or Status
**/
extern Handle snd_open(const PIVFS& vfs, const VfsPath& pathname);
/**
* 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 hvs Handle to sound instance. Zeroed afterwards.
* @return Status
**/
extern Status snd_free(Handle& hvs);
/**
* Start playing the sound.
*
* Notes:
* - once done playing, the sound is automatically closed (allows
* fire-and-forget play code).
* - if no hardware voice is available, this sound may not be
* played at all, or in the case of looped sounds, start later.
*
* @param hvs Handle to VSrc.
* @param static_pri (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 Status
**/
extern Status snd_play(Handle hvs, float static_pri = 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 hvs Handle to the sound.
* @param x,y,z
* @param relative treat (x,y,z) as relative to the listener;
* if false (the default), it is the position in world coordinates.
* @return Status
**/
extern Status snd_set_pos(Handle hvs, 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 Status
**/
extern Status 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 shift: 1.0 means no change; each doubling/halving equals a
* pitch shift of +/-12 semitones (one octave). zero is invalid.
* @return Status
**/
extern Status 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:
* - looping sounds are not discarded if they cannot be played for
* - lack of a hardware voice at the moment play was requested.
* - once looping is again disabled and the sound has reached its end,
* the sound instance is freed automatically (as if never looped).
*
* @param hvs Handle to the sound.
* @param loop Boolean to enable/disable lopping on the sound.
* @return Status
**/
extern Status snd_set_loop(Handle hvs, 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 hvs Handle to the sound.
* @param initial_gain
* @param final_gain
* @param length
* @param type 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 Status
**/
extern Status 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 raise a warning.
*
* 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.
*
* @param disabled
* @return Status
**/
extern Status 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 Status
**/
extern Status snd_update(const float* pos, const float* dir, const float* up);
/**
* find out if a sound is still playing
*
* @param hvs Handle to the snd to check.
* @return bool true if playing
**/
extern bool snd_is_playing(Handle hvs);
/**
* free all resources and shut down the sound system.
* call before h_mgr_shutdown.
**/
extern void snd_shutdown();
#endif // #ifndef INCLUDED_SND_MGR

View File

@ -39,7 +39,6 @@ that of Atlas depending on commandline parameters.
#include "lib/ogl.h"
#include "lib/timer.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/res/sound/snd_mgr.h"
#include "ps/ArchiveBuilder.h"
#include "ps/CConsole.h"
@ -320,7 +319,7 @@ static void Frame()
// If we are not running a multiplayer game, disable updates when the game is
// minimized or out of focus and relinquish the CPU a bit, in order to make
// debugging easier.
if( g_PauseOnFocusLoss && !g_NetClient && !g_app_has_focus )
if(g_PauseOnFocusLoss && !g_NetClient && !g_app_has_focus)
{
PROFILE3("non-focus delay");
need_update = false;
@ -375,28 +374,6 @@ static void Frame()
g_Game->Update(realTimeSinceLastFrame);
g_Game->GetView()->Update(float(realTimeSinceLastFrame));
CCamera* camera = g_Game->GetView()->GetCamera();
CMatrix3D& orientation = camera->m_Orientation;
float* pos = &orientation._data[12];
float* dir = &orientation._data[8];
float* up = &orientation._data[4];
// HACK: otherwise sound effects are L/R flipped. No idea what else
// is going wrong, because the listener and camera are supposed to
// coincide in position and orientation.
float down[3] = { -up[0], -up[1], -up[2] };
{
PROFILE3("sound update");
if (snd_update(pos, dir, down) < 0)
debug_printf(L"snd_update failed\n");
}
}
else
{
PROFILE3("sound update (0)");
if (snd_update(0, 0, 0) < 0)
debug_printf(L"snd_update (pos=0 version) failed\n");
}
// Immediately flush any messages produced by simulation code
@ -480,8 +457,6 @@ static void RunGameOrAtlas(int argc, const char* argv[])
// run non-visual simulation replay if requested
if (args.Has("replay"))
{
snd_disable(true);
Paths paths(args);
g_VFS = CreateVfs(20 * MiB);
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE);

View File

@ -23,6 +23,7 @@
#include "graphics/LOSTexture.h"
#include "graphics/ParticleManager.h"
#include "graphics/UnitManager.h"
#include "gui/GUIManager.h"
#include "lib/timer.h"
#include "network/NetClient.h"
#include "network/NetServer.h"
@ -44,8 +45,7 @@
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/components/ICmpPlayerManager.h"
#include "gui/GUIManager.h"
#include "soundmanager/SoundManager.h"
extern bool g_GameRestarted;
@ -294,6 +294,7 @@ bool CGame::Update(const double deltaRealTime, bool doInterpolate)
if (doInterpolate)
{
m_TurnManager->Interpolate(deltaSimTime, deltaRealTime);
g_SoundManager->IdleTask();
}
// TODO: maybe we should add a CCmpParticleInterface that passes the interpolation commands

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -17,13 +17,13 @@
#include "precompiled.h"
#include "Config.h"
#include "ps/ConfigDB.h"
#include "ps/CConsole.h"
#include "ps/GameSetup/CmdLineArgs.h"
#include "lib/timer.h"
#include "lib/res/sound/snd_mgr.h"
#include "Config.h"
#include "soundmanager/SoundManager.h"
// (these variables are documented in the header.)
@ -83,9 +83,26 @@ static void LoadGlobals()
CFG_GET_USER_VAL("silhouettes", Bool, g_Silhouettes);
float gain = -1.0f;
float musicGain = -1.0f;
float ambientGain = -1.0f;
float actionGain = -1.0f;
int bufferCount = 50;
unsigned long bufferSize = 65536;
CFG_GET_USER_VAL("sound.mastergain", Float, gain);
if(gain >= 0.0f)
WARN_IF_ERR(snd_set_master_gain(gain));
CFG_GET_USER_VAL("sound.musicgain", Float, musicGain);
CFG_GET_USER_VAL("sound.ambientgain", Float, ambientGain);
CFG_GET_USER_VAL("sound.actiongain", Float, actionGain);
CFG_GET_USER_VAL("sound.bufferCount", Int, bufferCount);
CFG_GET_USER_VAL("sound.bufferSize", UnsignedLong, bufferSize);
g_SoundManager->SetMasterGain(gain);
g_SoundManager->SetMusicGain(musicGain);
g_SoundManager->SetAmbientGain(ambientGain);
g_SoundManager->SetActionGain(actionGain);
g_SoundManager->SetMemoryUsage(bufferSize, bufferCount);
}

View File

@ -25,7 +25,6 @@
#include "lib/file/common/file_stats.h"
#include "lib/res/h_mgr.h"
#include "lib/res/graphics/cursor.h"
#include "lib/res/sound/snd_mgr.h"
#include "lib/sysdep/cursor.h"
#include "lib/sysdep/cpu.h"
#include "lib/sysdep/gfx.h"
@ -35,12 +34,34 @@
#include "lib/sysdep/os/win/wversion.h"
#endif
#include "graphics/CinemaTrack.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/MapReader.h"
#include "graphics/MaterialManager.h"
#include "graphics/TerrainTextureManager.h"
#include "gui/GUI.h"
#include "gui/GUIManager.h"
#include "gui/scripting/JSInterface_IGUIObject.h"
#include "gui/scripting/JSInterface_GUITypes.h"
#include "gui/scripting/ScriptFunctions.h"
#include "maths/MathUtil.h"
#include "maths/scripting/JSInterface_Vector3D.h"
#include "network/NetServer.h"
#include "network/NetClient.h"
#include "ps/CConsole.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
#include "ps/Font.h"
#include "ps/Game.h"
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/GameSetup/Paths.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/CmdLineArgs.h"
#include "ps/GameSetup/HWDetect.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Joystick.h"
@ -49,60 +70,26 @@
#include "ps/Profile.h"
#include "ps/ProfileViewer.h"
#include "ps/Profiler2.h"
#include "ps/Pyrogenesis.h" // psSetLogDir
#include "ps/scripting/JSInterface_Console.h"
#include "ps/TouchInput.h"
#include "ps/UserReport.h"
#include "ps/Util.h"
#include "ps/VideoMode.h"
#include "ps/World.h"
#include "graphics/CinemaTrack.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/MapReader.h"
#include "graphics/MaterialManager.h"
#include "graphics/TerrainTextureManager.h"
#include "renderer/Renderer.h"
#include "renderer/VertexBufferManager.h"
#include "renderer/ModelRenderer.h"
#include "maths/MathUtil.h"
#include "simulation2/Simulation2.h"
#include "scripting/ScriptingHost.h"
#include "scripting/ScriptGlue.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/ScriptStats.h"
#include "maths/scripting/JSInterface_Vector3D.h"
#include "ps/scripting/JSInterface_Console.h"
#include "gui/GUI.h"
#include "gui/GUIManager.h"
#include "gui/scripting/JSInterface_IGUIObject.h"
#include "gui/scripting/JSInterface_GUITypes.h"
#include "gui/scripting/ScriptFunctions.h"
#include "sound/JSI_Sound.h"
#include "network/NetServer.h"
#include "network/NetClient.h"
#include "ps/Pyrogenesis.h" // psSetLogDir
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/GameSetup/Paths.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/CmdLineArgs.h"
#include "ps/GameSetup/HWDetect.h"
#include "simulation2/Simulation2.h"
#include "soundmanager/SoundManager.h"
#include "tools/atlas/GameInterface/GameLoop.h"
#include "tools/atlas/GameInterface/View.h"
#if !(OS_WIN || OS_MACOSX || OS_ANDROID) // assume all other platforms use X11 for wxWidgets
#define MUST_INIT_X11 1
#include <X11/Xlib.h>
@ -203,6 +190,8 @@ void Render()
{
PROFILE3("render");
g_SoundManager->IdleTask();
ogl_WarnIfError();
g_Profiler2.RecordGPUFrameStart();
@ -331,15 +320,15 @@ static void RegisterJavascriptInterfaces()
// maths
JSI_Vector3D::init();
// sound
CSoundManager::ScriptingInit();
// graphics
CGameView::ScriptingInit();
// renderer
CRenderer::ScriptingInit();
// sound
JSI_Sound::ScriptingInit();
// ps
JSI_Console::init();
@ -695,7 +684,7 @@ void Shutdown(int UNUSED(flags))
// resource
// first shut down all resource owners, and then the handle manager.
TIMER_BEGIN(L"resource modules");
snd_shutdown();
delete g_SoundManager;
g_VFS.reset();
@ -872,6 +861,8 @@ void Init(const CmdLineArgs& args, int UNUSED(flags))
g_ScriptStatsTable = new CScriptStatsTable;
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
g_SoundManager = new CSoundManager();
InitScripting(); // before GUI
// g_ConfigDB, command line args, globals
@ -934,7 +925,7 @@ void InitGraphics(const CmdLineArgs& args, int flags)
// speed up startup by disabling all sound
// (OpenAL init will be skipped).
// must be called before first snd_open.
snd_disable(true);
g_SoundManager->SetEnabled(false);
}
g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface());

View File

@ -492,8 +492,6 @@ void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, ShadowM
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderManager& shaderManager = g_Renderer.GetShaderManager();
// render the solid black sides of the map first
CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect("gui_solid");
techSolid->BeginPass();

View File

@ -24,7 +24,7 @@
#include "simulation2/MessageTypes.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "sound/SoundGroup.h"
#include "soundmanager/js/SoundGroup.h"
class CCmpSoundManager : public ICmpSoundManager
{

View File

@ -1,200 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "JSI_Sound.h"
#include "maths/Vector3D.h"
#include "lib/utf8.h"
#include "lib/res/sound/snd_mgr.h"
#include "lib/res/h_mgr.h" // h_filename
#include "ps/Filesystem.h"
JSI_Sound::JSI_Sound(const VfsPath& pathname)
{
m_Handle = snd_open(g_VFS, pathname);
// if open failed, we still have to return a valid non-null object to
// the script, so just reset the handle to 0 so all subsequent method
// calls will do nothing
if (m_Handle < 0)
{
m_Handle = 0;
return;
}
(void)snd_set_pos(m_Handle, 0,0,0, true);
}
JSI_Sound::~JSI_Sound()
{
(void)this->Free(0, 0, 0);
}
bool JSI_Sound::SetGain(JSContext* cx, uintN argc, jsval* argv)
{
if (! m_Handle)
return false;
ENSURE(argc >= 1); // FIXME
float gain;
if (! ToPrimitive<float>(cx, argv[0], gain))
return false;
(void)snd_set_gain(m_Handle, gain);
return true;
}
bool JSI_Sound::SetPitch(JSContext* cx, uintN argc, jsval* argv)
{
if (! m_Handle)
return false;
ENSURE(argc >= 1); // FIXME
float pitch;
if (! ToPrimitive<float>(cx, argv[0], pitch))
return false;
(void)snd_set_pitch(m_Handle, pitch);
return true;
}
bool JSI_Sound::SetPosition(JSContext* cx, uintN argc, jsval* argv)
{
if (! m_Handle)
return false;
ENSURE(argc >= 1); // FIXME
CVector3D pos;
// absolute world coords
if (ToPrimitive<CVector3D>(cx, argv[0], pos))
(void)snd_set_pos(m_Handle, pos[0], pos[1], pos[2]);
// relative, 0 offset - right on top of the listener
// (we don't need displacement from the listener, e.g. always behind)
else
(void)snd_set_pos(m_Handle, 0,0,0, true);
return true;
}
bool JSI_Sound::Fade(JSContext* cx, uintN argc, jsval* argv)
{
if (! m_Handle)
return false;
ENSURE(argc >= 3); // FIXME
float initial_gain, final_gain;
float length;
if (! (ToPrimitive<float>(cx, argv[0], initial_gain)
&& ToPrimitive<float>(cx, argv[1], final_gain)
&& ToPrimitive<float>(cx, argv[2], length)))
return false;
(void)snd_fade(m_Handle, initial_gain, final_gain, length, FT_S_CURVE);
// HACK: snd_fade causes <m_Handle> to be automatically freed when a
// fade to 0 has completed. however, we're still holding on to a
// reference, which will cause a double-free warning when Free() is
// called from the dtor. solution is to neuter our Handle by
// setting it to 0 (ok since it'll be freed). this does mean that
// no further operations can be carried out during that final fade.
if (final_gain == 0.0f)
m_Handle = 0;
return true;
}
// start playing the sound (one-shot).
// it will automatically be freed when done.
bool JSI_Sound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
if (! m_Handle)
return false;
(void)snd_play(m_Handle);
// We can't do anything else with this sound now, since it's impossible to
// know whether or not it's still valid (since it might have finished playing
// already). So set it to 0, so we don't try doing anything (like freeing it)
// in the future.
m_Handle = 0;
return true;
}
// request the sound be played until free() is called. returns immediately.
bool JSI_Sound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
if (! m_Handle)
return false;
(void)snd_set_loop(m_Handle, true);
(void)snd_play(m_Handle);
return true;
}
// stop sound if currently playing and free resources.
// doesn't need to be called unless played via loop() -
// sounds are freed automatically when done playing.
bool JSI_Sound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
if (! m_Handle)
return false;
(void)snd_free(m_Handle); // resets it to 0
return true;
}
// Script-bound functions
void JSI_Sound::ScriptingInit()
{
AddMethod<CStr, &JSI_Sound::ToString>("toString", 0);
AddMethod<bool, &JSI_Sound::Play>("play", 0);
AddMethod<bool, &JSI_Sound::Loop>("loop", 0);
AddMethod<bool, &JSI_Sound::Free>("free", 0);
AddMethod<bool, &JSI_Sound::SetGain>("setGain", 0);
AddMethod<bool, &JSI_Sound::SetPitch>("setPitch", 0);
AddMethod<bool, &JSI_Sound::SetPosition>("setPosition", 0);
AddMethod<bool, &JSI_Sound::Fade>("fade", 0);
CJSObject<JSI_Sound>::ScriptingInit("Sound", &JSI_Sound::Construct, 1);
}
CStr JSI_Sound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
return "[object Sound: " + (m_Handle ? utf8_from_wstring(h_filename(m_Handle).string()) : "(null)") + "]";
}
JSBool JSI_Sound::Construct(JSContext* cx, uintN argc, jsval* vp)
{
JSU_REQUIRE_MIN_PARAMS(1);
CStrW filename;
if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename))
return JS_FALSE;
JSI_Sound* newObject = new JSI_Sound(filename);
newObject->m_EngineOwned = false;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript()));
return JS_TRUE;
}

View File

@ -1,85 +0,0 @@
PLAYING AMBIENT SOUND WHEN CAMERA IS OVER BUILDING
==================================================
data flow:
C++ JS
> list of visible entities (1)
< list of sound requests and their weights
list of changed sounds
C++ code generates list of entities on screen
rationale: done in C++ for performance and to avoid having to expose the "patch" subdivision scheme to JS.
UPDATE: this is much thornier than expected.
1) we only want stuff that is visible to be played. hearing things off screen would be distressing and weird
2) don't play sound for buildings in FoW, to prevent "audio spying"
most straightforward way is simply scan all entities; if building, check against visible frustum.
if inside AND within 3d distance cutoff of viewer pos, add to visible_building
optimization for later: get all patches within 3d distance cutoff; discard all not within frustum; only test
entities within those remaining patches.
pass that to JS decideWhatToPlay
it returns list of sounds it wants playing
format: see AmbientSoundReq below
C++ sound engine compares its list of currently sounds;
fades out those no longer wanted and starts new ones immediately
new entity properties needed:
-----------------------------
- ambientGroup: a soundGroup of several sounds; one is picked at random to be played (see below)
- priority: to determine which entity should trump the others in a crowded city.
// weight is so that we can play several ambient sounds at a time, but ones for buildings at edge of screen are quieter
// values: 0..1; probably calculated from distance to camera
type AmbientSoundReq = (SoundGroupString, weight)
// (suggestion only; may be revised if too much cacophony/constant sound results)
JS_decideWhatToPlay(in HEntity visible_ents[], out AmbientSoundReq desired_ambient_sounds[])
{
// * "important" means the building is at center of attention and should mostly override the other sounds.
// this is complicated as well. the camera may have any orientation, especially in cinematic mode;
// that means we can't just rely on casting a ray from viewer through middle screen pixel to terrain.
// our building list already covers only the visible ones, so we can use 3d distance from viewer
// (without worrying about including buildings behind us i.e. out of sight).
// important := fairly small distance (covers the case where users zoom really close to a building) OR
// building is close to center of screen (as determined via ray cast method; skip this if the camera
// is weird, i.e. looking above the horizon, in which case the ray cast would fail).
// note: not playing sounds when looking down vertically at the town but zoomed out very far is ok (desirable even).
if an entity is important(*)
desired_ambient_sounds[i++] = ent.ambientGroup
forall ambient types (farm, dock etc.) in decreasing order of priority
stop if >= 3 sounds in list
if enough buildings on screen
desired_ambient_sounds[i++] = ent.ambientGroup
maybe add some random variation to spice things up? (i.e. also play some other building sounds;
don't always have one building override the others)
}
C++ code keeps a list of all active sounds:
type ActiveSound = (Handle, SoundGroupString)
ActiveSound active_sound_list[]
Handle startPlayingAmbient(soundGroup, weight)
{
filename = soundGroup.pickRandom()
return snd_play(filename, globalAmbientVolume*soundGroup.volume*weight)
}
updateAmbientSounds(desired_ambient_sounds)
{
for all in desired_ambient_sounds but not active_sound_list:
handle = startPlayingAmbient(amb.group, amb.weight)
active_sound_list[i++] = (handle, amb.group)
for all in active_sound_list but not desired_ambient_sounds:
snd_fade(OUT, active.handle)
}
RANDOMIZED SOUNDS
==================================================
(needed for ambient and normal battle sounds)

View File

@ -0,0 +1,314 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "SoundManager.h"
#include "soundmanager/data/SoundData.h"
#include "soundmanager/items/CSoundItem.h"
#include "soundmanager/items/CBufferItem.h"
#include "soundmanager/items/CStreamItem.h"
#include "soundmanager/js/SoundPlayer.h"
#include "soundmanager/js/AmbientSound.h"
#include "soundmanager/js/MusicSound.h"
#include "soundmanager/js/Sound.h"
CSoundManager* g_SoundManager;
void CSoundManager::ScriptingInit()
{
JAmbientSound::ScriptingInit();
JMusicSound::ScriptingInit();
JSound::ScriptingInit();
JSoundPlayer::ScriptingInit();
}
CSoundManager::CSoundManager()
{
m_Items = new ItemsList;
m_CurrentEnvirons = 0;
m_CurrentTune = 0;
m_Gain = 1;
m_MusicGain = 1;
m_AmbientGain = 1;
m_ActionGain = 1;
m_Enabled = true;
m_BufferCount = 50;
m_BufferSize = 65536;
m_MusicEnabled = true;
AlcInit();
}
CSoundManager::~CSoundManager()
{
ItemsList::iterator lstr = m_Items->begin();
while (lstr != m_Items->end())
{
(*lstr)->Stop();
delete *lstr;
lstr++;
}
alcDestroyContext(m_Context);
alcCloseDevice(m_Device);
delete m_Items;
m_Items = 0L;
m_CurrentEnvirons = 0;
m_CurrentTune = 0;
}
Status CSoundManager::AlcInit()
{
Status ret = INFO::OK;
m_Device = alcOpenDevice(NULL);
if(m_Device)
{
m_Context = alcCreateContext(m_Device, 0); // no attrlist needed
if(m_Context)
alcMakeContextCurrent(m_Context);
}
// check if init succeeded.
// some OpenAL implementations don't indicate failure here correctly;
// we need to check if the device and context pointers are actually valid.
ALCenum err = alcGetError(m_Device);
if(err != ALC_NO_ERROR || !m_Device || !m_Context)
{
#if OS_UNIX
ret = INFO::OK;
#else
ret = ERR::FAIL;
#endif
}
const char* dev_name = (const char*)alcGetString(m_Device, ALC_DEVICE_SPECIFIER);
wchar_t buf[200];
swprintf(buf, ARRAY_SIZE(buf), L"SND| alc_init: success, using %hs\n", dev_name);
return ret;
}
void CSoundManager::SetMemoryUsage(long bufferSize, int bufferCount)
{
m_BufferCount = bufferCount;
m_BufferSize = bufferSize;
}
long CSoundManager::GetBufferCount()
{
return m_BufferCount;
}
long CSoundManager::GetBufferSize()
{
return m_BufferSize;
}
void CSoundManager::SetMasterGain(float gain)
{
m_Gain = gain;
}
void CSoundManager::SetMusicGain(float gain)
{
m_MusicGain = gain;
}
void CSoundManager::SetAmbientGain(float gain)
{
m_AmbientGain = gain;
}
void CSoundManager::SetActionGain(float gain)
{
m_ActionGain = gain;
}
ISoundItem* CSoundManager::LoadItem(const VfsPath* itemPath)
{
CSoundData* itemData = CSoundData::SoundDataFromFile(itemPath);
ISoundItem* answer = NULL;
if (itemData != NULL)
{
if (itemData->IsOneShot())
{
if (itemData->GetBufferCount() == 1)
answer = new CSoundItem(itemData);
else
answer = new CBufferItem(itemData);
}
else
{
answer = new CStreamItem(itemData);
}
if (answer != NULL)
m_Items->push_back(answer);
}
return answer;
}
unsigned long CSoundManager::Count()
{
return m_Items->size();
}
void CSoundManager::IdleTask()
{
if (m_Items)
{
ItemsList::iterator lstr = m_Items->begin();
ItemsList deadItemList;
ItemsList* nextItemList = new ItemsList;
while (lstr != m_Items->end()) {
if ((*lstr)->IdleTask())
nextItemList->push_back(*lstr);
else
deadItemList.push_back(*lstr);
lstr++;
}
delete m_Items;
m_Items = nextItemList;
ItemsList::iterator deadItems = deadItemList.begin();
while (deadItems != deadItemList.end())
{
delete *deadItems;
deadItems++;
}
}
if (m_CurrentTune)
m_CurrentTune->EnsurePlay();
if (m_CurrentEnvirons)
m_CurrentEnvirons->EnsurePlay();
}
void CSoundManager::DeleteItem(long itemNum)
{
ItemsList::iterator lstr = m_Items->begin();
lstr += itemNum;
delete *lstr;
m_Items->erase(lstr);
}
ISoundItem* CSoundManager::GetSoundItem(unsigned long itemRow)
{
return (*m_Items)[itemRow];
}
void CSoundManager::InitListener()
{
ALfloat listenerPos[]={0.0,0.0,0.0};
ALfloat listenerVel[]={0.0,0.0,0.0};
ALfloat listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0};
alListenerfv(AL_POSITION,listenerPos);
alListenerfv(AL_VELOCITY,listenerVel);
alListenerfv(AL_ORIENTATION,listenerOri);
alDistanceModel(AL_EXPONENT_DISTANCE);
}
void CSoundManager::SetEnabled(bool doEnable)
{
m_Enabled = doEnable;
}
void CSoundManager::PlayActionItem(ISoundItem* anItem)
{
if (anItem)
{
if (m_Enabled && (m_ActionGain > 0))
{
anItem->SetGain(m_Gain * m_ActionGain);
anItem->Play();
}
}
}
void CSoundManager::PlayGroupItem(ISoundItem* anItem, ALfloat groupGain)
{
if (anItem)
{
if (m_Enabled && (m_ActionGain > 0)) {
anItem->SetGain(m_Gain * groupGain);
anItem->Play();
}
}
}
void CSoundManager::SetMusicEnabled (bool isEnabled)
{
if (m_CurrentTune && !isEnabled)
{
m_CurrentTune->FadeAndDelete(1.00);
m_CurrentTune = 0L;
}
m_MusicEnabled = isEnabled;
}
void CSoundManager::SetMusicItem(ISoundItem* anItem)
{
if (m_CurrentTune)
{
m_CurrentTune->FadeAndDelete(3.00);
m_CurrentTune = 0L;
}
IdleTask();
if (anItem)
{
if (m_MusicEnabled && m_Enabled)
{
m_CurrentTune = anItem;
m_CurrentTune->SetGain(0);
m_CurrentTune->PlayLoop();
m_CurrentTune->FadeToIn(m_Gain * m_MusicGain, 2.00);
}
else
{
anItem->StopAndDelete();
}
}
}
void CSoundManager::SetAmbientItem(ISoundItem* anItem)
{
if (m_CurrentEnvirons)
{
m_CurrentEnvirons->FadeAndDelete(3.00);
m_CurrentEnvirons = 0L;
}
IdleTask();
if (anItem)
{
if (m_Enabled && (m_AmbientGain > 0))
{
m_CurrentEnvirons = anItem;
m_CurrentEnvirons->SetGain(0);
m_CurrentEnvirons->PlayLoop();
m_CurrentEnvirons->FadeToIn(m_Gain * m_AmbientGain, 2.00);
}
}
}

View File

@ -0,0 +1,91 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SOUNDMANAGER_H
#define INCLUDED_SOUNDMANAGER_H
#include "soundmanager/items/ISoundItem.h"
#include "lib/file/vfs/vfs_path.h"
#include <vector>
#include <map>
typedef std::vector<ISoundItem*> ItemsList;
class CSoundManager
{
protected:
ALuint m_ALEnvironment;
ALCcontext* m_Context;
ALCdevice* m_Device;
ISoundItem* m_CurrentTune;
ISoundItem* m_CurrentEnvirons;
ItemsList* m_Items;
float m_Gain;
float m_MusicGain;
float m_AmbientGain;
float m_ActionGain;
bool m_Enabled;
long m_BufferSize;
int m_BufferCount;
bool m_MusicEnabled;
public:
CSoundManager();
virtual ~CSoundManager();
ISoundItem* LoadItem(const VfsPath* itemPath);
static void ScriptingInit();
void SetMusicEnabled (bool isEnabled);
ISoundItem* ItemFromWAV(VfsPath& fname);
ISoundItem* ItemFromOgg(VfsPath& fname);
ISoundItem* GetSoundItem(unsigned long itemRow);
unsigned long Count();
void IdleTask();
void DeleteItem(long itemNum);
void SetMemoryUsage(long bufferSize, int bufferCount);
long GetBufferCount();
long GetBufferSize();
void SetMusicItem(ISoundItem* anItem);
void SetAmbientItem(ISoundItem* anItem);
void PlayActionItem(ISoundItem* anItem);
void PlayGroupItem(ISoundItem* anItem, ALfloat groupGain);
void SetMasterGain(float gain);
void SetMusicGain(float gain);
void SetAmbientGain(float gain);
void SetActionGain(float gain);
void SetEnabled(bool doEnable);
protected:
void InitListener();
virtual Status AlcInit();
};
extern CSoundManager* g_SoundManager;
#endif // INCLUDED_SOUNDMANAGER_H

View File

@ -0,0 +1,163 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "OggData.h"
#include "soundmanager/SoundManager.h"
#include <wchar.h>
#include <iostream>
COggData::COggData()
{
m_OneShot = false;
}
COggData::~COggData()
{
alDeleteBuffers(m_BuffersUsed, m_Buffer);
ov_clear(&m_vf);
}
void COggData::SetFormatAndFreq(int form, ALsizei freq)
{
m_Format = form;
m_Frequency = freq;
}
bool COggData::InitOggFile(const wchar_t* fileLoc)
{
int buffersToStart = g_SoundManager->GetBufferCount();
char nameH[300];
sprintf(nameH, "%ls", fileLoc);
FILE* f = fopen(nameH, "rb");
m_current_section = 0;
int err = ov_open_callbacks(f, &m_vf, NULL, 0, OV_CALLBACKS_DEFAULT);
if (err < 0)
{
fprintf(stderr,"Input does not appear to be an Ogg bitstream :%d :%d.\n", err, ferror(f));
return false;
}
m_FileName = CStrW(fileLoc);
m_FileFinished = false;
SetFormatAndFreq((m_vf.vi->channels == 1)? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16 , (ALsizei)m_vf.vi->rate);
alGetError(); /* clear error */
alGenBuffers(buffersToStart, m_Buffer);
if(alGetError() != AL_NO_ERROR)
{
printf("- Error creating initial buffer !!\n");
return false;
}
else
{
m_BuffersUsed = FetchDataIntoBuffer(buffersToStart, m_Buffer);
if (m_FileFinished)
{
m_OneShot = true;
if (m_BuffersUsed < buffersToStart)
{
alDeleteBuffers(buffersToStart - m_BuffersUsed, &m_Buffer[m_BuffersUsed]);
}
}
}
return true;
}
ALsizei COggData::GetBufferCount()
{
return m_BuffersUsed;
}
bool COggData::IsFileFinished()
{
return m_FileFinished;
}
void COggData::ResetFile()
{
ov_time_seek(&m_vf, 0);
m_current_section = 0;
m_FileFinished = false;
}
bool COggData::IsOneShot()
{
return m_OneShot;
}
int COggData::FetchDataIntoBuffer(int count, ALuint* buffers)
{
long bufferSize = g_SoundManager->GetBufferSize();
char* pcmout = new char[bufferSize + 5000];
int buffersWritten = 0;
for (int i = 0; (i < count) && !m_FileFinished; i++)
{
char* readDest = pcmout;
long totalRet = 0;
while (totalRet < bufferSize)
{
long ret=ov_read(&m_vf,readDest, 4096,0,2,1, &m_current_section);
if (ret == 0)
{
m_FileFinished=true;
break;
}
else if (ret < 0)
{
/* error in the stream. Not a problem, just reporting it in
case we (the app) cares. In this case, we don't. */
}
else
{
totalRet += ret;
readDest += ret;
}
}
if (totalRet > 0)
{
buffersWritten++;
alBufferData(buffers[i], m_Format, pcmout, (ALsizei)totalRet, (int)m_Frequency);
}
}
delete[] pcmout;
return buffersWritten;
}
ALuint COggData::GetBuffer()
{
return m_Buffer[0];
}
ALuint* COggData::GetBufferPtr()
{
return m_Buffer;
}

View File

@ -0,0 +1,57 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_OGGDATA_H
#define INCLUDED_OGGDATA_H
#include "SoundData.h"
#include "lib/external_libraries/openal.h"
#include "vorbis/vorbisfile.h"
class COggData : public CSoundData
{
ALuint m_Format;
long m_Frequency;
public:
COggData();
virtual ~COggData();
virtual bool InitOggFile(const wchar_t* fileLoc);
virtual bool IsFileFinished();
virtual bool IsOneShot();
virtual int FetchDataIntoBuffer(int count, ALuint* buffers);
virtual void ResetFile();
protected:
OggVorbis_File m_vf;
int m_current_section;
bool m_FileFinished;
bool m_OneShot;
ALuint m_Buffer[100];
int m_BuffersUsed;
bool AddDataBuffer(char* data, long length);
void SetFormatAndFreq(int form, ALsizei freq);
ALsizei GetBufferCount();
ALuint GetBuffer();
ALuint* GetBufferPtr();
};
#endif // INCLUDED_OGGDATA_H

View File

@ -0,0 +1,142 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "SoundData.h"
#include "OggData.h"
#include "lib/file/vfs/vfs_util.h"
#include "ps/Filesystem.h"
#include <iostream>
DataMap* CSoundData::sSoundData = NULL;
CSoundData::CSoundData()
{
InitProperties();
}
CSoundData::~CSoundData()
{
if (m_ALBuffer != 0)
alDeleteBuffers(1, &m_ALBuffer);
}
void CSoundData::InitProperties()
{
m_ALBuffer = 0;
m_RetentionCount = 0;
}
void CSoundData::ReleaseSoundData(CSoundData* theData)
{
DataMap::iterator itemFind;
if (theData->DecrementCount())
{
if ((itemFind = CSoundData::sSoundData->find(theData->GetFileName())) != sSoundData->end())
{
CSoundData* dier = itemFind->second;
CSoundData::sSoundData->erase(itemFind);
delete dier;
}
}
}
CSoundData* CSoundData::SoundDataFromFile(const VfsPath* itemPath)
{
if (CSoundData::sSoundData == NULL)
CSoundData::sSoundData = new DataMap;
Path fExt = itemPath->Extension();
DataMap::iterator itemFind;
CSoundData* answer = NULL;
if ((itemFind = CSoundData::sSoundData->find(itemPath->string())) != sSoundData->end())
{
answer = itemFind->second;
}
else
{
if (fExt == ".ogg")
answer = SoundDataFromOgg(itemPath);
if (answer && answer->IsOneShot())
(*CSoundData::sSoundData)[itemPath->string()] = answer;
}
return answer;
}
bool CSoundData::IsOneShot()
{
return true;
}
CSoundData* CSoundData::SoundDataFromOgg(const VfsPath* itemPath)
{
CSoundData* answer = NULL;
COggData* oggAnswer = new COggData();
OsPath realPath;
Status ret = g_VFS->GetRealPath(*itemPath, realPath);
if (ret == INFO::OK)
{
if (oggAnswer->InitOggFile(realPath.string().c_str()))
{
answer = oggAnswer;
}
}
return answer;
}
ALsizei CSoundData::GetBufferCount()
{
return 1;
}
CStrW CSoundData::GetFileName()
{
return m_FileName;
}
CSoundData* CSoundData::IncrementCount()
{
m_RetentionCount++;
return this;
}
bool CSoundData::DecrementCount()
{
m_RetentionCount--;
return (m_RetentionCount <= 0);
}
ALuint CSoundData::GetBuffer()
{
return m_ALBuffer;
}
ALuint* CSoundData::GetBufferPtr()
{
return &m_ALBuffer;
}

View File

@ -0,0 +1,64 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SOUNDDATA_H
#define INCLUDED_SOUNDDATA_H
#include "lib/external_libraries/openal.h"
#include "lib/file/vfs/vfs_path.h"
#include "lib/os_path.h"
#include <string>
#include <map>
class CSoundData;
typedef std::map<std::wstring, CSoundData*> DataMap;
class CSoundData
{
public:
static CSoundData* SoundDataFromFile(const VfsPath* itemPath);
static CSoundData* SoundDataFromOgg(const VfsPath* itemPath);
static void ReleaseSoundData(CSoundData* theData);
CSoundData();
CSoundData(ALuint dataSource);
virtual ~CSoundData();
CSoundData* IncrementCount();
bool DecrementCount();
void InitProperties();
virtual bool IsOneShot();
virtual ALuint GetBuffer();
virtual ALsizei GetBufferCount();
CStrW GetFileName();
virtual ALuint* GetBufferPtr();
protected:
static DataMap* sSoundData;
ALuint m_ALBuffer;
int m_RetentionCount;
CStrW m_FileName;
};
#endif // INCLUDED_SOUNDDATA_H

View File

@ -0,0 +1,90 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CBufferItem.h"
#include "soundmanager/data/SoundData.h"
#include <iostream>
CBufferItem::CBufferItem(CSoundData* sndData)
{
ResetVars();
if (InitOpenAL())
Attach(sndData);
}
CBufferItem::~CBufferItem()
{
Stop();
int num_processed;
alGetSourcei(m_ALSource, AL_BUFFERS_PROCESSED, &num_processed);
if (num_processed > 0)
{
ALuint* al_buf = new ALuint[num_processed];
alSourceUnqueueBuffers(m_ALSource, num_processed, al_buf);
delete[] al_buf;
}
}
bool CBufferItem::IdleTask()
{
HandleFade();
if (m_LastPlay)
{
int proc_state;
alGetSourceiv(m_ALSource, AL_SOURCE_STATE, &proc_state);
return (proc_state != AL_STOPPED);
}
if (GetLooping())
{
int num_processed;
alGetSourcei(m_ALSource, AL_BUFFERS_PROCESSED, &num_processed);
for (int i = 0; i < num_processed; i++)
{
ALuint al_buf;
alSourceUnqueueBuffers(m_ALSource, 1, &al_buf);
alSourceQueueBuffers(m_ALSource, 1, &al_buf);
}
}
return true;
}
void CBufferItem::Attach(CSoundData* itemData)
{
if (itemData != NULL)
{
m_SoundData = itemData->IncrementCount();
alSourceQueueBuffers(m_ALSource, m_SoundData->GetBufferCount(),(const ALuint *) m_SoundData->GetBufferPtr());
}
}
void CBufferItem::SetLooping(bool loops)
{
m_Looping = loops;
}

View File

@ -0,0 +1,39 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CBUFFERITEM_H
#define INCLUDED_CBUFFERITEM_H
#include "CSoundBase.h"
class CBufferItem : public CSoundBase
{
public:
CBufferItem(CSoundData* sndData);
virtual ~CBufferItem();
virtual void SetLooping(bool loops);
virtual bool IdleTask();
protected:
virtual void Attach(CSoundData* itemData);
};
#endif // INCLUDED_CBUFFERITEM_H

View File

@ -0,0 +1,276 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CSoundBase.h"
#include "lib/timer.h"
#include "soundmanager/SoundManager.h"
#include "soundmanager/data/SoundData.h"
#include <iostream>
CSoundBase::CSoundBase()
{
ResetVars();
}
CSoundBase::~CSoundBase()
{
Stop();
if (m_ALSource != 0)
{
alDeleteSources(1, &m_ALSource);
m_ALSource = 0;
}
if (m_SoundData != 0)
{
CSoundData::ReleaseSoundData(m_SoundData);
m_SoundData = 0;
}
if (m_Name)
delete m_Name;
}
void CSoundBase::ResetVars()
{
m_ALSource = 0;
m_SoundData = 0;
m_LastPlay = false;
m_Looping = false;
m_StartFadeTime = 0;
m_EndFadeTime = 0;
m_StartVolume = 0;
m_EndVolume = 0;
ResetFade();
m_Name = new std::string("sound name");
}
void CSoundBase::ResetFade()
{
m_StartFadeTime = 0;
m_EndFadeTime = 0;
m_StartVolume = 0;
m_EndVolume = 0;
m_ShouldBePlaying = false;
}
void CSoundBase::SetGain(ALfloat gain)
{
alSourcef(m_ALSource, AL_GAIN, gain);
}
void CSoundBase::SetRollOff(ALfloat rolls)
{
alSourcef(m_ALSource, AL_ROLLOFF_FACTOR, rolls);
}
void CSoundBase::EnsurePlay()
{
if (m_ShouldBePlaying && !IsPlaying())
Play();
}
void CSoundBase::SetCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain)
{
alSourcef(m_ALSource, innerCone, AL_CONE_INNER_ANGLE);
alSourcef(m_ALSource, outerCone, AL_CONE_OUTER_ANGLE);
alSourcef(m_ALSource, coneGain, AL_CONE_OUTER_GAIN);
}
void CSoundBase::SetPitch(ALfloat pitch)
{
alSourcef(m_ALSource, AL_PITCH, pitch);
}
void CSoundBase::SetDirection(const CVector3D& direction)
{
alSourcefv(m_ALSource, AL_DIRECTION, direction.GetFloatArray());
}
bool CSoundBase::InitOpenAL()
{
alGetError(); /* clear error */
alGenSources(1, &m_ALSource);
long anErr = alGetError();
if (anErr != AL_NO_ERROR)
{
printf("- Error creating sources %ld !!\n", anErr);
}
else
{
ALfloat source0Pos[]={ -2.0, 0.0, 0.0};
ALfloat source0Vel[]={ 0.0, 0.0, 0.0};
alSourcef(m_ALSource,AL_PITCH,1.0f);
alSourcef(m_ALSource,AL_GAIN,1.0f);
alSourcefv(m_ALSource,AL_POSITION,source0Pos);
alSourcefv(m_ALSource,AL_VELOCITY,source0Vel);
alSourcei(m_ALSource,AL_LOOPING,AL_FALSE);
return true;
}
return false;
}
bool CSoundBase::IsPlaying()
{
int proc_state;
alGetSourceiv(m_ALSource, AL_SOURCE_STATE, &proc_state);
return (proc_state == AL_PLAYING);
}
void CSoundBase::SetLastPlay(bool last)
{
m_LastPlay = last;
}
bool CSoundBase::IdleTask()
{
return true;
}
void CSoundBase::SetLocation (const CVector3D& position)
{
alSourcefv(m_ALSource,AL_POSITION, position.GetFloatArray());
}
bool CSoundBase::HandleFade()
{
if (m_StartFadeTime != 0)
{
double currTime = timer_Time();
double pctDone = std::min(1.0, (currTime - m_StartFadeTime) / (m_EndFadeTime - m_StartFadeTime));
pctDone = std::max(0.0, pctDone);
ALfloat curGain = ((m_EndVolume - m_StartVolume) * pctDone) + m_StartVolume;
if (curGain == 0)
Stop();
else if (curGain == m_EndVolume)
{
alSourcef(m_ALSource, AL_GAIN, curGain);
ResetFade();
}
else
alSourcef(m_ALSource, AL_GAIN, curGain);
}
return true;
}
bool CSoundBase::GetLooping()
{
return m_Looping;
}
void CSoundBase::SetLooping(bool loops)
{
m_Looping = loops;
alSourcei(m_ALSource, AL_LOOPING, loops ? AL_TRUE : AL_FALSE);
}
void CSoundBase::Play()
{
m_ShouldBePlaying = true;
if (m_ALSource != 0)
alSourcePlay(m_ALSource);
}
void CSoundBase::PlayAndDelete()
{
SetLastPlay(true);
Play();
}
void CSoundBase::FadeAndDelete(double fadeTime)
{
SetLastPlay(true);
FadeToIn(0, fadeTime);
}
void CSoundBase::StopAndDelete()
{
SetLastPlay(true);
Stop();
}
void CSoundBase::PlayLoop()
{
if (m_ALSource != 0)
{
SetLooping(true);
Play();
}
}
void CSoundBase::FadeToIn(ALfloat newVolume, double fadeDuration)
{
int proc_state;
alGetSourceiv(m_ALSource, AL_SOURCE_STATE, &proc_state);
if (proc_state == AL_PLAYING)
{
m_StartFadeTime = timer_Time();
m_EndFadeTime = m_StartFadeTime + fadeDuration;
alGetSourcef(m_ALSource, AL_GAIN, &m_StartVolume);
m_EndVolume = newVolume;
}
}
void CSoundBase::PlayAsMusic()
{
g_SoundManager->SetMusicItem(this);
}
void CSoundBase::PlayAsAmbient()
{
g_SoundManager->SetAmbientItem(this);
}
void CSoundBase::Stop()
{
m_ShouldBePlaying = false;
if (m_ALSource != 0)
{
int proc_state;
alSourcei(m_ALSource, AL_LOOPING, AL_FALSE);
alGetSourceiv(m_ALSource, AL_SOURCE_STATE, &proc_state);
if (proc_state == AL_PLAYING)
alSourceStop(m_ALSource);
}
}
const char* CSoundBase::Name()
{
return m_Name->c_str();
}
std::string CSoundBase::GetName()
{
return std::string(m_Name->c_str());
}
void CSoundBase::SetNameFromPath(char* fileLoc)
{
std::string anst(fileLoc);
size_t pos = anst.find_last_of("/");
if(pos != std::wstring::npos)
m_Name->assign(anst.begin() + pos + 1, anst.end());
else
m_Name->assign(anst.begin(), anst.end());
}

View File

@ -0,0 +1,89 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CSOUNDBASE_H
#define INCLUDED_CSOUNDBASE_H
#include "lib/external_libraries/openal.h"
#include "soundmanager/items/ISoundItem.h"
#include "soundmanager/data/SoundData.h"
#include <string>
class CSoundBase : public ISoundItem
{
protected:
ALuint m_ALSource;
CSoundData* m_SoundData;
std::string* m_Name;
bool m_LastPlay;
bool m_Looping;
bool m_ShouldBePlaying;
double m_StartFadeTime;
double m_EndFadeTime;
ALfloat m_StartVolume;
ALfloat m_EndVolume;
public:
CSoundBase();
virtual ~CSoundBase();
virtual bool InitOpenAL();
virtual void ResetVars();
virtual void EnsurePlay();
virtual void SetGain(ALfloat gain);
virtual void SetRollOff(ALfloat gain);
virtual void SetPitch(ALfloat pitch);
virtual void SetDirection(const CVector3D& direction);
virtual void SetCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain);
virtual void SetLastPlay(bool last);
void Play();
void PlayAndDelete();
bool IdleTask();
void PlayLoop();
void Stop();
void StopAndDelete();
void FadeToIn(ALfloat newVolume, double fadeDuration);
void PlayAsMusic();
void PlayAsAmbient();
const char* Name();
std::string GetName();
virtual bool GetLooping();
virtual void SetLooping(bool loops);
virtual bool IsPlaying();
virtual void SetLocation(const CVector3D& position);
virtual void FadeAndDelete(double fadeTime);
protected:
void SetNameFromPath(char* fileLoc);
void ResetFade();
bool HandleFade();
};
#endif // INCLUDED_CSOUNDBASE_H

View File

@ -0,0 +1,67 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CSoundItem.h"
#include "soundmanager/data/SoundData.h"
#include <iostream>
CSoundItem::CSoundItem()
{
ResetVars();
}
CSoundItem::CSoundItem(CSoundData* sndData)
{
ResetVars();
if (InitOpenAL())
Attach(sndData);
}
CSoundItem::~CSoundItem()
{
ALuint al_buf;
Stop();
alSourceUnqueueBuffers(m_ALSource, 1, &al_buf);
}
bool CSoundItem::IdleTask()
{
HandleFade();
if (m_LastPlay)
{
int proc_state;
alGetSourceiv(m_ALSource, AL_SOURCE_STATE, &proc_state);
return (proc_state != AL_STOPPED);
}
return true;
}
void CSoundItem::Attach(CSoundData* itemData)
{
if (itemData != NULL)
{
m_SoundData = itemData->IncrementCount();
alSourcei(m_ALSource, AL_BUFFER, m_SoundData->GetBuffer());
}
}

View File

@ -0,0 +1,37 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CSOUNDITEM_H
#define INCLUDED_CSOUNDITEM_H
#include "CSoundBase.h"
#include "soundmanager/data/SoundData.h"
class CSoundItem : public CSoundBase
{
public:
CSoundItem();
CSoundItem(CSoundData* sndData);
virtual ~CSoundItem();
void Attach(CSoundData* itemData);
bool IdleTask();
};
#endif // INCLUDED_CSOUNDITEM_H

View File

@ -0,0 +1,99 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CStreamItem.h"
#include "soundmanager/data/OggData.h"
#include <iostream>
CStreamItem::CStreamItem(CSoundData* sndData)
{
ResetVars();
if (InitOpenAL())
Attach(sndData);
}
CStreamItem::~CStreamItem()
{
Stop();
int num_processed;
alGetSourcei(m_ALSource, AL_BUFFERS_PROCESSED, &num_processed);
if (num_processed > 0)
{
ALuint* al_buf = new ALuint[num_processed];
alSourceUnqueueBuffers(m_ALSource, num_processed, al_buf);
delete[] al_buf;
}
}
bool CStreamItem::IdleTask()
{
HandleFade();
int proc_state;
alGetSourceiv(m_ALSource, AL_SOURCE_STATE, &proc_state);
if (proc_state == AL_STOPPED)
{
if (m_LastPlay)
return (proc_state != AL_STOPPED);
}
else
{
COggData* tmp = (COggData*)m_SoundData;
if (! tmp->IsFileFinished())
{
int num_processed;
alGetSourcei(m_ALSource, AL_BUFFERS_PROCESSED, &num_processed);
if (num_processed > 0)
{
ALuint* al_buf = new ALuint[num_processed];
alSourceUnqueueBuffers(m_ALSource, num_processed, al_buf);
int didWrite = tmp->FetchDataIntoBuffer(num_processed, al_buf);
alSourceQueueBuffers(m_ALSource, didWrite, al_buf);
delete[] al_buf;
}
}
else if (GetLooping())
{
tmp->ResetFile();
}
}
return true;
}
void CStreamItem::Attach(CSoundData* itemData)
{
if (itemData != NULL)
{
m_SoundData = itemData->IncrementCount();
alSourceQueueBuffers(m_ALSource, m_SoundData->GetBufferCount(), (const ALuint *)m_SoundData->GetBufferPtr());
}
}
void CStreamItem::SetLooping(bool loops)
{
m_Looping = loops;
}

View File

@ -0,0 +1,38 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_CSTREAMITEM_H
#define INCLUDED_CSTREAMITEM_H
#include "soundmanager/data/SoundData.h"
#include "CSoundBase.h"
class CStreamItem : public CSoundBase
{
public:
CStreamItem(CSoundData* sndData);
virtual ~CStreamItem();
virtual void SetLooping(bool loops);
virtual bool IdleTask();
protected:
virtual void Attach(CSoundData* itemData);
};
#endif // INCLUDED_CSTREAMITEM_H

View File

@ -0,0 +1,60 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_ISOUNDITEM_H
#define INCLUDED_ISOUNDITEM_H
#include "lib/external_libraries/openal.h"
#include "maths/Vector3D.h"
#include <string>
class ISoundItem
{
public:
virtual ~ISoundItem(){};
virtual bool GetLooping() = 0;
virtual void SetLooping(bool loop) = 0;
virtual bool IsPlaying() = 0;
virtual std::string GetName() = 0;
virtual bool IdleTask() = 0;
virtual void Play() = 0;
virtual void Stop() = 0;
virtual void EnsurePlay() = 0;
virtual void PlayAsMusic() = 0;
virtual void PlayAsAmbient() = 0;
virtual void PlayAndDelete() = 0;
virtual void StopAndDelete() = 0;
virtual void FadeToIn(ALfloat newVolume, double fadeDuration) = 0;
virtual void FadeAndDelete(double fadeTime) = 0;
virtual void PlayLoop() = 0;
virtual void SetCone(ALfloat innerCone, ALfloat outerCone, ALfloat coneGain) = 0;
virtual void SetPitch(ALfloat pitch) = 0;
virtual void SetGain(ALfloat gain) = 0;
virtual void SetLocation(const CVector3D& position) = 0;
virtual void SetRollOff(ALfloat gain) = 0;
};
#endif // INCLUDED_ISOUNDITEM_H

View File

@ -0,0 +1,107 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "AmbientSound.h"
#include "lib/utf8.h"
#include "maths/Vector3D.h"
#include "ps/Filesystem.h"
#include "soundmanager/SoundManager.h"
JAmbientSound::JAmbientSound(const VfsPath& pathname)
{
m_FileName = new VfsPath(pathname.string().c_str());
}
JAmbientSound::~JAmbientSound()
{
delete m_FileName;
}
// start playing the sound, all ambient sounds loop
bool JAmbientSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
ISoundItem* aSnd = g_SoundManager->LoadItem(m_FileName);
if (aSnd)
aSnd->PlayAsAmbient();
else
debug_printf(L"sound item could not be loaded to play at: %hs\n", m_FileName);
return true;
}
// start playing the sound, all ambient sounds loop
bool JAmbientSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
ISoundItem* aSnd = g_SoundManager->LoadItem(m_FileName);
if (aSnd)
aSnd->PlayAsAmbient();
else
debug_printf(L"sound item could not be loaded to loop at: %hs\n", m_FileName);
return true;
}
bool JAmbientSound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
g_SoundManager->SetAmbientItem(0L);
return true;
}
// Script-bound functions
void JAmbientSound::ScriptingInit()
{
AddMethod<CStr, &JAmbientSound::ToString>("toString", 0);
AddMethod<bool, &JAmbientSound::Play>("play", 0);
AddMethod<bool, &JAmbientSound::Loop>("loop", 0);
AddMethod<bool, &JAmbientSound::Free>("free", 0);
CJSObject<JAmbientSound>::ScriptingInit("AmbientSound", &JAmbientSound::Construct, 1);
}
CStr JAmbientSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
std::ostringstream stringStream;
stringStream << "[object AmbientSound: ";
stringStream << m_FileName->string().c_str();
return stringStream.str();
}
JSBool JAmbientSound::Construct(JSContext* cx, uintN UNUSED(argc), jsval* vp)
{
// JSU_REQUIRE_MIN_PARAMS(1);
CStrW filename;
if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename))
return JS_FALSE;
JAmbientSound* newObject = new JAmbientSound(filename);
newObject->m_EngineOwned = false;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript()));
return JS_TRUE;
}

View File

@ -0,0 +1,51 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_JAMBIENTSOUND
#define INCLUDED_JAMBIENTSOUND
#include "scripting/ScriptableObject.h"
#include "soundmanager/items/ISoundItem.h"
class JAmbientSound : public CJSObject<JAmbientSound>
{
public:
JAmbientSound(const VfsPath& pathname);
virtual ~JAmbientSound();
CStr ToString(JSContext* cx, uintN argc, jsval* argv);
bool Play(JSContext* cx, uintN argc, jsval* argv);
bool Loop(JSContext* cx, uintN argc, jsval* argv);
bool Free(JSContext* cx, uintN argc, jsval* argv);
bool SetGain(JSContext* cx, uintN argc, jsval* argv);
bool SetPitch(JSContext* cx, uintN argc, jsval* argv);
bool Fade(JSContext* cx, uintN argc, jsval* argv);
static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
void clearSoundItem();
static void ScriptingInit();
protected:
VfsPath* m_FileName;
};
#endif // #ifndef INCLUDED_JAMBIENTSOUND

View File

@ -0,0 +1,86 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "MusicSound.h"
#include "lib/utf8.h"
#include "maths/Vector3D.h"
#include "ps/Filesystem.h"
#include "soundmanager/SoundManager.h"
JMusicSound::JMusicSound(const VfsPath& pathname)
{
m_FileName = new VfsPath(pathname.string().c_str());
}
JMusicSound::~JMusicSound()
{
delete m_FileName;
}
bool JMusicSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
ISoundItem* aSnd = g_SoundManager->LoadItem(m_FileName);
if (aSnd != NULL)
aSnd->PlayAsMusic();
return true;
}
// request the sound be played until free() is called. returns immediately.
bool JMusicSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
ISoundItem* aSnd = g_SoundManager->LoadItem(m_FileName);
if (aSnd != NULL)
aSnd->PlayAsMusic();
return true;
}
void JMusicSound::ScriptingInit()
{
AddMethod<CStr, &JMusicSound::ToString>("toString", 0);
AddMethod<bool, &JMusicSound::Play>("play", 0);
AddMethod<bool, &JMusicSound::Loop>("loop", 0);
CJSObject<JMusicSound>::ScriptingInit("MusicSound", &JMusicSound::Construct, 1);
}
CStr JMusicSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
std::ostringstream stringStream;
stringStream << "[object MusicSound: ";
stringStream << m_FileName->string().c_str();
return stringStream.str();
}
JSBool JMusicSound::Construct(JSContext* cx, uintN UNUSED(argc), jsval* vp)
{
CStrW filename;
if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename))
return JS_FALSE;
JMusicSound* newObject = new JMusicSound(filename);
newObject->m_EngineOwned = false;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript()));
return JS_TRUE;
}

View File

@ -0,0 +1,59 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// JS sound binding
// interface rationale:
// - can't just expose fire and forget playSound to script code:
// we sometimes need to loop until a certain condition is met
// (e.g. building is complete) => need means of access (Handle) to sound.
//
// - the current 64-bit Handle can't be stored as-is by JS code;
// we could make it 32 bit, but that limits its usefulness
// (barely enough tag bits).
//
// - instead, we provide a thin class wrapper (using scriptableobject.h)
// on top of the snd API that encapsulates the Handle.
#ifndef INCLUDED_MUSICSOUND_H
#define INCLUDED_MUSICSOUND_H
#include "scripting/ScriptableObject.h"
#include "soundmanager/items/ISoundItem.h"
class JMusicSound : public CJSObject<JMusicSound>
{
public:
JMusicSound(const VfsPath& pathname);
virtual ~JMusicSound();
// Script-bound functions
CStr ToString(JSContext* cx, uintN argc, jsval* argv);
bool Play(JSContext* cx, uintN argc, jsval* argv);
bool Loop(JSContext* cx, uintN argc, jsval* argv);
static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
static void ScriptingInit();
protected:
VfsPath* m_FileName;
};
#endif // #ifndef INCLUDED_MUSICSOUND_H

View File

@ -0,0 +1,174 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "Sound.h"
#include "lib/utf8.h"
#include "maths/Vector3D.h"
#include "ps/Filesystem.h"
#include "soundmanager/SoundManager.h"
JSound::JSound(const VfsPath& pathname)
{
m_SndItem = g_SoundManager->LoadItem(&pathname);
}
JSound::~JSound()
{
if (m_SndItem)
{
m_SndItem->FadeAndDelete(0.2);
m_SndItem = 0;
}
}
bool JSound::ClearSoundItem()
{
m_SndItem = 0L;
return true;
}
bool JSound::SetGain(JSContext* cx, uintN UNUSED(argc), jsval* argv)
{
if (! m_SndItem)
return false;
float gain;
if (! ToPrimitive<float>(cx, argv[0], gain))
return false;
m_SndItem->SetGain(gain);
return true;
}
bool JSound::SetPitch(JSContext* cx, uintN UNUSED(argc), jsval* argv)
{
if (! m_SndItem)
return false;
float pitch;
if (! ToPrimitive<float>(cx, argv[0], pitch))
return false;
m_SndItem->SetPitch(pitch);
return true;
}
bool JSound::SetPosition(JSContext* cx, uintN argc, jsval* argv)
{
if (! m_SndItem)
return false;
ENSURE(argc >= 1); // FIXME
CVector3D pos;
// absolute world coords
if (!ToPrimitive<CVector3D>(cx, argv[0], pos))
return false;
m_SndItem->SetLocation(pos);
return true;
}
bool JSound::Fade(JSContext* cx, uintN UNUSED(argc), jsval* argv)
{
if (! m_SndItem)
return false;
// ENSURE(argc >= 3); // FIXME
float initial_gain, final_gain;
float length;
if (! (ToPrimitive<float>(cx, argv[0], initial_gain)
&& ToPrimitive<float>(cx, argv[1], final_gain)
&& ToPrimitive<float>(cx, argv[2], length)))
return false;
m_SndItem->SetGain(initial_gain);
m_SndItem->FadeToIn(final_gain, length);
return true;
}
bool JSound::Play(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
if (! m_SndItem)
return false;
m_SndItem->Play();
return true;
}
bool JSound::Loop(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
if (! m_SndItem)
return false;
m_SndItem->PlayLoop();
return true;
}
bool JSound::Free(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
if (m_SndItem)
{
m_SndItem->FadeAndDelete(0.2);
m_SndItem = 0;
}
return true;
}
void JSound::ScriptingInit()
{
AddMethod<CStr, &JSound::ToString>("toString", 0);
AddMethod<bool, &JSound::Play>("play", 0);
AddMethod<bool, &JSound::Loop>("loop", 0);
AddMethod<bool, &JSound::Free>("free", 0);
AddMethod<bool, &JSound::SetGain>("setGain", 0);
AddMethod<bool, &JSound::SetPitch>("setPitch", 0);
AddMethod<bool, &JSound::SetPosition>("setPosition", 0);
AddMethod<bool, &JSound::Fade>("fade", 0);
CJSObject<JSound>::ScriptingInit("Sound", &JSound::Construct, 1);
}
CStr JSound::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
return "[object Sound: " + (m_SndItem ? m_SndItem->GetName() : "(null)") + "]";
}
JSBool JSound::Construct(JSContext* cx, uintN UNUSED(argc), jsval* vp)
{
// JSU_REQUIRE_MIN_PARAMS(1);
CStrW filename;
if (! ToPrimitive<CStrW>(cx, JS_ARGV(cx, vp)[0], filename))
return JS_FALSE;
JSound* newObject = new JSound(filename);
newObject->m_EngineOwned = false;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript()));
return JS_TRUE;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -29,50 +29,39 @@
// - instead, we provide a thin class wrapper (using scriptableobject.h)
// on top of the snd API that encapsulates the Handle.
#ifndef INCLUDED_JSI_SOUND
#define INCLUDED_JSI_SOUND
#ifndef INCLUDED_JSOUND
#define INCLUDED_JSOUND
#include "scripting/ScriptableObject.h"
#include "lib/res/handle.h"
#include "soundmanager/items/ISoundItem.h"
class JSI_Sound : public CJSObject<JSI_Sound>
class JSound : public CJSObject<JSound>
{
public:
Handle m_Handle;
// note: filename is stored by handle manager; no need to keep a copy here.
JSI_Sound(const VfsPath& pathname);
~JSI_Sound();
// Script-bound functions
JSound(const VfsPath& pathname);
virtual ~JSound();
CStr ToString(JSContext* cx, uintN argc, jsval* argv);
// start playing the sound (one-shot).
// it will automatically be freed when done.
bool Play(JSContext* cx, uintN argc, jsval* argv);
// request the sound be played until free() is called. returns immediately.
bool Loop(JSContext* cx, uintN argc, jsval* argv);
// stop sound if currently playing and free resources.
// doesn't need to be called unless played via loop() -
// sounds are freed automatically when done playing.
bool Free(JSContext* cx, uintN argc, jsval* argv);
bool SetGain(JSContext* cx, uintN argc, jsval* argv);
bool SetPitch(JSContext* cx, uintN argc, jsval* argv);
bool SetPosition(JSContext* cx, uintN argc, jsval* argv);
bool ClearSoundItem();
bool Fade(JSContext* cx, uintN argc, jsval* argv);
static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
static void ScriptingInit();
protected:
ISoundItem* m_SndItem;
};
#endif // #ifndef INCLUDED_JSI_SOUND
#endif // #ifndef INCLUDED_JSOUND

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -17,24 +17,33 @@
/**
* =========================================================================
* File : SoundGroup.cpp
* Project : 0 A.D.
* File : SoundGroup.cpp
* Project : 0 A.D.
* Description : Loads up a group of sound files with shared properties,
* and provides a simple interface for playing them.
* and provides a simple interface for playing them.
* =========================================================================
*/
#include "precompiled.h"
#include "SoundGroup.h"
#include <algorithm>
#include "graphics/Camera.h"
#include "graphics/GameView.h"
#include "lib/rand.h"
#include "ps/XML/Xeromyces.h"
#include "ps/Game.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Util.h"
#include "ps/XML/Xeromyces.h"
#include "soundmanager/items/ISoundItem.h"
#include "soundmanager/SoundManager.h"
#include <algorithm>
extern CGame *g_Game;
#define PI 3.14126f
static const bool DISABLE_INTENSITY = true; // disable for now since it's broken
@ -53,7 +62,7 @@ void CSoundGroup::SetDefaultValues()
m_CurTime = 0.0f;
// sane defaults; will probably be replaced by the values read during LoadSoundGroup.
SetGain(0.5f);
SetGain(0.7f);
m_Pitch = 1.0f;
m_Priority = 60;
m_PitchUpper = 1.1f;
@ -90,150 +99,163 @@ static float RandFloat(float min, float max)
return float(rand(min*100.0f, max*100.0f) / 100.0f);
}
void CSoundGroup::UploadPropertiesAndPlay(Handle hSound, const CVector3D& position)
float CSoundGroup::RadiansOffCenter(const CVector3D& position, bool& onScreen, float& itemRollOff)
{
// interface/UI sounds should always be played at the listener's
// position, which is achieved by setting position to 0 and
// having that treated as relative to the listener.
float x = 0.0f, y = 0.0f, z = 0.0f;
bool relative = true;
if(!TestFlag(eOmnipresent))
float x, y;
float answer = 0.0;
const size_t screenWidth = g_Game->GetView()->GetCamera()->GetViewPort().m_Width;
const size_t screenHeight = g_Game->GetView()->GetCamera()->GetViewPort().m_Height;
float bufferSize = screenWidth * 0.10;
const size_t audioWidth = screenWidth;
float radianCap = PI / 3;
g_Game->GetView()->GetCamera()->GetScreenCoordinates(position, x, y);
onScreen = true;
if (x < -bufferSize)
{
x = position.X;
y = position.Y;
z = position.Z;
relative = false;
onScreen = false;
answer = -radianCap;
}
else if (x > screenWidth + bufferSize)
{
onScreen = false;
answer = radianCap;
}
else {
if ((x < 0) || (x > screenWidth))
{
itemRollOff = 0.5;
}
float pixPerRadian = audioWidth / (radianCap * 2);
answer = (x - (screenWidth/2)) / pixPerRadian;
}
snd_set_pos(hSound, x, y, z, relative);
if (y < -bufferSize)
{
onScreen = false;
}
else if (y > screenHeight + bufferSize)
{
onScreen = false;
}
else {
if ((y < 0) || (y > screenHeight))
{
itemRollOff = 0.5;
}
}
float gain = TestFlag(eRandGain)? RandFloat(m_GainLower, m_GainUpper) : m_Gain;
gain = std::min(gain, 1.0f); // guard against roundoff error in RandFloat or too high m_GainUpper
snd_set_gain(hSound, gain);
// debug_printf(L"do play at x portion:%f pts x:%f, y=%f at radians=%f\n\n", answer, x, y, answer);
const float pitch = TestFlag(eRandPitch)? RandFloat(m_PitchLower, m_PitchUpper) : m_Pitch;
snd_set_pitch(hSound, pitch);
return answer;
}
snd_play(hSound, m_Priority);
void CSoundGroup::UploadPropertiesAndPlay(int theIndex, const CVector3D& position)
{
bool isOnscreen;
ALfloat initialRolllOff = 0.02f;
ALfloat itemRollOff = initialRolllOff;
float offSet = RadiansOffCenter(position, isOnscreen, itemRollOff);
if (isOnscreen || TestFlag(eDistanceless) || TestFlag(eOmnipresent))
{
if (snd_group.size() == 0)
Reload();
ISoundItem* hSound = snd_group[theIndex];
CVector3D origin = g_Game->GetView()->GetCamera()->GetOrientation().GetTranslation();
float sndDist = origin.Y;
if (!TestFlag(eOmnipresent))
{
hSound->SetLocation(CVector3D((sndDist * sin(offSet)), 0, sndDist * cos(offSet)));
if (TestFlag(eDistanceless))
hSound->SetRollOff(initialRolllOff);
else
hSound->SetRollOff(itemRollOff);
}
if (TestFlag(eRandPitch))
hSound->SetPitch(RandFloat(m_PitchLower, m_PitchUpper));
else
hSound->SetPitch(m_Pitch);
ALfloat theGain = m_Gain;
if (TestFlag(eRandGain))
theGain = RandFloat(m_GainLower, m_GainUpper);
hSound->SetCone(m_ConeInnerAngle, m_ConeOuterAngle, m_ConeOuterGain);
g_SoundManager->PlayGroupItem(hSound, theGain);
}
}
static void HandleError(const std::wstring& message, const VfsPath& pathname, Status err)
static void HandleError(const CStrW& message, const VfsPath& pathname, Status err)
{
if(err == ERR::AGAIN)
if (err == ERR::AGAIN)
return; // open failed because sound is disabled (don't log this)
LOGERROR(L"%ls: pathname=%ls, error=%ls", message.c_str(), pathname.string().c_str(), ErrorString(err));
}
void CSoundGroup::PlayNext(const CVector3D& position)
{
if(m_Intensity >= m_IntensityThreshold && !DISABLE_INTENSITY)
{
if(!snd_is_playing(m_hReplacement))
{
// load up replacement file
const VfsPath pathname(m_filepath / m_intensity_file);
m_hReplacement = snd_open(g_VFS, pathname);
if(m_hReplacement < 0)
{
HandleError(L"PlayNext: snd_open for replacement file failed", pathname, (Status)m_hReplacement);
return;
}
UploadPropertiesAndPlay(m_hReplacement, position);
}
}
else
{
// if no sounds, return
if (filenames.size() == 0)
return;
// try loading on the fly only when we need the sound to see if that fixes release problems...
if(TestFlag(eRandOrder))
m_index = (size_t)rand(0, (size_t)filenames.size());
// (note: previously snd_group[m_index] was used in place of hs)
const VfsPath pathname(m_filepath / filenames[m_index]);
Handle hs = snd_open(g_VFS, pathname);
if(hs < 0)
{
HandleError(L"PlayNext: snd_open failed", pathname, (Status)hs);
return;
}
UploadPropertiesAndPlay(hs, position);
}
// if no sounds, return
if (filenames.size() == 0)
return;
playtimes.at(m_index) = 0.0f;
m_index++;
m_Intensity++;
if(m_Intensity > m_IntensityThreshold)
m_Intensity = m_IntensityThreshold;
if(m_index >= filenames.size())
Reload();
m_index = (size_t)rand(0, (size_t)filenames.size());
UploadPropertiesAndPlay(m_index, position);
}
void CSoundGroup::Reload()
{
m_index = 0; // reset our index
// get rid of the used handles
snd_group.clear();
// clear out the old timers
playtimes.clear();
//Reload the sounds
/*for(size_t i = 0; i < filenames.size(); i++)
{
string szTemp = m_filepath + filenames[i];
Handle temp = snd_open(m_filepath + filenames[i]);
snd_set_gain(temp, m_Gain);
snd_set_pitch(temp, m_Pitch);
snd_set_cone(temp, m_ConeInnerAngle, m_ConeOuterAngle, m_ConeOuterGain);
snd_group.push_back(temp);
}*/
while(playtimes.size() < filenames.size())
playtimes.push_back(-1.0f);
//if(TestFlag(eRandOrder))
//random_shuffle(snd_group.begin(), snd_group.end());
snd_group.clear();
for (size_t i = 0; i < filenames.size(); i++)
{
VfsPath thePath = m_filepath/filenames[i];
ISoundItem* temp = g_SoundManager->LoadItem(&thePath);
if (temp == NULL)
HandleError(L"error loading sound", thePath, NULL);
else
snd_group.push_back(temp);
}
if (TestFlag(eRandOrder))
random_shuffle(snd_group.begin(), snd_group.end());
}
void CSoundGroup::ReleaseGroup()
{
for(size_t i = m_index; i<snd_group.size(); i++)
for (size_t i = 0; i < snd_group.size(); i++)
{
//if(!snd_is_playing(snd_group[i]))
snd_free(snd_group[i]);
snd_group[i]->FadeAndDelete(0.2);
}
snd_group.clear();
playtimes.clear();
//if(snd_is_playing(m_hReplacement))
// snd_free(m_hReplacement);
m_index = 0;
}
void CSoundGroup::Update(float TimeSinceLastFrame)
void CSoundGroup::Update(float UNUSED(TimeSinceLastFrame))
{
for(size_t i = 0; i < playtimes.size(); i++)
{
if(playtimes[i] >= 0.0f)
playtimes[i] += TimeSinceLastFrame;
if(playtimes[i] >= m_Decay)
{
playtimes[i] = -1.0f;
m_Intensity--;
}
}
}
bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
{
// LOGERROR(L"loading new sound group '%ls'", pathnameXML.string().c_str());
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK)
{
HandleError(L"error loading file", pathnameXML, NULL);
return false;
}
// Define elements used in XML file
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
@ -241,6 +263,7 @@ bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
EL(gain);
EL(looping);
EL(omnipresent);
EL(distanceless);
EL(pitch);
EL(priority);
EL(randorder);
@ -257,7 +280,6 @@ bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
EL(path);
EL(threshold);
EL(decay);
EL(replacement);
#undef AT
#undef EL
@ -288,6 +310,11 @@ bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
if(child.GetText().ToInt() == 1)
SetFlag(eOmnipresent);
}
else if(child_name == el_distanceless)
{
if(child.GetText().ToInt() == 1)
SetFlag(eDistanceless);
}
else if(child_name == el_pitch)
{
this->m_Pitch = child.GetText().ToFloat();
@ -355,12 +382,7 @@ bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
{
m_Decay = child.GetText().ToFloat();
}
else if(child_name == el_replacement)
{
m_intensity_file = child.GetText().FromUTF8();
}
}
Reload();
return true;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -17,10 +17,10 @@
/**
* =========================================================================
* File : SoundGroup.h
* Project : 0 A.D.
* File : SoundGroup.h
* Project : 0 A.D.
* Description : Loads up a group of sound files with shared properties,
* and provides a simple interface for playing them.
* and provides a simple interface for playing them.
* =========================================================================
*/
@ -49,24 +49,24 @@ Example SoundGroup.xml
*/
#ifndef INCLUDED_SOUNDGROUP
#define INCLUDED_SOUNDGROUP
#ifndef INCLUDED_SOUNDGROUP_H
#define INCLUDED_SOUNDGROUP_H
#include "lib/res/handle.h"
#include "lib/file/vfs/vfs_path.h"
#include "ps/CStr.h"
#include "maths/Vector3D.h"
#include "lib/res/sound/snd_mgr.h"
#include <vector>
class CVector3D;
class ISoundItem;
enum eSndGrpFlags
{
eRandOrder = 0x01,
eRandGain = 0x02,
eRandPitch = 0x04,
eLoop = 0x08,
eOmnipresent = 0x10
eRandOrder = 0x01,
eRandGain = 0x02,
eRandPitch = 0x04,
eLoop = 0x08,
eOmnipresent = 0x10,
eDistanceless = 0x20
};
@ -75,14 +75,16 @@ class CSoundGroup
NONCOPYABLE(CSoundGroup);
public:
CSoundGroup(const VfsPath& pathnameXML);
CSoundGroup(void);
~CSoundGroup(void);
CSoundGroup();
~CSoundGroup();
// Play next sound in group
// @param position world position of the entity generating the sound
// (ignored if the eOmnipresent flag is set)
void PlayNext(const CVector3D& position);
float RadiansOffCenter(const CVector3D& position, bool& onScreen, float& itemRollOff);
// Load a group
bool LoadSoundGroup(const VfsPath& pathnameXML);
@ -102,18 +104,15 @@ public:
private:
void SetGain(float gain);
void UploadPropertiesAndPlay(Handle hSound, const CVector3D& position);
void UploadPropertiesAndPlay(int theIndex, const CVector3D& position);
void SetDefaultValues();
size_t m_index; // index of the next sound to play
Handle m_hReplacement;
std::vector<Handle> snd_group; // we store the handles so we can load now and play later
std::vector<ISoundItem*> snd_group; // we store the handles so we can load now and play later
std::vector<std::wstring> filenames; // we need the filenames so we can reload when necessary.
std::vector<float> playtimes; // it would be better to store this in with the Handles perhaps?
VfsPath m_filepath; // the file path for the list of sound file resources
std::wstring m_intensity_file; // the replacement aggregate 'intense' sound
float m_CurTime; // Time elapsed since soundgroup was created
float m_TimeWindow; // The Intensity Threshold Window
@ -134,4 +133,4 @@ private:
float m_ConeOuterAngle;
};
#endif //#ifndef INCLUDED_SOUNDGROUP
#endif //#ifndef INCLUDED_SOUNDGROUP_H

View File

@ -0,0 +1,75 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "SoundPlayer.h"
#include "lib/utf8.h"
#include "maths/Vector3D.h"
#include "ps/Filesystem.h"
#include "soundmanager/SoundManager.h"
JSoundPlayer::JSoundPlayer()
{
}
JSoundPlayer::~JSoundPlayer()
{
}
bool JSoundPlayer::StartMusic(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
g_SoundManager->SetMusicEnabled(true);
return true;
}
// request the sound be played until free() is called. returns immediately.
bool JSoundPlayer::StopMusic(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
g_SoundManager->SetMusicEnabled(false);
return true;
}
void JSoundPlayer::ScriptingInit()
{
AddMethod<CStr, &JSoundPlayer::ToString>("toString", 0);
AddMethod<bool, &JSoundPlayer::StartMusic>("startMusic", 0);
AddMethod<bool, &JSoundPlayer::StopMusic>("stopMusic", 0);
CJSObject<JSoundPlayer>::ScriptingInit("SoundPlayer", &JSoundPlayer::Construct, 1);
}
JSBool JSoundPlayer::Construct(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* vp)
{
JSoundPlayer* newObject = new JSoundPlayer();
newObject->m_EngineOwned = false;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(newObject->GetScript()));
return JS_TRUE;
}
CStr JSoundPlayer::ToString(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
std::ostringstream stringStream;
stringStream << "[object MusicPlayer]";
return stringStream.str();
}

View File

@ -0,0 +1,58 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// JS sound binding
// interface rationale:
// - can't just expose fire and forget playSound to script code:
// we sometimes need to loop until a certain condition is met
// (e.g. building is complete) => need means of access (Handle) to sound.
//
// - the current 64-bit Handle can't be stored as-is by JS code;
// we could make it 32 bit, but that limits its usefulness
// (barely enough tag bits).
//
// - instead, we provide a thin class wrapper (using scriptableobject.h)
// on top of the snd API that encapsulates the Handle.
#ifndef INCLUDED_JSOUNDPLAYER
#define INCLUDED_JSOUNDPLAYER
#include "scripting/ScriptableObject.h"
#include "soundmanager/items/ISoundItem.h"
class JSoundPlayer : public CJSObject<JSoundPlayer>
{
public:
JSoundPlayer();
virtual ~JSoundPlayer();
// Script-bound functions
CStr ToString(JSContext* cx, uintN argc, jsval* argv);
bool StartMusic(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv));
bool StopMusic(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv));
static JSBool Construct(JSContext* cx, uintN argc, jsval* vp);
static void ScriptingInit();
protected:
VfsPath* m_FileName;
};
#endif // #ifndef INCLUDED_JSOUNDPLAYER