Rewrite the entire audio support library on top of OpenAL rather than plib's
sound manager. The interface was simplified and cleaned up a bit, and I haven't back ported these changes to the plib sound wrappers ... we could I suppose if someone really had a problem, but I haven't seen anything so far that would indicate the extra effort is worth it.
This commit is contained in:
parent
7657632024
commit
5bbcd386fa
14
configure.ac
14
configure.ac
@ -270,7 +270,21 @@ esac
|
||||
opengl_LIBS="$LIBS"
|
||||
LIBS="$base_LIBS"
|
||||
|
||||
dnl check for OpenAL libraries
|
||||
case "${host}" in
|
||||
*)
|
||||
dnl default unix style machines
|
||||
|
||||
AC_SEARCH_LIBS(alGenBuffers, openal)
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
openal_LIBS="$LIBS"
|
||||
LIBS="$base_LIBS"
|
||||
|
||||
AC_SUBST(base_LIBS)
|
||||
AC_SUBST(openal_LIBS)
|
||||
AC_SUBST(opengl_LIBS)
|
||||
AC_SUBST(thread_LIBS)
|
||||
AC_SUBST(network_LIBS)
|
||||
|
@ -1,11 +1,29 @@
|
||||
includedir = @includedir@/sound
|
||||
|
||||
EXTRA_DIST = jet.wav
|
||||
|
||||
lib_LIBRARIES = libsgsound.a
|
||||
|
||||
noinst_HEADERS =
|
||||
|
||||
include_HEADERS = sound.hxx soundmgr.hxx
|
||||
include_HEADERS = \
|
||||
sample_openal.hxx \
|
||||
soundmgr_openal.hxx \
|
||||
xmlsound.hxx
|
||||
|
||||
libsgsound_a_SOURCES = sound.cxx soundmgr.cxx
|
||||
libsgsound_a_SOURCES = \
|
||||
sample_openal.cxx \
|
||||
soundmgr_openal.cxx \
|
||||
xmlsound.cxx
|
||||
|
||||
noinst_PROGRAMS = openal_test1 openal_test2
|
||||
|
||||
openal_test1_SOURCES = openal_test1.cxx
|
||||
openal_test2_SOURCES = openal_test2.cxx
|
||||
|
||||
openal_test1_LDADD = -lsgdebug -lopenal
|
||||
openal_test2_LDADD = \
|
||||
$(top_builddir)/simgear/sound/libsgsound.a \
|
||||
-lsgdebug -lsgmisc -lsgstructure $(openal_LIBS)
|
||||
|
||||
INCLUDES = -I$(top_srcdir)
|
||||
|
BIN
simgear/sound/jet.wav
Normal file
BIN
simgear/sound/jet.wav
Normal file
Binary file not shown.
125
simgear/sound/openal_test1.cxx
Normal file
125
simgear/sound/openal_test1.cxx
Normal file
@ -0,0 +1,125 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alut.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
static void print_openal_error( ALuint error ) {
|
||||
if ( error == AL_INVALID_NAME ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_NAME" );
|
||||
} else if ( error == AL_ILLEGAL_ENUM ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_ENUM" );
|
||||
} else if ( error == AL_INVALID_VALUE ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_VALUE" );
|
||||
} else if ( error == AL_ILLEGAL_COMMAND ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_COMMAND" );
|
||||
} else if ( error == AL_OUT_OF_MEMORY ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_OUT_OF_MEMORY" );
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char *argv[] ) {
|
||||
// initialize OpenAL
|
||||
alutInit( 0, NULL );
|
||||
alGetError();
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
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;
|
||||
|
||||
alListenerfv( AL_POSITION, listener_pos );
|
||||
alListenerfv( AL_VELOCITY, listener_vel );
|
||||
alListenerfv( AL_ORIENTATION, listener_ori );
|
||||
|
||||
// Buffers hold sound data.
|
||||
ALuint buffer;
|
||||
|
||||
// Sources are points emitting sound.
|
||||
ALuint source;
|
||||
|
||||
// Position of the source sound.
|
||||
ALfloat source_pos[3];
|
||||
|
||||
// Velocity of the source sound.
|
||||
ALfloat source_vel[3];
|
||||
|
||||
// configuration values
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALvoid* data;
|
||||
ALsizei freq;
|
||||
ALboolean loop;
|
||||
|
||||
source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
|
||||
source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
alGenBuffers(1, &buffer);
|
||||
ALuint error = alGetError();
|
||||
if ( error != AL_NO_ERROR ) {
|
||||
print_openal_error( error );
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to gen OpenAL buffer." );
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Buffer created ok!" );
|
||||
}
|
||||
|
||||
// Load the sample file
|
||||
alutLoadWAVFile( (ALbyte *)"jet.wav", &format, &data, &size, &freq, &loop );
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load wav file.");
|
||||
}
|
||||
|
||||
// Copy data to the internal OpenAL buffer
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Failed to buffer data.");
|
||||
}
|
||||
|
||||
alutUnloadWAV( format, data, size, freq );
|
||||
|
||||
alGenSources(1, &source);
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
print_openal_error( error );
|
||||
}
|
||||
|
||||
alSourcei( source, AL_BUFFER, buffer );
|
||||
alSourcef( source, AL_PITCH, 1.0 );
|
||||
alSourcef( source, AL_GAIN, 1.0 );
|
||||
alSourcefv( source, AL_POSITION, source_pos );
|
||||
alSourcefv( source, AL_VELOCITY, source_vel );
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
|
||||
alSourcePlay( source );
|
||||
|
||||
sleep(10);
|
||||
|
||||
return 0;
|
||||
}
|
47
simgear/sound/openal_test2.cxx
Normal file
47
simgear/sound/openal_test2.cxx
Normal file
@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sample_openal.hxx"
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
|
||||
int main( int argc, char *argv[] ) {
|
||||
SGSoundMgr sm;
|
||||
|
||||
SGSoundSample sample1( ".", "jet.wav" );
|
||||
sample1.set_volume(0.5);
|
||||
sample1.set_volume(0.2);
|
||||
sample1.play_looped();
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample2( ".", "jet.wav" );
|
||||
sample2.set_volume(0.5);
|
||||
sample2.set_pitch(0.4);
|
||||
sample2.play_looped();
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample3( ".", "jet.wav" );
|
||||
sample3.set_volume(0.5);
|
||||
sample3.set_pitch(0.8);
|
||||
sample3.play_looped();
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample4( ".", "jet.wav" );
|
||||
sample4.set_volume(0.5);
|
||||
sample4.set_pitch(1.2);
|
||||
sample4.play_looped();
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample5( ".", "jet.wav" );
|
||||
sample5.set_volume(0.5);
|
||||
sample5.set_pitch(1.6);
|
||||
sample5.play_looped();
|
||||
sleep(1);
|
||||
|
||||
SGSoundSample sample6( ".", "jet.wav" );
|
||||
sample6.set_volume(0.5);
|
||||
sample6.set_pitch(2.0);
|
||||
sample6.play_looped();
|
||||
sleep(1);
|
||||
|
||||
sleep(10);
|
||||
}
|
181
simgear/sound/sample_openal.cxx
Normal file
181
simgear/sound/sample_openal.cxx
Normal file
@ -0,0 +1,181 @@
|
||||
// sample.cxx -- Sound sample encapsulation class
|
||||
//
|
||||
// Written by Curtis Olson, started April 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - curt@flightgear.org
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alut.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
|
||||
//
|
||||
// SGSoundSample
|
||||
//
|
||||
|
||||
|
||||
static void print_openal_error( ALuint error ) {
|
||||
if ( error == AL_INVALID_NAME ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_NAME" );
|
||||
} else if ( error == AL_ILLEGAL_ENUM ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_ENUM" );
|
||||
} else if ( error == AL_INVALID_VALUE ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_VALUE" );
|
||||
} else if ( error == AL_ILLEGAL_COMMAND ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_COMMAND" );
|
||||
} else if ( error == AL_OUT_OF_MEMORY ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "AL_OUT_OF_MEMORY" );
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// constructor
|
||||
SGSoundSample::SGSoundSample( const char *path, const char *file ) :
|
||||
pitch(1.0),
|
||||
volume(1.0),
|
||||
loop(AL_FALSE)
|
||||
{
|
||||
SGPath samplepath( path );
|
||||
if ( strlen(file) ) {
|
||||
samplepath.append( file );
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "From file sounds sample = "
|
||||
<< samplepath.str() );
|
||||
|
||||
ALuint error;
|
||||
|
||||
source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
|
||||
source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
|
||||
|
||||
// clear errors from elsewhere?
|
||||
alGetError();
|
||||
|
||||
// create an OpenAL buffer handle
|
||||
alGenBuffers(1, &buffer);
|
||||
error = alGetError();
|
||||
if ( error != AL_NO_ERROR ) {
|
||||
print_openal_error( error );
|
||||
throw sg_exception("Failed to gen OpenAL buffer.");
|
||||
} else {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Buffer created ok!" );
|
||||
}
|
||||
|
||||
// Load the sample file
|
||||
alutLoadWAVFile( (ALbyte *)samplepath.c_str(),
|
||||
&format, &data, &size, &freq, &loop );
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
throw sg_exception("Failed to load wav file.");
|
||||
}
|
||||
|
||||
// Copy data to the internal OpenAL buffer
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
throw sg_exception("Failed to buffer data.");
|
||||
}
|
||||
|
||||
alutUnloadWAV( format, data, size, freq );
|
||||
|
||||
// Bind buffer with a source.
|
||||
alGenSources(1, &source);
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
throw sg_exception("Failed to gen source.");
|
||||
}
|
||||
|
||||
alSourcei( source, AL_BUFFER, buffer );
|
||||
alSourcef( source, AL_PITCH, pitch );
|
||||
alSourcef( source, AL_GAIN, volume );
|
||||
alSourcefv( source, AL_POSITION, source_pos );
|
||||
alSourcefv( source, AL_VELOCITY, source_vel );
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
}
|
||||
|
||||
|
||||
// constructor
|
||||
SGSoundSample::SGSoundSample( unsigned char *_data, int len, int _freq ) :
|
||||
pitch(1.0),
|
||||
volume(1.0),
|
||||
loop(AL_FALSE)
|
||||
{
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "In memory sounds sample" );
|
||||
|
||||
source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
|
||||
source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
|
||||
|
||||
// Load wav data into a buffer.
|
||||
alGenBuffers(1, &buffer);
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Error in alGenBuffers()" );
|
||||
return;
|
||||
}
|
||||
|
||||
format = AL_FORMAT_MONO8;
|
||||
size = len;
|
||||
data = _data;
|
||||
freq = _freq;
|
||||
|
||||
alBufferData( buffer, format, data, size, freq );
|
||||
|
||||
// Bind buffer with a source.
|
||||
alGenSources(1, &source);
|
||||
if (alGetError() != AL_NO_ERROR) {
|
||||
throw sg_exception("Failed to gen source.");
|
||||
}
|
||||
|
||||
alSourcei( source, AL_BUFFER, buffer );
|
||||
alSourcef( source, AL_PITCH, pitch );
|
||||
alSourcef( source, AL_GAIN, volume );
|
||||
alSourcefv( source, AL_POSITION, source_pos );
|
||||
alSourcefv( source, AL_VELOCITY, source_vel );
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
SGSoundSample::~SGSoundSample() {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Deleting a sample" );
|
||||
alDeleteSources(1, &source);
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
|
||||
// play the sample
|
||||
void SGSoundSample::play( bool _loop ) {
|
||||
loop = _loop;
|
||||
|
||||
// make sure sound isn't already playing
|
||||
alSourceStop( source );
|
||||
|
||||
alSourcei( source, AL_LOOPING, loop );
|
||||
alSourcePlay( source );
|
||||
}
|
||||
|
||||
|
||||
// stop playing the sample
|
||||
void SGSoundSample::stop() {
|
||||
alSourceStop( source );
|
||||
}
|
170
simgear/sound/sample_openal.hxx
Normal file
170
simgear/sound/sample_openal.hxx
Normal file
@ -0,0 +1,170 @@
|
||||
// sample.hxx -- Sound sample encapsulation class
|
||||
//
|
||||
// Written by Curtis Olson, started April 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - curt@flightgear.org
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* \file sample.hxx
|
||||
* Provides a sound sample encapsulation
|
||||
*/
|
||||
|
||||
#ifndef _SG_SAMPLE_HXX
|
||||
#define _SG_SAMPLE_HXX 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
/**
|
||||
* manages everything we need to know for an individual sound sample
|
||||
*/
|
||||
|
||||
class SGSoundSample {
|
||||
|
||||
private:
|
||||
|
||||
// Buffers hold sound data.
|
||||
ALuint buffer;
|
||||
|
||||
// Sources are points emitting sound.
|
||||
ALuint source;
|
||||
|
||||
// Position of the source sound.
|
||||
ALfloat source_pos[3];
|
||||
|
||||
// Velocity of the source sound.
|
||||
ALfloat source_vel[3];
|
||||
|
||||
// configuration values
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALvoid* data;
|
||||
ALsizei freq;
|
||||
|
||||
double pitch;
|
||||
double volume;
|
||||
ALboolean loop;
|
||||
|
||||
public:
|
||||
|
||||
SGSoundSample( const char *path, const char *file );
|
||||
SGSoundSample( unsigned char *_data, int len, int _freq );
|
||||
~SGSoundSample();
|
||||
|
||||
/**
|
||||
* Start playing this sample.
|
||||
*
|
||||
* @param looped Define wether the sound should be played in a loop.
|
||||
*/
|
||||
void play( bool _loop );
|
||||
|
||||
/**
|
||||
* Stop playing this sample.
|
||||
*
|
||||
* @param sched A pointer to the appropriate scheduler.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Play this sample once.
|
||||
* @see #play
|
||||
*/
|
||||
inline void play_once() { play(false); }
|
||||
|
||||
/**
|
||||
* Play this sample looped.
|
||||
* @see #play
|
||||
*/
|
||||
inline void play_looped() { play(true); }
|
||||
|
||||
/**
|
||||
* Test if a sample is curretnly playing.
|
||||
* @return true if is is playing, false otherwise.
|
||||
*/
|
||||
inline bool is_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()!" );
|
||||
}
|
||||
return (result == AL_PLAYING) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current pitch setting of this sample.
|
||||
*/
|
||||
inline double get_pitch() const { return pitch; }
|
||||
|
||||
/**
|
||||
* Set the pitch of this sample.
|
||||
*/
|
||||
inline void set_pitch( double p ) {
|
||||
pitch = p;
|
||||
alSourcef( source, AL_PITCH, pitch );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error in sample set_pitch()! " << p );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current volume setting of this sample.
|
||||
*/
|
||||
inline double get_volume() const { return volume; }
|
||||
|
||||
/**
|
||||
* Set the volume of this sample.
|
||||
*/
|
||||
inline void set_volume( double v ) {
|
||||
volume = v;
|
||||
alSourcef( source, AL_GAIN, volume );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error in sample set_volume()!" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the sounds sample
|
||||
*/
|
||||
inline int get_size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pointer to the raw data
|
||||
*/
|
||||
inline char *get_data() {
|
||||
return (char *)data;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // _SG_SAMPLE_HXX
|
||||
|
||||
|
@ -1,416 +0,0 @@
|
||||
// soundmgr.cxx -- Sound effect management class
|
||||
//
|
||||
// Sound manager initially written by David Findlay
|
||||
// <david_j_findlay@yahoo.com.au> 2001
|
||||
//
|
||||
// C++-ified by Curtis Olson, started March 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "soundmgr.hxx"
|
||||
|
||||
#define SOUND_SAFETY_MULT 3
|
||||
#define MAX_SOUND_SAFETY ( 1.0 / SOUND_SAFETY_MULT )
|
||||
|
||||
//
|
||||
// SGSimpleSound
|
||||
//
|
||||
|
||||
// constructor
|
||||
SGSimpleSound::SGSimpleSound( const char *path, const char *file )
|
||||
: sample(NULL),
|
||||
pitch_envelope(NULL),
|
||||
volume_envelope(NULL),
|
||||
pitch(1.0),
|
||||
volume(1.0)
|
||||
{
|
||||
SGPath slfile( path );
|
||||
if ( file )
|
||||
slfile.append( file );
|
||||
|
||||
sample = new slSample ( slfile.c_str() );
|
||||
pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
|
||||
volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
|
||||
pitch_envelope->setStep ( 0, 0.01, 1.0 );
|
||||
volume_envelope->setStep ( 0, 0.01, 1.0 );
|
||||
}
|
||||
|
||||
SGSimpleSound::SGSimpleSound( unsigned char *buffer, int len )
|
||||
: pitch(1.0),
|
||||
volume(1.0)
|
||||
{
|
||||
sample = new slSample ( buffer, len );
|
||||
pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
|
||||
volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT );
|
||||
pitch_envelope->setStep ( 0, 0.01, 1.0 );
|
||||
volume_envelope->setStep ( 0, 0.01, 1.0 );
|
||||
}
|
||||
|
||||
// destructor
|
||||
SGSimpleSound::~SGSimpleSound() {
|
||||
delete pitch_envelope;
|
||||
delete volume_envelope;
|
||||
delete sample;
|
||||
}
|
||||
|
||||
void SGSimpleSound::play( slScheduler *sched, bool looped ) {
|
||||
|
||||
// make sure sound isn't already playing
|
||||
if ( sample->getPlayCount() > 0 ) {
|
||||
sched->stopSample(sample);
|
||||
// return;
|
||||
}
|
||||
|
||||
if ( looped ) {
|
||||
sched->loopSample(sample);
|
||||
} else {
|
||||
sched->playSample(sample);
|
||||
}
|
||||
|
||||
sched->addSampleEnvelope(sample, 0, 0, pitch_envelope, SL_PITCH_ENVELOPE);
|
||||
sched->addSampleEnvelope(sample, 0, 1, volume_envelope, SL_VOLUME_ENVELOPE);
|
||||
}
|
||||
|
||||
void SGSimpleSound::stop( slScheduler *sched ) {
|
||||
|
||||
sched->stopSample( sample );
|
||||
}
|
||||
|
||||
//
|
||||
// Sound Manager
|
||||
//
|
||||
|
||||
// constructor
|
||||
SGSoundMgr::SGSoundMgr() {
|
||||
audio_sched = new slScheduler( 8000 );
|
||||
if ( audio_sched->notWorking() ) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
|
||||
} else {
|
||||
audio_sched -> setMaxConcurrent ( SL_MAX_MIXERINPUTS );
|
||||
|
||||
audio_mixer = new smMixer;
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_INFO,
|
||||
"Rate = " << audio_sched->getRate()
|
||||
<< " Bps = " << audio_sched->getBps()
|
||||
<< " Stereo = " << audio_sched->getStereo() );
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
|
||||
SGSoundMgr::~SGSoundMgr() {
|
||||
|
||||
//
|
||||
// Remove the samples from the sample manager.
|
||||
//
|
||||
sample_map_iterator sample_current = samples.begin();
|
||||
sample_map_iterator sample_end = samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
sample_ref *sr = sample_current->second;
|
||||
|
||||
audio_sched->stopSample(sr->sample);
|
||||
delete sr->sample;
|
||||
delete sr;
|
||||
}
|
||||
|
||||
//
|
||||
// Remove the sounds from the sound manager.
|
||||
//
|
||||
sound_map_iterator sound_current = sounds.begin();
|
||||
sound_map_iterator sound_end = sounds.end();
|
||||
for ( ; sound_current != sound_end; ++sound_current ) {
|
||||
SGSimpleSound *s = sound_current->second;
|
||||
|
||||
audio_sched->stopSample(s->get_sample());
|
||||
delete s->get_sample();
|
||||
delete s;
|
||||
}
|
||||
|
||||
delete audio_sched;
|
||||
delete audio_mixer;
|
||||
}
|
||||
|
||||
|
||||
// initialize the sound manager
|
||||
void SGSoundMgr::init() {
|
||||
safety = MAX_SOUND_SAFETY;
|
||||
|
||||
// audio_mixer -> setMasterVolume ( 80 ) ; /* 80% of max volume. */
|
||||
audio_sched -> setSafetyMargin ( SOUND_SAFETY_MULT * safety ) ;
|
||||
|
||||
|
||||
//
|
||||
// Remove the samples from the sample manager.
|
||||
//
|
||||
sample_map_iterator sample_current = samples.begin();
|
||||
sample_map_iterator sample_end = samples.end();
|
||||
for ( ; sample_current != sample_end; ++sample_current ) {
|
||||
sample_ref *sr = sample_current->second;
|
||||
|
||||
audio_sched->stopSample(sr->sample);
|
||||
delete sr->sample;
|
||||
delete sr;
|
||||
}
|
||||
samples.clear();
|
||||
|
||||
//
|
||||
// Remove the sounds from the sound manager.
|
||||
//
|
||||
sound_map_iterator sound_current = sounds.begin();
|
||||
sound_map_iterator sound_end = sounds.end();
|
||||
for ( ; sound_current != sound_end; ++sound_current ) {
|
||||
SGSimpleSound *s = sound_current->second;
|
||||
|
||||
audio_sched->stopSample(s->get_sample());
|
||||
delete s->get_sample();
|
||||
delete s;
|
||||
}
|
||||
sounds.clear();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SGSoundMgr::bind ()
|
||||
{
|
||||
// no properties yet
|
||||
}
|
||||
|
||||
|
||||
void SGSoundMgr::unbind ()
|
||||
{
|
||||
// no properties yet
|
||||
}
|
||||
|
||||
|
||||
// run the audio scheduler
|
||||
void SGSoundMgr::update( double dt ) {
|
||||
if ( dt > safety ) {
|
||||
safety = dt;
|
||||
} else {
|
||||
safety = safety * 0.99 + dt * 0.01;
|
||||
}
|
||||
if ( safety > MAX_SOUND_SAFETY ) {
|
||||
safety = MAX_SOUND_SAFETY;
|
||||
}
|
||||
// cout << "safety = " << safety << endl;
|
||||
audio_sched -> setSafetyMargin ( SOUND_SAFETY_MULT * safety ) ;
|
||||
|
||||
if ( !audio_sched->not_working() )
|
||||
audio_sched -> update();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundMgr::pause ()
|
||||
{
|
||||
audio_sched->pauseSample(0, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundMgr::resume ()
|
||||
{
|
||||
audio_sched->resumeSample(0, 0);
|
||||
}
|
||||
|
||||
|
||||
// add a sound effect, return true if successful
|
||||
bool SGSoundMgr::add( SGSimpleSound *sound, const string& refname ) {
|
||||
|
||||
sound_map_iterator sound_it = sounds.find( refname );
|
||||
if ( sound_it != sounds.end() ) {
|
||||
// sound already exists
|
||||
return false;
|
||||
}
|
||||
|
||||
sample_map_iterator sample_it = samples.find( refname );
|
||||
if ( sample_it != samples.end() ) {
|
||||
// this sound has existed in the past and it's sample is still
|
||||
// here, delete the sample so we can replace it.
|
||||
samples.erase( sample_it );
|
||||
}
|
||||
|
||||
sample_ref *sr = new sample_ref;
|
||||
|
||||
sr->n=1;
|
||||
sr->sample = sound->get_sample();
|
||||
samples[refname] = sr;
|
||||
|
||||
sounds[refname] = sound;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// add a sound from a file, return the sample if successful, else return NULL
|
||||
SGSimpleSound *SGSoundMgr::add( const string &refname,
|
||||
const char *path, const char *file ) {
|
||||
SGSimpleSound *sound;
|
||||
|
||||
SGPath slfile( path );
|
||||
if ( file )
|
||||
slfile.append( file );
|
||||
|
||||
if ( slfile.str().empty() )
|
||||
return NULL;
|
||||
|
||||
sample_map_iterator it = samples.find(slfile.str());
|
||||
if (it == samples.end()) {
|
||||
|
||||
sound = new SGSimpleSound(slfile.c_str());
|
||||
sounds[refname] = sound;
|
||||
|
||||
sample_ref *sr = new sample_ref;
|
||||
|
||||
sr->n=1;
|
||||
sr->sample = sound->get_sample();
|
||||
samples[slfile.str()] = sr;
|
||||
|
||||
} else {
|
||||
sample_ref *sr = it->second;
|
||||
|
||||
sr->n++;
|
||||
sound =
|
||||
new SGSimpleSound(sr->sample->getBuffer(), sr->sample->getLength());
|
||||
sounds[refname] = sound;
|
||||
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
|
||||
// remove a sound effect, return true if successful
|
||||
bool SGSoundMgr::remove( const string &refname ) {
|
||||
|
||||
sound_map_iterator it = sounds.find( refname );
|
||||
if ( it != sounds.end() ) {
|
||||
// first stop the sound from playing (so we don't bomb the
|
||||
// audio scheduler)
|
||||
SGSimpleSound *sample = it->second;
|
||||
|
||||
// cout << "Playing " << sample->get_sample()->getPlayCount()
|
||||
// << " instances!" << endl;
|
||||
|
||||
audio_sched->stopSample( sample->get_sample() );
|
||||
audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0,
|
||||
NULL,
|
||||
SL_PITCH_ENVELOPE );
|
||||
audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1,
|
||||
NULL,
|
||||
SL_VOLUME_ENVELOPE );
|
||||
|
||||
// must call audio_sched->update() after stopping the sound
|
||||
// but before deleting it.
|
||||
audio_sched -> update();
|
||||
// cout << "Still playing " << sample->get_sample()->getPlayCount()
|
||||
// << " instances!" << endl;
|
||||
|
||||
//
|
||||
// FIXME:
|
||||
// Due to the change in the sound manager, samples live
|
||||
// until the sound manager gets removed.
|
||||
//
|
||||
// delete sample;
|
||||
sounds.erase( it );
|
||||
|
||||
// cout << "sndmgr: removed -> " << refname << endl;
|
||||
return true;
|
||||
} else {
|
||||
// cout << "sndmgr: failed remove -> " << refname << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// return true of the specified sound exists in the sound manager system
|
||||
bool SGSoundMgr::exists( const string &refname ) {
|
||||
sound_map_iterator it = sounds.find( refname );
|
||||
if ( it != sounds.end() ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// return a pointer to the SGSimpleSound if the specified sound exists
|
||||
// in the sound manager system, otherwise return NULL
|
||||
SGSimpleSound *SGSoundMgr::find( const string &refname ) {
|
||||
sound_map_iterator it = sounds.find( refname );
|
||||
if ( it != sounds.end() ) {
|
||||
return it->second;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// tell the scheduler to play the indexed sample in a continuous
|
||||
// loop
|
||||
bool SGSoundMgr::play_looped( const string &refname ) {
|
||||
SGSimpleSound *sample;
|
||||
|
||||
if ((sample = find( refname )) == NULL)
|
||||
return false;
|
||||
|
||||
sample->play(audio_sched, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// tell the scheduler to play the indexed sample once
|
||||
bool SGSoundMgr::play_once( const string& refname ) {
|
||||
SGSimpleSound *sample;
|
||||
|
||||
if ((sample = find( refname )) == NULL)
|
||||
return false;
|
||||
|
||||
sample->play(audio_sched, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// return true of the specified sound is currently being played
|
||||
bool SGSoundMgr::is_playing( const string& refname ) {
|
||||
SGSimpleSound *sample;
|
||||
|
||||
if ((sample = find( refname )) == NULL)
|
||||
return false;
|
||||
|
||||
return (sample->get_sample()->getPlayCount() > 0 );
|
||||
}
|
||||
|
||||
|
||||
// immediate stop playing the sound
|
||||
bool SGSoundMgr::stop( const string& refname ) {
|
||||
SGSimpleSound *sample;
|
||||
|
||||
if ((sample = find( refname )) == NULL)
|
||||
return false;
|
||||
|
||||
audio_sched->stopSample( sample->get_sample() );
|
||||
return true;
|
||||
}
|
@ -1,296 +0,0 @@
|
||||
// soundmgr.hxx -- Sound effect management class
|
||||
//
|
||||
// Sound manager initially written by David Findlay
|
||||
// <david_j_findlay@yahoo.com.au> 2001
|
||||
//
|
||||
// C++-ified by Curtis Olson, started March 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* \file soundmgr.hxx
|
||||
* Provides a sound manager class to keep track of
|
||||
* multiple sounds and manage playing them with different effects and
|
||||
* timings.
|
||||
*/
|
||||
|
||||
#ifndef _SG_SOUNDMGR_HXX
|
||||
#define _SG_SOUNDMGR_HXX 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
#include STL_STRING
|
||||
#include <map>
|
||||
|
||||
#include <plib/sl.h>
|
||||
#include <plib/sm.h>
|
||||
|
||||
SG_USING_STD(map);
|
||||
SG_USING_STD(string);
|
||||
|
||||
|
||||
/**
|
||||
* manages everything we need to know for an individual sound sample
|
||||
*/
|
||||
class SGSimpleSound {
|
||||
|
||||
private:
|
||||
|
||||
slSample *sample;
|
||||
slEnvelope *pitch_envelope;
|
||||
slEnvelope *volume_envelope;
|
||||
double pitch;
|
||||
double volume;
|
||||
|
||||
public:
|
||||
|
||||
SGSimpleSound( const char *path, const char *file = NULL );
|
||||
SGSimpleSound( unsigned char *buffer, int len );
|
||||
~SGSimpleSound();
|
||||
|
||||
/**
|
||||
* Start playing this sample.
|
||||
*
|
||||
* @param sched A pointer to the appropriate scheduler.
|
||||
* @param looped Define wether the sound should be played in a loop.
|
||||
*/
|
||||
void play( slScheduler *sched, bool looped );
|
||||
|
||||
/**
|
||||
* Stop playing this sample.
|
||||
*
|
||||
* @param sched A pointer to the appropriate scheduler.
|
||||
*/
|
||||
void stop( slScheduler *sched );
|
||||
|
||||
/**
|
||||
* Play this sample once.
|
||||
* @see #play
|
||||
*/
|
||||
inline void play_once( slScheduler *sched ) { play( sched, false); }
|
||||
|
||||
/**
|
||||
* Play this sample looped.
|
||||
* @see #play
|
||||
*/
|
||||
inline void play_looped( slScheduler *sched ) { play( sched, true); }
|
||||
|
||||
/**
|
||||
* Test if a sample is curretnly playing.
|
||||
* @return true if is is playing, false otherwise.
|
||||
*/
|
||||
inline bool is_playing( ) {
|
||||
return ( sample->getPlayCount() > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current pitch setting of this sample.
|
||||
*/
|
||||
inline double get_pitch() const { return pitch; }
|
||||
|
||||
/**
|
||||
* Set the pitch of this sample.
|
||||
*/
|
||||
inline void set_pitch( double p ) {
|
||||
pitch = p;
|
||||
pitch_envelope->setStep( 0, 0.01, pitch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current volume setting of this sample.
|
||||
*/
|
||||
inline double get_volume() const { return volume; }
|
||||
|
||||
/**
|
||||
* Set the volume of this sample.
|
||||
*/
|
||||
inline void set_volume( double v ) {
|
||||
volume = v;
|
||||
volume_envelope->setStep( 0, 0.01, volume );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a refference to the raw sample.
|
||||
*/
|
||||
inline slSample *get_sample() { return sample; }
|
||||
|
||||
/**
|
||||
* Get the pitch envelope setting of this sample.
|
||||
*/
|
||||
inline slEnvelope *get_pitch_envelope() { return pitch_envelope; }
|
||||
|
||||
/**
|
||||
* Get the volume envelope setting of this sample.
|
||||
*/
|
||||
inline slEnvelope *get_volume_envelope() { return volume_envelope; }
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
int n;
|
||||
slSample *sample;
|
||||
} sample_ref;
|
||||
|
||||
typedef map < string, sample_ref * > sample_map;
|
||||
typedef sample_map::iterator sample_map_iterator;
|
||||
typedef sample_map::const_iterator const_sample_map_iterator;
|
||||
|
||||
typedef map < string, SGSimpleSound * > sound_map;
|
||||
typedef sound_map::iterator sound_map_iterator;
|
||||
typedef sound_map::const_iterator const_sound_map_iterator;
|
||||
|
||||
|
||||
/**
|
||||
* Manage a collection of SGSimpleSound instances
|
||||
*/
|
||||
class SGSoundMgr
|
||||
{
|
||||
|
||||
slScheduler *audio_sched;
|
||||
smMixer *audio_mixer;
|
||||
|
||||
sound_map sounds;
|
||||
sample_map samples;
|
||||
|
||||
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 resume ();
|
||||
|
||||
|
||||
/**
|
||||
* is audio working?
|
||||
*/
|
||||
inline bool is_working() const { return !audio_sched->notWorking(); }
|
||||
|
||||
/**
|
||||
* reinitialize the sound manager
|
||||
*/
|
||||
inline void reinit() { init(); }
|
||||
|
||||
/**
|
||||
* add a sound effect, return true if successful
|
||||
*/
|
||||
bool add( SGSimpleSound *sound, const string& refname);
|
||||
|
||||
/**
|
||||
* Add a sound file to the sound manager.
|
||||
*
|
||||
* The advantage of using this function over the previous one is that
|
||||
* it doesn't load a sample if it already is in memory, but instead it
|
||||
* uses the already loaded sample data.
|
||||
*
|
||||
* @param refname A refference name to make a distincion between samples.
|
||||
* @param path The path or full filename of the sample to load.
|
||||
* @param file An optional filename which will be appended to the path.
|
||||
* @return An instance of the sound for further manipulation.
|
||||
*/
|
||||
SGSimpleSound *add( const string& refname,
|
||||
const char *path, const char *file = NULL );
|
||||
|
||||
/**
|
||||
* 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 SGSimpleSound if the specified sound
|
||||
* exists in the sound manager system, otherwise return NULL
|
||||
*/
|
||||
SGSimpleSound *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 );
|
||||
|
||||
/**
|
||||
* return the audio scheduler
|
||||
*/
|
||||
inline slScheduler *get_scheduler( ) { return audio_sched; };
|
||||
};
|
||||
|
||||
|
||||
#endif // _SG_SOUNDMGR_HXX
|
||||
|
||||
|
263
simgear/sound/soundmgr_openal.cxx
Normal file
263
simgear/sound/soundmgr_openal.cxx
Normal file
@ -0,0 +1,263 @@
|
||||
// soundmgr.cxx -- Sound effect management class
|
||||
//
|
||||
// Sound manager initially written by David Findlay
|
||||
// <david_j_findlay@yahoo.com.au> 2001
|
||||
//
|
||||
// C++-ified by Curtis Olson, started March 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alut.h>
|
||||
#include <AL/alc.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
|
||||
//
|
||||
// Sound Manager
|
||||
//
|
||||
|
||||
// constructor
|
||||
SGSoundMgr::SGSoundMgr() {
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Initializing OpenAL sound manager" );
|
||||
|
||||
// initialize OpenAL
|
||||
alutInit( 0, NULL );
|
||||
alGetError();
|
||||
if ( alGetError() == AL_NO_ERROR) {
|
||||
working = true;
|
||||
} else {
|
||||
working = false;
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" );
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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!" );
|
||||
}
|
||||
}
|
||||
|
||||
// destructor
|
||||
|
||||
SGSoundMgr::~SGSoundMgr() {
|
||||
|
||||
//
|
||||
// Remove the samples from the sample manager.
|
||||
//
|
||||
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;
|
||||
delete sample;
|
||||
}
|
||||
|
||||
alutExit();
|
||||
}
|
||||
|
||||
|
||||
// initialize the sound manager
|
||||
void SGSoundMgr::init() {
|
||||
//
|
||||
// Remove the samples from the sample manager.
|
||||
//
|
||||
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;
|
||||
delete sample;
|
||||
}
|
||||
samples.clear();
|
||||
}
|
||||
|
||||
|
||||
void SGSoundMgr::bind ()
|
||||
{
|
||||
// no properties
|
||||
}
|
||||
|
||||
|
||||
void SGSoundMgr::unbind ()
|
||||
{
|
||||
// no properties
|
||||
}
|
||||
|
||||
|
||||
// run the audio scheduler
|
||||
void SGSoundMgr::update( double dt ) {
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundMgr::pause ()
|
||||
{
|
||||
ALCcontext *pCurContext = alcGetCurrentContext();
|
||||
alcSuspendContext( pCurContext );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error after soundmgr pause()!" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SGSoundMgr::resume ()
|
||||
{
|
||||
ALCcontext *pCurContext = alcGetCurrentContext();
|
||||
alcProcessContext( pCurContext );
|
||||
if ( alGetError() != AL_NO_ERROR) {
|
||||
SG_LOG( SG_GENERAL, SG_ALERT,
|
||||
"Oops AL error after soundmgr resume()!" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add a sound effect, return true if successful
|
||||
bool SGSoundMgr::add( SGSoundSample *sound, const string& refname ) {
|
||||
|
||||
sample_map_iterator sample_it = samples.find( refname );
|
||||
if ( sample_it != samples.end() ) {
|
||||
// sound already exists
|
||||
return false;
|
||||
}
|
||||
|
||||
samples[refname] = sound;
|
||||
|
||||
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)
|
||||
SGSoundSample *sample = sample_it->second;
|
||||
delete sample;
|
||||
samples.erase( sample_it );
|
||||
|
||||
// cout << "sndmgr: removed -> " << refname << endl;
|
||||
return true;
|
||||
} else {
|
||||
// cout << "sndmgr: failed remove -> " << refname << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// return a pointer to the SGSoundSample 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// tell the scheduler to play the indexed sample in a continuous
|
||||
// loop
|
||||
bool SGSoundMgr::play_looped( const string &refname ) {
|
||||
SGSoundSample *sample;
|
||||
|
||||
if ( (sample = find( refname )) == NULL ) {
|
||||
return false;
|
||||
} else {
|
||||
sample->play( true );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 );
|
||||
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;
|
||||
}
|
||||
}
|
177
simgear/sound/soundmgr_openal.hxx
Normal file
177
simgear/sound/soundmgr_openal.hxx
Normal file
@ -0,0 +1,177 @@
|
||||
// soundmgr.hxx -- Sound effect management class
|
||||
//
|
||||
// Sound manager initially written by David Findlay
|
||||
// <david_j_findlay@yahoo.com.au> 2001
|
||||
//
|
||||
// C++-ified by Curtis Olson, started March 2001.
|
||||
//
|
||||
// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org
|
||||
//
|
||||
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
/**
|
||||
* \file soundmgr.hxx
|
||||
* Provides a sound manager class to keep track of
|
||||
* multiple sounds and manage playing them with different effects and
|
||||
* timings.
|
||||
*/
|
||||
|
||||
#ifndef _SG_SOUNDMGR_OPENAL_HXX
|
||||
#define _SG_SOUNDMGR_OPENAL_HXX 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include STL_STRING
|
||||
#include <map>
|
||||
|
||||
#include <AL/al.h>
|
||||
|
||||
#include "sample_openal.hxx"
|
||||
|
||||
SG_USING_STD(map);
|
||||
SG_USING_STD(string);
|
||||
|
||||
|
||||
typedef map < string, SGSoundSample * > sample_map;
|
||||
typedef sample_map::iterator sample_map_iterator;
|
||||
typedef sample_map::const_iterator const_sample_map_iterator;
|
||||
|
||||
|
||||
/**
|
||||
* Manage a collection of SGSoundSample instances
|
||||
*/
|
||||
class SGSoundMgr
|
||||
{
|
||||
|
||||
// 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 resume();
|
||||
|
||||
|
||||
/**
|
||||
* is audio working?
|
||||
*/
|
||||
inline bool is_working() const { return working; }
|
||||
|
||||
/**
|
||||
* reinitialize the sound manager
|
||||
*/
|
||||
inline void reinit() { init(); }
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
};
|
||||
|
||||
|
||||
#endif // _SG_SOUNDMGR_OPENAL_HXX
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include <simgear/math/fastmath.hxx>
|
||||
|
||||
|
||||
#include "sound.hxx"
|
||||
#include "xmlsound.hxx"
|
||||
|
||||
|
||||
// static double _snd_lin(double v) { return v; }
|
||||
@ -62,13 +62,13 @@ static const struct {
|
||||
{"", NULL}
|
||||
};
|
||||
|
||||
SGSound::SGSound()
|
||||
SGXmlSound::SGXmlSound()
|
||||
: _sample(NULL),
|
||||
_condition(NULL),
|
||||
_property(NULL),
|
||||
_active(false),
|
||||
_name(""),
|
||||
_mode(SGSound::ONCE),
|
||||
_mode(SGXmlSound::ONCE),
|
||||
_prev_value(0),
|
||||
_dt_play(0.0),
|
||||
_dt_stop(0.0),
|
||||
@ -76,24 +76,24 @@ SGSound::SGSound()
|
||||
{
|
||||
}
|
||||
|
||||
SGSound::~SGSound()
|
||||
SGXmlSound::~SGXmlSound()
|
||||
{
|
||||
_mgr->get_scheduler()->stopSample(_sample->get_sample());
|
||||
_sample->stop();
|
||||
|
||||
if (_property)
|
||||
delete _property;
|
||||
if (_property)
|
||||
delete _property;
|
||||
|
||||
if (_condition)
|
||||
delete _condition;
|
||||
if (_condition)
|
||||
delete _condition;
|
||||
|
||||
_volume.clear();
|
||||
_pitch.clear();
|
||||
delete _sample;
|
||||
_volume.clear();
|
||||
_pitch.clear();
|
||||
delete _sample;
|
||||
}
|
||||
|
||||
void
|
||||
SGSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
const string &path)
|
||||
SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
const string &path)
|
||||
{
|
||||
|
||||
//
|
||||
@ -105,13 +105,13 @@ SGSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
|
||||
const char *mode_str = node->getStringValue("mode", "");
|
||||
if ( !strcmp(mode_str, "looped") ) {
|
||||
_mode = SGSound::LOOPED;
|
||||
_mode = SGXmlSound::LOOPED;
|
||||
|
||||
} else if ( !strcmp(mode_str, "in-transit") ) {
|
||||
_mode = SGSound::IN_TRANSIT;
|
||||
_mode = SGXmlSound::IN_TRANSIT;
|
||||
|
||||
} else {
|
||||
_mode = SGSound::ONCE;
|
||||
_mode = SGXmlSound::ONCE;
|
||||
|
||||
if ( strcmp(mode_str, "") )
|
||||
SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'");
|
||||
@ -132,7 +132,7 @@ SGSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
unsigned int i;
|
||||
float v = 0.0;
|
||||
vector<SGPropertyNode_ptr> kids = node->getChildren("volume");
|
||||
for (i = 0; (i < kids.size()) && (i < SGSound::MAXPROP); i++) {
|
||||
for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) {
|
||||
_snd_prop volume = {NULL, NULL, NULL, 1.0, 0.0, 0.0, 0.0, false};
|
||||
|
||||
if (strcmp(kids[i]->getStringValue("property"), ""))
|
||||
@ -186,7 +186,7 @@ SGSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
//
|
||||
float p = 0.0;
|
||||
kids = node->getChildren("pitch");
|
||||
for (i = 0; (i < kids.size()) && (i < SGSound::MAXPROP); i++) {
|
||||
for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) {
|
||||
_snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false};
|
||||
|
||||
if (strcmp(kids[i]->getStringValue("property", ""), ""))
|
||||
@ -237,15 +237,18 @@ SGSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
|
||||
// Initialize the sample
|
||||
//
|
||||
_mgr = sndmgr;
|
||||
if ((_sample = _mgr->find(_name)) == NULL)
|
||||
_sample = _mgr->add(_name, path.c_str(), node->getStringValue("path", ""));
|
||||
if ( (_sample = _mgr->find(_name)) == NULL ) {
|
||||
_sample = new SGSoundSample( path.c_str(),
|
||||
node->getStringValue("path", "") );
|
||||
_mgr->add( _sample, _name );
|
||||
}
|
||||
|
||||
_sample->set_volume(v);
|
||||
_sample->set_pitch(p);
|
||||
}
|
||||
|
||||
void
|
||||
SGSound::update (double dt)
|
||||
SGXmlSound::update (double dt)
|
||||
{
|
||||
double curr_value = 0.0;
|
||||
|
||||
@ -260,17 +263,17 @@ SGSound::update (double dt)
|
||||
(!_condition && _property &&
|
||||
(
|
||||
!curr_value ||
|
||||
( (_mode == SGSound::IN_TRANSIT) && (curr_value == _prev_value) )
|
||||
( (_mode == SGXmlSound::IN_TRANSIT) && (curr_value == _prev_value) )
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
if ((_mode != SGSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) {
|
||||
if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) {
|
||||
if (_sample->is_playing()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Stopping audio after " << _dt_play
|
||||
<< " sec: " << _name );
|
||||
|
||||
_sample->stop( _mgr->get_scheduler() );
|
||||
_sample->stop();
|
||||
}
|
||||
|
||||
_active = false;
|
||||
@ -287,7 +290,7 @@ SGSound::update (double dt)
|
||||
// If the mode is ONCE and the sound is still playing,
|
||||
// we have nothing to do anymore.
|
||||
//
|
||||
if (_active && (_mode == SGSound::ONCE)) {
|
||||
if (_active && (_mode == SGXmlSound::ONCE)) {
|
||||
|
||||
if (!_sample->is_playing()) {
|
||||
_dt_stop += dt;
|
||||
@ -392,11 +395,11 @@ SGSound::update (double dt)
|
||||
//
|
||||
if (!_active) {
|
||||
|
||||
if (_mode == SGSound::ONCE)
|
||||
_sample->play(_mgr->get_scheduler(), false);
|
||||
if (_mode == SGXmlSound::ONCE)
|
||||
_sample->play(false);
|
||||
|
||||
else
|
||||
_sample->play(_mgr->get_scheduler(), true);
|
||||
_sample->play(true);
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Playing audio after " << _dt_stop
|
||||
<< " sec: " << _name);
|
@ -36,7 +36,8 @@
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/props/condition.hxx>
|
||||
|
||||
#include "soundmgr.hxx"
|
||||
#include "sample_openal.hxx"
|
||||
#include "soundmgr_openal.hxx"
|
||||
|
||||
static const double MAX_TRANSIT_TIME = 0.1; // 100 ms.
|
||||
|
||||
@ -49,13 +50,13 @@ static const double MAX_TRANSIT_TIME = 0.1; // 100 ms.
|
||||
* settings, setting up its internal states, and managing sound
|
||||
* playback whenever such an event happens.
|
||||
*/
|
||||
class SGSound
|
||||
class SGXmlSound
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
SGSound();
|
||||
virtual ~SGSound();
|
||||
SGXmlSound();
|
||||
virtual ~SGXmlSound();
|
||||
|
||||
/**
|
||||
* Initialize the sound event.
|
||||
@ -117,7 +118,7 @@ protected:
|
||||
enum { ONCE=0, LOOPED, IN_TRANSIT };
|
||||
enum { LEVEL=0, INVERTED, FLIPFLOP };
|
||||
|
||||
// SGSound properties
|
||||
// SGXmlSound properties
|
||||
typedef struct {
|
||||
SGPropertyNode * prop;
|
||||
double (*fn)(double);
|
||||
@ -132,7 +133,7 @@ protected:
|
||||
private:
|
||||
|
||||
SGSoundMgr * _mgr;
|
||||
SGSimpleSound * _sample;
|
||||
SGSoundSample * _sample;
|
||||
|
||||
SGCondition * _condition;
|
||||
SGPropertyNode * _property;
|
Loading…
Reference in New Issue
Block a user