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:
Tim Moore
2009-12-20 10:40:26 +01:00
18 changed files with 2315 additions and 1085 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 );

View File

@@ -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
View 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.

View File

@@ -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;
}

View File

@@ -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;
}

View 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;
}

View 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;
}

View 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

View File

@@ -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;
}

View File

@@ -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();
};

View File

@@ -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;
}

View File

@@ -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 );
};

View File

@@ -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 );

View File

@@ -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;

View File

@@ -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,