Initial commit of the new sound system, expect more updates to follow
This commit is contained in:
@@ -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_base_position(pos);
|
||||
snd->play_once();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if( !soundMgr->is_playing("thunder") ) {
|
||||
if( !sampleGroup->is_playing("thunder") ) {
|
||||
snd_active = false;
|
||||
snd_playing = false;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ using std::vector;
|
||||
using std::string;
|
||||
|
||||
class SGLightning;
|
||||
class SGSoundMgr;
|
||||
class SGSampleGroup;
|
||||
|
||||
/**
|
||||
* Simulate some echo on a weather radar.
|
||||
@@ -84,7 +84,7 @@ private:
|
||||
sgVec4 fog_color;
|
||||
sgMat4 transform;
|
||||
double last_lon, last_lat, last_alt;
|
||||
SGSoundMgr *soundMgr;
|
||||
SGSampleGroup *sampleGroup;
|
||||
bool snd_active, snd_playing;
|
||||
double snd_timer, snd_wait, snd_pos_lat, snd_pos_lon, snd_dist;
|
||||
double min_time_before_lt;
|
||||
@@ -243,7 +243,7 @@ public:
|
||||
* Forward the sound manager instance to be able to play samples.
|
||||
* @param mgr a running sound manager
|
||||
*/
|
||||
void set_soundMgr(SGSoundMgr *mgr);
|
||||
void set_sampleGroup(SGSampleGroup *sgr);
|
||||
|
||||
void setFOV( float w, float h );
|
||||
void getFOV( float &w, float &h );
|
||||
|
||||
@@ -7,11 +7,13 @@ 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
|
||||
@@ -27,10 +29,10 @@ libsgsound_a_SOURCES = \
|
||||
|
||||
#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/structure/libsgstructure.a \
|
||||
# $(openal_LIBS) \
|
||||
# -lOpenThreads
|
||||
# $(openal_LIBS)
|
||||
|
||||
INCLUDES = -I$(top_srcdir) -DSRC_DIR=\"$(top_srcdir)/simgear/sound\"
|
||||
|
||||
Binary file not shown.
@@ -7,48 +7,94 @@ static unsigned int sleep(unsigned int secs) { return 0; }
|
||||
#include <unistd.h> // sleep()
|
||||
#endif
|
||||
|
||||
#include "sample_openal.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
|
||||
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();
|
||||
smgr->set_volume(0.9);
|
||||
sgr = smgr->find("default", true);
|
||||
|
||||
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);
|
||||
sgr->update(0.5);
|
||||
sgr->stop("sound4");
|
||||
sgr->stop("sound5");
|
||||
sgr->stop("sound6");
|
||||
sgr->update(1);
|
||||
sleep(1);
|
||||
|
||||
smgr->unbind();
|
||||
sleep(2);
|
||||
delete smgr;
|
||||
}
|
||||
|
||||
443
simgear/sound/sample_group.cxx
Normal file
443
simgear/sound/sample_group.cxx
Normal file
@@ -0,0 +1,443 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#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
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
# define isnan(x) _isnan(x)
|
||||
#endif
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
#include "sample_group.hxx"
|
||||
|
||||
SGSampleGroup::SGSampleGroup () :
|
||||
_smgr(NULL),
|
||||
_active(false),
|
||||
_changed(true),
|
||||
_position_changed(true),
|
||||
_position(SGVec3d::zeros().data()),
|
||||
_orientation(SGVec3f::zeros().data())
|
||||
{
|
||||
_samples.clear();
|
||||
}
|
||||
|
||||
SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr, const string &refname ) :
|
||||
_smgr(smgr),
|
||||
_active(false),
|
||||
_changed(true),
|
||||
_position_changed(true),
|
||||
_position(SGVec3d::zeros().data()),
|
||||
_orientation(SGVec3f::zeros().data())
|
||||
{
|
||||
_smgr->add(this, refname);
|
||||
_active = _smgr->is_working();
|
||||
_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 = 0;
|
||||
}
|
||||
|
||||
void SGSampleGroup::update( double dt ) {
|
||||
|
||||
if ( !_active ) return;
|
||||
|
||||
// testForALError("start of update!!\n");
|
||||
|
||||
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.
|
||||
//
|
||||
ALboolean looping = sample->get_looping() ? AL_TRUE : AL_FALSE;
|
||||
|
||||
if ( !sample->is_valid_buffer() ) {
|
||||
// sample was not yet loaded or removed again
|
||||
|
||||
// TODO: Create a buffer cache that checks whether a file is already present
|
||||
// as an OpenAL buffer since buffers can be shared among sources.
|
||||
load_file(sample);
|
||||
if ( testForALError("load sample") ) {
|
||||
throw sg_exception("Failed to load sound sample.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
ALuint buffer;
|
||||
alGenBuffers(1, &buffer);
|
||||
if ( testForALError("generate buffer") ) {
|
||||
throw sg_exception("Failed to generate OpenAL buffer.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy data to the internal OpenAL buffer
|
||||
const ALvoid *data = sample->get_data();
|
||||
ALenum format = sample->get_format();
|
||||
ALsizei size = sample->get_size();
|
||||
ALsizei freq = sample->get_frequency();
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
sample->free_data();
|
||||
if ( testForALError("buffer add data") ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sample->set_buffer(buffer);
|
||||
}
|
||||
|
||||
// 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 );
|
||||
|
||||
alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
|
||||
alSourcei( source, AL_LOOPING, looping );
|
||||
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->no_valid_source();
|
||||
sample->stop();
|
||||
_smgr->release_source( sample->get_source() );
|
||||
} else {
|
||||
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->no_valid_source();
|
||||
sample->stop();
|
||||
_smgr->release_source( source );
|
||||
|
||||
}
|
||||
}
|
||||
testForALError("update");
|
||||
}
|
||||
}
|
||||
|
||||
// add a sound effect, return true if successful
|
||||
bool SGSampleGroup::add( 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;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
|
||||
// stop playing all associated samples
|
||||
void
|
||||
SGSampleGroup::suspend ()
|
||||
{
|
||||
_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() ) {
|
||||
unsigned int source = sample->get_source();
|
||||
alSourcePause( source );
|
||||
}
|
||||
}
|
||||
testForALError("suspend");
|
||||
}
|
||||
|
||||
// resume playing all associated samples
|
||||
void
|
||||
SGSampleGroup::resume ()
|
||||
{
|
||||
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() ) {
|
||||
unsigned int source = sample->get_source();
|
||||
alSourcePlay( source );
|
||||
}
|
||||
}
|
||||
testForALError("resume");
|
||||
_active = true;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
// set source position of all managed sounds
|
||||
void SGSampleGroup::set_position( SGVec3d pos ) {
|
||||
if ( isnan(pos.data()[0]) || isnan(pos.data()[1]) || isnan(pos.data()[2]) )
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup postion");
|
||||
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_base_position( pos );
|
||||
}
|
||||
}
|
||||
|
||||
// set source velocity of all managed sounds
|
||||
void SGSampleGroup::set_velocity( SGVec3f vel ) {
|
||||
if ( isnan(vel.data()[0]) || isnan(vel.data()[1]) || isnan(vel.data()[2]) )
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup velocity");
|
||||
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_velocity( vel );
|
||||
}
|
||||
}
|
||||
|
||||
// ste the source orientation of all managed sounds
|
||||
void SGSampleGroup::set_orientation( SGVec3f ori ) {
|
||||
if ( isnan(ori.data()[0]) || isnan(ori.data()[1]) || isnan(ori.data()[2]) )
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup orientation");
|
||||
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_orientation( ori );
|
||||
}
|
||||
}
|
||||
|
||||
void SGSampleGroup::update_sample_config( SGSoundSample *sample ) {
|
||||
if ( sample->is_valid_source() ) {
|
||||
unsigned int source = sample->get_source();
|
||||
|
||||
alSourcefv( source, AL_POSITION, sample->get_position());
|
||||
alSourcefv( source, AL_DIRECTION, sample->get_direction() );
|
||||
alSourcefv( source, AL_VELOCITY, sample->get_velocity() );
|
||||
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_ROLLOFF_FACTOR, 1.0 );
|
||||
alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
|
||||
alSourcef( source, AL_REFERENCE_DISTANCE,
|
||||
sample->get_reference_dist() );
|
||||
testForALError("distance rolloff");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALvoid
|
||||
SGSampleGroup::load_file(SGSoundSample *sample) {
|
||||
if (sample->is_file()) {
|
||||
unsigned int size;
|
||||
int freq, format;
|
||||
void *data;
|
||||
|
||||
string sample_name = sample->get_sample_name();
|
||||
_smgr->load(sample_name, &data, &format, &size, &freq);
|
||||
|
||||
sample->set_data( (unsigned char *)data );
|
||||
sample->set_frequency( freq );
|
||||
sample->set_format( format );
|
||||
sample->set_size( size );
|
||||
}
|
||||
}
|
||||
|
||||
void SGSampleGroup::set_volume( float vol )
|
||||
{
|
||||
_volume = vol;
|
||||
if (_volume < 0.0) _volume = 0.0;
|
||||
if (_volume > 1.0) _volume = 1.0;
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
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 (sample group): "
|
||||
<< alGetString(error) << " at " << s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
181
simgear/sound/sample_group.hxx
Normal file
181
simgear/sound/sample_group.hxx
Normal file
@@ -0,0 +1,181 @@
|
||||
// soundmgr.hxx -- Sound effect management class
|
||||
//
|
||||
// Sampel Group handler initially written by Erik Hofman
|
||||
//
|
||||
// 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>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#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:
|
||||
SGSampleGroup ();
|
||||
SGSampleGroup ( SGSoundMgr *smgr, const string &refname );
|
||||
~SGSampleGroup ();
|
||||
|
||||
virtual void update (double dt);
|
||||
|
||||
/**
|
||||
* add a sound effect, return true if successful
|
||||
*/
|
||||
bool add( SGSoundSample *sound, const string& refname );
|
||||
|
||||
/**
|
||||
* remove a sound effect, return true if successful
|
||||
*/
|
||||
bool remove( const string& refname );
|
||||
|
||||
/**
|
||||
* return true of the specified sound exists in the sound manager system
|
||||
*/
|
||||
bool exists( const string& refname );
|
||||
|
||||
/**
|
||||
* return a pointer to the SGSoundSample if the specified sound
|
||||
* exists in the sound manager system, otherwise return NULL
|
||||
*/
|
||||
SGSoundSample *find( const string& refname );
|
||||
|
||||
/**
|
||||
* request to stop playing all associated samples until further notice
|
||||
*/
|
||||
void suspend();
|
||||
|
||||
/**
|
||||
* request to resume playing all associated samples
|
||||
*/
|
||||
void resume();
|
||||
|
||||
|
||||
/**
|
||||
* request to start playing the associated samples
|
||||
*/
|
||||
bool play( const string& refname, bool looping );
|
||||
|
||||
/**
|
||||
* tell the scheduler to play the indexed sample in a continuous
|
||||
* loop
|
||||
*/
|
||||
inline bool play_looped( const string& refname ) {
|
||||
return play( refname, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* tell the scheduler to play the indexed sample once
|
||||
*/
|
||||
inline bool play_once( const string& refname ) {
|
||||
return play( refname, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* return true of the specified sound is currently being played
|
||||
*/
|
||||
bool is_playing( const string& refname );
|
||||
|
||||
/**
|
||||
* request to stop playing the associated samples
|
||||
*/
|
||||
bool stop( const string& refname );
|
||||
|
||||
/**
|
||||
* set overall volume for the application.
|
||||
* @param must be between 0.0 and 1.0
|
||||
*/
|
||||
void set_volume( float vol );
|
||||
|
||||
/**
|
||||
* set the positions of all managed sound sources
|
||||
*/
|
||||
void set_position( SGVec3d pos );
|
||||
|
||||
/**
|
||||
* set the velocities of all managed sound sources
|
||||
*/
|
||||
void set_velocity( SGVec3f vel );
|
||||
|
||||
/**
|
||||
* set the orientation of all managed sound sources
|
||||
*/
|
||||
void set_orientation( SGVec3f ori );
|
||||
|
||||
/**
|
||||
* load the data of the sound sample
|
||||
*/
|
||||
void load_file(SGSoundSample *sound);
|
||||
|
||||
protected:
|
||||
SGSoundMgr *_smgr;
|
||||
bool _active;
|
||||
|
||||
private:
|
||||
bool _changed;
|
||||
bool _position_changed;
|
||||
|
||||
float _volume;
|
||||
|
||||
SGVec3d _position;
|
||||
SGVec3f _orientation;
|
||||
|
||||
sample_map _samples;
|
||||
|
||||
bool testForALError(string s);
|
||||
bool testForError(void *p, string s);
|
||||
|
||||
void update_sample_config( SGSoundSample *sound );
|
||||
};
|
||||
|
||||
#endif // _SG_SAMPLE_GROUP_OPENAL_HXX
|
||||
|
||||
@@ -15,8 +15,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 +24,12 @@
|
||||
# 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 <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/math/SGQuat.hxx>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
|
||||
@@ -45,477 +37,140 @@
|
||||
// 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().data()),
|
||||
_relative_pos(SGVec3f::zeros().data()),
|
||||
_base_pos(SGVec3d::zeros().data()),
|
||||
_direction(SGVec3f::zeros().data()),
|
||||
_velocity(SGVec3f::zeros().data()),
|
||||
_sample_name(""),
|
||||
_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().data()),
|
||||
_relative_pos(SGVec3f::zeros().data()),
|
||||
_base_pos(SGVec3d::zeros().data()),
|
||||
_direction(SGVec3f::zeros().data()),
|
||||
_velocity(SGVec3f::zeros().data()),
|
||||
_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();
|
||||
_sample_name = samplepath.str();
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "From file sounds sample = "
|
||||
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");
|
||||
}
|
||||
|
||||
// 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( unsigned char *data, int len, int freq, int format ) :
|
||||
_absolute_pos(SGVec3d::zeros().data()),
|
||||
_relative_pos(SGVec3f::zeros().data()),
|
||||
_base_pos(SGVec3d::zeros().data()),
|
||||
_direction(SGVec3f::zeros().data()),
|
||||
_velocity(SGVec3f::zeros().data()),
|
||||
_data(data),
|
||||
_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)
|
||||
{
|
||||
_sample_name = "unknown, data supplied by caller";
|
||||
SG_LOG( SG_GENERAL, SG_DEBUG, "In memory sounds sample" );
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
SGSoundSample::~SGSoundSample() {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Deleting a sample" );
|
||||
if (buffer)
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
|
||||
// play the sample
|
||||
void SGSoundSample::play( bool _loop ) {
|
||||
|
||||
if ( source ) {
|
||||
alSourceStop( source );
|
||||
}
|
||||
|
||||
playing = bind_source();
|
||||
if ( playing ) {
|
||||
loop = _loop;
|
||||
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
alSourcePlay( source );
|
||||
|
||||
print_openal_error("play (alSourcePlay)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
#if 0
|
||||
if (_data != NULL) {
|
||||
delete[] _data;
|
||||
_data = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SGSoundSample::set_reference_dist( ALfloat dist ) {
|
||||
reference_dist = dist;
|
||||
if (playing) {
|
||||
alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist );
|
||||
}
|
||||
void SGSoundSample::set_base_position( SGVec3d pos ) {
|
||||
_base_pos = pos;
|
||||
update_absolute_position();
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundSample::set_max_dist( ALfloat dist ) {
|
||||
max_dist = dist;
|
||||
if (playing) {
|
||||
alSourcef( source, AL_MAX_DISTANCE, max_dist );
|
||||
}
|
||||
void SGSoundSample::set_relative_position( SGVec3f pos ) {
|
||||
_relative_pos = pos;
|
||||
update_absolute_position();
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
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;
|
||||
void SGSoundSample::set_orientation( SGVec3f dir ) {
|
||||
_direction = dir;
|
||||
update_absolute_position();
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
void SGSoundSample::update_absolute_position() {
|
||||
SGQuatf orient = SGQuatf::fromAngleAxis(_direction);
|
||||
SGVec3f modified_relative_pos = orient.transform(_relative_pos);
|
||||
_absolute_pos = _base_pos + toVec3d(modified_relative_pos);
|
||||
}
|
||||
|
||||
@@ -32,37 +32,16 @@
|
||||
# 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;
|
||||
|
||||
/**
|
||||
@@ -73,45 +52,51 @@ 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];
|
||||
SGVec3d _absolute_pos; // absolute position
|
||||
SGVec3f _relative_pos; // position relative to the base position
|
||||
SGVec3d _base_pos; // base position
|
||||
|
||||
// The orientation of the sound (direction and cut-off angles)
|
||||
ALfloat direction[3];
|
||||
ALfloat inner, outer, outergain;
|
||||
SGVec3f _direction;
|
||||
|
||||
// Velocity of the source sound.
|
||||
ALfloat source_vel[3];
|
||||
SGVec3f _velocity;
|
||||
|
||||
string _sample_name;
|
||||
unsigned char *_data;
|
||||
|
||||
// configuration values
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
int _format;
|
||||
int _size;
|
||||
int _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;
|
||||
// Buffers hold sound data.
|
||||
bool _valid_buffer;
|
||||
unsigned int _buffer;
|
||||
|
||||
bool playing;
|
||||
bool bind_source();
|
||||
bool no_Doppler_effect;
|
||||
// Sources are points emitting sound.
|
||||
bool _valid_source;
|
||||
unsigned int _source;
|
||||
|
||||
// The orientation of the 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;
|
||||
|
||||
void update_absolute_position();
|
||||
|
||||
public:
|
||||
|
||||
@@ -128,7 +113,7 @@ public:
|
||||
should usually be true unless you want to manipulate the data
|
||||
later.)
|
||||
*/
|
||||
SGSoundSample( const char *path, const char *file, bool no_Doppler_effect = true );
|
||||
SGSoundSample( const char *path, const char *file );
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -139,23 +124,50 @@ public:
|
||||
should usually be true unless you want to manipulate the data
|
||||
later.)
|
||||
*/
|
||||
SGSoundSample( unsigned char *_data, int len, int _freq, bool no_Doppler_effect = true );
|
||||
SGSoundSample( unsigned char *data, int len, int freq, int format = AL_FORMAT_MONO8 );
|
||||
|
||||
~SGSoundSample ();
|
||||
|
||||
/**
|
||||
* detect wheter the sample holds the information of a sound file
|
||||
*/
|
||||
inline bool is_file() const { return _is_file; }
|
||||
|
||||
/**
|
||||
* Test whether this sample has a changed configuration since the last
|
||||
* call. (Calling this function resets the value).
|
||||
*/
|
||||
inline bool has_changed() {
|
||||
bool b = _changed; _changed = false; return b;
|
||||
}
|
||||
|
||||
inline bool has_static_data_changed() {
|
||||
bool b = _static_changed; _static_changed = false; return b;
|
||||
}
|
||||
|
||||
~SGSoundSample();
|
||||
|
||||
/**
|
||||
* Start playing this sample.
|
||||
*
|
||||
* @param _loop Define whether the sound should be played in a loop.
|
||||
*/
|
||||
void play( bool _loop );
|
||||
inline void play( bool loop ) {
|
||||
_playing = true; _loop = loop; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the sample is looping or not.
|
||||
*/
|
||||
inline bool get_looping() { return _loop; }
|
||||
|
||||
/**
|
||||
* Stop playing this sample.
|
||||
*
|
||||
* @param sched A pointer to the appropriate scheduler.
|
||||
*/
|
||||
void stop();
|
||||
inline void stop() {
|
||||
_playing = false; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play this sample once.
|
||||
@@ -173,77 +185,226 @@ public:
|
||||
* Test if a sample is currently playing.
|
||||
* @return true if is is playing, false otherwise.
|
||||
*/
|
||||
bool is_playing( );
|
||||
inline bool is_playing() { return _playing; }
|
||||
|
||||
/**
|
||||
* set the data associated with this sample
|
||||
*/
|
||||
inline void set_data( unsigned char* data ) {
|
||||
_data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data associated with this sample
|
||||
*/
|
||||
inline void* get_data() const { return _data; }
|
||||
|
||||
/**
|
||||
* free the data associated with this sample
|
||||
*/
|
||||
inline void free_data() {
|
||||
if (_data != NULL) { delete[] _data; _data = NULL; }
|
||||
}
|
||||
|
||||
/**
|
||||
* set the source id of this source
|
||||
*/
|
||||
inline void set_source(unsigned int s) {
|
||||
_source = s; _valid_source = true; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the source id of this source
|
||||
*/
|
||||
inline unsigned int get_source() { return _source; }
|
||||
|
||||
/**
|
||||
* detect wheter the source id of the sample is valid
|
||||
*/
|
||||
inline bool is_valid_source() const { return _valid_source; }
|
||||
|
||||
/**
|
||||
* set the source id of the sample to invalid.
|
||||
*/
|
||||
inline void no_valid_source() {
|
||||
_valid_source = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the buffer id of this source
|
||||
*/
|
||||
inline void set_buffer(unsigned int b) {
|
||||
_buffer = b; _valid_buffer = true; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the buffer id of this source
|
||||
*/
|
||||
inline unsigned int get_buffer() { return _buffer; }
|
||||
|
||||
/**
|
||||
* detect wheter the source id of the sample is valid
|
||||
*/
|
||||
inline bool is_valid_buffer() const { return _valid_buffer; }
|
||||
|
||||
/**
|
||||
* set the source id of the sample to invalid.
|
||||
*/
|
||||
inline void no_valid_buffer() {
|
||||
_valid_buffer = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current pitch setting of this sample.
|
||||
*/
|
||||
inline double get_pitch() const { return pitch; }
|
||||
inline float get_pitch() { return _pitch; }
|
||||
|
||||
/**
|
||||
* Set the pitch of this sample.
|
||||
*/
|
||||
void set_pitch( double p );
|
||||
inline void set_pitch( float p ) {
|
||||
_pitch = p; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current volume setting of this sample.
|
||||
*/
|
||||
inline double get_volume() const { return volume; }
|
||||
inline float get_volume() { return _volume * _master_volume; }
|
||||
|
||||
/**
|
||||
* Set the master (sampel group) volume of this sample.
|
||||
*/
|
||||
inline void set_master_volume( float v ) {
|
||||
_master_volume = v; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the volume of this sample.
|
||||
*/
|
||||
void set_volume( double v );
|
||||
inline void set_volume( float v ) {
|
||||
_volume = v; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the format of the sounds sample
|
||||
*/
|
||||
inline void set_format( int format ) {
|
||||
_format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the format of the sounds sample
|
||||
*/
|
||||
inline int get_format() { return _format; }
|
||||
|
||||
|
||||
/**
|
||||
* Set the frequency of the sounds sample
|
||||
*/
|
||||
inline void set_frequency( int freq ) {
|
||||
_freq = freq; _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the frequency of the sounds sample
|
||||
*/
|
||||
inline int get_frequency() { return _freq; }
|
||||
|
||||
/**
|
||||
* Returns the size of the sounds sample
|
||||
*/
|
||||
inline int get_size() {
|
||||
return size;
|
||||
inline void set_size( int size ) {
|
||||
_size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set position of sound source (uses same coordinate system as opengl)
|
||||
* Returns the size of the sounds sample
|
||||
*/
|
||||
void set_source_pos( ALfloat *pos );
|
||||
inline int get_size() const { return _size; }
|
||||
|
||||
/**
|
||||
* Set "constant" offset position of sound source (uses same
|
||||
* coordinate system as opengl)
|
||||
* Set position of the sound source (uses same coordinate system as opengl)
|
||||
*/
|
||||
void set_offset_pos( ALfloat *pos );
|
||||
void set_base_position( SGVec3d pos );
|
||||
void set_relative_position( SGVec3f pos );
|
||||
|
||||
/**
|
||||
* Get position of the sound source (uses same coordinate system as opengl)
|
||||
*/
|
||||
inline float *get_position() const { return toVec3f(_absolute_pos).data(); }
|
||||
|
||||
/**
|
||||
* Set the orientation of the sound source, both for direction
|
||||
* and audio cut-off angles.
|
||||
*/
|
||||
void set_orientation( ALfloat *dir, ALfloat inner_angle=360.0,
|
||||
ALfloat outer_angle=360.0,
|
||||
ALfloat outer_gain=0.0);
|
||||
void set_orientation( SGVec3f dir );
|
||||
|
||||
/**
|
||||
* Set velocity of sound source (uses same coordinate system as opengl)
|
||||
* Define the audio cone parameters for directional audio
|
||||
*/
|
||||
void set_source_vel( ALfloat *vel, ALfloat *listener_vel );
|
||||
inline 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 of the sound source, the inner or outer angle
|
||||
* or outer gain.
|
||||
*/
|
||||
inline float *get_orientation() { return _direction.data(); }
|
||||
inline float *get_direction() { return _direction.data(); }
|
||||
inline float get_innerangle() { return _inner_angle; }
|
||||
inline float get_outerangle() { return _outer_angle; }
|
||||
inline float get_outergain() { return _outer_gain; }
|
||||
|
||||
/**
|
||||
* Set velocity of the sound source (uses same coordinate system as opengl)
|
||||
*/
|
||||
inline void set_velocity( SGVec3f vel ) {
|
||||
_velocity = SGVec3f(vel); _changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get velocity of the sound source (uses same coordinate system as opengl)
|
||||
*/
|
||||
inline float *get_velocity() { return _velocity.data(); }
|
||||
|
||||
|
||||
/**
|
||||
* Set reference distance of sound (the distance where the gain
|
||||
* will be half.)
|
||||
*/
|
||||
void set_reference_dist( ALfloat dist );
|
||||
inline void set_reference_dist( float dist ) {
|
||||
_reference_dist = dist; _static_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference distance of sound (the distance where the gain
|
||||
* will be half.)
|
||||
*/
|
||||
inline float get_reference_dist() { return _reference_dist; }
|
||||
|
||||
|
||||
/**
|
||||
* Set maximum distance of sound (the distance where the sound is
|
||||
* no longer audible.
|
||||
*/
|
||||
void set_max_dist( ALfloat dist );
|
||||
void set_max_dist( float dist ) {
|
||||
_max_dist = dist; _static_changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a sound file into a memory buffer only.
|
||||
* Get maximum istance of sound (the distance where the sound is
|
||||
* no longer audible.
|
||||
*/
|
||||
ALvoid* load_file(const char *path, const char *file);
|
||||
inline float get_max_dist() { return _max_dist; }
|
||||
|
||||
/**
|
||||
* Get the name of this sample
|
||||
*/
|
||||
inline string get_sample_name() { return _sample_name; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,8 +18,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,173 +27,192 @@
|
||||
# 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 "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>
|
||||
|
||||
|
||||
#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),
|
||||
_changed(true),
|
||||
_volume(0.5),
|
||||
_device(NULL),
|
||||
_context(NULL),
|
||||
_listener_pos(SGVec3d::zeros().data()),
|
||||
_listener_vel(SGVec3f::zeros().data()),
|
||||
_devname(NULL)
|
||||
{
|
||||
#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++;
|
||||
}
|
||||
_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();
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Initializing OpenAL sound manager" );
|
||||
|
||||
ALCdevice *device = alcOpenDevice(_devname);
|
||||
if ( testForError(device, "No default audio device available.") ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALCcontext *context = alcCreateContext(device, NULL);
|
||||
if ( testForError(context, "Unable to create a valid context.") ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !alcMakeContextCurrent(context) ) {
|
||||
testForALCError("context initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
_working = true;
|
||||
|
||||
_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.2f );
|
||||
alListenerfv( AL_POSITION, toVec3f(_listener_pos).data() );
|
||||
alListenerfv( AL_ORIENTATION, _listener_ori );
|
||||
alListenerfv( AL_VELOCITY, _listener_vel.data() );
|
||||
|
||||
alDopplerFactor(1.0);
|
||||
alDopplerVelocity(340.3); // speed of sound in meters per second.
|
||||
|
||||
if ( alIsExtensionPresent((const ALchar*)"EXT_exponent_distance") ) {
|
||||
alDistanceModel(AL_EXPONENT_DISTANCE);
|
||||
} else {
|
||||
alDistanceModel(AL_INVERSE_DISTANCE);
|
||||
}
|
||||
|
||||
testForALError("listener initialization");
|
||||
|
||||
alGetError(); // clear any undetetced error, just to be sure
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// suspend the sound manager
|
||||
void SGSoundMgr::stop() {
|
||||
if (_working) {
|
||||
_working = false;
|
||||
|
||||
_context = alcGetCurrentContext();
|
||||
_device = alcGetContextsDevice(_context);
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcDestroyContext(_context);
|
||||
alcCloseDevice(_device);
|
||||
}
|
||||
}
|
||||
|
||||
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->suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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.at( i );
|
||||
alDeleteSources( 1 , &source );
|
||||
}
|
||||
|
||||
_free_sources.clear();
|
||||
_sources_in_use.clear();
|
||||
}
|
||||
|
||||
|
||||
// run the audio scheduler
|
||||
void SGSoundMgr::update( double dt ) {
|
||||
}
|
||||
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->update(dt);
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
alListenerf( AL_GAIN, _volume );
|
||||
alListenerfv( AL_VELOCITY, _listener_vel.data() );
|
||||
alListenerfv( AL_ORIENTATION, _listener_ori );
|
||||
alListenerfv( AL_POSITION, toVec3f(_listener_pos).data() );
|
||||
// alDopplerVelocity(340.3); // TODO: altitude dependent
|
||||
testForALError("update");
|
||||
_changed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,151 +221,241 @@ SGSoundMgr::pause ()
|
||||
void
|
||||
SGSoundMgr::resume ()
|
||||
{
|
||||
if (context) {
|
||||
alcProcessContext( context );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error after soundmgr 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// add a sampel group, return true if successful
|
||||
bool SGSoundMgr::add( SGSampleGroup *sgrp, const string& refname )
|
||||
{
|
||||
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;
|
||||
_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( refname );
|
||||
|
||||
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);
|
||||
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 ) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
// Free up a source id for further use
|
||||
void SGSoundMgr::release_source( unsigned int source )
|
||||
{
|
||||
for (unsigned int i = 0; i<_sources_in_use.size(); i++) {
|
||||
if ( _sources_in_use[i] == source ) {
|
||||
ALint result;
|
||||
|
||||
alGetSourcei( source, AL_SOURCE_STATE, &result );
|
||||
if ( result == AL_PLAYING ) {
|
||||
alSourceStop( source );
|
||||
}
|
||||
testForALError("free_source");
|
||||
|
||||
_free_sources.push_back(source);
|
||||
_sources_in_use.erase(_sources_in_use.begin()+i,
|
||||
_sources_in_use.begin()+i+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SGSoundMgr::load(string &samplepath, void **dbuf, int *fmt,
|
||||
unsigned int *sz, int *frq )
|
||||
{
|
||||
ALenum format = (ALenum)*fmt;
|
||||
ALsizei size = (ALsizei)*sz;
|
||||
ALsizei freq = (ALsizei)*frq;
|
||||
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 {
|
||||
sample->play( true );
|
||||
}
|
||||
|
||||
#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 = alutGetError();
|
||||
if ( error != ALUT_ERROR_NO_ERROR ) {
|
||||
string msg = "Failed to load wav file: ";
|
||||
msg.append(alutGetErrorString(error));
|
||||
throw sg_io_exception(msg.c_str(), sg_location(samplepath));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
*dbuf = (void *)data;
|
||||
*fmt = (int)format;
|
||||
*sz = (unsigned int)size;
|
||||
*frq = (int)freq;
|
||||
|
||||
return 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.
|
||||
*/
|
||||
void SGSoundMgr::set_orientation( SGQuatd ori )
|
||||
{
|
||||
SGVec3d sgv_up = ori.rotate(SGVec3d::e2());
|
||||
SGVec3d sgv_at = ori.rotate(SGVec3d::e3());
|
||||
for (int i=0; i<3; i++) {
|
||||
_listener_ori[i] = sgv_at[i];
|
||||
_listener_ori[i+3] = sgv_up[i];
|
||||
}
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// tell the scheduler to play the indexed sample once
|
||||
bool SGSoundMgr::play_once( const string& refname ) {
|
||||
SGSoundSample *sample;
|
||||
|
||||
if ( (sample = find( refname )) == NULL ) {
|
||||
return false;
|
||||
} else {
|
||||
sample->play( false );
|
||||
bool SGSoundMgr::testForALUTError(string s)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,113 +37,68 @@
|
||||
# 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>
|
||||
#else
|
||||
# include <AL/al.h>
|
||||
# include <AL/alc.h>
|
||||
# include <AL/alut.h>
|
||||
#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;
|
||||
|
||||
|
||||
typedef map < string, SGSharedPtr<SGSoundSample> > sample_map;
|
||||
typedef sample_map::iterator sample_map_iterator;
|
||||
typedef sample_map::const_iterator const_sample_map_iterator;
|
||||
typedef 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 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?
|
||||
*/
|
||||
inline bool is_working() const { return working; }
|
||||
inline bool is_working() const { return _working; }
|
||||
|
||||
/**
|
||||
* reinitialize the sound manager
|
||||
* add a sample group, return true if successful
|
||||
*/
|
||||
inline void reinit() { init(); }
|
||||
|
||||
/**
|
||||
* add a sound effect, return true if successful
|
||||
*/
|
||||
bool add( SGSoundSample *sound, const string& refname);
|
||||
bool add( SGSampleGroup *sgrp, const string& refname );
|
||||
|
||||
/**
|
||||
* remove a sound effect, return true if successful
|
||||
* remove a sample group, return true if successful
|
||||
*/
|
||||
bool remove( const string& refname );
|
||||
|
||||
@@ -153,94 +108,91 @@ public:
|
||||
bool exists( const string& refname );
|
||||
|
||||
/**
|
||||
* return a pointer to the SGSoundSample if the specified sound
|
||||
* return a pointer to the SGSampleGroup if the specified sound
|
||||
* exists in the sound manager system, otherwise return NULL
|
||||
*/
|
||||
SGSoundSample *find( const string& refname );
|
||||
|
||||
/**
|
||||
* tell the scheduler to play the indexed sample in a continuous
|
||||
* loop
|
||||
*/
|
||||
bool play_looped( const string& refname );
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
SGSampleGroup *find( const string& refname, bool create = false );
|
||||
|
||||
/**
|
||||
* set the position of the listener (in opengl coordinates)
|
||||
*/
|
||||
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 );
|
||||
inline void set_position( SGVec3d pos ) {
|
||||
_listener_pos = pos;
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
inline double *get_position() {
|
||||
return _listener_pos.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* set the velocity of the listener (in opengl coordinates)
|
||||
*/
|
||||
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 void set_velocity( SGVec3f vel ) {
|
||||
_listener_vel = vel;
|
||||
_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.
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
void set_orientation( SGQuatd ori );
|
||||
|
||||
enum {
|
||||
NO_SOURCE = (unsigned int)-1,
|
||||
NO_BUFFER = (unsigned int)-1
|
||||
};
|
||||
|
||||
void set_volume( float v );
|
||||
inline float get_volume() { return _volume; }
|
||||
|
||||
/**
|
||||
* set the positions of all managed sound sources
|
||||
* get a new OpenAL source id
|
||||
* returns NO_SOURCE is no source is available
|
||||
*/
|
||||
void set_source_pos_all( ALfloat *pos );
|
||||
unsigned int request_source();
|
||||
|
||||
/**
|
||||
* set the velocities of all managed sound sources
|
||||
* give back an OpenAL source id for further use.
|
||||
*/
|
||||
void set_source_vel_all( ALfloat *pos );
|
||||
void release_source( unsigned int source );
|
||||
|
||||
bool load(string &samplepath, void **data, int *format, unsigned int*size,
|
||||
int *freq );
|
||||
|
||||
|
||||
private:
|
||||
static int _alut_init;
|
||||
|
||||
bool _working;
|
||||
bool _changed;
|
||||
float _volume;
|
||||
|
||||
ALCdevice *_device;
|
||||
ALCcontext *_context;
|
||||
|
||||
// Position of the listener.
|
||||
SGVec3d _listener_pos;
|
||||
|
||||
// Velocity of the listener.
|
||||
SGVec3f _listener_vel;
|
||||
|
||||
// Orientation of the listener.
|
||||
// first 3 elements are "at" vector, second 3 are "up" vector
|
||||
ALfloat _listener_ori[6];
|
||||
|
||||
sample_group_map _sample_groups;
|
||||
|
||||
vector<ALuint> _free_sources;
|
||||
vector<ALuint> _sources_in_use;
|
||||
|
||||
char *_devname;
|
||||
|
||||
bool testForALError(string s);
|
||||
bool testForALCError(string s);
|
||||
bool testForALUTError(string s);
|
||||
bool testForError(void *p, string s);
|
||||
void update_sample_config( SGSampleGroup *sound );
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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,18 +81,14 @@ SGXmlSound::~SGXmlSound()
|
||||
}
|
||||
|
||||
void
|
||||
SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
const string &path)
|
||||
SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node,
|
||||
SGSampleGroup *sgrp, const string &path)
|
||||
{
|
||||
|
||||
//
|
||||
// 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 );
|
||||
|
||||
@@ -236,8 +229,7 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
//
|
||||
// Relative position
|
||||
//
|
||||
sgVec3 offset_pos;
|
||||
sgSetVec3( offset_pos, 0.0, 0.0, 0.0 );
|
||||
SGVec3f offset_pos = SGVec3f::zeros();
|
||||
SGPropertyNode_ptr pos = node->getChild("position");
|
||||
if ( pos != NULL ) {
|
||||
offset_pos[0] = pos->getDoubleValue("x", 0.0);
|
||||
@@ -248,9 +240,8 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
//
|
||||
// Orientation
|
||||
//
|
||||
sgVec3 dir;
|
||||
SGVec3f dir = SGVec3f::zeros();
|
||||
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");
|
||||
@@ -266,8 +257,8 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
//
|
||||
// Initialize the sample
|
||||
//
|
||||
_mgr = sndmgr;
|
||||
if ( (_sample = _mgr->find(_name)) == NULL ) {
|
||||
_sgrp = sgrp;
|
||||
if ( (_sample = _sgrp->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
|
||||
@@ -276,17 +267,16 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
// "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 );
|
||||
node->getStringValue("path", "") );
|
||||
_sgrp->add( _sample, _name );
|
||||
}
|
||||
|
||||
_sample->set_offset_pos( offset_pos );
|
||||
_sample->set_orientation(dir, inner, outer, outer_gain);
|
||||
_sample->set_volume(v);
|
||||
_sample->set_relative_position( offset_pos );
|
||||
_sample->set_orientation( dir );
|
||||
_sample->set_audio_cone(inner, outer, outer_gain);
|
||||
_sample->set_reference_dist( reference_dist );
|
||||
_sample->set_max_dist( max_dist );
|
||||
_sample->set_volume(v);
|
||||
_sample->set_pitch(p);
|
||||
}
|
||||
|
||||
@@ -316,7 +306,8 @@ SGXmlSound::update (double dt)
|
||||
)
|
||||
)
|
||||
{
|
||||
if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) {
|
||||
if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME))
|
||||
{
|
||||
if (_sample->is_playing()) {
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Stopping audio after " << _dt_play
|
||||
<< " sec: " << _name );
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/props/condition.hxx>
|
||||
|
||||
#include "sample_group.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
static const double MAX_TRANSIT_TIME = 0.1; // 100 ms.
|
||||
|
||||
@@ -99,10 +99,10 @@ 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 path The path where the audio files remain.
|
||||
*/
|
||||
virtual void init (SGPropertyNode *, SGPropertyNode *, SGSoundMgr *,
|
||||
virtual void init (SGPropertyNode *, SGPropertyNode *, SGSampleGroup *,
|
||||
const string &);
|
||||
|
||||
/**
|
||||
@@ -135,7 +135,7 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
SGSoundMgr * _mgr;
|
||||
SGSampleGroup * _sgrp;
|
||||
SGSharedPtr<SGSoundSample> _sample;
|
||||
|
||||
SGSharedPtr<SGCondition> _condition;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "SGAtomic.hxx"
|
||||
|
||||
#if defined(SGATOMIC_USE_GCC4_BUILTINS) && defined(__i386__)
|
||||
#if !defined(SGATOMIC_USE_GCC4_BUILTINS) && defined (__i386__)
|
||||
|
||||
// Usually the apropriate functions are inlined by gcc.
|
||||
// But if gcc is called with something aequivalent to -march=i386,
|
||||
|
||||
Reference in New Issue
Block a user