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)); }
00045 static double _snd_log(double v) { return log(fabs(v)); }
00046
00047
00048
00049 static const struct {
00050 char *name;
00051 double (*fn)(double);
00052 } __sound_fn[] = {
00053
00054 {"inv", _snd_inv},
00055 {"abs", _snd_abs},
00056 {"sqrt", _snd_sqrt},
00057 {"log", _snd_log10},
00058 {"ln", _snd_log},
00059
00060
00061 {"", NULL}
00062 };
00063
00064 SGXmlSound::SGXmlSound()
00065 : _sample(NULL),
00066 _condition(NULL),
00067 _active(false),
00068 _name(""),
00069 _mode(SGXmlSound::ONCE),
00070 _prev_value(0),
00071 _dt_play(0.0),
00072 _dt_stop(0.0),
00073 _stopping(0.0)
00074 {
00075 }
00076
00077 SGXmlSound::~SGXmlSound()
00078 {
00079 if (_sample)
00080 _sample->stop();
00081
00082 delete _condition;
00083
00084 _volume.clear();
00085 _pitch.clear();
00086 }
00087
00088 void
00089 SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr,
00090 const string &path)
00091 {
00092
00093
00094
00095
00096
00097 _name = node->getStringValue("name", "");
00098 SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name );
00099
00100 const char *mode_str = node->getStringValue("mode", "");
00101 if ( !strcmp(mode_str, "looped") ) {
00102 _mode = SGXmlSound::LOOPED;
00103
00104 } else if ( !strcmp(mode_str, "in-transit") ) {
00105 _mode = SGXmlSound::IN_TRANSIT;
00106
00107 } else {
00108 _mode = SGXmlSound::ONCE;
00109
00110 if ( strcmp(mode_str, "") )
00111 SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'");
00112 }
00113
00114 _property = root->getNode(node->getStringValue("property", ""), true);
00115 SGPropertyNode *condition = node->getChild("condition");
00116 if (condition != NULL)
00117 _condition = sgReadCondition(root, condition);
00118
00119 if (!_property && !_condition)
00120 SG_LOG(SG_GENERAL, SG_WARN,
00121 " Neither a condition nor a property specified");
00122
00123
00124
00125
00126 unsigned int i;
00127 float v = 0.0;
00128 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 float reference_dist = node->getDoubleValue("reference-dist", 500.0);
00178 float max_dist = node->getDoubleValue("max-dist", 3000.0);
00179
00180
00181
00182
00183 float p = 0.0;
00184 kids = node->getChildren("pitch");
00185 for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) {
00186 _snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false};
00187
00188 if (strcmp(kids[i]->getStringValue("property", ""), ""))
00189 pitch.prop = root->getNode(kids[i]->getStringValue("property", ""), true);
00190
00191 const char *intern_str = kids[i]->getStringValue("internal", "");
00192 if (!strcmp(intern_str, "dt_play"))
00193 pitch.intern = &_dt_play;
00194 else if (!strcmp(intern_str, "dt_stop"))
00195 pitch.intern = &_dt_stop;
00196
00197 if ((pitch.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0)
00198 if (pitch.factor < 0.0) {
00199 pitch.factor = -pitch.factor;
00200 pitch.subtract = true;
00201 }
00202
00203 const char *type_str = kids[i]->getStringValue("type", "");
00204 if ( strcmp(type_str, "") ) {
00205
00206 for (int j=0; __sound_fn[j].fn; j++)
00207 if ( !strcmp(type_str, __sound_fn[j].name) ) {
00208 pitch.fn = __sound_fn[j].fn;
00209 break;
00210 }
00211
00212 if (!pitch.fn)
00213 SG_LOG(SG_GENERAL,SG_INFO,
00214 " Unknown pitch type, default to 'lin'");
00215 }
00216
00217 pitch.offset = kids[i]->getDoubleValue("offset", 1.0);
00218
00219 if ((pitch.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0)
00220 SG_LOG(SG_GENERAL,SG_WARN,
00221 " Pitch minimum value below 0. Forced to 0.");
00222
00223 pitch.max = kids[i]->getDoubleValue("max", 0.0);
00224 if (pitch.max && (pitch.max < pitch.min) )
00225 SG_LOG(SG_GENERAL,SG_ALERT,
00226 " Pitch maximum below minimum. Neglected");
00227
00228 _pitch.push_back(pitch);
00229 p += pitch.offset;
00230 }
00231
00232
00233
00234
00235 sgVec3 offset_pos;
00236 sgSetVec3( offset_pos, 0.0, 0.0, 0.0 );
00237 SGPropertyNode_ptr pos = node->getChild("position");
00238 if ( pos != NULL ) {
00239 offset_pos[0] = pos->getDoubleValue("x", 0.0);
00240 offset_pos[1] = -pos->getDoubleValue("y", 0.0);
00241 offset_pos[2] = pos->getDoubleValue("z", 0.0);
00242 }
00243
00244
00245
00246
00247 sgVec3 dir;
00248 float inner, outer, outer_gain;
00249 sgSetVec3( dir, 0.0, 0.0, 0.0 );
00250 inner = outer = 360.0;
00251 outer_gain = 0.0;
00252 pos = node->getChild("orientation");
00253 if ( pos != NULL ) {
00254 dir[0] = pos->getDoubleValue("x", 0.0);
00255 dir[1] = -pos->getDoubleValue("y", 0.0);
00256 dir[2] = pos->getDoubleValue("z", 0.0);
00257 inner = pos->getDoubleValue("inner-angle", 360.0);
00258 outer = pos->getDoubleValue("outer-angle", 360.0);
00259 outer_gain = pos->getDoubleValue("outer-gain", 0.0);
00260 }
00261
00262
00263
00264
00265 _mgr = sndmgr;
00266 if ( (_sample = _mgr->find(_name)) == NULL ) {
00267
00268
00269
00270
00271
00272
00273
00274 _sample = new SGSoundSample( path.c_str(),
00275 node->getStringValue("path", ""),
00276 false );
00277
00278 _mgr->add( _sample, _name );
00279 }
00280
00281 _sample->set_offset_pos( offset_pos );
00282 _sample->set_orientation(dir, inner, outer, outer_gain);
00283 _sample->set_volume(v);
00284 _sample->set_reference_dist( reference_dist );
00285 _sample->set_max_dist( max_dist );
00286 _sample->set_pitch(p);
00287 }
00288
00289 void
00290 SGXmlSound::update (double dt)
00291 {
00292 double curr_value = 0.0;
00293
00294
00295
00296
00297 if (_property)
00298 curr_value = _property->getDoubleValue();
00299
00300 if (
00301 (_condition && !_condition->test()) ||
00302 (!_condition && _property &&
00303 (
00304 !curr_value ||
00305 ( (_mode == SGXmlSound::IN_TRANSIT) && (curr_value == _prev_value) )
00306 )
00307 )
00308 )
00309 {
00310 if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) {
00311 if (_sample->is_playing()) {
00312 SG_LOG(SG_GENERAL, SG_INFO, "Stopping audio after " << _dt_play
00313 << " sec: " << _name );
00314
00315 _sample->stop();
00316 }
00317
00318 _active = false;
00319 _dt_stop += dt;
00320 _dt_play = 0.0;
00321 } else {
00322 _stopping += dt;
00323 }
00324
00325 return;
00326 }
00327
00328
00329
00330
00331
00332 if (_active && (_mode == SGXmlSound::ONCE)) {
00333
00334 if (!_sample->is_playing()) {
00335 _dt_stop += dt;
00336 _dt_play = 0.0;
00337 } else {
00338 _dt_play += dt;
00339 }
00340
00341 return;
00342 }
00343
00344
00345
00346
00347
00348 _dt_play += dt;
00349 _prev_value = curr_value;
00350 _stopping = 0.0;
00351
00352
00353
00354
00355 int i;
00356 int max = _volume.size();
00357 double volume = 1.0;
00358 double volume_offset = 0.0;
00359
00360 for(i = 0; i < max; i++) {
00361 double v = 1.0;
00362
00363 if (_volume[i].prop)
00364 v = _volume[i].prop->getDoubleValue();
00365
00366 else if (_volume[i].intern)
00367 v = *_volume[i].intern;
00368
00369 if (_volume[i].fn)
00370 v = _volume[i].fn(v);
00371
00372 v *= _volume[i].factor;
00373
00374 if (_volume[i].max && (v > _volume[i].max))
00375 v = _volume[i].max;
00376
00377 else if (v < _volume[i].min)
00378 v = _volume[i].min;
00379
00380 if (_volume[i].subtract)
00381 volume = _volume[i].offset - v;
00382
00383 else {
00384 volume_offset += _volume[i].offset;
00385 volume *= v;
00386 }
00387 }
00388
00389
00390
00391
00392 max = _pitch.size();
00393 double pitch = 1.0;
00394 double pitch_offset = 0.0;
00395
00396 for(i = 0; i < max; i++) {
00397 double p = 1.0;
00398
00399 if (_pitch[i].prop)
00400 p = _pitch[i].prop->getDoubleValue();
00401
00402 else if (_pitch[i].intern)
00403 p = *_pitch[i].intern;
00404
00405 if (_pitch[i].fn)
00406 p = _pitch[i].fn(p);
00407
00408 p *= _pitch[i].factor;
00409
00410 if (_pitch[i].max && (p > _pitch[i].max))
00411 p = _pitch[i].max;
00412
00413 else if (p < _pitch[i].min)
00414 p = _pitch[i].min;
00415
00416 if (_pitch[i].subtract)
00417 pitch = _pitch[i].offset - p;
00418
00419 else {
00420 pitch_offset += _pitch[i].offset;
00421 pitch *= p;
00422 }
00423 }
00424
00425
00426
00427
00428
00429 double vol = volume_offset + volume;
00430 if (vol > 1.0) {
00431 SG_LOG(SG_GENERAL, SG_WARN, "Sound volume too large for '"
00432 << _name << "': " << vol << " -> clipping to 1.0");
00433 vol = 1.0;
00434 }
00435 _sample->set_volume(vol);
00436 _sample->set_pitch( pitch_offset + pitch );
00437
00438
00439
00440
00441
00442 if (!_active) {
00443
00444 if (_mode == SGXmlSound::ONCE)
00445 _sample->play(false);
00446
00447 else
00448 _sample->play(true);
00449
00450 SG_LOG(SG_GENERAL, SG_INFO, "Playing audio after " << _dt_stop
00451 << " sec: " << _name);
00452 SG_LOG(SG_GENERAL, SG_BULK,
00453 "Playing " << ((_mode == ONCE) ? "once" : "looped"));
00454
00455 _active = true;
00456 _dt_stop = 0.0;
00457 }
00458 }