Initial commit of the new sound system, expect more updates to follow

This commit is contained in:
ehofman
2009-10-04 13:52:27 +00:00
committed by Tim Moore
parent b784bebaa9
commit e2e1524454
14 changed files with 1514 additions and 972 deletions

View File

@@ -30,7 +30,7 @@
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/math/point3d.hxx>
#include <simgear/math/polar3d.hxx>
#include <simgear/sound/soundmgr_openal.hxx>
#include <simgear/sound/sample_group.hxx>
#include <simgear/scene/sky/cloudfield.hxx>
#include <simgear/scene/sky/newcloud.hxx>
#include <simgear/props/props.hxx>
@@ -175,7 +175,7 @@ SGEnviro::SGEnviro() :
lightning_enable_state(false),
elapsed_time(0.0),
dt(0.0),
soundMgr(NULL),
sampleGroup(NULL),
snd_active(false),
snd_dist(0.0),
min_time_before_lt(0.0),
@@ -189,6 +189,8 @@ SGEnviro::SGEnviro() :
}
SGEnviro::~SGEnviro(void) {
if (sampleGroup) delete sampleGroup;
// OSGFIXME
return;
list_of_lightning::iterator iLightning;
@@ -530,8 +532,8 @@ void SGEnviro::drawRain(double pitch, double roll, double heading, double hspeed
}
void SGEnviro::set_soundMgr(SGSoundMgr *mgr) {
soundMgr = mgr;
void SGEnviro::set_sampleGroup(SGSampleGroup *sgr) {
sampleGroup = sgr;
}
void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double heading, double hspeed) {
@@ -616,7 +618,7 @@ void SGLightning::lt_build(void) {
top[PY] = alt;
top[PZ] = 0;
lt_build_tree_branch(0, top, 1.0, 50, top[PY] / 8.0);
if( ! sgEnviro.soundMgr )
if( ! sgEnviro.sampleGroup )
return;
Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 );
Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 );
@@ -751,15 +753,15 @@ void SGEnviro::drawLightning(void) {
double ax = 0.0, ay = 0.0;
ax = cos(course) * dist;
ay = sin(course) * dist;
SGSharedPtr<SGSoundSample> snd = soundMgr->find("thunder");
SGSharedPtr<SGSoundSample> snd = sampleGroup->find("thunder");
if( snd ) {
ALfloat pos[3]={ax, ay, -sgEnviro.last_alt };
snd->set_source_pos(pos);
SGVec3d pos = SGVec3d(ax, ay, -sgEnviro.last_alt);
snd->set_base_position(pos);
snd->play_once();
}
}
} else {
if( !soundMgr->is_playing("thunder") ) {
if( !sampleGroup->is_playing("thunder") ) {
snd_active = false;
snd_playing = false;
}

View File

@@ -32,7 +32,7 @@ using std::vector;
using std::string;
class SGLightning;
class SGSoundMgr;
class SGSampleGroup;
/**
* Simulate some echo on a weather radar.
@@ -84,7 +84,7 @@ private:
sgVec4 fog_color;
sgMat4 transform;
double last_lon, last_lat, last_alt;
SGSoundMgr *soundMgr;
SGSampleGroup *sampleGroup;
bool snd_active, snd_playing;
double snd_timer, snd_wait, snd_pos_lat, snd_pos_lon, snd_dist;
double min_time_before_lt;
@@ -243,7 +243,7 @@ public:
* Forward the sound manager instance to be able to play samples.
* @param mgr a running sound manager
*/
void set_soundMgr(SGSoundMgr *mgr);
void set_sampleGroup(SGSampleGroup *sgr);
void setFOV( float w, float h );
void getFOV( float &w, float &h );

View File

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

View File

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

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,8 +38,8 @@
#include <simgear/compiler.h>
#include <simgear/props/condition.hxx>
#include "sample_group.hxx"
#include "sample_openal.hxx"
#include "soundmgr_openal.hxx"
static const double MAX_TRANSIT_TIME = 0.1; // 100 ms.
@@ -99,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;

View File

@@ -20,7 +20,7 @@
#include "SGAtomic.hxx"
#if defined(SGATOMIC_USE_GCC4_BUILTINS) && defined(__i386__)
#if !defined(SGATOMIC_USE_GCC4_BUILTINS) && defined (__i386__)
// Usually the apropriate functions are inlined by gcc.
// But if gcc is called with something aequivalent to -march=i386,