00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifdef HAVE_CONFIG_H
00025 # include <simgear_config.h>
00026 #endif
00027
00028 #include <simgear/compiler.h>
00029
00030 #include <string.h>
00031
00032 #include <simgear/debug/logstream.hxx>
00033 #include <simgear/props/condition.hxx>
00034 #include <simgear/math/SGMath.hxx>
00035
00036
00037 #include "xmlsound.hxx"
00038
00039
00040
00041 static double _snd_inv(double v) { return (v == 0) ? 1e99 : 1/v; }
00042 static double _snd_abs(double v) { return (v >= 0) ? v : -v; }
00043 static double _snd_sqrt(double v) { return sqrt(fabs(v)); }
00044 static double _snd_log10(double v) { return log10(fabs(v)+1e-9); }
00045 static double _snd_log(double v) { return log(fabs(v)+1e-9); }
00046
00047
00048
00049 static const struct {
00050 const char *name;
00051 double (*fn)(double);
00052 } __sound_fn[] = {
00053 {"inv", _snd_inv},
00054 {"abs", _snd_abs},
00055 {"sqrt", _snd_sqrt},
00056 {"log", _snd_log10},
00057 {"ln", _snd_log},
00058 {"", NULL}
00059 };
00060
00061 SGXmlSound::SGXmlSound()
00062 : _sample(NULL),
00063 _active(false),
00064 _name(""),
00065 _mode(SGXmlSound::ONCE),
00066 _prev_value(0),
00067 _dt_play(0.0),
00068 _dt_stop(0.0),
00069 _delay(0.0),
00070 _stopping(0.0)
00071 {
00072 }
00073
00074 SGXmlSound::~SGXmlSound()
00075 {
00076 if (_sample)
00077 _sample->stop();
00078
00079 _volume.clear();
00080 _pitch.clear();
00081 }
00082
00083 void
00084 SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node,
00085 SGSampleGroup *sgrp, SGSampleGroup *avionics,
00086 const string &path)
00087 {
00088
00089
00090
00091
00092
00093 _name = node->getStringValue("name", "");
00094 SG_LOG(SG_GENERAL, SG_DEBUG, "Loading sound information for: " << _name );
00095
00096 const char *mode_str = node->getStringValue("mode", "");
00097 if ( !strcmp(mode_str, "looped") ) {
00098 _mode = SGXmlSound::LOOPED;
00099
00100 } else if ( !strcmp(mode_str, "in-transit") ) {
00101 _mode = SGXmlSound::IN_TRANSIT;
00102
00103 } else {
00104 _mode = SGXmlSound::ONCE;
00105 }
00106
00107 bool is_avionics = false;
00108 const char *type_str = node->getStringValue("type", "fx");
00109 if ( !strcmp(type_str, "avionics") )
00110 is_avionics = true;
00111
00112 _property = root->getNode(node->getStringValue("property", ""), true);
00113 SGPropertyNode *condition = node->getChild("condition");
00114 if (condition != NULL)
00115 _condition = sgReadCondition(root, condition);
00116
00117 if (!_property && !_condition)
00118 SG_LOG(SG_GENERAL, SG_WARN,
00119 " Neither a condition nor a property specified");
00120
00121 _delay = node->getDoubleValue("delay-sec", 0.0);
00122
00123
00124
00125
00126 unsigned int i;
00127 float v = 0.0;
00128 std::vector<SGPropertyNode_ptr> kids = node->getChildren("volume");
00129 for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) {
00130 _snd_prop volume = {NULL, NULL, NULL, 1.0, 0.0, 0.0, 0.0, false};
00131
00132 if (strcmp(kids[i]->getStringValue("property"), ""))
00133 volume.prop = root->getNode(kids[i]->getStringValue("property", ""), true);
00134
00135 const char *intern_str = kids[i]->getStringValue("internal", "");
00136 if (!strcmp(intern_str, "dt_play"))
00137 volume.intern = &_dt_play;
00138 else if (!strcmp(intern_str, "dt_stop"))
00139 volume.intern = &_dt_stop;
00140
00141 if ((volume.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0)
00142 if (volume.factor < 0.0) {
00143 volume.factor = -volume.factor;
00144 volume.subtract = true;
00145 }
00146
00147 const char *type_str = kids[i]->getStringValue("type", "");
00148 if ( strcmp(type_str, "") ) {
00149
00150 for (int j=0; __sound_fn[j].fn; j++)
00151 if ( !strcmp(type_str, __sound_fn[j].name) ) {
00152 volume.fn = __sound_fn[j].fn;
00153 break;
00154 }
00155
00156 if (!volume.fn)
00157 SG_LOG(SG_GENERAL,SG_INFO,
00158 " Unknown volume type, default to 'lin'");
00159 }
00160
00161 volume.offset = kids[i]->getDoubleValue("offset", 0.0);
00162
00163 if ((volume.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0)
00164 SG_LOG( SG_GENERAL, SG_WARN,
00165 "Volume minimum value below 0. Forced to 0.");
00166
00167 volume.max = kids[i]->getDoubleValue("max", 0.0);
00168 if (volume.max && (volume.max < volume.min) )
00169 SG_LOG(SG_GENERAL,SG_ALERT,
00170 " Volume maximum below minimum. Neglected.");
00171
00172 _volume.push_back(volume);
00173 v += volume.offset;
00174
00175 }
00176
00177
00178 float reference_dist = node->getDoubleValue("reference-dist", 60.0);
00179 float max_dist = node->getDoubleValue("max-dist", 6000.0);
00180
00181
00182
00183
00184 float p = 0.0;
00185 kids = node->getChildren("pitch");
00186 for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) {
00187 _snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false};
00188
00189 if (strcmp(kids[i]->getStringValue("property", ""), ""))
00190 pitch.prop = root->getNode(kids[i]->getStringValue("property", ""), true);
00191
00192 const char *intern_str = kids[i]->getStringValue("internal", "");
00193 if (!strcmp(intern_str, "dt_play"))
00194 pitch.intern = &_dt_play;
00195 else if (!strcmp(intern_str, "dt_stop"))
00196 pitch.intern = &_dt_stop;
00197
00198 if ((pitch.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0)
00199 if (pitch.factor < 0.0) {
00200 pitch.factor = -pitch.factor;
00201 pitch.subtract = true;
00202 }
00203
00204 const char *type_str = kids[i]->getStringValue("type", "");
00205 if ( strcmp(type_str, "") ) {
00206
00207 for (int j=0; __sound_fn[j].fn; j++)
00208 if ( !strcmp(type_str, __sound_fn[j].name) ) {
00209 pitch.fn = __sound_fn[j].fn;
00210 break;
00211 }
00212
00213 if (!pitch.fn)
00214 SG_LOG(SG_GENERAL,SG_INFO,
00215 " Unknown pitch type, default to 'lin'");
00216 }
00217
00218 pitch.offset = kids[i]->getDoubleValue("offset", 1.0);
00219
00220 if ((pitch.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0)
00221 SG_LOG(SG_GENERAL,SG_WARN,
00222 " Pitch minimum value below 0. Forced to 0.");
00223
00224 pitch.max = kids[i]->getDoubleValue("max", 0.0);
00225 if (pitch.max && (pitch.max < pitch.min) )
00226 SG_LOG(SG_GENERAL,SG_ALERT,
00227 " Pitch maximum below minimum. Neglected");
00228
00229 _pitch.push_back(pitch);
00230 p += pitch.offset;
00231 }
00232
00233
00234
00235
00236 SGVec3f offset_pos = SGVec3f::zeros();
00237 SGPropertyNode_ptr prop = node->getChild("position");
00238 if ( prop != NULL ) {
00239 offset_pos[0] = -prop->getDoubleValue("x", 0.0);
00240 offset_pos[1] = -prop->getDoubleValue("y", 0.0);
00241 offset_pos[2] = -prop->getDoubleValue("z", 0.0);
00242 }
00243
00244
00245
00246
00247 SGVec3f dir = SGVec3f::zeros();
00248 float inner = 360.0;
00249 float outer = 360.0;
00250 float outer_gain = 0.0;
00251 prop = node->getChild("orientation");
00252 if ( prop != NULL ) {
00253 dir = SGVec3f(-prop->getFloatValue("x", 0.0),
00254 -prop->getFloatValue("y", 0.0),
00255 -prop->getFloatValue("z", 0.0));
00256 inner = prop->getFloatValue("inner-angle", 360.0);
00257 outer = prop->getFloatValue("outer-angle", 360.0);
00258 outer_gain = prop->getFloatValue("outer-gain", 0.0);
00259 }
00260
00261
00262
00263
00264 if (is_avionics) {
00265 _sgrp = avionics;
00266 } else {
00267 _sgrp = sgrp;
00268 }
00269 _sample = new SGSoundSample( path.c_str(), node->getStringValue("path", ""));
00270 _sample->set_relative_position( offset_pos );
00271 _sample->set_direction( dir );
00272 _sample->set_audio_cone( inner, outer, outer_gain );
00273 _sample->set_reference_dist( reference_dist );
00274 _sample->set_max_dist( max_dist );
00275 _sample->set_volume( v );
00276 _sample->set_pitch( p );
00277 _sgrp->add( _sample, _name );
00278 }
00279
00280 void
00281 SGXmlSound::update (double dt)
00282 {
00283 double curr_value = 0.0;
00284
00285
00286
00287
00288 if (_property)
00289 curr_value = _property->getDoubleValue();
00290
00291
00292
00293
00294
00295
00296 if (
00297 (_condition && !_condition->test()) ||
00298 (!_condition && _property &&
00299 (
00300 !curr_value ||
00301 ( (_mode == SGXmlSound::IN_TRANSIT) && (curr_value == _prev_value) )
00302 )
00303 )
00304 )
00305 {
00306 if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME))
00307 {
00308 if (_sample->is_playing()) {
00309 SG_LOG(SG_GENERAL, SG_DEBUG, "Stopping audio after " << _dt_play
00310 << " sec: " << _name );
00311
00312 _sample->stop();
00313 }
00314
00315 _active = false;
00316 _dt_stop += dt;
00317 _dt_play = 0.0;
00318 } else {
00319 _stopping += dt;
00320 }
00321
00322 return;
00323 }
00324
00325
00326
00327
00328 if (_active && (_mode == SGXmlSound::ONCE)) {
00329
00330 if (!_sample->is_playing()) {
00331 _dt_stop += dt;
00332 _dt_play = 0.0;
00333 } else {
00334 _dt_play += dt;
00335 }
00336
00337 } else {
00338
00339
00340
00341
00342
00343 _dt_play += dt;
00344 _prev_value = curr_value;
00345 _stopping = 0.0;
00346 }
00347
00348 if (_dt_play < _delay)
00349 return;
00350
00351
00352
00353
00354 int i;
00355 int max = _volume.size();
00356 double volume = 1.0;
00357 double volume_offset = 0.0;
00358
00359 for(i = 0; i < max; i++) {
00360 double v = 1.0;
00361
00362 if (_volume[i].prop)
00363 v = _volume[i].prop->getDoubleValue();
00364
00365 else if (_volume[i].intern)
00366 v = *_volume[i].intern;
00367
00368 if (_volume[i].fn)
00369 v = _volume[i].fn(v);
00370
00371 v *= _volume[i].factor;
00372
00373 if (_volume[i].max && (v > _volume[i].max))
00374 v = _volume[i].max;
00375
00376 else if (v < _volume[i].min)
00377 v = _volume[i].min;
00378
00379 if (_volume[i].subtract)
00380 volume = _volume[i].offset - v;
00381
00382 else {
00383 volume_offset += _volume[i].offset;
00384 volume *= v;
00385 }
00386 }
00387
00388
00389
00390
00391 max = _pitch.size();
00392 double pitch = 1.0;
00393 double pitch_offset = 0.0;
00394
00395 for(i = 0; i < max; i++) {
00396 double p = 1.0;
00397
00398 if (_pitch[i].prop)
00399 p = _pitch[i].prop->getDoubleValue();
00400
00401 else if (_pitch[i].intern)
00402 p = *_pitch[i].intern;
00403
00404 if (_pitch[i].fn)
00405 p = _pitch[i].fn(p);
00406
00407 p *= _pitch[i].factor;
00408
00409 if (_pitch[i].max && (p > _pitch[i].max))
00410 p = _pitch[i].max;
00411
00412 else if (p < _pitch[i].min)
00413 p = _pitch[i].min;
00414
00415 if (_pitch[i].subtract)
00416 pitch = _pitch[i].offset - p;
00417
00418 else {
00419 pitch_offset += _pitch[i].offset;
00420 pitch *= p;
00421 }
00422 }
00423
00424
00425
00426
00427
00428 double vol = volume_offset + volume;
00429 if (vol > 1.0) {
00430 SG_LOG(SG_GENERAL, SG_DEBUG, "Sound volume too large for '"
00431 << _name << "': " << vol << " -> clipping to 1.0");
00432 vol = 1.0;
00433 }
00434 _sample->set_volume(vol);
00435 _sample->set_pitch( pitch_offset + pitch );
00436
00437
00438
00439
00440
00441 if (!_active) {
00442
00443 if (_mode == SGXmlSound::ONCE)
00444 _sample->play(false);
00445
00446 else
00447 _sample->play(true);
00448
00449 SG_LOG(SG_GENERAL, SG_DEBUG, "Playing audio after " << _dt_stop
00450 << " sec: " << _name);
00451 SG_LOG(SG_GENERAL, SG_DEBUG,
00452 "Playing " << ((_mode == ONCE) ? "once" : "looped"));
00453
00454 _active = true;
00455 _dt_stop = 0.0;
00456 }
00457 }