00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 # include <simgear_config.h>
00025 #endif
00026
00027 #if defined( __APPLE__ )
00028 # define AL_ILLEGAL_ENUM AL_INVALID_ENUM
00029 # define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
00030 # include <OpenAL/al.h>
00031 # include <OpenAL/alut.h>
00032 #else
00033 # include <AL/al.h>
00034 # include <AL/alut.h>
00035 #endif
00036
00037 #include <simgear/debug/logstream.hxx>
00038 #include <simgear/misc/sg_path.hxx>
00039 #include <simgear/structure/exception.hxx>
00040
00041 #include "sample_openal.hxx"
00042
00043
00044
00045
00046
00047
00048
00049 static bool print_openal_error(const string &s = "unknown") {
00050 ALuint error = alGetError();
00051 if ( error == AL_NO_ERROR ) {
00052 return false;
00053 } else if ( error == AL_INVALID_NAME ) {
00054 SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_INVALID_NAME): " << s );
00055 } else if ( error == AL_ILLEGAL_ENUM ) {
00056 SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_ILLEGAL_ENUM): " << s );
00057 } else if ( error == AL_INVALID_VALUE ) {
00058 SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_INVALID_VALUE): " << s );
00059 } else if ( error == AL_ILLEGAL_COMMAND ) {
00060 SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_ILLEGAL_COMMAND): " << s );
00061 } else if ( error == AL_OUT_OF_MEMORY ) {
00062 SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_OUT_OF_MEMORY): " << s );
00063 } else {
00064 SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error );
00065 }
00066 return error;
00067 }
00068
00069
00070 SGSoundSample::SGSoundSample() :
00071 buffer(0),
00072 source(0),
00073 pitch(1.0),
00074 volume(1.0),
00075 reference_dist(500.0),
00076 max_dist(3000.),
00077 loop(AL_FALSE),
00078 #ifdef USE_SOFTWARE_DOPPLER
00079 doppler_pitch_factor(1),
00080 doppler_volume_factor(1),
00081 #endif
00082 playing(false),
00083 no_Doppler_effect(true)
00084 {
00085 }
00086
00087
00088 SGSoundSample::SGSoundSample( const char *path, const char *file, bool _no_Doppler_effect ) :
00089 buffer(0),
00090 source(0),
00091 pitch(1.0),
00092 volume(1.0),
00093 reference_dist(500.0),
00094 max_dist(3000.),
00095 loop(AL_FALSE),
00096 #ifdef USE_SOFTWARE_DOPPLER
00097 doppler_pitch_factor(1),
00098 doppler_volume_factor(1),
00099 #endif
00100 playing(false),
00101 no_Doppler_effect(_no_Doppler_effect)
00102 {
00103 SGPath samplepath( path );
00104 if ( strlen(file) ) {
00105 samplepath.append( file );
00106 }
00107 sample_name = samplepath.str();
00108
00109 SG_LOG( SG_GENERAL, SG_DEBUG, "From file sounds sample = "
00110 << samplepath.str() );
00111
00112 source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
00113 offset_pos[0] = 0.0; offset_pos[1] = 0.0; offset_pos[2] = 0.0;
00114 source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
00115 direction[0] = 0.0; direction[1] = 0.0; direction[2] = 0.0;
00116 inner = outer = 360.0; outergain = 0.0;
00117
00118
00119 alGetError();
00120
00121
00122 alGenBuffers(1, &buffer);
00123 if ( print_openal_error("constructor (alGenBuffers)") ) {
00124 throw sg_exception("Failed to gen OpenAL buffer.");
00125 }
00126
00127
00128 #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
00129
00130 buffer = alutCreateBufferFromFile(samplepath.c_str());
00131 if (buffer == AL_NONE) {
00132 ALenum error = alutGetError ();
00133 print_openal_error("constructor (alutCreateBufferFromFile)");
00134 throw sg_io_exception("Failed to load wav file: ",
00135 sg_location(string(alutGetErrorString (error))));
00136 }
00137
00138 #else
00139
00140
00141
00142 ALvoid* data = load_file(path, file);
00143
00144
00145 alBufferData( buffer, format, data, size, freq );
00146
00147 if ( print_openal_error("constructor (alBufferData)") ) {
00148 throw sg_exception("Failed to buffer data.");
00149 }
00150
00151 alutUnloadWAV( format, data, size, freq );
00152 #endif
00153
00154 print_openal_error("constructor return");
00155 }
00156
00157
00158 SGSoundSample::SGSoundSample( unsigned char *_data, int len, int _freq, bool _no_Doppler_effect ) :
00159 buffer(0),
00160 source(0),
00161 pitch(1.0),
00162 volume(1.0),
00163 reference_dist(500.0),
00164 max_dist(3000.),
00165 loop(AL_FALSE),
00166 #ifdef USE_SOFTWARE_DOPPLER
00167 doppler_pitch_factor(1),
00168 doppler_volume_factor(1),
00169 #endif
00170 playing(false),
00171 no_Doppler_effect(_no_Doppler_effect)
00172 {
00173 SG_LOG( SG_GENERAL, SG_DEBUG, "In memory sounds sample" );
00174
00175 sample_name = "unknown, generated from data";
00176
00177 source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0;
00178 offset_pos[0] = 0.0; offset_pos[1] = 0.0; offset_pos[2] = 0.0;
00179 source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0;
00180 direction[0] = 0.0; direction[1] = 0.0; direction[2] = 0.0;
00181 inner = outer = 360.0; outergain = 0.0;
00182
00183
00184 alGetError();
00185
00186
00187 alGenBuffers(1, &buffer);
00188 if ( print_openal_error("constructor (alGenBuffers)") ) {
00189 throw sg_exception("Failed to gen buffer." );
00190 }
00191
00192 format = AL_FORMAT_MONO8;
00193 size = len;
00194 freq = _freq;
00195
00196 alBufferData( buffer, format, _data, size, freq );
00197 if ( print_openal_error("constructor (alBufferData)") ) {
00198 throw sg_exception("Failed to buffer data.");
00199 }
00200
00201 print_openal_error("constructor return");
00202 }
00203
00204
00205
00206 SGSoundSample::~SGSoundSample() {
00207 SG_LOG( SG_GENERAL, SG_INFO, "Deleting a sample" );
00208 if (buffer)
00209 alDeleteBuffers(1, &buffer);
00210 }
00211
00212
00213
00214 void SGSoundSample::play( bool _loop ) {
00215
00216 if ( source ) {
00217 alSourceStop( source );
00218 }
00219
00220 playing = bind_source();
00221 if ( playing ) {
00222 loop = _loop;
00223
00224 alSourcei( source, AL_LOOPING, loop );
00225 alSourcePlay( source );
00226
00227 print_openal_error("play (alSourcePlay)");
00228 }
00229 }
00230
00231
00232
00233 void SGSoundSample::stop() {
00234 if (playing) {
00235 alSourceStop( source );
00236 alDeleteSources(1, &source);
00237 source = 0;
00238 print_openal_error("stop (alDeleteSources)");
00239 }
00240 playing = false;
00241 }
00242
00243
00244 bool
00245 SGSoundSample::bind_source() {
00246
00247 if ( playing ) {
00248 return true;
00249 }
00250 if ( buffer == 0 ) {
00251 return false;
00252 }
00253
00254
00255 alGetError();
00256 alGenSources(1, &source);
00257 if ( print_openal_error("bind_source (alGenSources)") ) {
00258
00259 SG_LOG( SG_GENERAL, SG_ALERT, "Failed to generate audio source.");
00260
00261 return false;
00262 }
00263
00264 alSourcei( source, AL_BUFFER, buffer );
00265 #ifndef USE_SOFTWARE_DOPPLER
00266 alSourcef( source, AL_PITCH, pitch );
00267 alSourcef( source, AL_GAIN, volume );
00268 #else
00269 print_openal_error("bind_sources return");
00270 alSourcef( source, AL_PITCH, pitch *doppler_pitch_factor );
00271 alGetError();
00272 alSourcef( source, AL_GAIN, volume *doppler_volume_factor );
00273 #endif
00274 alSourcefv( source, AL_POSITION, source_pos );
00275 alSourcefv( source, AL_DIRECTION, direction );
00276 alSourcef( source, AL_CONE_INNER_ANGLE, inner );
00277 alSourcef( source, AL_CONE_OUTER_ANGLE, outer );
00278 alSourcef( source, AL_CONE_OUTER_GAIN, outergain);
00279 #ifdef USE_OPEN_AL_DOPPLER
00280 alSourcefv( source, AL_VELOCITY, source_vel );
00281 #endif
00282 alSourcei( source, AL_LOOPING, loop );
00283
00284 alSourcei( source, AL_SOURCE_RELATIVE, AL_TRUE );
00285 alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist );
00286 alSourcef( source, AL_MAX_DISTANCE, max_dist );
00287
00288 print_openal_error("bind_sources return");
00289
00290 return true;
00291 }
00292
00293 void
00294 SGSoundSample::set_pitch( double p ) {
00295
00296 if ( p < 0.01 ) { p = 0.01; }
00297 if ( p > 2.0 ) { p = 2.0; }
00298 pitch = p;
00299 if (playing) {
00300 #ifndef USE_SOFTWARE_DOPPLER
00301 alSourcef( source, AL_PITCH, pitch );
00302 print_openal_error("set_pitch");
00303 #else
00304 alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor );
00305 alGetError();
00306 #endif
00307 }
00308 }
00309
00310 void
00311 SGSoundSample::set_volume( double v ) {
00312 volume = v;
00313 if (playing) {
00314 #ifndef USE_SOFTWARE_DOPPLER
00315 alSourcef( source, AL_GAIN, volume );
00316 #else
00317 alSourcef( source, AL_GAIN, volume * doppler_volume_factor );
00318 #endif
00319 print_openal_error("set_volume");
00320 }
00321 }
00322
00323
00324 bool
00325 SGSoundSample::is_playing( ) {
00326 if (playing) {
00327 ALint result;
00328 alGetSourcei( source, AL_SOURCE_STATE, &result );
00329 if ( alGetError() != AL_NO_ERROR) {
00330 SG_LOG( SG_GENERAL, SG_ALERT,
00331 "Oops AL error in sample is_playing(): " << sample_name );
00332 }
00333 return (result == AL_PLAYING) ;
00334 } else
00335 return false;
00336 }
00337
00338 void
00339 SGSoundSample::set_source_pos( ALfloat *pos ) {
00340 source_pos[0] = pos[0];
00341 source_pos[1] = pos[1];
00342 source_pos[2] = pos[2];
00343
00344 if (playing) {
00345 sgVec3 final_pos;
00346 sgAddVec3( final_pos, source_pos, offset_pos );
00347
00348 alSourcefv( source, AL_POSITION, final_pos );
00349 print_openal_error("set_source_pos");
00350 }
00351 }
00352
00353 void
00354 SGSoundSample::set_offset_pos( ALfloat *pos ) {
00355 offset_pos[0] = pos[0];
00356 offset_pos[1] = pos[1];
00357 offset_pos[2] = pos[2];
00358
00359 if (playing) {
00360 sgVec3 final_pos;
00361 sgAddVec3( final_pos, source_pos, offset_pos );
00362
00363 alSourcefv( source, AL_POSITION, final_pos );
00364 print_openal_error("set_offset_pos");
00365 }
00366 }
00367
00368 void
00369 SGSoundSample::set_orientation( ALfloat *dir, ALfloat inner_angle,
00370 ALfloat outer_angle,
00371 ALfloat outer_gain)
00372 {
00373 inner = inner_angle;
00374 outer = outer_angle;
00375 outergain = outer_gain;
00376 direction[0] = dir[0];
00377 direction[1] = dir[1];
00378 direction[2] = dir[2];
00379 if (playing) {
00380 alSourcefv( source, AL_DIRECTION, dir);
00381 alSourcef( source, AL_CONE_INNER_ANGLE, inner );
00382 alSourcef( source, AL_CONE_OUTER_ANGLE, outer );
00383 alSourcef( source, AL_CONE_OUTER_GAIN, outergain );
00384 }
00385 }
00386
00387 void
00388 SGSoundSample::set_source_vel( ALfloat *vel, ALfloat *listener_vel ) {
00389 if (no_Doppler_effect) {
00390 source_vel[0] = listener_vel[0];
00391 source_vel[1] = listener_vel[1];
00392 source_vel[2] = listener_vel[2];
00393 } else {
00394 source_vel[0] = vel[0];
00395 source_vel[1] = vel[1];
00396 source_vel[2] = vel[2];
00397 }
00398 #ifdef USE_OPEN_AL_DOPPLER
00399 if (playing) {
00400 alSourcefv( source, AL_VELOCITY, source_vel );
00401 }
00402 #elif defined (USE_OPEN_AL_DOPPLER_WITH_FIXED_LISTENER)
00403 if (playing) {
00404 sgVec3 relative_vel;
00405 sgSubVec3( relative_vel, source_vel, listener_vel );
00406 alSourcefv( source, AL_VELOCITY, relative_vel );
00407 }
00408 #else
00409 if (no_Doppler_effect) {
00410 doppler_pitch_factor = 1;
00411 doppler_volume_factor = 1;
00412 return;
00413 }
00414 double doppler, mfp;
00415 sgVec3 final_pos;
00416 sgAddVec3( final_pos, source_pos, offset_pos );
00417 mfp = sgLengthVec3(final_pos);
00418 if (mfp > 1e-6) {
00419 double vls = -sgScalarProductVec3( listener_vel, final_pos ) / mfp;
00420 double vss = -sgScalarProductVec3( source_vel, final_pos ) / mfp;
00421 if (fabs(340 - vss) > 1e-6)
00422 {
00423 doppler = (340 - vls) / (340 - vss);
00424 doppler = ( doppler > 0) ? ( ( doppler < 10) ? doppler : 10 ) : 0;
00425 }
00426 else
00427 doppler = 0;
00428 }
00429 else
00430 doppler = 1;
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446 if (doppler > 0.1) {
00447 if (doppler < 10) {
00448 doppler_pitch_factor = doppler;
00449 doppler_volume_factor = 1;
00450 }
00451 else {
00452 doppler_pitch_factor = (doppler < 11) ? doppler : 11;
00453 doppler_volume_factor = (doppler < 11) ? 11 - doppler : 0;
00454 }
00455 }
00456 else {
00457 doppler_pitch_factor = 0.1;
00458 doppler_volume_factor = (doppler > 0) ? doppler * 10 : 0;
00459 }
00460 if (playing) {
00461 alSourcef( source, AL_GAIN, volume * doppler_volume_factor );
00462 print_openal_error("set_source_vel: volume");
00463 alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor );
00464 alGetError();
00465 }
00466 #endif
00467 }
00468
00469 void
00470 SGSoundSample::set_reference_dist( ALfloat dist ) {
00471 reference_dist = dist;
00472 if (playing) {
00473 alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist );
00474 }
00475 }
00476
00477
00478 void
00479 SGSoundSample::set_max_dist( ALfloat dist ) {
00480 max_dist = dist;
00481 if (playing) {
00482 alSourcef( source, AL_MAX_DISTANCE, max_dist );
00483 }
00484 }
00485
00486 ALvoid *
00487 SGSoundSample::load_file(const char *path, const char *file)
00488 {
00489 ALvoid* data = 0;
00490
00491 SGPath samplepath( path );
00492 if ( strlen(file) ) {
00493 samplepath.append( file );
00494 }
00495
00496 #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
00497 ALfloat freqf;
00498 data = alutLoadMemoryFromFile(samplepath.c_str(), &format, &size, &freqf );
00499 if (data == NULL) {
00500 throw sg_io_exception("Failed to load wav file.",
00501 sg_location(samplepath.str()));
00502 }
00503 freq = (ALsizei)freqf;
00504 #else
00505 # if defined (__APPLE__)
00506 alutLoadWAVFile( (ALbyte *)samplepath.c_str(),
00507 &format, &data, &size, &freq );
00508 # else
00509 alutLoadWAVFile( (ALbyte *)samplepath.c_str(),
00510 &format, &data, &size, &freq, &loop );
00511 # endif
00512 if ( print_openal_error("constructor (alutLoadWAVFile)") ) {
00513 throw sg_io_exception("Failed to load wav file.",
00514 sg_location(samplepath.str()));
00515 }
00516 #endif
00517
00518 return data;
00519 }
00520