Merge branch 'ehofman/sound'
Changed include of SGQuat.hxx to SGMath.hxx in sound/sample_openal.cxx; somehow this happened in a merge commit when the sound branch was merged to next.
This commit is contained in:
@@ -131,9 +131,35 @@
|
||||
//
|
||||
|
||||
#ifdef __APPLE__
|
||||
# ifdef __GNUC__
|
||||
# if ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 3 )
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
#else
|
||||
# else
|
||||
// any C++ header file undefines isinf and isnan
|
||||
// so this should be included before <iostream>
|
||||
// the functions are STILL in libm (libSystem on mac os x)
|
||||
extern "C" int (isnan)(double);
|
||||
extern "C" int (isinf)(double);
|
||||
# endif
|
||||
# else
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__)
|
||||
# if __FreeBSD_version < 500000
|
||||
extern "C" {
|
||||
inline int isnan(double r) { return !(r <= 0 || r >= 0); }
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__CYGWIN__)
|
||||
# include <ieeefp.h> // isnan
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
# define isnan(x) _isnan(x)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/math/point3d.hxx>
|
||||
#include <simgear/math/polar3d.hxx>
|
||||
#include <simgear/sound/soundmgr_openal.hxx>
|
||||
#include <simgear/sound/sample_group.hxx>
|
||||
#include <simgear/scene/sky/cloudfield.hxx>
|
||||
#include <simgear/scene/sky/newcloud.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
@@ -175,7 +175,7 @@ SGEnviro::SGEnviro() :
|
||||
lightning_enable_state(false),
|
||||
elapsed_time(0.0),
|
||||
dt(0.0),
|
||||
soundMgr(NULL),
|
||||
sampleGroup(NULL),
|
||||
snd_active(false),
|
||||
snd_dist(0.0),
|
||||
min_time_before_lt(0.0),
|
||||
@@ -189,6 +189,8 @@ SGEnviro::SGEnviro() :
|
||||
}
|
||||
|
||||
SGEnviro::~SGEnviro(void) {
|
||||
if (sampleGroup) delete sampleGroup;
|
||||
|
||||
// OSGFIXME
|
||||
return;
|
||||
list_of_lightning::iterator iLightning;
|
||||
@@ -530,8 +532,8 @@ void SGEnviro::drawRain(double pitch, double roll, double heading, double hspeed
|
||||
|
||||
}
|
||||
|
||||
void SGEnviro::set_soundMgr(SGSoundMgr *mgr) {
|
||||
soundMgr = mgr;
|
||||
void SGEnviro::set_sampleGroup(SGSampleGroup *sgr) {
|
||||
sampleGroup = sgr;
|
||||
}
|
||||
|
||||
void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double heading, double hspeed) {
|
||||
@@ -616,7 +618,7 @@ void SGLightning::lt_build(void) {
|
||||
top[PY] = alt;
|
||||
top[PZ] = 0;
|
||||
lt_build_tree_branch(0, top, 1.0, 50, top[PY] / 8.0);
|
||||
if( ! sgEnviro.soundMgr )
|
||||
if( ! sgEnviro.sampleGroup )
|
||||
return;
|
||||
Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
|
||||
@@ -751,15 +753,15 @@ void SGEnviro::drawLightning(void) {
|
||||
double ax = 0.0, ay = 0.0;
|
||||
ax = cos(course) * dist;
|
||||
ay = sin(course) * dist;
|
||||
SGSharedPtr<SGSoundSample> snd = soundMgr->find("thunder");
|
||||
SGSharedPtr<SGSoundSample> snd = sampleGroup->find("thunder");
|
||||
if( snd ) {
|
||||
ALfloat pos[3]={ax, ay, -sgEnviro.last_alt };
|
||||
snd->set_source_pos(pos);
|
||||
SGVec3d pos = SGVec3d(ax, ay, -sgEnviro.last_alt);
|
||||
snd->set_position(pos);
|
||||
snd->play_once();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if( !soundMgr->is_playing("thunder") ) {
|
||||
if( !sampleGroup->is_playing("thunder") ) {
|
||||
snd_active = false;
|
||||
snd_playing = false;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ using std::vector;
|
||||
using std::string;
|
||||
|
||||
class SGLightning;
|
||||
class SGSoundMgr;
|
||||
class SGSampleGroup;
|
||||
|
||||
/**
|
||||
* Simulate some echo on a weather radar.
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
sgVec4 fog_color;
|
||||
sgMat4 transform;
|
||||
double last_lon, last_lat, last_alt;
|
||||
SGSoundMgr *soundMgr;
|
||||
SGSampleGroup *sampleGroup;
|
||||
bool snd_active, snd_playing;
|
||||
double snd_timer, snd_wait, snd_pos_lat, snd_pos_lon, snd_dist;
|
||||
double min_time_before_lt;
|
||||
@@ -243,7 +243,7 @@ public:
|
||||
* Forward the sound manager instance to be able to play samples.
|
||||
* @param mgr a running sound manager
|
||||
*/
|
||||
void set_soundMgr(SGSoundMgr *mgr);
|
||||
void set_sampleGroup(SGSampleGroup *sgr);
|
||||
|
||||
void setFOV( float w, float h );
|
||||
void getFOV( float &w, float &h );
|
||||
|
||||
@@ -7,30 +7,43 @@ lib_LIBRARIES = libsgsound.a
|
||||
noinst_HEADERS =
|
||||
|
||||
include_HEADERS = \
|
||||
sample_group.hxx \
|
||||
sample_openal.hxx \
|
||||
soundmgr_openal.hxx \
|
||||
xmlsound.hxx
|
||||
|
||||
libsgsound_a_SOURCES = \
|
||||
sample_group.cxx \
|
||||
sample_openal.cxx \
|
||||
soundmgr_openal.cxx \
|
||||
xmlsound.cxx
|
||||
|
||||
#noinst_PROGRAMS = openal_test1 openal_test2
|
||||
check_PROGRAMS = openal_test1 openal_test2 openal_test3
|
||||
|
||||
#openal_test1_SOURCES = openal_test1.cxx
|
||||
#openal_test2_SOURCES = openal_test2.cxx
|
||||
openal_test1_SOURCES = openal_test1.cxx
|
||||
openal_test2_SOURCES = openal_test2.cxx
|
||||
openal_test3_SOURCES = openal_test3.cxx
|
||||
|
||||
#openal_test1_LDADD = \
|
||||
# $(top_builddir)/simgear/debug/libsgdebug.a \
|
||||
# $(openal_LIBS)
|
||||
openal_test1_LDADD = \
|
||||
$(top_builddir)/simgear/debug/libsgdebug.a \
|
||||
$(openal_LIBS)
|
||||
|
||||
#openal_test2_LDADD = \
|
||||
# libsgsound.a \
|
||||
# $(top_builddir)/simgear/debug/libsgdebug.a \
|
||||
# $(top_builddir)/simgear/misc/libsgmisc.a \
|
||||
# $(top_builddir)/simgear/structure/libsgstructure.a \
|
||||
# $(openal_LIBS) \
|
||||
# -lOpenThreads
|
||||
openal_test2_LDADD = \
|
||||
libsgsound.a \
|
||||
$(top_builddir)/simgear/structure/libsgstructure.a \
|
||||
$(top_builddir)/simgear/timing/libsgtiming.a \
|
||||
$(top_builddir)/simgear/debug/libsgdebug.a \
|
||||
$(top_builddir)/simgear/misc/libsgmisc.a \
|
||||
$(top_builddir)/simgear/math/libsgmath.a \
|
||||
$(openal_LIBS)
|
||||
|
||||
openal_test3_LDADD = \
|
||||
libsgsound.a \
|
||||
$(top_builddir)/simgear/structure/libsgstructure.a \
|
||||
$(top_builddir)/simgear/timing/libsgtiming.a \
|
||||
$(top_builddir)/simgear/debug/libsgdebug.a \
|
||||
$(top_builddir)/simgear/misc/libsgmisc.a \
|
||||
$(top_builddir)/simgear/math/libsgmath.a \
|
||||
$(openal_LIBS) -lstdc++
|
||||
|
||||
INCLUDES = -I$(top_srcdir) -DSRC_DIR=\"$(top_srcdir)/simgear/sound\"
|
||||
|
||||
40
simgear/sound/README
Normal file
40
simgear/sound/README
Normal file
@@ -0,0 +1,40 @@
|
||||
|
||||
All code in this directory uses the OpenAL coordinate system for maximum
|
||||
useablity. The OpenAL coordinate system is equal to that of OpenGL with the
|
||||
main difference that objects behind the viewer can still be heard:
|
||||
- positive x is to the right
|
||||
- positive y is upwards
|
||||
- positive z is towards the back of the viewer/listener
|
||||
|
||||
see: http://www.falloutsoftware.com/tutorials/gl/cartesian.gif
|
||||
|
||||
All positions are in cartesian space with a unit length of one meter.
|
||||
|
||||
Velocities are three tuples indicating speed and direction in the same space
|
||||
as positions do (so they are not in the models local space).
|
||||
|
||||
|
||||
There is one SoundMgr that handles multiple SoundGroup classes.
|
||||
Each SoundGroup class handles multiple SoundSample classes.
|
||||
|
||||
A SoundSample class defines the properties of each individual sound like
|
||||
pitch, volume, position, orientation, sound cone paramters, etc. This
|
||||
class can be created all over the code but *has* to be assigned to a
|
||||
SampelGroup before you can hear it. Current sample groups are "atc",
|
||||
"avionics" and "fx" for the master airplane effects. The position of a
|
||||
SoundSample is relative to (0,0,0) of the model and hence relative to
|
||||
the base position of the SampleGroup.
|
||||
|
||||
A SampleGroup class has to be assigned to the SoundManager to be heard
|
||||
and holds data of a group of samples (maybe 'sample cloud' might be a
|
||||
good description). This class has to be created for each individual
|
||||
model that can produce one or more sounds. The SampleGroup class can be
|
||||
altered by modifying it's volume, position and orientation.
|
||||
Repositioning this class also means repositioning all it's associated
|
||||
samples. Altering it's orientation also means repositioning the absolute
|
||||
(real world) position (and orientation) of the associated sound samples.
|
||||
|
||||
The SoundMaganer can be repositioned which basically means moving the
|
||||
listener around. It's also possible to alter the listener orientation en
|
||||
velocity with this class, together with the master volume.
|
||||
|
||||
Binary file not shown.
@@ -14,6 +14,10 @@ static unsigned int sleep(unsigned int secs) { return 0; }
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alc.h>
|
||||
# include <OpenAL/alut.h>
|
||||
#elif defined(OPENALSDK)
|
||||
# include <al.h>
|
||||
# include <alc.h>
|
||||
# include <AL/alut.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alc.h>
|
||||
@@ -46,6 +50,9 @@ int main( int argc, char *argv[] ) {
|
||||
ALCdevice *dev;
|
||||
ALCcontext *context;
|
||||
|
||||
alutInit(&argc, argv);
|
||||
sglog().setLogLevels( SG_ALL, SG_ALERT );
|
||||
|
||||
// initialize OpenAL
|
||||
if ( (dev = alcOpenDevice( NULL )) != NULL
|
||||
&& ( context = alcCreateContext( dev, NULL )) != NULL ) {
|
||||
@@ -158,6 +165,7 @@ int main( int argc, char *argv[] ) {
|
||||
alSourcePlay( source );
|
||||
|
||||
sleep(10);
|
||||
alutExit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,54 +1,102 @@
|
||||
#include <stdio.h>
|
||||
#ifdef __MINGW32__
|
||||
// This is broken, but allows the file to compile without a POSIX
|
||||
// environment.
|
||||
static unsigned int sleep(unsigned int secs) { return 0; }
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define sleep(x) Sleep(x*1000)
|
||||
#else
|
||||
#include <unistd.h> // sleep()
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "sample_openal.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
SGGeod pos = SGGeod::fromDeg(0,0);
|
||||
|
||||
int main( int argc, char *argv[] ) {
|
||||
SGSoundMgr sm;
|
||||
SGSampleGroup *sgr;
|
||||
SGSoundMgr *smgr;
|
||||
|
||||
SGSoundSample sample1( SRC_DIR, "jet.wav" );
|
||||
sample1.set_volume(0.5);
|
||||
sample1.set_volume(0.2);
|
||||
sample1.play_looped();
|
||||
smgr = new SGSoundMgr;
|
||||
|
||||
smgr->bind();
|
||||
smgr->init();
|
||||
sgr = smgr->find("default", true);
|
||||
smgr->set_volume(0.9);
|
||||
smgr->set_position( SGVec3d::fromGeod(pos), pos );
|
||||
smgr->activate();
|
||||
|
||||
SGSoundSample *sample1 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample1->set_volume(1.0);
|
||||
sample1->set_pitch(1.0);
|
||||
sample1->play_looped();
|
||||
sgr->add(sample1, "sound1");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample1\n");
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample2( SRC_DIR, "jet.wav" );
|
||||
sample2.set_volume(0.5);
|
||||
sample2.set_pitch(0.4);
|
||||
sample2.play_looped();
|
||||
SGSoundSample *sample2 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample2->set_volume(0.5);
|
||||
sample2->set_pitch(0.4);
|
||||
sample2->play_looped();
|
||||
sgr->add(sample2, "sound2");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample2\n");
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample3( SRC_DIR, "jet.wav" );
|
||||
sample3.set_volume(0.5);
|
||||
sample3.set_pitch(0.8);
|
||||
sample3.play_looped();
|
||||
SGSoundSample *sample3 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample3->set_volume(0.5);
|
||||
sample3->set_pitch(0.8);
|
||||
sample3->play_looped();
|
||||
sgr->add(sample3, "sound3");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample3\n");
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample4( SRC_DIR, "jet.wav" );
|
||||
sample4.set_volume(0.5);
|
||||
sample4.set_pitch(1.2);
|
||||
sample4.play_looped();
|
||||
SGSoundSample *sample4 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample4->set_volume(0.5);
|
||||
sample4->set_pitch(1.2);
|
||||
sample4->play_looped();
|
||||
sgr->add(sample4, "sound4");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample4\n");
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample5( SRC_DIR, "jet.wav" );
|
||||
sample5.set_volume(0.5);
|
||||
sample5.set_pitch(1.6);
|
||||
sample5.play_looped();
|
||||
SGSoundSample *sample5 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample5->set_volume(0.5);
|
||||
sample5->set_pitch(1.6);
|
||||
sample5->play_looped();
|
||||
sgr->add(sample5, "sound5");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample5\n");
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample6( SRC_DIR, "jet.wav" );
|
||||
sample6.set_volume(0.5);
|
||||
sample6.set_pitch(2.0);
|
||||
sample6.play_looped();
|
||||
SGSoundSample *sample6 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample6->set_volume(0.5);
|
||||
sample6->set_pitch(2.0);
|
||||
sample6->play_looped();
|
||||
sgr->add(sample6, "sound6");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample6\n");
|
||||
sleep(1);
|
||||
|
||||
sleep(10);
|
||||
for (int i=0; i<10; i++) {
|
||||
sleep(1);
|
||||
smgr->update(1);
|
||||
}
|
||||
|
||||
sgr->stop("sound1");
|
||||
sgr->stop("sound2");
|
||||
sgr->stop("sound3");
|
||||
sleep(0.5);
|
||||
smgr->update(0.5);
|
||||
sgr->stop("sound4");
|
||||
sgr->stop("sound5");
|
||||
sgr->stop("sound6");
|
||||
smgr->update(1);
|
||||
sleep(1);
|
||||
|
||||
smgr->unbind();
|
||||
sleep(2);
|
||||
delete smgr;
|
||||
}
|
||||
|
||||
57
simgear/sound/openal_test3.cxx
Normal file
57
simgear/sound/openal_test3.cxx
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define sleep(x) Sleep(x*1000)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
|
||||
int main( int argc, char *argv[] ) {
|
||||
SGSampleGroup *sgr;
|
||||
SGSoundMgr *smgr;
|
||||
SGGeod pos;
|
||||
|
||||
smgr = new SGSoundMgr;
|
||||
|
||||
smgr->bind();
|
||||
smgr->init();
|
||||
sgr = smgr->find("default", true);
|
||||
smgr->set_volume(0.9);
|
||||
smgr->activate();
|
||||
|
||||
printf("default position and orientation\n");
|
||||
SGSoundSample *sample1 = new SGSoundSample( SRC_DIR, "jet.wav" );
|
||||
sample1->set_volume(1.0);
|
||||
sample1->set_pitch(1.0);
|
||||
sample1->play_looped();
|
||||
sgr->add(sample1, "sound1");
|
||||
smgr->update(1.0);
|
||||
printf("playing sample\n");
|
||||
sleep(3);
|
||||
sample1->stop();
|
||||
smgr->update(3.0);
|
||||
sleep(1);
|
||||
|
||||
printf("source at lat,lon = (10,-10), listener at (9.99,-9.99)\n");
|
||||
pos = SGGeod::fromDeg(9.99,-9.99);
|
||||
sample1->set_position( SGVec3d::fromGeod(SGGeod::fromDeg(10,-10)) );
|
||||
smgr->set_position( SGVec3d::fromGeod(pos), pos );
|
||||
sample1->play_looped();
|
||||
smgr->update(1.0);
|
||||
printf("playing sample\n");
|
||||
sleep(3);
|
||||
sample1->stop();
|
||||
smgr->update(3.0);
|
||||
sleep(1);
|
||||
|
||||
sgr->remove("sound1");
|
||||
smgr->unbind();
|
||||
sleep(2);
|
||||
delete smgr;
|
||||
}
|
||||
453
simgear/sound/sample_group.cxx
Normal file
453
simgear/sound/sample_group.cxx
Normal file
@@ -0,0 +1,453 @@
|
||||
// sample_group.cxx -- Manage a group of samples relative to a base position
|
||||
//
|
||||
// Written for the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
#include "sample_group.hxx"
|
||||
|
||||
bool isNaN(float *v) {
|
||||
return (isnan(v[0]) || isnan(v[1]) || isnan(v[2]));
|
||||
}
|
||||
|
||||
SGSampleGroup::SGSampleGroup () :
|
||||
_smgr(NULL),
|
||||
_refname(""),
|
||||
_active(false),
|
||||
_changed(false),
|
||||
_pause(false),
|
||||
_volume(1.0),
|
||||
_tied_to_listener(false),
|
||||
_velocity(SGVec3d::zeros()),
|
||||
_orientation(SGQuatd::zeros())
|
||||
{
|
||||
_samples.clear();
|
||||
}
|
||||
|
||||
SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr, const string &refname ) :
|
||||
_smgr(smgr),
|
||||
_refname(refname),
|
||||
_active(false),
|
||||
_changed(false),
|
||||
_pause(false),
|
||||
_volume(1.0),
|
||||
_tied_to_listener(false),
|
||||
_velocity(SGVec3d::zeros()),
|
||||
_orientation(SGQuatd::zeros())
|
||||
{
|
||||
_smgr->add(this, refname);
|
||||
_samples.clear();
|
||||
}
|
||||
|
||||
SGSampleGroup::~SGSampleGroup ()
|
||||
{
|
||||
_active = false;
|
||||
|
||||
sample_map_iterator sample_current = _samples.begin();
|
||||
sample_map_iterator sample_end = _samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
|
||||
if ( sample->is_valid_source() && sample->is_playing() ) {
|
||||
sample->no_valid_source();
|
||||
_smgr->release_source( sample->get_source() );
|
||||
_smgr->release_buffer( sample );
|
||||
}
|
||||
}
|
||||
|
||||
_smgr = 0;
|
||||
}
|
||||
|
||||
void SGSampleGroup::update( double dt ) {
|
||||
|
||||
if ( !_active || _pause ) return;
|
||||
|
||||
testForALError("start of update!!\n");
|
||||
|
||||
// Delete any OpenAL buffers that might still be in use.
|
||||
unsigned int size = _removed_samples.size();
|
||||
for (unsigned int i=0; i<size; ) {
|
||||
SGSoundSample *sample = _removed_samples[i];
|
||||
ALint result = AL_STOPPED;
|
||||
|
||||
if ( sample->is_valid_source() ) {
|
||||
if ( sample->is_looping() ) {
|
||||
sample->no_valid_source();
|
||||
_smgr->release_source( sample->get_source() );
|
||||
}
|
||||
else
|
||||
alGetSourcei( sample->get_source(), AL_SOURCE_STATE, &result );
|
||||
}
|
||||
|
||||
if ( result == AL_STOPPED ) {
|
||||
ALuint buffer = sample->get_buffer();
|
||||
alDeleteBuffers( 1, &buffer );
|
||||
testForALError("buffer remove");
|
||||
_removed_samples.erase( _removed_samples.begin()+i );
|
||||
size--;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// Update the position and orientation information for all samples.
|
||||
if ( _changed || _smgr->has_changed() ) {
|
||||
update_pos_and_orientation();
|
||||
_changed = false;
|
||||
}
|
||||
|
||||
sample_map_iterator sample_current = _samples.begin();
|
||||
sample_map_iterator sample_end = _samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
|
||||
if ( !sample->is_valid_source() && sample->is_playing() ) {
|
||||
//
|
||||
// a request to start playing a sound has been filed.
|
||||
//
|
||||
if ( _smgr->request_buffer(sample) == SGSoundMgr::NO_BUFFER )
|
||||
continue;
|
||||
|
||||
// start playing the sample
|
||||
ALuint buffer = sample->get_buffer();
|
||||
ALuint source = _smgr->request_source();
|
||||
if (alIsSource(source) == AL_TRUE && alIsBuffer(buffer) == AL_TRUE)
|
||||
{
|
||||
sample->set_source( source );
|
||||
|
||||
alSourcei( source, AL_BUFFER, buffer );
|
||||
testForALError("assign buffer to source");
|
||||
|
||||
sample->set_source( source );
|
||||
update_sample_config( sample );
|
||||
|
||||
ALboolean looping = sample->is_looping() ? AL_TRUE : AL_FALSE;
|
||||
alSourcei( source, AL_LOOPING, looping );
|
||||
alSourcef( source, AL_ROLLOFF_FACTOR, 0.3 );
|
||||
alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
|
||||
alSourcePlay( source );
|
||||
testForALError("sample play");
|
||||
} else {
|
||||
if (alIsBuffer(buffer) == AL_FALSE)
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "No such buffer!\n");
|
||||
// sample->no_valid_source();
|
||||
// sadly, no free source available at this time
|
||||
}
|
||||
|
||||
} else if ( sample->is_valid_source() && sample->has_changed() ) {
|
||||
if ( !sample->is_playing() ) {
|
||||
// a request to stop playing the sound has been filed.
|
||||
|
||||
sample->stop();
|
||||
sample->no_valid_source();
|
||||
_smgr->release_source( sample->get_source() );
|
||||
} else if ( _smgr->has_changed() ) {
|
||||
update_sample_config( sample );
|
||||
}
|
||||
|
||||
} else if ( sample->is_valid_source() ) {
|
||||
// check if the sound has stopped by itself
|
||||
|
||||
unsigned int source = sample->get_source();
|
||||
int result;
|
||||
|
||||
alGetSourcei( source, AL_SOURCE_STATE, &result );
|
||||
if ( result == AL_STOPPED ) {
|
||||
// sample is stoped because it wasn't looping
|
||||
sample->stop();
|
||||
sample->no_valid_source();
|
||||
_smgr->release_source( source );
|
||||
_smgr->release_buffer( sample );
|
||||
remove( sample->get_sample_name() );
|
||||
}
|
||||
}
|
||||
testForALError("update");
|
||||
}
|
||||
}
|
||||
|
||||
// add a sound effect, return true if successful
|
||||
bool SGSampleGroup::add( SGSharedPtr<SGSoundSample> sound, const string& refname ) {
|
||||
|
||||
sample_map_iterator sample_it = _samples.find( refname );
|
||||
if ( sample_it != _samples.end() ) {
|
||||
// sample name already exists
|
||||
return false;
|
||||
}
|
||||
|
||||
_samples[refname] = sound;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// remove a sound effect, return true if successful
|
||||
bool SGSampleGroup::remove( const string &refname ) {
|
||||
|
||||
sample_map_iterator sample_it = _samples.find( refname );
|
||||
if ( sample_it == _samples.end() ) {
|
||||
// sample was not found
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( sample_it->second->is_valid_buffer() )
|
||||
_removed_samples.push_back( sample_it->second );
|
||||
_samples.erase( sample_it );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return true of the specified sound exists in the sound manager system
|
||||
bool SGSampleGroup::exists( const string &refname ) {
|
||||
sample_map_iterator sample_it = _samples.find( refname );
|
||||
if ( sample_it == _samples.end() ) {
|
||||
// sample was not found
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return a pointer to the SGSoundSample if the specified sound exists
|
||||
// in the sound manager system, otherwise return NULL
|
||||
SGSoundSample *SGSampleGroup::find( const string &refname ) {
|
||||
sample_map_iterator sample_it = _samples.find( refname );
|
||||
if ( sample_it == _samples.end() ) {
|
||||
// sample was not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sample_it->second;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSampleGroup::stop ()
|
||||
{
|
||||
_pause = true;
|
||||
sample_map_iterator sample_current = _samples.begin();
|
||||
sample_map_iterator sample_end = _samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
|
||||
if ( sample->is_valid_source() ) {
|
||||
ALint source = sample->get_source();
|
||||
if ( sample->is_playing() ) {
|
||||
alSourceStop( source );
|
||||
alSourcei( source, AL_BUFFER, 0 );
|
||||
}
|
||||
_smgr->release_source( source );
|
||||
sample->no_valid_source();
|
||||
}
|
||||
|
||||
if (sample->is_valid_buffer() ) {
|
||||
_smgr->release_buffer( sample );
|
||||
sample->no_valid_buffer();
|
||||
}
|
||||
}
|
||||
testForALError("stop");
|
||||
}
|
||||
|
||||
// stop playing all associated samples
|
||||
void
|
||||
SGSampleGroup::suspend ()
|
||||
{
|
||||
if (_pause == false) {
|
||||
_pause = true;
|
||||
sample_map_iterator sample_current = _samples.begin();
|
||||
sample_map_iterator sample_end = _samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
|
||||
if ( sample->is_valid_source() && sample->is_playing() ) {
|
||||
alSourcePause( sample->get_source() );
|
||||
}
|
||||
}
|
||||
testForALError("suspend");
|
||||
}
|
||||
}
|
||||
|
||||
// resume playing all associated samples
|
||||
void
|
||||
SGSampleGroup::resume ()
|
||||
{
|
||||
if (_pause == true) {
|
||||
sample_map_iterator sample_current = _samples.begin();
|
||||
sample_map_iterator sample_end = _samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
|
||||
if ( sample->is_valid_source() && sample->is_playing() ) {
|
||||
alSourcePlay( sample->get_source() );
|
||||
}
|
||||
}
|
||||
testForALError("resume");
|
||||
_pause = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// tell the scheduler to play the indexed sample in a continuous loop
|
||||
bool SGSampleGroup::play( const string &refname, bool looping = false ) {
|
||||
SGSoundSample *sample = find( refname );
|
||||
|
||||
if ( sample == NULL ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sample->play( looping );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return true of the specified sound is currently being played
|
||||
bool SGSampleGroup::is_playing( const string& refname ) {
|
||||
SGSoundSample *sample = find( refname );
|
||||
|
||||
if ( sample == NULL ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( sample->is_playing() ) ? true : false;
|
||||
}
|
||||
|
||||
// immediate stop playing the sound
|
||||
bool SGSampleGroup::stop( const string& refname ) {
|
||||
SGSoundSample *sample = find( refname );
|
||||
|
||||
if ( sample == NULL ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sample->stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SGSampleGroup::set_volume( float vol )
|
||||
{
|
||||
if (vol > _volume*1.01 || vol < _volume*0.99) {
|
||||
_volume = vol;
|
||||
if (_volume < 0.0) _volume = 0.0;
|
||||
if (_volume > 1.0) _volume = 1.0;
|
||||
_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set the source position and orientation of all managed sounds
|
||||
void SGSampleGroup::update_pos_and_orientation() {
|
||||
|
||||
SGVec3d position = SGVec3d::fromGeod(_base_pos) - _smgr->get_position();
|
||||
SGQuatd hlOr = SGQuatd::fromLonLat(_base_pos);
|
||||
SGQuatd ec2body = hlOr*_orientation;
|
||||
|
||||
SGVec3f velocity = SGVec3f::zeros();
|
||||
if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
|
||||
velocity = toVec3f( hlOr.backTransform(_velocity*SG_FEET_TO_METER) );
|
||||
}
|
||||
|
||||
sample_map_iterator sample_current = _samples.begin();
|
||||
sample_map_iterator sample_end = _samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
sample->set_master_volume( _volume );
|
||||
sample->set_orientation( _orientation );
|
||||
sample->set_rotation( ec2body );
|
||||
sample->set_position( position );
|
||||
sample->set_velocity( velocity );
|
||||
}
|
||||
}
|
||||
|
||||
void SGSampleGroup::update_sample_config( SGSoundSample *sample ) {
|
||||
SGVec3f orientation, velocity;
|
||||
SGVec3d position;
|
||||
|
||||
if ( _tied_to_listener ) {
|
||||
orientation = _smgr->get_direction();
|
||||
position = SGVec3d::zeros();
|
||||
velocity = _smgr->get_velocity();
|
||||
} else {
|
||||
sample->update_pos_and_orientation();
|
||||
orientation = sample->get_orientation();
|
||||
position = sample->get_position();
|
||||
velocity = sample->get_velocity();
|
||||
}
|
||||
|
||||
if (_smgr->bad_doppler_effect()) {
|
||||
velocity *= 100.0f;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (length(position) > 20000)
|
||||
printf("%s source and listener distance greater than 20km!\n",
|
||||
_refname.c_str());
|
||||
if (isNaN(toVec3f(position).data())) printf("NaN in source position\n");
|
||||
if (isNaN(orientation.data())) printf("NaN in source orientation\n");
|
||||
if (isNaN(velocity.data())) printf("NaN in source velocity\n");
|
||||
#endif
|
||||
|
||||
unsigned int source = sample->get_source();
|
||||
alSourcefv( source, AL_POSITION, toVec3f(position).data() );
|
||||
alSourcefv( source, AL_VELOCITY, velocity.data() );
|
||||
alSourcefv( source, AL_DIRECTION, orientation.data() );
|
||||
testForALError("position and orientation");
|
||||
|
||||
alSourcef( source, AL_PITCH, sample->get_pitch() );
|
||||
alSourcef( source, AL_GAIN, sample->get_volume() );
|
||||
testForALError("pitch and gain");
|
||||
|
||||
if ( sample->has_static_data_changed() ) {
|
||||
alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() );
|
||||
alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() );
|
||||
alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() );
|
||||
testForALError("audio cone");
|
||||
|
||||
alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
|
||||
alSourcef( source, AL_REFERENCE_DISTANCE,
|
||||
sample->get_reference_dist() );
|
||||
testForALError("distance rolloff");
|
||||
}
|
||||
}
|
||||
|
||||
bool SGSampleGroup::testForError(void *p, string s)
|
||||
{
|
||||
if (p == NULL) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Error (sample group): " << s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SGSampleGroup::testForALError(string s)
|
||||
{
|
||||
ALenum error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (" << _refname << "): "
|
||||
<< alGetString(error) << " at " << s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
249
simgear/sound/sample_group.hxx
Normal file
249
simgear/sound/sample_group.hxx
Normal file
@@ -0,0 +1,249 @@
|
||||
// sample_group.hxx -- Manage a group of samples relative to a base position
|
||||
//
|
||||
// Written for the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* \file sample_group.hxx
|
||||
* sample groups contain all sounds related to one specific object and
|
||||
* have to be added to the sound manager, otherwise they won't get processed.
|
||||
*/
|
||||
|
||||
#ifndef _SG_SAMPLE_GROUP_OPENAL_HXX
|
||||
#define _SG_SAMPLE_GROUP_OPENAL_HXX 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <OpenAL/al.h>
|
||||
#elif defined(OPENALSDK)
|
||||
# include <al.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
typedef map < string, SGSharedPtr<SGSoundSample> > sample_map;
|
||||
typedef sample_map::iterator sample_map_iterator;
|
||||
typedef sample_map::const_iterator const_sample_map_iterator;
|
||||
|
||||
class SGSoundMgr;
|
||||
|
||||
class SGSampleGroup : public SGReferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Empty constructor for class encapsulation.
|
||||
* Note: The sample group should still be activated before use
|
||||
*/
|
||||
SGSampleGroup ();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Register this sample group at the sound manager using refname
|
||||
* Note: The sample group should still be activated before use
|
||||
* @param smgr Pointer to a pre-initialized sound manager class
|
||||
* @param refname Name of this group for reference purposes.
|
||||
*/
|
||||
SGSampleGroup ( SGSoundMgr *smgr, const string &refname );
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~SGSampleGroup ();
|
||||
|
||||
/**
|
||||
* Set the status of this sample group to active.
|
||||
*/
|
||||
inline void activate() { _active = true; }
|
||||
|
||||
/**
|
||||
* Update function.
|
||||
* Call this function periodically to update the OpenAL state of all
|
||||
* samples associated with this class. None op the configuration changes
|
||||
* take place without a call to this function.
|
||||
*/
|
||||
virtual void update (double dt);
|
||||
|
||||
/**
|
||||
* Register an audio sample to this group.
|
||||
* @param sound Pointer to a pre-initialized audio sample
|
||||
* @param refname Name of this audio sample for reference purposes
|
||||
* @return return true if successful
|
||||
*/
|
||||
bool add( SGSharedPtr<SGSoundSample> sound, const string& refname );
|
||||
|
||||
/**
|
||||
* Remove an audio sample from this group.
|
||||
* @param refname Reference name of the audio sample to remove
|
||||
* @return return true if successful
|
||||
*/
|
||||
bool remove( const string& refname );
|
||||
|
||||
/**
|
||||
* Test if a specified audio sample is registered at this sample group
|
||||
* @param refname Reference name of the audio sample to test for
|
||||
* @return true if the specified audio sample exists
|
||||
*/
|
||||
bool exists( const string& refname );
|
||||
|
||||
/**
|
||||
* Find a specified audio sample in this sample group
|
||||
* @param refname Reference name of the audio sample to find
|
||||
* @return A pointer to the SGSoundSample
|
||||
*/
|
||||
SGSoundSample *find( const string& refname );
|
||||
|
||||
/**
|
||||
* Stop all playing samples and set the source id to invalid.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Request to stop playing all audio samples until further notice.
|
||||
*/
|
||||
void suspend();
|
||||
|
||||
/**
|
||||
* Request to resume playing all audio samples.
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* Request to start playing the refered audio sample.
|
||||
* @param refname Reference name of the audio sample to start playing
|
||||
* @param looping Define if the sound should loop continuously
|
||||
* @return true if the audio sample exsists and is scheduled for playing
|
||||
*/
|
||||
bool play( const string& refname, bool looping );
|
||||
|
||||
/**
|
||||
* Request to start playing the refered audio sample looping.
|
||||
* @param refname Reference name of the audio sample to start playing
|
||||
* @return true if the audio sample exsists and is scheduled for playing
|
||||
*/
|
||||
inline bool play_looped( const string& refname ) {
|
||||
return play( refname, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to start playing the refered audio sample once.
|
||||
* @param refname Reference name of the audio sample to start playing
|
||||
* @return true if the audio sample exsists and is scheduled for playing
|
||||
*/
|
||||
inline bool play_once( const string& refname ) {
|
||||
return play( refname, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a referenced sample is playing or not.
|
||||
* @param refname Reference name of the audio sample to test for
|
||||
* @return True of the specified sound is currently playing
|
||||
*/
|
||||
bool is_playing( const string& refname );
|
||||
|
||||
/**
|
||||
* Request to stop playing the refered audio sample.
|
||||
* @param refname Reference name of the audio sample to stop
|
||||
* @return true if the audio sample exsists and is scheduled to stop
|
||||
*/
|
||||
bool stop( const string& refname );
|
||||
|
||||
/**
|
||||
* Set the master volume for this sample group.
|
||||
* @param vol Volume (must be between 0.0 and 1.0)
|
||||
*/
|
||||
void set_volume( float vol );
|
||||
|
||||
/**
|
||||
* Set the velocity vector of this sample group.
|
||||
* This is in the local frame coordinate system; x=north, y=east, z=down
|
||||
* @param vel Velocity vector
|
||||
*/
|
||||
void set_velocity( const SGVec3d& vel ) {
|
||||
_velocity = vel; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position of this sample group.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
|
||||
* @param pos Base position
|
||||
*/
|
||||
void set_position_geod( const SGGeod& pos ) {
|
||||
_base_pos = pos; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the orientation of this sample group.
|
||||
* @param ori Quaternation containing the orientation information
|
||||
*/
|
||||
void set_orientation( const SGQuatd& ori ) {
|
||||
_orientation = ori; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tie this sample group to the listener position, orientation and velocity
|
||||
*/
|
||||
void tie_to_listener() { _tied_to_listener = true; }
|
||||
|
||||
protected:
|
||||
SGSoundMgr *_smgr;
|
||||
string _refname;
|
||||
bool _active;
|
||||
|
||||
private:
|
||||
bool _changed;
|
||||
bool _pause;
|
||||
float _volume;
|
||||
bool _tied_to_listener;
|
||||
|
||||
SGVec3d _velocity;
|
||||
SGGeod _base_pos;
|
||||
SGQuatd _orientation;
|
||||
|
||||
sample_map _samples;
|
||||
std::vector< SGSharedPtr<SGSoundSample> > _removed_samples;
|
||||
|
||||
bool testForALError(string s);
|
||||
bool testForError(void *p, string s);
|
||||
|
||||
void update_pos_and_orientation();
|
||||
void update_sample_config( SGSoundSample *sound );
|
||||
};
|
||||
|
||||
#endif // _SG_SAMPLE_GROUP_OPENAL_HXX
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// sample.cxx -- Sound sample encapsulation class
|
||||
// sample_openal.cxx -- Audio sample encapsulation class
|
||||
//
|
||||
// Written by Curtis Olson, started April 2004.
|
||||
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@@ -15,8 +17,8 @@
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
@@ -24,20 +26,14 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#if defined( __APPLE__ )
|
||||
# define AL_ILLEGAL_ENUM AL_INVALID_ENUM
|
||||
# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alut.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alut.h>
|
||||
#endif
|
||||
#include <stdlib.h> // rand()
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
|
||||
@@ -45,477 +41,181 @@
|
||||
// SGSoundSample
|
||||
//
|
||||
|
||||
|
||||
static bool print_openal_error(const string &s = "unknown") {
|
||||
ALuint error = alGetError();
|
||||
if ( error == AL_NO_ERROR ) {
|
||||
return false;
|
||||
} else if ( error == AL_INVALID_NAME ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_INVALID_NAME): " << s );
|
||||
} else if ( error == AL_ILLEGAL_ENUM ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_ILLEGAL_ENUM): " << s );
|
||||
} else if ( error == AL_INVALID_VALUE ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_INVALID_VALUE): " << s );
|
||||
} else if ( error == AL_ILLEGAL_COMMAND ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_ILLEGAL_COMMAND): " << s );
|
||||
} else if ( error == AL_OUT_OF_MEMORY ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_OUT_OF_MEMORY): " << s );
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error );
|
||||
}
|
||||
return error != 0;
|
||||
}
|
||||
|
||||
// empty constructor
|
||||
SGSoundSample::SGSoundSample() :
|
||||
buffer(0),
|
||||
source(0),
|
||||
pitch(1.0),
|
||||
volume(1.0),
|
||||
reference_dist(500.0),
|
||||
max_dist(3000.),
|
||||
loop(AL_FALSE),
|
||||
#ifdef USE_SOFTWARE_DOPPLER
|
||||
doppler_pitch_factor(1),
|
||||
doppler_volume_factor(1),
|
||||
#endif
|
||||
playing(false),
|
||||
no_Doppler_effect(true)
|
||||
_absolute_pos(SGVec3d::zeros()),
|
||||
_relative_pos(SGVec3d::zeros()),
|
||||
_direction(SGVec3d::zeros()),
|
||||
_velocity(SGVec3f::zeros()),
|
||||
_orientation(SGQuatd::zeros()),
|
||||
_orivec(SGVec3f::zeros()),
|
||||
_base_pos(SGVec3d::zeros()),
|
||||
_rotation(SGQuatd::zeros()),
|
||||
_refname(random_string()),
|
||||
_data(NULL),
|
||||
_format(AL_FORMAT_MONO8),
|
||||
_size(0),
|
||||
_freq(0),
|
||||
_valid_buffer(false),
|
||||
_buffer(SGSoundMgr::NO_BUFFER),
|
||||
_valid_source(false),
|
||||
_source(SGSoundMgr::NO_SOURCE),
|
||||
_inner_angle(360.0),
|
||||
_outer_angle(360.0),
|
||||
_outer_gain(0.0),
|
||||
_pitch(1.0),
|
||||
_volume(1.0),
|
||||
_master_volume(1.0),
|
||||
_reference_dist(500.0),
|
||||
_max_dist(3000.0),
|
||||
_loop(AL_FALSE),
|
||||
_playing(false),
|
||||
_changed(true),
|
||||
_static_changed(true),
|
||||
_is_file(false)
|
||||
{
|
||||
}
|
||||
|
||||
// constructor
|
||||
SGSoundSample::SGSoundSample( const char *path, const char *file, bool _no_Doppler_effect ) :
|
||||
buffer(0),
|
||||
source(0),
|
||||
pitch(1.0),
|
||||
volume(1.0),
|
||||
reference_dist(500.0),
|
||||
max_dist(3000.),
|
||||
loop(AL_FALSE),
|
||||
#ifdef USE_SOFTWARE_DOPPLER
|
||||
doppler_pitch_factor(1),
|
||||
doppler_volume_factor(1),
|
||||
#endif
|
||||
playing(false),
|
||||
no_Doppler_effect(_no_Doppler_effect)
|
||||
SGSoundSample::SGSoundSample( const char *path, const char *file ) :
|
||||
_absolute_pos(SGVec3d::zeros()),
|
||||
_relative_pos(SGVec3d::zeros()),
|
||||
_direction(SGVec3d::zeros()),
|
||||
_velocity(SGVec3f::zeros()),
|
||||
_orientation(SGQuatd::zeros()),
|
||||
_orivec(SGVec3f::zeros()),
|
||||
_base_pos(SGVec3d::zeros()),
|
||||
_rotation(SGQuatd::zeros()),
|
||||
_refname(file),
|
||||
_data(NULL),
|
||||
_format(AL_FORMAT_MONO8),
|
||||
_size(0),
|
||||
_freq(0),
|
||||
_valid_buffer(false),
|
||||
_buffer(SGSoundMgr::NO_BUFFER),
|
||||
_valid_source(false),
|
||||
_source(SGSoundMgr::NO_SOURCE),
|
||||
_inner_angle(360.0),
|
||||
_outer_angle(360.0),
|
||||
_outer_gain(0.0),
|
||||
_pitch(1.0),
|
||||
_volume(1.0),
|
||||
_master_volume(1.0),
|
||||
_reference_dist(500.0),
|
||||
_max_dist(3000.0),
|
||||
_loop(AL_FALSE),
|
||||
_playing(false),
|
||||
_changed(true),
|
||||
_static_changed(true),
|
||||
_is_file(true)
|
||||
{
|
||||
SGPath samplepath( path );
|
||||
if ( strlen(file) ) {
|
||||
samplepath.append( file );
|
||||
}
|
||||
sample_name = samplepath.str();
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "From file sounds sample = "
|
||||
<< samplepath.str() );
|
||||
|
||||
source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
|
||||
offset_pos[0] = 0.0; offset_pos[1] = 0.0; offset_pos[2] = 0.0;
|
||||
source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
|
||||
direction[0] = 0.0; direction[1] = 0.0; direction[2] = 0.0;
|
||||
inner = outer = 360.0; outergain = 0.0;
|
||||
|
||||
// clear errors from elsewhere?
|
||||
alGetError();
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
alGenBuffers(1, &buffer);
|
||||
if ( print_openal_error("constructor (alGenBuffers)") ) {
|
||||
throw sg_exception("Failed to gen OpenAL buffer.");
|
||||
}
|
||||
|
||||
// Load the sample file
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
|
||||
buffer = alutCreateBufferFromFile(samplepath.c_str());
|
||||
if (buffer == AL_NONE) {
|
||||
ALenum error = alutGetError ();
|
||||
print_openal_error("constructor (alutCreateBufferFromFile)");
|
||||
throw sg_io_exception("Failed to load wav file: ",
|
||||
sg_location(string(alutGetErrorString (error))));
|
||||
}
|
||||
|
||||
#else
|
||||
//
|
||||
// pre 1.0 alut version
|
||||
//
|
||||
ALvoid* data = load_file(path, file);
|
||||
|
||||
// Copy data to the internal OpenAL buffer
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
|
||||
if ( print_openal_error("constructor (alBufferData)") ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Trying to use file " << file );
|
||||
throw sg_exception("Failed to buffer data.");
|
||||
}
|
||||
|
||||
alutUnloadWAV( format, data, size, freq );
|
||||
#endif
|
||||
|
||||
print_openal_error("constructor return");
|
||||
_refname = samplepath.str();
|
||||
}
|
||||
|
||||
// constructor
|
||||
SGSoundSample::SGSoundSample( unsigned char *_data, int len, int _freq, bool _no_Doppler_effect ) :
|
||||
buffer(0),
|
||||
source(0),
|
||||
pitch(1.0),
|
||||
volume(1.0),
|
||||
reference_dist(500.0),
|
||||
max_dist(3000.),
|
||||
loop(AL_FALSE),
|
||||
#ifdef USE_SOFTWARE_DOPPLER
|
||||
doppler_pitch_factor(1),
|
||||
doppler_volume_factor(1),
|
||||
#endif
|
||||
playing(false),
|
||||
no_Doppler_effect(_no_Doppler_effect)
|
||||
SGSoundSample::SGSoundSample( const unsigned char** data,
|
||||
int len, int freq, int format ) :
|
||||
_absolute_pos(SGVec3d::zeros()),
|
||||
_relative_pos(SGVec3d::zeros()),
|
||||
_direction(SGVec3d::zeros()),
|
||||
_velocity(SGVec3f::zeros()),
|
||||
_orientation(SGQuatd::zeros()),
|
||||
_orivec(SGVec3f::zeros()),
|
||||
_base_pos(SGVec3d::zeros()),
|
||||
_rotation(SGQuatd::zeros()),
|
||||
_refname(random_string()),
|
||||
_format(format),
|
||||
_size(len),
|
||||
_freq(freq),
|
||||
_valid_buffer(false),
|
||||
_buffer(SGSoundMgr::NO_BUFFER),
|
||||
_valid_source(false),
|
||||
_source(SGSoundMgr::NO_SOURCE),
|
||||
_inner_angle(360.0),
|
||||
_outer_angle(360.0),
|
||||
_outer_gain(0.0),
|
||||
_pitch(1.0),
|
||||
_volume(1.0),
|
||||
_master_volume(1.0),
|
||||
_reference_dist(500.0),
|
||||
_max_dist(3000.0),
|
||||
_loop(AL_FALSE),
|
||||
_playing(false),
|
||||
_changed(true),
|
||||
_static_changed(true),
|
||||
_is_file(false)
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "In memory sounds sample" );
|
||||
_data = (unsigned char*)*data; *data = NULL;
|
||||
}
|
||||
|
||||
sample_name = "unknown, generated from data";
|
||||
|
||||
source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
|
||||
offset_pos[0] = 0.0; offset_pos[1] = 0.0; offset_pos[2] = 0.0;
|
||||
source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
|
||||
direction[0] = 0.0; direction[1] = 0.0; direction[2] = 0.0;
|
||||
inner = outer = 360.0; outergain = 0.0;
|
||||
|
||||
// clear errors from elsewhere?
|
||||
alGetError();
|
||||
|
||||
// Load wav data into a buffer.
|
||||
alGenBuffers(1, &buffer);
|
||||
if ( print_openal_error("constructor (alGenBuffers)") ) {
|
||||
throw sg_exception("Failed to gen buffer." );
|
||||
}
|
||||
|
||||
format = AL_FORMAT_MONO8;
|
||||
size = len;
|
||||
freq = _freq;
|
||||
|
||||
alBufferData( buffer, format, _data, size, freq );
|
||||
if ( print_openal_error("constructor (alBufferData)") ) {
|
||||
throw sg_exception("Failed to buffer data.");
|
||||
}
|
||||
|
||||
print_openal_error("constructor return");
|
||||
// constructor
|
||||
SGSoundSample::SGSoundSample( void** data, int len, int freq, int format ) :
|
||||
_absolute_pos(SGVec3d::zeros()),
|
||||
_relative_pos(SGVec3d::zeros()),
|
||||
_direction(SGVec3d::zeros()),
|
||||
_velocity(SGVec3f::zeros()),
|
||||
_orientation(SGQuatd::zeros()),
|
||||
_orivec(SGVec3f::zeros()),
|
||||
_base_pos(SGVec3d::zeros()),
|
||||
_rotation(SGQuatd::zeros()),
|
||||
_refname(random_string()),
|
||||
_format(format),
|
||||
_size(len),
|
||||
_freq(freq),
|
||||
_valid_buffer(false),
|
||||
_buffer(SGSoundMgr::NO_BUFFER),
|
||||
_valid_source(false),
|
||||
_source(SGSoundMgr::NO_SOURCE),
|
||||
_inner_angle(360.0),
|
||||
_outer_angle(360.0),
|
||||
_outer_gain(0.0),
|
||||
_pitch(1.0),
|
||||
_volume(1.0),
|
||||
_master_volume(1.0),
|
||||
_reference_dist(500.0),
|
||||
_max_dist(3000.0),
|
||||
_loop(AL_FALSE),
|
||||
_playing(false),
|
||||
_changed(true),
|
||||
_static_changed(true),
|
||||
_is_file(false)
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "In memory sounds sample" );
|
||||
_data = (unsigned char*)*data; *data = NULL;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
SGSoundSample::~SGSoundSample() {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Deleting a sample" );
|
||||
if (buffer)
|
||||
alDeleteBuffers(1, &buffer);
|
||||
if ( _data != NULL ) free(_data);
|
||||
}
|
||||
|
||||
void SGSoundSample::update_pos_and_orientation() {
|
||||
|
||||
// play the sample
|
||||
void SGSoundSample::play( bool _loop ) {
|
||||
|
||||
if ( source ) {
|
||||
alSourceStop( source );
|
||||
_absolute_pos = _base_pos;
|
||||
if (_relative_pos[0] || _relative_pos[1] || _relative_pos[2] ) {
|
||||
_absolute_pos += _rotation.rotate( _relative_pos );
|
||||
}
|
||||
|
||||
playing = bind_source();
|
||||
if ( playing ) {
|
||||
loop = _loop;
|
||||
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
alSourcePlay( source );
|
||||
|
||||
print_openal_error("play (alSourcePlay)");
|
||||
_orivec = SGVec3f::zeros();
|
||||
if ( _direction[0] || _direction[1] || _direction[2] ) {
|
||||
_orivec = toVec3f( _rotation.rotate( _direction ) );
|
||||
}
|
||||
}
|
||||
|
||||
string SGSoundSample::random_string() {
|
||||
static const char *r = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
string rstr = "System generated name: ";
|
||||
for (int i=0; i<10; i++) {
|
||||
rstr.push_back( r[rand() % strlen(r)] );
|
||||
}
|
||||
|
||||
// stop playing the sample
|
||||
void SGSoundSample::stop() {
|
||||
if (playing) {
|
||||
alSourceStop( source );
|
||||
alDeleteSources(1, &source);
|
||||
source = 0;
|
||||
print_openal_error("stop (alDeleteSources)");
|
||||
}
|
||||
playing = false;
|
||||
}
|
||||
|
||||
// Generate sound source
|
||||
bool
|
||||
SGSoundSample::bind_source() {
|
||||
|
||||
if ( playing ) {
|
||||
return true;
|
||||
}
|
||||
if ( buffer == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind buffer with a source.
|
||||
alGetError();
|
||||
alGenSources(1, &source);
|
||||
if ( print_openal_error("bind_source (alGenSources)") ) {
|
||||
// No biggy, better luck next time.
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to generate audio source.");
|
||||
// SG_LOG( SG_GENERAL, SG_ALERT, "Please update your sound driver and try again.");
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourcei( source, AL_BUFFER, buffer );
|
||||
#ifndef USE_SOFTWARE_DOPPLER
|
||||
alSourcef( source, AL_PITCH, pitch );
|
||||
alSourcef( source, AL_GAIN, volume );
|
||||
#else
|
||||
print_openal_error("bind_sources return");
|
||||
alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor );
|
||||
alGetError(); // ignore if the pitch is clamped by the driver
|
||||
alSourcef( source, AL_GAIN, volume * doppler_volume_factor );
|
||||
#endif
|
||||
alSourcefv( source, AL_POSITION, source_pos );
|
||||
alSourcefv( source, AL_DIRECTION, direction );
|
||||
alSourcef( source, AL_CONE_INNER_ANGLE, inner );
|
||||
alSourcef( source, AL_CONE_OUTER_ANGLE, outer );
|
||||
alSourcef( source, AL_CONE_OUTER_GAIN, outergain);
|
||||
#ifdef USE_OPEN_AL_DOPPLER
|
||||
alSourcefv( source, AL_VELOCITY, source_vel );
|
||||
#endif
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
|
||||
alSourcei( source, AL_SOURCE_RELATIVE, AL_TRUE );
|
||||
alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist );
|
||||
alSourcef( source, AL_MAX_DISTANCE, max_dist );
|
||||
|
||||
print_openal_error("bind_sources return");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_pitch( double p ) {
|
||||
// clamp in the range of 0.01 to 2.0
|
||||
if ( p < 0.01 ) { p = 0.01; }
|
||||
if ( p > 2.0 ) { p = 2.0; }
|
||||
pitch = p;
|
||||
if (playing) {
|
||||
#ifndef USE_SOFTWARE_DOPPLER
|
||||
alSourcef( source, AL_PITCH, pitch );
|
||||
print_openal_error("set_pitch");
|
||||
#else
|
||||
alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor );
|
||||
alGetError(); // ignore if the pitch is clamped by the driver
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_volume( double v ) {
|
||||
volume = v;
|
||||
if (playing) {
|
||||
#ifndef USE_SOFTWARE_DOPPLER
|
||||
alSourcef( source, AL_GAIN, volume );
|
||||
#else
|
||||
alSourcef( source, AL_GAIN, volume * doppler_volume_factor );
|
||||
#endif
|
||||
print_openal_error("set_volume");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SGSoundSample::is_playing( ) {
|
||||
if (playing) {
|
||||
ALint result;
|
||||
alGetSourcei( source, AL_SOURCE_STATE, &result );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error in sample is_playing(): " << sample_name );
|
||||
}
|
||||
return (result == AL_PLAYING) ;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_source_pos( ALfloat *pos ) {
|
||||
source_pos[0] = pos[0];
|
||||
source_pos[1] = pos[1];
|
||||
source_pos[2] = pos[2];
|
||||
|
||||
if (playing) {
|
||||
sgVec3 final_pos;
|
||||
sgAddVec3( final_pos, source_pos, offset_pos );
|
||||
|
||||
alSourcefv( source, AL_POSITION, final_pos );
|
||||
print_openal_error("set_source_pos");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_offset_pos( ALfloat *pos ) {
|
||||
offset_pos[0] = pos[0];
|
||||
offset_pos[1] = pos[1];
|
||||
offset_pos[2] = pos[2];
|
||||
|
||||
if (playing) {
|
||||
sgVec3 final_pos;
|
||||
sgAddVec3( final_pos, source_pos, offset_pos );
|
||||
|
||||
alSourcefv( source, AL_POSITION, final_pos );
|
||||
print_openal_error("set_offset_pos");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_orientation( ALfloat *dir, ALfloat inner_angle,
|
||||
ALfloat outer_angle,
|
||||
ALfloat outer_gain)
|
||||
{
|
||||
inner = inner_angle;
|
||||
outer = outer_angle;
|
||||
outergain = outer_gain;
|
||||
direction[0] = dir[0];
|
||||
direction[1] = dir[1];
|
||||
direction[2] = dir[2];
|
||||
if (playing) {
|
||||
alSourcefv( source, AL_DIRECTION, dir);
|
||||
alSourcef( source, AL_CONE_INNER_ANGLE, inner );
|
||||
alSourcef( source, AL_CONE_OUTER_ANGLE, outer );
|
||||
alSourcef( source, AL_CONE_OUTER_GAIN, outergain );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_source_vel( ALfloat *vel, ALfloat *listener_vel ) {
|
||||
if (no_Doppler_effect) {
|
||||
source_vel[0] = listener_vel[0];
|
||||
source_vel[1] = listener_vel[1];
|
||||
source_vel[2] = listener_vel[2];
|
||||
} else {
|
||||
source_vel[0] = vel[0];
|
||||
source_vel[1] = vel[1];
|
||||
source_vel[2] = vel[2];
|
||||
}
|
||||
#ifdef USE_OPEN_AL_DOPPLER
|
||||
if (playing) {
|
||||
alSourcefv( source, AL_VELOCITY, source_vel );
|
||||
}
|
||||
#elif defined (USE_OPEN_AL_DOPPLER_WITH_FIXED_LISTENER)
|
||||
if (playing) {
|
||||
sgVec3 relative_vel;
|
||||
sgSubVec3( relative_vel, source_vel, listener_vel );
|
||||
alSourcefv( source, AL_VELOCITY, relative_vel );
|
||||
}
|
||||
#else
|
||||
if (no_Doppler_effect) {
|
||||
doppler_pitch_factor = 1;
|
||||
doppler_volume_factor = 1;
|
||||
return;
|
||||
}
|
||||
double doppler, mfp;
|
||||
sgVec3 final_pos;
|
||||
sgAddVec3( final_pos, source_pos, offset_pos );
|
||||
mfp = sgLengthVec3(final_pos);
|
||||
if (mfp > 1e-6) {
|
||||
double vls = -sgScalarProductVec3( listener_vel, final_pos ) / mfp;
|
||||
double vss = -sgScalarProductVec3( source_vel, final_pos ) / mfp;
|
||||
if (fabs(340 - vss) > 1e-6)
|
||||
{
|
||||
doppler = (340 - vls) / (340 - vss);
|
||||
doppler = ( doppler > 0) ? ( ( doppler < 10) ? doppler : 10 ) : 0;
|
||||
}
|
||||
else
|
||||
doppler = 0;
|
||||
}
|
||||
else
|
||||
doppler = 1;
|
||||
/* the OpenAL documentation of the Doppler calculation
|
||||
SS: AL_SPEED_OF_SOUND = speed of sound (default value 343.3)
|
||||
DF: AL_DOPPLER_FACTOR = Doppler factor (default 1.0)
|
||||
vls: Listener velocity scalar (scalar, projected on source-to-listener vector)
|
||||
vss: Source velocity scalar (scalar, projected on source-to-listener vector)
|
||||
SL = source to listener vector
|
||||
SV = Source Velocity vector
|
||||
LV = Listener Velocity vector
|
||||
vls = DotProduct(SL, LV) / Mag(SL)
|
||||
vss = DotProduct(SL, SV) / Mag(SL)
|
||||
Dopper Calculation:
|
||||
vss = min(vss, SS/DF)
|
||||
vls = min(vls, SS/DF)
|
||||
f' = f * (SS - DF*vls) / (SS - DF*vss)
|
||||
*/
|
||||
if (doppler > 0.1) {
|
||||
if (doppler < 10) {
|
||||
doppler_pitch_factor = doppler;
|
||||
doppler_volume_factor = 1;
|
||||
}
|
||||
else {
|
||||
doppler_pitch_factor = (doppler < 11) ? doppler : 11;
|
||||
doppler_volume_factor = (doppler < 11) ? 11-doppler : 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
doppler_pitch_factor = 0.1;
|
||||
doppler_volume_factor = (doppler > 0) ? doppler * 10 : 0;
|
||||
}
|
||||
if (playing) {
|
||||
alSourcef( source, AL_GAIN, volume * doppler_volume_factor );
|
||||
print_openal_error("set_source_vel: volume");
|
||||
alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor );
|
||||
alGetError(); //ignore if the pitch is clamped
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_reference_dist( ALfloat dist ) {
|
||||
reference_dist = dist;
|
||||
if (playing) {
|
||||
alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundSample::set_max_dist( ALfloat dist ) {
|
||||
max_dist = dist;
|
||||
if (playing) {
|
||||
alSourcef( source, AL_MAX_DISTANCE, max_dist );
|
||||
}
|
||||
}
|
||||
|
||||
ALvoid *
|
||||
SGSoundSample::load_file(const char *path, const char *file)
|
||||
{
|
||||
ALvoid* data = 0;
|
||||
|
||||
SGPath samplepath( path );
|
||||
if ( strlen(file) ) {
|
||||
samplepath.append( file );
|
||||
}
|
||||
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
ALfloat freqf;
|
||||
data = alutLoadMemoryFromFile(samplepath.c_str(), &format, &size, &freqf );
|
||||
if (data == NULL) {
|
||||
throw sg_io_exception("Failed to load wav file.",
|
||||
sg_location(samplepath.str()));
|
||||
}
|
||||
freq = (ALsizei)freqf;
|
||||
#else
|
||||
# if defined (__APPLE__)
|
||||
alutLoadWAVFile( (ALbyte *)samplepath.c_str(),
|
||||
&format, &data, &size, &freq );
|
||||
# else
|
||||
alutLoadWAVFile( (ALbyte *)samplepath.c_str(),
|
||||
&format, &data, &size, &freq, &loop );
|
||||
# endif
|
||||
if ( print_openal_error("constructor (alutLoadWAVFile)") ) {
|
||||
throw sg_io_exception("Failed to load wav file.",
|
||||
sg_location(samplepath.str()));
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
return rstr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// sample.hxx -- Sound sample encapsulation class
|
||||
// sample_openal.hxx -- Audio sample encapsulation class
|
||||
//
|
||||
// Written by Curtis Olson, started April 2004.
|
||||
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@@ -15,14 +17,14 @@
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* \file sample.hxx
|
||||
* Provides a sound sample encapsulation
|
||||
* \file audio sample.hxx
|
||||
* Provides a audio sample encapsulation
|
||||
*/
|
||||
|
||||
#ifndef _SG_SAMPLE_HXX
|
||||
@@ -32,87 +34,21 @@
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <plib/sg.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define AL_ILLEGAL_ENUM AL_INVALID_ENUM
|
||||
# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alut.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alut.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_WINDOWS_H
|
||||
#ifdef AL_VERSION_1_2
|
||||
#define USE_OPEN_AL_DOPPLER should work
|
||||
#else
|
||||
#define USE_OPEN_AL_DOPPLER_WITH_FIXED_LISTENER better than nothing
|
||||
#endif
|
||||
#else
|
||||
// the Open_AL Doppler calculation seems to be buggy on windows
|
||||
#define USE_SOFTWARE_DOPPLER seem to be necessary
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
// #include <plib/sg.h>
|
||||
|
||||
/**
|
||||
* manages everything we need to know for an individual sound sample
|
||||
* manages everything we need to know for an individual audio sample
|
||||
*/
|
||||
|
||||
class SGSoundSample : public SGReferenced {
|
||||
|
||||
private:
|
||||
|
||||
string sample_name;
|
||||
|
||||
// Buffers hold sound data.
|
||||
ALuint buffer;
|
||||
|
||||
// Sources are points emitting sound.
|
||||
ALuint source;
|
||||
|
||||
// Position of the source sound.
|
||||
ALfloat source_pos[3];
|
||||
|
||||
// A constant offset to be applied to the final source_pos
|
||||
ALfloat offset_pos[3];
|
||||
|
||||
// The orientation of the sound (direction and cut-off angles)
|
||||
ALfloat direction[3];
|
||||
ALfloat inner, outer, outergain;
|
||||
|
||||
// Velocity of the source sound.
|
||||
ALfloat source_vel[3];
|
||||
|
||||
// configuration values
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
|
||||
double pitch;
|
||||
double volume;
|
||||
#ifdef USE_SOFTWARE_DOPPLER
|
||||
double doppler_pitch_factor;
|
||||
double doppler_volume_factor;
|
||||
#endif
|
||||
double reference_dist;
|
||||
double max_dist;
|
||||
ALboolean loop;
|
||||
|
||||
bool playing;
|
||||
bool bind_source();
|
||||
bool no_Doppler_effect;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@@ -125,125 +61,439 @@ public:
|
||||
* Constructor
|
||||
* @param path Path name to sound
|
||||
* @param file File name of sound
|
||||
should usually be true unless you want to manipulate the data
|
||||
later.)
|
||||
Buffer data is freed by the sample group
|
||||
*/
|
||||
SGSoundSample( const char *path, const char *file, bool no_Doppler_effect = true );
|
||||
SGSoundSample( const char *path, const char *file );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param _data Pointer to a memory buffer containing the sample data
|
||||
the application is responsible for freeing the buffer data.
|
||||
* @param data Pointer to a memory buffer containing this audio sample data
|
||||
The application may free the data by calling free_data(), otherwise it
|
||||
will be resident untill the class is destroyed. This pointer will be
|
||||
set to NULL after calling this function.
|
||||
* @param len Byte length of array
|
||||
* @param _freq Frequency of the provided data (bytes per second)
|
||||
should usually be true unless you want to manipulate the data
|
||||
later.)
|
||||
* @param freq Frequency of the provided data (bytes per second)
|
||||
* @param format OpenAL format id of the data
|
||||
*/
|
||||
SGSoundSample( unsigned char *_data, int len, int _freq, bool no_Doppler_effect = true );
|
||||
|
||||
~SGSoundSample();
|
||||
SGSoundSample( void** data, int len, int freq, int format=AL_FORMAT_MONO8 );
|
||||
SGSoundSample( const unsigned char** data, int len, int freq,
|
||||
int format = AL_FORMAT_MONO8 );
|
||||
|
||||
/**
|
||||
* Start playing this sample.
|
||||
*
|
||||
* @param _loop Define whether the sound should be played in a loop.
|
||||
* Destructor
|
||||
*/
|
||||
void play( bool _loop );
|
||||
~SGSoundSample ();
|
||||
|
||||
/**
|
||||
* Stop playing this sample.
|
||||
*
|
||||
* @param sched A pointer to the appropriate scheduler.
|
||||
* Detect wheter this audio sample holds the information of a sound file.
|
||||
* @return Return true if this audio sample is to be constructed from a file.
|
||||
*/
|
||||
void stop();
|
||||
inline bool is_file() const { return _is_file; }
|
||||
|
||||
/**
|
||||
* Play this sample once.
|
||||
* Test if this audio sample configuration has changed since the last call.
|
||||
* Calling this function will reset the flag so calling it a second
|
||||
* time in a row will return false.
|
||||
* @return Return true is the configuration has changed in the mean time.
|
||||
*/
|
||||
bool has_changed() {
|
||||
bool b = _changed; _changed = false; return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if static dataa of audio sample configuration has changed.
|
||||
* Calling this function will reset the flag so calling it a second
|
||||
* time in a row will return false.
|
||||
* @return Return true is the static data has changed in the mean time.
|
||||
*/
|
||||
bool has_static_data_changed() {
|
||||
bool b = _static_changed; _static_changed = false; return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule this audio sample for playing. Actual playing will only start
|
||||
* at the next call op SoundGroup::update()
|
||||
* @param _loop Define whether this sound should be played in a loop.
|
||||
*/
|
||||
void play( bool loop ) {
|
||||
_playing = true; _loop = loop; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this audio sample is set to be continuous looping.
|
||||
* @return Return true if this audio sample is set to looping.
|
||||
*/
|
||||
inline bool is_looping() { return _loop; }
|
||||
|
||||
/**
|
||||
* Schedule this audio sample to stop playing.
|
||||
*/
|
||||
void stop() {
|
||||
_playing = false; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule this audio sample to play once.
|
||||
* @see #play
|
||||
*/
|
||||
inline void play_once() { play(false); }
|
||||
|
||||
/**
|
||||
* Play this sample looped.
|
||||
* Schedule this audio sample to play looped.
|
||||
* @see #play
|
||||
*/
|
||||
inline void play_looped() { play(true); }
|
||||
|
||||
/**
|
||||
* Test if a sample is currently playing.
|
||||
* @return true if is is playing, false otherwise.
|
||||
* Test if a audio sample is scheduled for playing.
|
||||
* @return true if this audio sample is playing, false otherwise.
|
||||
*/
|
||||
bool is_playing( );
|
||||
inline bool is_playing() { return _playing; }
|
||||
|
||||
/**
|
||||
* Get the current pitch setting of this sample.
|
||||
* Set the data associated with this audio sample
|
||||
* @param data Pointer to a memory block containg this audio sample data.
|
||||
This pointer will be set to NULL after calling this function.
|
||||
*/
|
||||
inline double get_pitch() const { return pitch; }
|
||||
|
||||
/**
|
||||
* Set the pitch of this sample.
|
||||
*/
|
||||
void set_pitch( double p );
|
||||
|
||||
/**
|
||||
* Get the current volume setting of this sample.
|
||||
*/
|
||||
inline double get_volume() const { return volume; }
|
||||
|
||||
/**
|
||||
* Set the volume of this sample.
|
||||
*/
|
||||
void set_volume( double v );
|
||||
|
||||
/**
|
||||
* Returns the size of the sounds sample
|
||||
*/
|
||||
inline int get_size() {
|
||||
return size;
|
||||
inline void set_data( const unsigned char **data ) {
|
||||
_data = (unsigned char*)*data; *data = NULL;
|
||||
}
|
||||
inline void set_data( void **data ) {
|
||||
_data = (unsigned char*)*data; *data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set position of sound source (uses same coordinate system as opengl)
|
||||
* Return the data associated with this audio sample.
|
||||
* @return A pointer to this sound data of this audio sample.
|
||||
*/
|
||||
void set_source_pos( ALfloat *pos );
|
||||
inline void* get_data() const { return _data; }
|
||||
|
||||
/**
|
||||
* Set "constant" offset position of sound source (uses same
|
||||
* coordinate system as opengl)
|
||||
* Free the data associated with this audio sample
|
||||
*/
|
||||
void set_offset_pos( ALfloat *pos );
|
||||
void free_data() {
|
||||
if ( _data != NULL ) free( _data ); _data = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the orientation of the sound source, both for direction
|
||||
* and audio cut-off angles.
|
||||
* Set the source id of this source
|
||||
* @param sid OpenAL source-id
|
||||
*/
|
||||
void set_orientation( ALfloat *dir, ALfloat inner_angle=360.0,
|
||||
ALfloat outer_angle=360.0,
|
||||
ALfloat outer_gain=0.0);
|
||||
void set_source(unsigned int sid) {
|
||||
_source = sid; _valid_source = true; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set velocity of sound source (uses same coordinate system as opengl)
|
||||
* Get the OpenAL source id of this source
|
||||
* @return OpenAL source-id
|
||||
*/
|
||||
void set_source_vel( ALfloat *vel, ALfloat *listener_vel );
|
||||
inline unsigned int get_source() { return _source; }
|
||||
|
||||
/**
|
||||
* Test if the source-id of this audio sample may be passed to OpenAL.
|
||||
* @return true if the source-id is valid
|
||||
*/
|
||||
inline bool is_valid_source() const { return _valid_source; }
|
||||
|
||||
/**
|
||||
* Set the source-id of this audio sample to invalid.
|
||||
*/
|
||||
inline void no_valid_source() { _valid_source = false; }
|
||||
|
||||
/**
|
||||
* Set the OpenAL buffer-id of this source
|
||||
* @param bid OpenAL buffer-id
|
||||
*/
|
||||
void set_buffer(unsigned int bid) {
|
||||
_buffer = bid; _valid_buffer = true; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OpenAL buffer-id of this source
|
||||
* @return OpenAL buffer-id
|
||||
*/
|
||||
inline unsigned int get_buffer() { return _buffer; }
|
||||
|
||||
/**
|
||||
* Test if the buffer-id of this audio sample may be passed to OpenAL.
|
||||
* @return true if the buffer-id is valid
|
||||
*/
|
||||
inline bool is_valid_buffer() const { return _valid_buffer; }
|
||||
|
||||
/**
|
||||
* Set the buffer-id of this audio sample to invalid.
|
||||
*/
|
||||
inline void no_valid_buffer() { _valid_buffer = false; }
|
||||
|
||||
/**
|
||||
* Set the playback pitch of this audio sample.
|
||||
* Should be between 0.0 and 2.0 for maximum compatibility.
|
||||
* @param p Pitch
|
||||
*/
|
||||
inline void set_pitch( float p ) {
|
||||
if (p > 2.0) p = 2.0; else if (p < 0.01) p = 0.01;
|
||||
_pitch = p; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current pitch value of this audio sample.
|
||||
* @return Pitch
|
||||
*/
|
||||
inline float get_pitch() { return _pitch; }
|
||||
|
||||
/**
|
||||
* Set the master volume of this sample. Should be between 0.0 and 1.0.
|
||||
* The final volume is calculated by multiplying the master and audio sample
|
||||
* volume.
|
||||
* @param v Volume
|
||||
*/
|
||||
inline void set_master_volume( float v ) {
|
||||
if (v > 1.0) v = 1.0; else if (v < 0.0) v = 0.0;
|
||||
_master_volume = v; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the volume of this audio sample. Should be between 0.0 and 1.0.
|
||||
* The final volume is calculated by multiplying the master and audio sample
|
||||
* volume.
|
||||
* @param v Volume
|
||||
*/
|
||||
inline void set_volume( float v ) {
|
||||
if (v > 1.0) v = 1.0; else if (v < 0.0) v = 0.0;
|
||||
_volume = v; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the final volume value of this audio sample.
|
||||
* @return Volume
|
||||
*/
|
||||
inline float get_volume() { return _volume * _master_volume; }
|
||||
|
||||
/**
|
||||
* Set the OpenAL format of this audio sample.
|
||||
* @param format OpenAL format-id
|
||||
*/
|
||||
inline void set_format( int format ) { _format = format; }
|
||||
|
||||
/**
|
||||
* Returns the format of this audio sample.
|
||||
* @return OpenAL format-id
|
||||
*/
|
||||
inline int get_format() { return _format; }
|
||||
|
||||
/**
|
||||
* Set the frequency (in Herz) of this audio sample.
|
||||
* @param freq Frequency
|
||||
*/
|
||||
inline void set_frequency( int freq ) { _freq = freq; }
|
||||
|
||||
/**
|
||||
* Returns the frequency (in Herz) of this audio sample.
|
||||
* @return Frequency
|
||||
*/
|
||||
inline int get_frequency() { return _freq; }
|
||||
|
||||
/**
|
||||
* Sets the size (in bytes) of this audio sample.
|
||||
* @param size Data size
|
||||
*/
|
||||
inline void set_size( size_t size ) { _size = size; }
|
||||
|
||||
/**
|
||||
* Returns the size (in bytes) of this audio sample.
|
||||
* @return Data size
|
||||
*/
|
||||
inline size_t get_size() const { return _size; }
|
||||
|
||||
/**
|
||||
* Set the position of this sound relative to the base position.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
|
||||
* @param pos Relative position of this sound
|
||||
*/
|
||||
inline void set_relative_position( const SGVec3f& pos ) {
|
||||
_relative_pos = toVec3d(pos); _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the base position in Cartesian coordinates
|
||||
* @param pos position in Cartesian coordinates
|
||||
*/
|
||||
inline void set_position( const SGVec3d& pos ) {
|
||||
_base_pos = pos; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute position of this sound.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
|
||||
* @return Absolute position
|
||||
*/
|
||||
SGVec3d& get_position() { return _absolute_pos; }
|
||||
|
||||
/**
|
||||
* Set the orientation of this sound.
|
||||
* @param ori Quaternation containing the orientation information
|
||||
*/
|
||||
inline void set_orientation( const SGQuatd& ori ) {
|
||||
_orientation = ori; _changed = true;
|
||||
}
|
||||
|
||||
inline void set_rotation( const SGQuatd& ec2body ) {
|
||||
_rotation = ec2body; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set direction of this sound relative to the orientation.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right
|
||||
* @param dir Sound emission direction
|
||||
*/
|
||||
inline void set_direction( const SGVec3f& dir ) {
|
||||
_direction = toVec3d(dir); _static_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the audio cone parameters for directional audio.
|
||||
* Note: setting it to 2 degree will result in 1 degree to both sides.
|
||||
* @param inner Inner cone angle (0 - 360 degrees)
|
||||
* @param outer Outer cone angle (0 - 360 degrees)
|
||||
* @param gain Remaining gain at the edge of the outer cone (0.0 - 1.0)
|
||||
*/
|
||||
void set_audio_cone( float inner, float outer, float gain ) {
|
||||
_inner_angle = inner; _outer_angle = outer; _outer_gain = gain;
|
||||
_static_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the orientation vector of this sound.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right
|
||||
* @return Orientaton vector
|
||||
*/
|
||||
SGVec3f& get_orientation() { return _orivec; }
|
||||
|
||||
/**
|
||||
* Get the inner angle of the audio cone.
|
||||
* @return Inner angle in degrees
|
||||
*/
|
||||
float get_innerangle() { return _inner_angle; }
|
||||
|
||||
/**
|
||||
* Get the outer angle of the audio cone.
|
||||
* @return Outer angle in degrees
|
||||
*/
|
||||
float get_outerangle() { return _outer_angle; }
|
||||
|
||||
/**
|
||||
* Get the remaining gain at the edge of the outer cone.
|
||||
* @return Gain
|
||||
*/
|
||||
float get_outergain() { return _outer_gain; }
|
||||
|
||||
/**
|
||||
* Set the velocity vector (in meters per second) of this sound.
|
||||
* This is in the local frame coordinate system; x=north, y=east, z=down
|
||||
* @param Velocity vector
|
||||
*/
|
||||
inline void set_velocity( const SGVec3f& vel ) {
|
||||
_velocity = vel; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get velocity vector (in meters per second) of this sound.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right
|
||||
* @return Velocity vector
|
||||
*/
|
||||
SGVec3f& get_velocity() { return _velocity; }
|
||||
|
||||
|
||||
/**
|
||||
* Set reference distance of sound (the distance where the gain
|
||||
* will be half.)
|
||||
* Set reference distance (in meters) of this sound.
|
||||
* This is the distance where the gain will be half.
|
||||
* @param dist Reference distance
|
||||
*/
|
||||
void set_reference_dist( ALfloat dist );
|
||||
inline void set_reference_dist( float dist ) {
|
||||
_reference_dist = dist; _static_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference distance ((in meters) of this sound.
|
||||
* This is the distance where the gain will be half.
|
||||
* @return Reference distance
|
||||
*/
|
||||
inline float get_reference_dist() { return _reference_dist; }
|
||||
|
||||
|
||||
/**
|
||||
* Set maximum distance of sound (the distance where the sound is
|
||||
* no longer audible.
|
||||
* Set maximum distance (in meters) of this sound.
|
||||
* This is the distance where this sound is no longer audible.
|
||||
* @param dist Maximum distance
|
||||
*/
|
||||
void set_max_dist( ALfloat dist );
|
||||
inline void set_max_dist( float dist ) {
|
||||
_max_dist = dist; _static_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a sound file into a memory buffer only.
|
||||
* Get maximum distance (in meters) of this sound.
|
||||
* This is the distance where this sound is no longer audible.
|
||||
* @return dist Maximum distance
|
||||
*/
|
||||
ALvoid* load_file(const char *path, const char *file);
|
||||
inline float get_max_dist() { return _max_dist; }
|
||||
|
||||
/**
|
||||
* Get the reference name of this audio sample.
|
||||
* @return Sample name
|
||||
*/
|
||||
inline std::string get_sample_name() const { return _refname; }
|
||||
|
||||
void update_pos_and_orientation();
|
||||
|
||||
private:
|
||||
|
||||
// Position of the source sound.
|
||||
SGVec3d _absolute_pos; // absolute position
|
||||
SGVec3d _relative_pos; // position relative to the base position
|
||||
SGVec3d _direction; // orientation offset
|
||||
SGVec3f _velocity; // Velocity of the source sound.
|
||||
|
||||
// The position and orientation of this sound
|
||||
SGQuatd _orientation; // base orientation
|
||||
SGVec3f _orivec; // orientation vector for OpenAL
|
||||
SGVec3d _base_pos; // base position
|
||||
|
||||
SGQuatd _rotation;
|
||||
|
||||
std::string _refname; // name or file path
|
||||
unsigned char* _data;
|
||||
|
||||
// configuration values
|
||||
int _format;
|
||||
size_t _size;
|
||||
int _freq;
|
||||
|
||||
// Buffers hold sound data.
|
||||
bool _valid_buffer;
|
||||
unsigned int _buffer;
|
||||
|
||||
// Sources are points emitting sound.
|
||||
bool _valid_source;
|
||||
unsigned int _source;
|
||||
|
||||
// The orientation of this sound (direction and cut-off angles)
|
||||
float _inner_angle;
|
||||
float _outer_angle;
|
||||
float _outer_gain;
|
||||
|
||||
float _pitch;
|
||||
float _volume;
|
||||
float _master_volume;
|
||||
float _reference_dist;
|
||||
float _max_dist;
|
||||
bool _loop;
|
||||
|
||||
bool _playing;
|
||||
bool _changed;
|
||||
bool _static_changed;
|
||||
bool _is_file;
|
||||
|
||||
string random_string();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
// <david_j_findlay@yahoo.com.au> 2001
|
||||
//
|
||||
// C++-ified by Curtis Olson, started March 2001.
|
||||
// Modified for the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@@ -18,8 +20,8 @@
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
@@ -27,326 +29,631 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alc.h>
|
||||
#if defined( __APPLE__ )
|
||||
# include <OpenAL/alut.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alc.h>
|
||||
# include <AL/alut.h>
|
||||
#endif
|
||||
|
||||
#if defined (__APPLE__)
|
||||
# ifdef __GNUC__
|
||||
# if ( __GNUC__ >= 3 ) && ( __GNUC_MINOR__ >= 3 )
|
||||
// # include <math.h>
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# else
|
||||
// any C++ header file undefines isinf and isnan
|
||||
// so this should be included before <iostream>
|
||||
// the functions are STILL in libm (libSystem on mac os x)
|
||||
extern "C" int isnan (double);
|
||||
extern "C" int isinf (double);
|
||||
# endif
|
||||
# else
|
||||
// inline int (isinf)(double r) { return isinf(r); }
|
||||
// inline int (isnan)(double r) { return isnan(r); }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__)
|
||||
# if __FreeBSD_version < 500000
|
||||
extern "C" {
|
||||
inline int isnan(double r) { return !(r <= 0 || r >= 0); }
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__CYGWIN__)
|
||||
#include <ieeefp.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <algorithm>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#define isnan(x) _isnan(x)
|
||||
#endif
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
extern bool isNaN(float *v);
|
||||
|
||||
#define MAX_SOURCES 128
|
||||
|
||||
//
|
||||
// Sound Manager
|
||||
//
|
||||
|
||||
int SGSoundMgr::_alut_init = 0;
|
||||
|
||||
// constructor
|
||||
SGSoundMgr::SGSoundMgr() {
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Initializing OpenAL sound manager" );
|
||||
|
||||
// initialize OpenAL
|
||||
SGSoundMgr::SGSoundMgr() :
|
||||
_working(false),
|
||||
_active(false),
|
||||
_changed(true),
|
||||
_volume(0.0),
|
||||
_device(NULL),
|
||||
_context(NULL),
|
||||
_absolute_pos(SGVec3d::zeros()),
|
||||
_offset_pos(SGVec3d::zeros()),
|
||||
_base_pos(SGVec3d::zeros()),
|
||||
_geod_pos(SGGeod::fromCart(SGVec3d::zeros())),
|
||||
_velocity(SGVec3d::zeros()),
|
||||
_orientation(SGQuatd::zeros()),
|
||||
_bad_doppler(false)
|
||||
{
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
if (!alutInit(NULL, NULL))
|
||||
{
|
||||
ALenum error = alutGetError ();
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, " "+string(alutGetErrorString(error)));
|
||||
working = false;
|
||||
context = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
working = true;
|
||||
context = alcGetCurrentContext();
|
||||
}
|
||||
#else
|
||||
if ( (dev = alcOpenDevice( NULL )) != NULL
|
||||
&& ( context = alcCreateContext( dev, NULL )) != NULL ) {
|
||||
working = true;
|
||||
alcMakeContextCurrent( context );
|
||||
} else {
|
||||
working = false;
|
||||
context = 0;
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
|
||||
return;
|
||||
if (_alut_init == 0) {
|
||||
if ( !alutInitWithoutContext(NULL, NULL) ) {
|
||||
testForALUTError("alut initialization");
|
||||
return;
|
||||
}
|
||||
}
|
||||
_alut_init++;
|
||||
#endif
|
||||
|
||||
listener_pos[0] = 0.0;
|
||||
listener_pos[1] = 0.0;
|
||||
listener_pos[2] = 0.0;
|
||||
|
||||
listener_vel[0] = 0.0;
|
||||
listener_vel[1] = 0.0;
|
||||
listener_vel[2] = 0.0;
|
||||
|
||||
listener_ori[0] = 0.0;
|
||||
listener_ori[1] = 0.0;
|
||||
listener_ori[2] = -1.0;
|
||||
listener_ori[3] = 0.0;
|
||||
listener_ori[4] = 1.0;
|
||||
listener_ori[5] = 0.0;
|
||||
|
||||
alListenerf( AL_GAIN, 0.0f );
|
||||
alListenerfv( AL_POSITION, listener_pos );
|
||||
alListenerfv( AL_VELOCITY, listener_vel );
|
||||
alListenerfv( AL_ORIENTATION, listener_ori );
|
||||
alGetError();
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error after audio initialization!" );
|
||||
}
|
||||
|
||||
// exaggerate the ear candy?
|
||||
alDopplerFactor(1.0);
|
||||
alDopplerVelocity(340.0); // speed of sound in meters per second.
|
||||
}
|
||||
|
||||
// destructor
|
||||
|
||||
SGSoundMgr::~SGSoundMgr() {
|
||||
|
||||
stop();
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
alutExit ();
|
||||
#else
|
||||
if (context)
|
||||
alcDestroyContext( context );
|
||||
_alut_init--;
|
||||
if (_alut_init == 0) {
|
||||
alutExit ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// initialize the sound manager
|
||||
void SGSoundMgr::init() {
|
||||
//
|
||||
// Remove the samples from the sample manager.
|
||||
//
|
||||
samples.clear();
|
||||
void SGSoundMgr::init(const char *devname) {
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Initializing OpenAL sound manager" );
|
||||
|
||||
ALCdevice *device = alcOpenDevice(devname);
|
||||
if ( testForError(device, "Audio device not available, trying default") ) {
|
||||
device = alcOpenDevice(NULL);
|
||||
if (testForError(device, "Default Audio device not available.") ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ALCcontext *context = alcCreateContext(device, NULL);
|
||||
if ( testForError(context, "Unable to create a valid context.") ) {
|
||||
alcCloseDevice (device);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !alcMakeContextCurrent(context) ) {
|
||||
testForALCError("context initialization");
|
||||
alcDestroyContext (context);
|
||||
alcCloseDevice (device);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_context != NULL)
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "context is already assigned");
|
||||
_context = context;
|
||||
_working = true;
|
||||
|
||||
_at_up_vec[0] = 0.0; _at_up_vec[1] = 0.0; _at_up_vec[2] = -1.0;
|
||||
_at_up_vec[3] = 0.0; _at_up_vec[4] = 1.0; _at_up_vec[5] = 0.0;
|
||||
|
||||
alListenerf( AL_GAIN, 0.0f );
|
||||
alListenerfv( AL_ORIENTATION, _at_up_vec );
|
||||
alListenerfv( AL_POSITION, SGVec3f::zeros().data() );
|
||||
alListenerfv( AL_VELOCITY, SGVec3f::zeros().data() );
|
||||
|
||||
alDopplerFactor(1.0);
|
||||
alDopplerVelocity(340.3); // speed of sound in meters per second.
|
||||
|
||||
// gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +
|
||||
// AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE));
|
||||
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
||||
|
||||
testForALError("listener initialization");
|
||||
|
||||
// get a free source one at a time
|
||||
// if an error is returned no more (hardware) sources are available
|
||||
for (unsigned int i=0; i<MAX_SOURCES; i++) {
|
||||
ALuint source;
|
||||
ALenum error;
|
||||
|
||||
alGetError();
|
||||
alGenSources(1, &source);
|
||||
error = alGetError();
|
||||
if ( error == AL_NO_ERROR ) {
|
||||
_free_sources.push_back( source );
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
string vendor = (const char *)alGetString(AL_VENDOR);
|
||||
string renderer = (const char *)alGetString(AL_RENDERER);
|
||||
if ( vendor != "OpenAL Community" ||
|
||||
(renderer != "Software" && renderer != "OpenAL Sample Implementation")
|
||||
)
|
||||
{
|
||||
_bad_doppler = true;
|
||||
}
|
||||
|
||||
if (_free_sources.size() == 0) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Unable to grab any OpenAL sources!");
|
||||
}
|
||||
}
|
||||
|
||||
void SGSoundMgr::activate() {
|
||||
if ( _working ) {
|
||||
_active = true;
|
||||
sample_group_map_iterator sample_grp_current = _sample_groups.begin();
|
||||
sample_group_map_iterator sample_grp_end = _sample_groups.end();
|
||||
for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
|
||||
SGSampleGroup *sgrp = sample_grp_current->second;
|
||||
sgrp->activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop the sound manager
|
||||
void SGSoundMgr::stop() {
|
||||
|
||||
// first stop all sample groups
|
||||
sample_group_map_iterator sample_grp_current = _sample_groups.begin();
|
||||
sample_group_map_iterator sample_grp_end = _sample_groups.end();
|
||||
for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
|
||||
SGSampleGroup *sgrp = sample_grp_current->second;
|
||||
sgrp->stop();
|
||||
}
|
||||
|
||||
// clear all OpenAL sources
|
||||
for (unsigned int i=0; i<_free_sources.size(); i++) {
|
||||
ALuint source = _free_sources[i];
|
||||
alDeleteSources( 1 , &source );
|
||||
}
|
||||
_free_sources.clear();
|
||||
|
||||
// clear any OpenAL buffers before shutting down
|
||||
buffer_map_iterator buffers_current = _buffers.begin();
|
||||
buffer_map_iterator buffers_end = _buffers.end();
|
||||
for ( ; buffers_current != buffers_end; ++buffers_current ) {
|
||||
refUint ref = buffers_current->second;
|
||||
ALuint buffer = ref.id;
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
_buffers.clear();
|
||||
|
||||
if (_working) {
|
||||
_working = false;
|
||||
_active = false;
|
||||
_context = alcGetCurrentContext();
|
||||
_device = alcGetContextsDevice(_context);
|
||||
alcDestroyContext(_context);
|
||||
alcCloseDevice(_device);
|
||||
_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SGSoundMgr::suspend() {
|
||||
if (_working) {
|
||||
sample_group_map_iterator sample_grp_current = _sample_groups.begin();
|
||||
sample_group_map_iterator sample_grp_end = _sample_groups.end();
|
||||
for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
|
||||
SGSampleGroup *sgrp = sample_grp_current->second;
|
||||
sgrp->stop();
|
||||
}
|
||||
_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SGSoundMgr::resume() {
|
||||
if (_working) {
|
||||
sample_group_map_iterator sample_grp_current = _sample_groups.begin();
|
||||
sample_group_map_iterator sample_grp_end = _sample_groups.end();
|
||||
for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
|
||||
SGSampleGroup *sgrp = sample_grp_current->second;
|
||||
sgrp->resume();
|
||||
}
|
||||
_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SGSoundMgr::bind ()
|
||||
{
|
||||
// no properties
|
||||
_free_sources.clear();
|
||||
_free_sources.reserve( MAX_SOURCES );
|
||||
_sources_in_use.clear();
|
||||
_sources_in_use.reserve( MAX_SOURCES );
|
||||
}
|
||||
|
||||
|
||||
void SGSoundMgr::unbind ()
|
||||
{
|
||||
// no properties
|
||||
}
|
||||
_sample_groups.clear();
|
||||
|
||||
// delete free sources
|
||||
for (unsigned int i=0; i<_free_sources.size(); i++) {
|
||||
ALuint source = _free_sources[i];
|
||||
alDeleteSources( 1 , &source );
|
||||
}
|
||||
|
||||
_free_sources.clear();
|
||||
_sources_in_use.clear();
|
||||
}
|
||||
|
||||
// run the audio scheduler
|
||||
void SGSoundMgr::update( double dt ) {
|
||||
}
|
||||
if (_active) {
|
||||
alcSuspendContext(_context);
|
||||
|
||||
|
||||
void
|
||||
SGSoundMgr::pause ()
|
||||
{
|
||||
if (context) {
|
||||
alcSuspendContext( context );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error after soundmgr pause()!" );
|
||||
if (_changed) {
|
||||
update_pos_and_orientation();
|
||||
}
|
||||
|
||||
sample_group_map_iterator sample_grp_current = _sample_groups.begin();
|
||||
sample_group_map_iterator sample_grp_end = _sample_groups.end();
|
||||
for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
|
||||
SGSampleGroup *sgrp = sample_grp_current->second;
|
||||
sgrp->update(dt);
|
||||
}
|
||||
|
||||
if (_changed) {
|
||||
#if 0
|
||||
if (isNaN(_at_up_vec)) printf("NaN in listener orientation\n");
|
||||
if (isNaN(toVec3f(_absolute_pos).data())) printf("NaN in listener position\n");
|
||||
if (isNaN(_velocity.data())) printf("NaN in listener velocity\n");
|
||||
#endif
|
||||
alListenerf( AL_GAIN, _volume );
|
||||
alListenerfv( AL_ORIENTATION, _at_up_vec );
|
||||
// alListenerfv( AL_POSITION, toVec3f(_absolute_pos).data() );
|
||||
|
||||
SGQuatd hlOr = SGQuatd::fromLonLat( _geod_pos );
|
||||
SGVec3d velocity = SGVec3d::zeros();
|
||||
if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
|
||||
velocity = hlOr.backTransform(_velocity*SG_FEET_TO_METER);
|
||||
}
|
||||
|
||||
if ( _bad_doppler ) {
|
||||
velocity *= 100.0f;
|
||||
}
|
||||
|
||||
alListenerfv( AL_VELOCITY, toVec3f(velocity).data() );
|
||||
// alDopplerVelocity(340.3); // TODO: altitude dependent
|
||||
testForALError("update");
|
||||
_changed = false;
|
||||
}
|
||||
|
||||
alcProcessContext(_context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundMgr::resume ()
|
||||
// add a sample group, return true if successful
|
||||
bool SGSoundMgr::add( SGSampleGroup *sgrp, const string& refname )
|
||||
{
|
||||
if (context) {
|
||||
alcProcessContext( context );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error after soundmgr resume()!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add a sound effect, return true if successful
|
||||
bool SGSoundMgr::add( SGSoundSample *sound, const string& refname ) {
|
||||
|
||||
sample_map_iterator sample_it = samples.find( refname );
|
||||
if ( sample_it != samples.end() ) {
|
||||
// sound already exists
|
||||
sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
|
||||
if ( sample_grp_it != _sample_groups.end() ) {
|
||||
// sample group already exists
|
||||
return false;
|
||||
}
|
||||
|
||||
samples[refname] = sound;
|
||||
if (_active) sgrp->activate();
|
||||
_sample_groups[refname] = sgrp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// remove a sound effect, return true if successful
|
||||
bool SGSoundMgr::remove( const string &refname ) {
|
||||
|
||||
sample_map_iterator sample_it = samples.find( refname );
|
||||
if ( sample_it != samples.end() ) {
|
||||
// first stop the sound from playing (so we don't bomb the
|
||||
// audio scheduler)
|
||||
samples.erase( sample_it );
|
||||
|
||||
// cout << "sndmgr: removed -> " << refname << endl;
|
||||
return true;
|
||||
} else {
|
||||
// cout << "sndmgr: failed remove -> " << refname << endl;
|
||||
bool SGSoundMgr::remove( const string &refname )
|
||||
{
|
||||
sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
|
||||
if ( sample_grp_it == _sample_groups.end() ) {
|
||||
// sample group was not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
_sample_groups.erase( sample_grp_it );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return true of the specified sound exists in the sound manager system
|
||||
bool SGSoundMgr::exists( const string &refname ) {
|
||||
sample_map_iterator sample_it = samples.find( refname );
|
||||
if ( sample_it != samples.end() ) {
|
||||
return true;
|
||||
} else {
|
||||
sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
|
||||
if ( sample_grp_it == _sample_groups.end() ) {
|
||||
// sample group was not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return a pointer to the SGSoundSample if the specified sound exists
|
||||
// return a pointer to the SGSampleGroup if the specified sound exists
|
||||
// in the sound manager system, otherwise return NULL
|
||||
SGSoundSample *SGSoundMgr::find( const string &refname ) {
|
||||
sample_map_iterator sample_it = samples.find( refname );
|
||||
if ( sample_it != samples.end() ) {
|
||||
return sample_it->second;
|
||||
} else {
|
||||
return NULL;
|
||||
SGSampleGroup *SGSoundMgr::find( const string &refname, bool create ) {
|
||||
sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
|
||||
if ( sample_grp_it == _sample_groups.end() ) {
|
||||
// sample group was not found.
|
||||
if (create) {
|
||||
SGSampleGroup* sgrp = new SGSampleGroup(this, refname);
|
||||
add( sgrp, refname );
|
||||
return sgrp;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sample_grp_it->second;
|
||||
}
|
||||
|
||||
|
||||
// tell the scheduler to play the indexed sample in a continuous
|
||||
// loop
|
||||
bool SGSoundMgr::play_looped( const string &refname ) {
|
||||
SGSoundSample *sample;
|
||||
void SGSoundMgr::set_volume( float v )
|
||||
{
|
||||
_volume = v;
|
||||
if (_volume > 1.0) _volume = 1.0;
|
||||
if (_volume < 0.0) _volume = 0.0;
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
if ( (sample = find( refname )) == NULL ) {
|
||||
return false;
|
||||
} else {
|
||||
sample->play( true );
|
||||
return true;
|
||||
// Get an unused source id
|
||||
//
|
||||
// The Sound Manager should keep track of the sources in use, the distance
|
||||
// of these sources to the listener and the volume (also based on audio cone
|
||||
// and hence orientation) of the sources.
|
||||
//
|
||||
// The Sound Manager is (and should be) the only one knowing about source
|
||||
// management. Sources further away should be suspendped to free resources for
|
||||
// newly added sounds close by.
|
||||
unsigned int SGSoundMgr::request_source()
|
||||
{
|
||||
unsigned int source = NO_SOURCE;
|
||||
|
||||
if (_free_sources.size() > 0) {
|
||||
source = _free_sources.back();
|
||||
_free_sources.pop_back();
|
||||
_sources_in_use.push_back(source);
|
||||
}
|
||||
else
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "No more free sources available\n");
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
// Free up a source id for further use
|
||||
void SGSoundMgr::release_source( unsigned int source )
|
||||
{
|
||||
vector<ALuint>::iterator it;
|
||||
|
||||
it = std::find(_sources_in_use.begin(), _sources_in_use.end(), source);
|
||||
if ( it != _sources_in_use.end() ) {
|
||||
ALint result;
|
||||
|
||||
alGetSourcei( source, AL_SOURCE_STATE, &result );
|
||||
if ( result == AL_PLAYING )
|
||||
alSourceStop( source );
|
||||
testForALError("release source");
|
||||
|
||||
alSourcei( source, AL_BUFFER, 0 );
|
||||
_free_sources.push_back( source );
|
||||
_sources_in_use.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
|
||||
{
|
||||
ALuint buffer = NO_BUFFER;
|
||||
|
||||
// tell the scheduler to play the indexed sample once
|
||||
bool SGSoundMgr::play_once( const string& refname ) {
|
||||
SGSoundSample *sample;
|
||||
if ( !sample->is_valid_buffer() ) {
|
||||
// sample was not yet loaded or removed again
|
||||
string sample_name = sample->get_sample_name();
|
||||
void *sample_data = NULL;
|
||||
|
||||
if ( (sample = find( refname )) == NULL ) {
|
||||
return false;
|
||||
} else {
|
||||
sample->play( false );
|
||||
return true;
|
||||
// see if the sample name is already cached
|
||||
buffer_map_iterator buffer_it = _buffers.find( sample_name );
|
||||
if ( buffer_it != _buffers.end() ) {
|
||||
buffer_it->second.refctr++;
|
||||
buffer = buffer_it->second.id;
|
||||
sample->set_buffer( buffer );
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// sample name was not found in the buffer cache.
|
||||
if ( sample->is_file() ) {
|
||||
int freq, format;
|
||||
size_t size;
|
||||
bool res;
|
||||
|
||||
res = load(sample_name, &sample_data, &format, &size, &freq);
|
||||
if (res == false) return buffer;
|
||||
|
||||
sample->set_frequency( freq );
|
||||
sample->set_format( format );
|
||||
sample->set_size( size );
|
||||
}
|
||||
else
|
||||
sample_data = sample->get_data();
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
alGenBuffers(1, &buffer);
|
||||
if ( !testForALError("generate buffer") ) {
|
||||
// Copy data to the internal OpenAL buffer
|
||||
|
||||
ALenum format = sample->get_format();
|
||||
ALsizei size = sample->get_size();
|
||||
ALsizei freq = sample->get_frequency();
|
||||
alBufferData( buffer, format, sample_data, size, freq );
|
||||
|
||||
if ( sample->is_file() ) free(sample_data);
|
||||
|
||||
if ( !testForALError("buffer add data") ) {
|
||||
sample->set_buffer(buffer);
|
||||
_buffers[sample_name] = refUint(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
buffer = sample->get_buffer();
|
||||
}
|
||||
|
||||
|
||||
// return true of the specified sound is currently being played
|
||||
bool SGSoundMgr::is_playing( const string& refname ) {
|
||||
SGSoundSample *sample;
|
||||
|
||||
if ( (sample = find( refname )) == NULL ) {
|
||||
return false;
|
||||
} else {
|
||||
return ( sample->is_playing() );
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
// immediate stop playing the sound
|
||||
bool SGSoundMgr::stop( const string& refname ) {
|
||||
SGSoundSample *sample;
|
||||
|
||||
if ( (sample = find( refname )) == NULL ) {
|
||||
return false;
|
||||
} else {
|
||||
sample->stop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set source position of all managed sounds
|
||||
void SGSoundMgr::set_source_pos_all( ALfloat *pos ) {
|
||||
if ( isnan(pos[0]) || isnan(pos[1]) || isnan(pos[2]) ) {
|
||||
// bail if a bad position is passed in
|
||||
void SGSoundMgr::release_buffer(SGSoundSample *sample)
|
||||
{
|
||||
string sample_name = sample->get_sample_name();
|
||||
buffer_map_iterator buffer_it = _buffers.find( sample_name );
|
||||
if ( buffer_it == _buffers.end() ) {
|
||||
// buffer was not found
|
||||
return;
|
||||
}
|
||||
|
||||
sample_map_iterator sample_current = samples.begin();
|
||||
sample_map_iterator sample_end = samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
sample->set_source_pos( pos );
|
||||
sample->no_valid_buffer();
|
||||
buffer_it->second.refctr--;
|
||||
if (buffer_it->second.refctr == 0) {
|
||||
ALuint buffer = buffer_it->second.id;
|
||||
alDeleteBuffers(1, &buffer);
|
||||
_buffers.erase( buffer_it );
|
||||
testForALError("release buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void SGSoundMgr::update_pos_and_orientation() {
|
||||
/**
|
||||
* Description: ORIENTATION is a pair of 3-tuples representing the
|
||||
* 'at' direction vector and 'up' direction of the Object in
|
||||
* Cartesian space. AL expects two vectors that are orthogonal to
|
||||
* each other. These vectors are not expected to be normalized. If
|
||||
* one or more vectors have zero length, implementation behavior
|
||||
* is undefined. If the two vectors are linearly dependent,
|
||||
* behavior is undefined.
|
||||
*
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
|
||||
*/
|
||||
SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3());
|
||||
SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2());
|
||||
_at_up_vec[0] = sgv_at[0];
|
||||
_at_up_vec[1] = sgv_at[1];
|
||||
_at_up_vec[2] = sgv_at[2];
|
||||
_at_up_vec[3] = sgv_up[0];
|
||||
_at_up_vec[4] = sgv_up[1];
|
||||
_at_up_vec[5] = sgv_up[2];
|
||||
|
||||
// set source velocity of all managed sounds
|
||||
void SGSoundMgr::set_source_vel_all( ALfloat *vel ) {
|
||||
if ( isnan(vel[0]) || isnan(vel[1]) || isnan(vel[2]) ) {
|
||||
// bail if a bad velocity is passed in
|
||||
return;
|
||||
}
|
||||
|
||||
sample_map_iterator sample_current = samples.begin();
|
||||
sample_map_iterator sample_end = samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
SGSoundSample *sample = sample_current->second;
|
||||
sample->set_source_vel( vel, listener_vel );
|
||||
}
|
||||
// static const SGQuatd q(-0.5, -0.5, 0.5, 0.5);
|
||||
// SGQuatd hlOr = SGQuatd::fromLonLat(SGGeod::fromCart(_base_pos));
|
||||
// SGQuatd ec2body = hlOr*_orientation;
|
||||
_absolute_pos = _base_pos; // + ec2body.backTransform( _offset_pos );
|
||||
}
|
||||
|
||||
bool SGSoundMgr::load(string &samplepath, void **dbuf, int *fmt,
|
||||
size_t *sz, int *frq )
|
||||
{
|
||||
if ( !_working ) return false;
|
||||
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
ALvoid *data;
|
||||
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
ALfloat freqf;
|
||||
data = alutLoadMemoryFromFile(samplepath.c_str(), &format, &size, &freqf );
|
||||
freq = (ALsizei)freqf;
|
||||
if (data == NULL) {
|
||||
int error = alutGetError();
|
||||
string msg = "Failed to load wav file: ";
|
||||
msg.append(alutGetErrorString(error));
|
||||
throw sg_io_exception(msg.c_str(), sg_location(samplepath));
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
ALbyte *fname = (ALbyte *)samplepath.c_str();
|
||||
# if defined (__APPLE__)
|
||||
alutLoadWAVFile( fname, &format, &data, &size, &freq );
|
||||
# else
|
||||
ALboolean loop;
|
||||
alutLoadWAVFile( fname, &format, &data, &size, &freq, &loop );
|
||||
# endif
|
||||
ALenum error = alGetError();
|
||||
if ( error != AL_NO_ERROR ) {
|
||||
string msg = "Failed to load wav file: ";
|
||||
msg.append(alGetString(error));
|
||||
throw sg_io_exception(msg.c_str(), sg_location(samplepath));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
*dbuf = (void *)data;
|
||||
*fmt = (int)format;
|
||||
*sz = (size_t)size;
|
||||
*frq = (int)freq;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<const char*> SGSoundMgr::get_available_devices()
|
||||
{
|
||||
vector<const char*> devices;
|
||||
const ALCchar *s;
|
||||
|
||||
if (alcIsExtensionPresent(NULL, "ALC_enumerate_all_EXT") == AL_TRUE) {
|
||||
s = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||
} else {
|
||||
s = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
}
|
||||
|
||||
if (s) {
|
||||
ALCchar *nptr, *ptr = (ALCchar *)s;
|
||||
|
||||
nptr = ptr;
|
||||
while (*(nptr += strlen(ptr)+1) != 0)
|
||||
{
|
||||
devices.push_back(ptr);
|
||||
ptr = nptr;
|
||||
}
|
||||
devices.push_back(ptr);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
||||
bool SGSoundMgr::testForError(void *p, string s)
|
||||
{
|
||||
if (p == NULL) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Error: " << s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool SGSoundMgr::testForALError(string s)
|
||||
{
|
||||
ALenum error = alGetError();
|
||||
if (error != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (sound manager): "
|
||||
<< alGetString(error) << " at " << s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SGSoundMgr::testForALCError(string s)
|
||||
{
|
||||
ALCenum error;
|
||||
error = alcGetError(_device);
|
||||
if (error != ALC_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "ALC Error (sound manager): "
|
||||
<< alcGetString(_device, error) << " at "
|
||||
<< s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SGSoundMgr::testForALUTError(string s)
|
||||
{
|
||||
#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
|
||||
ALenum error;
|
||||
error = alutGetError ();
|
||||
if (error != ALUT_ERROR_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "ALUT Error (sound manager): "
|
||||
<< alutGetErrorString(error) << " at "
|
||||
<< s);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
// <david_j_findlay@yahoo.com.au> 2001
|
||||
//
|
||||
// C++-ified by Curtis Olson, started March 2001.
|
||||
// Modified for the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@@ -18,8 +20,8 @@
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
@@ -37,210 +39,296 @@
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#if defined( __APPLE__ )
|
||||
#if defined(__APPLE__)
|
||||
# define AL_ILLEGAL_ENUM AL_INVALID_ENUM
|
||||
# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
|
||||
# include <OpenAL/al.h>
|
||||
# include <OpenAL/alc.h>
|
||||
# include <OpenAL/alut.h>
|
||||
#elif defined(OPENALSDK)
|
||||
# include <al.h>
|
||||
# include <alc.h>
|
||||
# include <AL/alut.h>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alc.h>
|
||||
# include <AL/alut.h>
|
||||
#endif
|
||||
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
# define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/math/SGMathFwd.hxx>
|
||||
|
||||
#include "sample_group.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
struct refUint {
|
||||
unsigned int refctr;
|
||||
ALuint id;
|
||||
|
||||
typedef map < string, SGSharedPtr<SGSoundSample> > sample_map;
|
||||
typedef sample_map::iterator sample_map_iterator;
|
||||
typedef sample_map::const_iterator const_sample_map_iterator;
|
||||
refUint() { refctr = 0; id = (ALuint)-1; };
|
||||
refUint(ALuint i) { refctr = 1; id = i; };
|
||||
~refUint() {};
|
||||
};
|
||||
|
||||
typedef std::map < string, refUint > buffer_map;
|
||||
typedef buffer_map::iterator buffer_map_iterator;
|
||||
typedef buffer_map::const_iterator const_buffer_map_iterator;
|
||||
|
||||
typedef std::map < string, SGSharedPtr<SGSampleGroup> > sample_group_map;
|
||||
typedef sample_group_map::iterator sample_group_map_iterator;
|
||||
typedef sample_group_map::const_iterator const_sample_group_map_iterator;
|
||||
|
||||
/**
|
||||
* Manage a collection of SGSoundSample instances
|
||||
* Manage a collection of SGSampleGroup instances
|
||||
*/
|
||||
class SGSoundMgr
|
||||
class SGSoundMgr : public SGSubsystem
|
||||
{
|
||||
|
||||
ALCdevice *dev;
|
||||
ALCcontext *context;
|
||||
|
||||
// Position of the listener.
|
||||
ALfloat listener_pos[3];
|
||||
|
||||
// Velocity of the listener.
|
||||
ALfloat listener_vel[3];
|
||||
|
||||
// Orientation of the listener. (first 3 elements are "at", second
|
||||
// 3 are "up")
|
||||
ALfloat listener_ori[6];
|
||||
|
||||
sample_map samples;
|
||||
|
||||
bool working;
|
||||
double safety;
|
||||
|
||||
public:
|
||||
|
||||
SGSoundMgr();
|
||||
~SGSoundMgr();
|
||||
|
||||
|
||||
/**
|
||||
* (re) initialize the sound manager.
|
||||
*/
|
||||
void init();
|
||||
|
||||
|
||||
/**
|
||||
* Bind properties for the sound manager.
|
||||
*/
|
||||
void init(const char *devname = NULL);
|
||||
void bind();
|
||||
|
||||
|
||||
/**
|
||||
* Unbind properties for the sound manager.
|
||||
*/
|
||||
void unbind();
|
||||
|
||||
|
||||
/**
|
||||
* Run the audio scheduler.
|
||||
*/
|
||||
void update(double dt);
|
||||
|
||||
|
||||
/**
|
||||
* Pause all sounds.
|
||||
*/
|
||||
void pause();
|
||||
|
||||
|
||||
/**
|
||||
* Resume all sounds.
|
||||
*/
|
||||
|
||||
void suspend();
|
||||
void resume();
|
||||
void stop();
|
||||
|
||||
inline void reinit() { stop(); init(); }
|
||||
|
||||
/**
|
||||
* is audio working?
|
||||
* Test is the sound manager is in a working condition.
|
||||
* @return true is the sound manager is working
|
||||
*/
|
||||
inline bool is_working() const { return working; }
|
||||
inline bool is_working() const { return _working; }
|
||||
|
||||
/**
|
||||
* reinitialize the sound manager
|
||||
* Set the sound manager to a working condition.
|
||||
*/
|
||||
inline void reinit() { init(); }
|
||||
void activate();
|
||||
|
||||
/**
|
||||
* add a sound effect, return true if successful
|
||||
* Test is the sound manager is in an active and working condition.
|
||||
* @return true is the sound manager is active
|
||||
*/
|
||||
bool add( SGSoundSample *sound, const string& refname);
|
||||
inline bool is_active() const { return _active; }
|
||||
|
||||
/**
|
||||
* Register a sample group to the sound manager.
|
||||
* @para sgrp Pointer to a sample group to add
|
||||
* @param refname Reference name of the sample group
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool add( SGSampleGroup *sgrp, const string& refname );
|
||||
|
||||
/**
|
||||
* remove a sound effect, return true if successful
|
||||
* Remove a sample group from the sound manager.
|
||||
* @param refname Reference name of the sample group to remove
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool remove( const string& refname );
|
||||
|
||||
/**
|
||||
* return true of the specified sound exists in the sound manager system
|
||||
* Test if a specified sample group is registered at the sound manager
|
||||
* @param refname Reference name of the sample group test for
|
||||
* @return true if the specified sample group exists
|
||||
*/
|
||||
bool exists( const string& refname );
|
||||
|
||||
/**
|
||||
* return a pointer to the SGSoundSample if the specified sound
|
||||
* exists in the sound manager system, otherwise return NULL
|
||||
* Find a specified sample group in the sound manager
|
||||
* @param refname Reference name of the sample group to find
|
||||
* @return A pointer to the SGSampleGroup
|
||||
*/
|
||||
SGSoundSample *find( const string& refname );
|
||||
SGSampleGroup *find( const string& refname, bool create = false );
|
||||
|
||||
/**
|
||||
* tell the scheduler to play the indexed sample in a continuous
|
||||
* loop
|
||||
* Set the Cartesian position of the sound manager.
|
||||
* @param pos OpenAL listener position
|
||||
*/
|
||||
bool play_looped( const string& refname );
|
||||
void set_position( const SGVec3d& pos, const SGGeod& pos_geod ) {
|
||||
_base_pos = pos; _geod_pos = pos_geod; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* tell the scheduler to play the indexed sample once
|
||||
*/
|
||||
bool play_once( const string& refname );
|
||||
|
||||
/**
|
||||
* return true of the specified sound is currently being played
|
||||
*/
|
||||
bool is_playing( const string& refname );
|
||||
|
||||
/**
|
||||
* immediate stop playing the sound
|
||||
*/
|
||||
bool stop( const string& refname );
|
||||
|
||||
/**
|
||||
* set overall volume for the application.
|
||||
* @param vol 1.0 is default, must be greater than 0
|
||||
*/
|
||||
inline void set_volume( const ALfloat vol ) {
|
||||
if ( vol > 0.0 ) {
|
||||
alListenerf( AL_GAIN, vol );
|
||||
}
|
||||
void set_position_offset( const SGVec3d& pos ) {
|
||||
_offset_pos = pos; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the position of the listener (in opengl coordinates)
|
||||
* Get the position of the sound manager.
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right
|
||||
* @return OpenAL listener position
|
||||
*/
|
||||
inline void set_listener_pos( ALfloat *pos ) {
|
||||
listener_pos[0] = pos[0];
|
||||
listener_pos[1] = pos[1];
|
||||
listener_pos[2] = pos[2];
|
||||
alListenerfv( AL_POSITION, listener_pos );
|
||||
SGVec3d& get_position() { return _absolute_pos; }
|
||||
|
||||
/**
|
||||
* Set the velocity vector (in meters per second) of the sound manager
|
||||
* This is the horizontal local frame; x=north, y=east, z=down
|
||||
* @param Velocity vector
|
||||
*/
|
||||
void set_velocity( const SGVec3d& vel ) {
|
||||
_velocity = vel; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the velocity of the listener (in opengl coordinates)
|
||||
* Get the velocity vector of the sound manager
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
|
||||
* @return Velocity vector of the OpenAL listener
|
||||
*/
|
||||
inline void set_listener_vel( ALfloat *vel ) {
|
||||
listener_vel[0] = vel[0];
|
||||
listener_vel[1] = vel[1];
|
||||
listener_vel[2] = vel[2];
|
||||
#ifdef USE_OPEN_AL_DOPPLER
|
||||
alListenerfv( AL_VELOCITY, listener_vel );
|
||||
#endif
|
||||
inline SGVec3f get_velocity() { return toVec3f(_velocity); }
|
||||
|
||||
/**
|
||||
* Set the orientation of the sound manager
|
||||
* @param ori Quaternation containing the orientation information
|
||||
*/
|
||||
void set_orientation( const SGQuatd& ori ) {
|
||||
_orientation = ori; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the orientation of the listener (in opengl coordinates)
|
||||
*
|
||||
* Description: ORIENTATION is a pair of 3-tuples representing the
|
||||
* 'at' direction vector and 'up' direction of the Object in
|
||||
* Cartesian space. AL expects two vectors that are orthogonal to
|
||||
* each other. These vectors are not expected to be normalized. If
|
||||
* one or more vectors have zero length, implementation behavior
|
||||
* is undefined. If the two vectors are linearly dependent,
|
||||
* behavior is undefined.
|
||||
* Get the orientation of the sound manager
|
||||
* @return Quaternation containing the orientation information
|
||||
*/
|
||||
inline void set_listener_orientation( ALfloat *ori ) {
|
||||
listener_ori[0] = ori[0];
|
||||
listener_ori[1] = ori[1];
|
||||
listener_ori[2] = ori[2];
|
||||
listener_ori[3] = ori[3];
|
||||
listener_ori[4] = ori[4];
|
||||
listener_ori[5] = ori[5];
|
||||
alListenerfv( AL_ORIENTATION, listener_ori );
|
||||
inline const SGQuatd& get_orientation() { return _orientation; }
|
||||
|
||||
/**
|
||||
* Get the direction vector of the sound manager
|
||||
* This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
|
||||
* @return Look-at direction of the OpenAL listener
|
||||
*/
|
||||
SGVec3f get_direction() {
|
||||
return SGVec3f(_at_up_vec[0], _at_up_vec[1], _at_up_vec[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the positions of all managed sound sources
|
||||
*/
|
||||
void set_source_pos_all( ALfloat *pos );
|
||||
enum {
|
||||
NO_SOURCE = (unsigned int)-1,
|
||||
NO_BUFFER = (unsigned int)-1
|
||||
};
|
||||
|
||||
/**
|
||||
* set the velocities of all managed sound sources
|
||||
* Set the master volume.
|
||||
* @param vol Volume (must be between 0.0 and 1.0)
|
||||
*/
|
||||
void set_source_vel_all( ALfloat *pos );
|
||||
void set_volume( float vol );
|
||||
|
||||
/**
|
||||
* Get the master volume.
|
||||
* @return Volume (must be between 0.0 and 1.0)
|
||||
*/
|
||||
inline float get_volume() { return _volume; }
|
||||
|
||||
/**
|
||||
* Get a free OpenAL source-id
|
||||
* @return NO_SOURCE if no source is available
|
||||
*/
|
||||
unsigned int request_source();
|
||||
|
||||
/**
|
||||
* Free an OpenAL source-id for future use
|
||||
* @param source OpenAL source-id to free
|
||||
*/
|
||||
void release_source( unsigned int source );
|
||||
|
||||
/**
|
||||
* Get a free OpenAL buffer-id
|
||||
* The buffer-id will be asigned to the sample by calling this function.
|
||||
* @param sample Pointer to an audio sample to assign the buffer-id to
|
||||
* @return NO_BUFFER if loading of the buffer failed.
|
||||
*/
|
||||
unsigned int request_buffer(SGSoundSample *sample);
|
||||
|
||||
/**
|
||||
* Free an OpenAL buffer-id for this sample
|
||||
* @param sample Pointer to an audio sample for which to free the buffer
|
||||
*/
|
||||
void release_buffer( SGSoundSample *sample );
|
||||
|
||||
/**
|
||||
* Test if the position of the sound manager has changed.
|
||||
* The value will be set to false upon the next call to update_late()
|
||||
* @return true if the position has changed
|
||||
*/
|
||||
inline bool has_changed() { return _changed; }
|
||||
|
||||
/**
|
||||
* Some implementations seem to need the velocity miltyplied by a
|
||||
* factor of 100 to make them distinct. I've not found if this is
|
||||
* a problem in the implementation or in out code. Until then
|
||||
* this function is used to detect the problematic implementations.
|
||||
*/
|
||||
inline bool bad_doppler_effect() { return _bad_doppler; }
|
||||
|
||||
/**
|
||||
* Load a sample file and return it's configuration and data.
|
||||
* @param samplepath Path to the file to load
|
||||
* @param data Pointer to a variable that points to the allocated data
|
||||
* @param format Pointer to a vairable that gets the OpenAL format
|
||||
* @param size Pointer to a vairable that gets the sample size in bytes
|
||||
* @param freq Pointer to a vairable that gets the sample frequency in Herz
|
||||
* @return true if succesful, false on error
|
||||
*/
|
||||
bool load(string &samplepath, void **data, int *format,
|
||||
size_t *size, int *freq );
|
||||
|
||||
/**
|
||||
* Get a list of available playback devices.
|
||||
*/
|
||||
vector<const char*> get_available_devices();
|
||||
|
||||
private:
|
||||
static int _alut_init;
|
||||
|
||||
bool _working;
|
||||
bool _active;
|
||||
bool _changed;
|
||||
float _volume;
|
||||
|
||||
ALCdevice *_device;
|
||||
ALCcontext *_context;
|
||||
|
||||
// Position of the listener.
|
||||
SGVec3d _absolute_pos;
|
||||
SGVec3d _offset_pos;
|
||||
SGVec3d _base_pos;
|
||||
SGGeod _geod_pos;
|
||||
|
||||
// Velocity of the listener.
|
||||
SGVec3d _velocity;
|
||||
|
||||
// Orientation of the listener.
|
||||
// first 3 elements are "at" vector, second 3 are "up" vector
|
||||
SGQuatd _orientation;
|
||||
ALfloat _at_up_vec[6];
|
||||
|
||||
sample_group_map _sample_groups;
|
||||
buffer_map _buffers;
|
||||
|
||||
vector<ALuint> _free_sources;
|
||||
vector<ALuint> _sources_in_use;
|
||||
|
||||
bool _bad_doppler;
|
||||
|
||||
bool testForALError(string s);
|
||||
bool testForALCError(string s);
|
||||
bool testForALUTError(string s);
|
||||
bool testForError(void *p, string s);
|
||||
|
||||
void update_pos_and_orientation();
|
||||
void update_sample_config( SGSampleGroup *sound );
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
static double _snd_inv(double v) { return (v == 0) ? 1e99 : 1/v; }
|
||||
static double _snd_abs(double v) { return (v >= 0) ? v : -v; }
|
||||
static double _snd_sqrt(double v) { return sqrt(fabs(v)); }
|
||||
static double _snd_log10(double v) { return log10(fabs(v)); }
|
||||
static double _snd_log(double v) { return log(fabs(v)); }
|
||||
static double _snd_log10(double v) { return log10(fabs(v)+1e-9); }
|
||||
static double _snd_log(double v) { return log(fabs(v)+1e-9); }
|
||||
// static double _snd_sqr(double v) { return v*v; }
|
||||
// static double _snd_pow3(double v) { return v*v*v; }
|
||||
|
||||
@@ -50,14 +50,11 @@ static const struct {
|
||||
const char *name;
|
||||
double (*fn)(double);
|
||||
} __sound_fn[] = {
|
||||
// {"lin", _snd_lin},
|
||||
{"inv", _snd_inv},
|
||||
{"abs", _snd_abs},
|
||||
{"sqrt", _snd_sqrt},
|
||||
{"log", _snd_log10},
|
||||
{"ln", _snd_log},
|
||||
// {"sqr", _snd_sqr},
|
||||
// {"pow3", _snd_pow3},
|
||||
{"", NULL}
|
||||
};
|
||||
|
||||
@@ -84,7 +81,8 @@ SGXmlSound::~SGXmlSound()
|
||||
}
|
||||
|
||||
void
|
||||
SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node,
|
||||
SGSampleGroup *sgrp, SGSampleGroup *avionics,
|
||||
const string &path)
|
||||
{
|
||||
|
||||
@@ -92,10 +90,6 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
// set global sound properties
|
||||
//
|
||||
|
||||
if (sndmgr->is_working() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
_name = node->getStringValue("name", "");
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Loading sound information for: " << _name );
|
||||
|
||||
@@ -108,11 +102,13 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
|
||||
} else {
|
||||
_mode = SGXmlSound::ONCE;
|
||||
|
||||
if ( strcmp(mode_str, "") )
|
||||
SG_LOG(SG_GENERAL,SG_INFO, "Unknown sound mode for '" << _name << "', default to 'once'");
|
||||
}
|
||||
|
||||
bool is_avionics = false;
|
||||
const char *type_str = node->getStringValue("type", "fx");
|
||||
if ( !strcmp(type_str, "avionics") )
|
||||
is_avionics = true;
|
||||
|
||||
_property = root->getNode(node->getStringValue("property", ""), true);
|
||||
SGPropertyNode *condition = node->getChild("condition");
|
||||
if (condition != NULL)
|
||||
@@ -178,9 +174,10 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
|
||||
}
|
||||
|
||||
float reference_dist = node->getDoubleValue("reference-dist", 500.0);
|
||||
float max_dist = node->getDoubleValue("max-dist", 3000.0);
|
||||
|
||||
// rule of thumb: make reference distance a 100th of the maximum distance.
|
||||
float reference_dist = node->getDoubleValue("reference-dist", 60.0);
|
||||
float max_dist = node->getDoubleValue("max-dist", 6000.0);
|
||||
|
||||
//
|
||||
// set pitch properties
|
||||
//
|
||||
@@ -236,58 +233,48 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
//
|
||||
// Relative position
|
||||
//
|
||||
sgVec3 offset_pos;
|
||||
sgSetVec3( offset_pos, 0.0, 0.0, 0.0 );
|
||||
SGPropertyNode_ptr pos = node->getChild("position");
|
||||
if ( pos != NULL ) {
|
||||
offset_pos[0] = pos->getDoubleValue("x", 0.0);
|
||||
offset_pos[1] = -pos->getDoubleValue("y", 0.0);
|
||||
offset_pos[2] = pos->getDoubleValue("z", 0.0);
|
||||
SGVec3f offset_pos = SGVec3f::zeros();
|
||||
SGPropertyNode_ptr prop = node->getChild("position");
|
||||
if ( prop != NULL ) {
|
||||
offset_pos[0] = -prop->getDoubleValue("x", 0.0);
|
||||
offset_pos[1] = -prop->getDoubleValue("y", 0.0);
|
||||
offset_pos[2] = -prop->getDoubleValue("z", 0.0);
|
||||
}
|
||||
|
||||
//
|
||||
// Orientation
|
||||
//
|
||||
sgVec3 dir;
|
||||
float inner, outer, outer_gain;
|
||||
sgSetVec3( dir, 0.0, 0.0, 0.0 );
|
||||
inner = outer = 360.0;
|
||||
outer_gain = 0.0;
|
||||
pos = node->getChild("orientation");
|
||||
if ( pos != NULL ) {
|
||||
dir[0] = pos->getDoubleValue("x", 0.0);
|
||||
dir[1] = -pos->getDoubleValue("y", 0.0);
|
||||
dir[2] = pos->getDoubleValue("z", 0.0);
|
||||
inner = pos->getDoubleValue("inner-angle", 360.0);
|
||||
outer = pos->getDoubleValue("outer-angle", 360.0);
|
||||
outer_gain = pos->getDoubleValue("outer-gain", 0.0);
|
||||
SGVec3f dir = SGVec3f::zeros();
|
||||
float inner = 360.0;
|
||||
float outer = 360.0;
|
||||
float outer_gain = 0.0;
|
||||
prop = node->getChild("orientation");
|
||||
if ( prop != NULL ) {
|
||||
dir = SGVec3f(-prop->getFloatValue("x", 0.0),
|
||||
-prop->getFloatValue("y", 0.0),
|
||||
-prop->getFloatValue("z", 0.0));
|
||||
inner = prop->getFloatValue("inner-angle", 360.0);
|
||||
outer = prop->getFloatValue("outer-angle", 360.0);
|
||||
outer_gain = prop->getFloatValue("outer-gain", 0.0);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Initialize the sample
|
||||
//
|
||||
_mgr = sndmgr;
|
||||
if ( (_sample = _mgr->find(_name)) == NULL ) {
|
||||
// FIXME: Does it make sense to overwrite a previous entry's
|
||||
// configuration just because a new entry has the same name?
|
||||
// Note that we can't match on identical "path" because we the
|
||||
// new entry could be at a different location with different
|
||||
// configuration so we need a new sample which creates a new
|
||||
// "alSource". The semantics of what is going on here seems
|
||||
// confused and needs to be thought through more carefully.
|
||||
_sample = new SGSoundSample( path.c_str(),
|
||||
node->getStringValue("path", ""),
|
||||
false );
|
||||
|
||||
_mgr->add( _sample, _name );
|
||||
if (is_avionics) {
|
||||
_sgrp = avionics;
|
||||
} else {
|
||||
_sgrp = sgrp;
|
||||
}
|
||||
|
||||
_sample->set_offset_pos( offset_pos );
|
||||
_sample->set_orientation(dir, inner, outer, outer_gain);
|
||||
_sample->set_volume(v);
|
||||
_sample = new SGSoundSample( path.c_str(), node->getStringValue("path", ""));
|
||||
_sample->set_relative_position( offset_pos );
|
||||
_sample->set_direction( dir );
|
||||
_sample->set_audio_cone( inner, outer, outer_gain );
|
||||
_sample->set_reference_dist( reference_dist );
|
||||
_sample->set_max_dist( max_dist );
|
||||
_sample->set_pitch(p);
|
||||
_sample->set_volume( v );
|
||||
_sample->set_pitch( p );
|
||||
_sgrp->add( _sample, _name );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -316,7 +303,8 @@ SGXmlSound::update (double dt)
|
||||
)
|
||||
)
|
||||
{
|
||||
if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) {
|
||||
if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME))
|
||||
{
|
||||
if (_sample->is_playing()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Stopping audio after " << _dt_play
|
||||
<< " sec: " << _name );
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/props/condition.hxx>
|
||||
|
||||
#include "sample_group.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
static const double MAX_TRANSIT_TIME = 0.1; // 100 ms.
|
||||
|
||||
@@ -99,11 +99,12 @@ public:
|
||||
* @param root The root node of the programs property tree.
|
||||
* @param child A pointer to the location of the current event as defined
|
||||
* in the configuration file.
|
||||
* @param sndmgr A pointer to a pre-initialized sound manager class.
|
||||
* @param sgrp A pointer to a pre-initialized sample group class.
|
||||
* @param avionics A pointer to the pre-initialized avionics sample group.
|
||||
* @param path The path where the audio files remain.
|
||||
*/
|
||||
virtual void init (SGPropertyNode *, SGPropertyNode *, SGSoundMgr *,
|
||||
const string &);
|
||||
virtual void init (SGPropertyNode *, SGPropertyNode *, SGSampleGroup *,
|
||||
SGSampleGroup *, const string &);
|
||||
|
||||
/**
|
||||
* Check whether an event has happened and if action has to be taken.
|
||||
@@ -135,7 +136,7 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
SGSoundMgr * _mgr;
|
||||
SGSampleGroup * _sgrp;
|
||||
SGSharedPtr<SGSoundSample> _sample;
|
||||
|
||||
SGSharedPtr<SGCondition> _condition;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "SGAtomic.hxx"
|
||||
|
||||
#if defined(SGATOMIC_USE_GCC4_BUILTINS) && defined(__i386__)
|
||||
#if defined(SGATOMIC_USE_GCC4_BUILTINS) && defined (__i386__)
|
||||
|
||||
// Usually the apropriate functions are inlined by gcc.
|
||||
// But if gcc is called with something aequivalent to -march=i386,
|
||||
|
||||
Reference in New Issue
Block a user