1 // sample.hxx -- Audio sample encapsulation class 2 // 3 // Written by Curtis Olson, started April 2004. 4 // Modified to match the new SoundSystem by Erik Hofman, October 2009 5 // 6 // Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt 7 // Copyright (C) 2009-2019 Erik Hofman <erik@ehofman.com> 8 // 9 // This program is free software; you can redistribute it and/or 10 // modify it under the terms of the GNU General Public License as 11 // published by the Free Software Foundation; either version 2 of the 12 // License, or (at your option) any later version. 13 // 14 // This program is distributed in the hope that it will be useful, but 15 // WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 // General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with this program; if not, write to the Free Software Foundation, 21 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 // 23 // $Id$ 24 25 /** 26 * \file 27 * Provides a audio sample encapsulation 28 */ 29 30 #ifndef _SG_SAMPLE_HXX 31 #define _SG_SAMPLE_HXX 1 32 33 #include <simgear/math/SGMath.hxx> 34 #include <simgear/props/props.hxx> 35 #include "soundmgr.hxx" 36 37 enum { 38 SG_SAMPLE_MONO = 1, 39 SG_SAMPLE_STEREO = 2, 40 41 SG_SAMPLE_4BITS = 4, 42 SG_SAMPLE_8BITS = 8, 43 SG_SAMPLE_16BITS = 16, 44 45 SG_SAMPLE_UNCOMPRESSED = 0, 46 SG_SAMPLE_COMPRESSED = 256, 47 48 SG_SAMPLE_MONO8 = (SG_SAMPLE_MONO|SG_SAMPLE_8BITS), 49 SG_SAMPLE_MONO16 = (SG_SAMPLE_MONO|SG_SAMPLE_16BITS), 50 SG_SAMPLE_MULAW = (SG_SAMPLE_MONO|SG_SAMPLE_8BITS|SG_SAMPLE_COMPRESSED), 51 SG_SAMPLE_ADPCM = (SG_SAMPLE_MONO|SG_SAMPLE_4BITS|SG_SAMPLE_COMPRESSED), 52 53 SG_SAMPLE_STEREO8 = (SG_SAMPLE_STEREO|SG_SAMPLE_8BITS), 54 SG_SAMPLE_STEREO16 = (SG_SAMPLE_STEREO|SG_SAMPLE_16BITS) 55 }; 56 57 58 /** 59 * manages everything we need to know for an individual audio sample 60 */ 61 62 class SGSoundSampleInfo 63 { 64 public: 65 SGSoundSampleInfo(); ~SGSoundSampleInfo()66 ~SGSoundSampleInfo() {} 67 68 /** 69 * Returns the format of this audio sample. 70 * @return SimGear format-id 71 */ get_format()72 inline unsigned int get_format() { 73 return (_tracks | _bits | _compressed*256); 74 } 75 76 /** 77 * Returns the block alignment of this audio sample. 78 * @return block alignment in bytes 79 */ get_block_align()80 inline unsigned int get_block_align() { 81 return _block_align; 82 } 83 84 /** 85 * Get the reference name of this audio sample. 86 * @return Sample name 87 */ get_sample_name() const88 inline std::string get_sample_name() const { return _refname; } 89 90 /** 91 * Returns the frequency (in Herz) of this audio sample. 92 * @return Frequency 93 */ get_frequency()94 inline unsigned int get_frequency() { return _frequency; } 95 96 /** 97 * Get the current pitch value of this audio sample. 98 * @return Pitch 99 */ get_pitch()100 inline float get_pitch() { return _pitch; } 101 102 /** 103 * Get the final volume value of this audio sample. 104 * @return Volume 105 */ get_volume()106 inline float get_volume() { return _volume * _master_volume; } 107 108 /** 109 * Returns the size (in bytes) of this audio sample. 110 * @return Data size 111 */ get_size() const112 inline size_t get_size() const { 113 return (_samples * _tracks * _bits)/8; 114 } get_no_samples()115 inline size_t get_no_samples() { return _samples; } get_no_tracks()116 inline size_t get_no_tracks() { return _tracks; } 117 118 119 /** 120 * Get the absolute position of this sound. 121 * This is in the same coordinate system as OpenGL; y=up, z=back, x=right. 122 * @return Absolute position 123 */ get_position()124 inline SGVec3d& get_position() { return _absolute_pos; } 125 126 /** 127 * Get the orientation vector of this sound. 128 * This is in the same coordinate system as OpenGL; y=up, z=back, x=right 129 * @return Orientaton vector 130 */ get_orientation()131 inline SGVec3f& get_orientation() { return _orivec; } 132 133 /** 134 * Get the inner angle of the audio cone. 135 * @return Inner angle in degrees 136 */ get_innerangle()137 inline float get_innerangle() { return _inner_angle; } 138 139 /** 140 * Get the outer angle of the audio cone. 141 * @return Outer angle in degrees 142 */ get_outerangle()143 inline float get_outerangle() { return _outer_angle; } 144 145 /** 146 * Get the remaining gain at the edge of the outer cone. 147 * @return Gain 148 */ get_outergain()149 inline float get_outergain() { return _outer_gain; } 150 151 /** 152 * Get velocity vector (in meters per second) of this sound. 153 * This is in the same coordinate system as OpenGL; y=up, z=back, x=right 154 * @return Velocity vector 155 */ get_velocity()156 inline SGVec3f& get_velocity() { return _velocity; } 157 158 /** 159 * Get reference distance ((in meters) of this sound. 160 * This is the distance where the gain will be half. 161 * @return Reference distance 162 */ get_reference_dist()163 inline float get_reference_dist() { return _reference_dist; } 164 165 /** 166 * Get maximum distance (in meters) of this sound. 167 * This is the distance where this sound is no longer audible. 168 * @return Maximum distance 169 */ get_max_dist()170 inline float get_max_dist() { return _max_dist; } 171 172 /** 173 * Get the temperature (in degrees Celsius) at the current altitude. 174 * @return temperature in degrees Celsius 175 */ get_temperature()176 inline float get_temperature() { return _degC; } 177 178 /** 179 * Get the relative humidity at the current altitude. 180 * @return Percent relative humidity (0.0 to 1.0) 181 */ get_humidity()182 inline float get_humidity() { return _humidity; } 183 184 /** 185 * Get the pressure at the current altitude. 186 * @return Pressure in kPa 187 */ get_pressure()188 inline float get_pressure() { return _pressure; } 189 190 /** 191 * Test if static data of audio sample configuration has changed. 192 * Calling this function will reset the flag so calling it a second 193 * time in a row will return false. 194 * @return Return true is the static data has changed in the mean time. 195 */ has_static_data_changed()196 bool has_static_data_changed() { 197 bool b = _static_changed; _static_changed = false; return b; 198 } 199 200 protected: 201 // static sound emitter info 202 std::string _refname; 203 unsigned int _bits = 16; 204 unsigned int _tracks = 1; 205 unsigned int _samples = 0; 206 unsigned int _frequency = 22500; 207 unsigned int _block_align = 2; 208 bool _compressed = false; 209 bool _loop = false; 210 211 // dynamic sound emitter info (non 3d) 212 bool _static_changed = true; 213 bool _playing = false; 214 215 float _pitch = 1.0f; 216 float _volume = 1.0f; 217 float _master_volume = 1.0f; 218 219 // dynamic sound emitter info (3d) 220 bool _use_pos_props = false; 221 bool _out_of_range = false; 222 223 float _inner_angle = 360.0f; 224 float _outer_angle = 360.0f; 225 float _outer_gain = 0.0f; 226 227 float _reference_dist = 500.0f; 228 float _max_dist = 3000.0f; 229 float _pressure = 101.325f; 230 float _humidity = 0.5f; 231 float _degC = 20.0f; 232 233 SGPropertyNode_ptr _pos_prop[3]; 234 SGVec3d _absolute_pos; // absolute position 235 SGVec3d _relative_pos; // position relative to the base position 236 SGVec3d _direction; // orientation offset 237 SGVec3f _velocity; // Velocity of the source sound. 238 239 // The position and orientation of this sound 240 SGQuatd _orientation; // base orientation 241 SGVec3f _orivec; // orientation vector 242 SGVec3d _base_pos; // base position 243 244 SGQuatd _rotation; 245 246 private: 247 static std::string random_string(); 248 }; 249 250 251 class SGSoundSample : public SGSoundSampleInfo, public SGReferenced { 252 public: 253 254 /** 255 * Empty constructor, can be used to read data to the systems 256 * memory and not to the driver. 257 */ 258 SGSoundSample() = default; 259 virtual ~SGSoundSample () = default; 260 261 /** 262 * Constructor 263 * @param file File name of sound 264 Buffer data is freed by the sample group 265 */ 266 SGSoundSample(const char *file, const SGPath& currentDir); 267 268 /** 269 * Constructor. 270 * @param data Pointer to a memory buffer containing this audio sample data 271 The application may free the data by calling free_data(), otherwise it 272 will be resident until the class is destroyed. This pointer will be 273 set to nullptr after calling this function. 274 * @param len Byte length of array 275 * @param freq Frequency of the provided data (bytes per second) 276 * @param format SimGear format id of the data 277 */ 278 SGSoundSample( std::unique_ptr<unsigned char, decltype(free)*>& data, 279 int len, int freq, 280 int format = SG_SAMPLE_MONO8 ); 281 282 /** 283 * Test if this audio sample configuration has changed since the last call. 284 * Calling this function will reset the flag so calling it a second 285 * time in a row will return false. 286 * @return Return true is the configuration has changed in the mean time. 287 */ has_changed()288 bool has_changed() { 289 bool b = _changed; _changed = false; return b; 290 } 291 292 /** 293 * Detect whether this audio sample holds the information of a sound file. 294 * @return Return true if this sample is to be constructed from a file. 295 */ is_file() const296 inline bool is_file() const { return _is_file; } 297 298 SGPath file_path() const; 299 300 /** 301 * Schedule this audio sample for playing. Actual playing will only start 302 * at the next call op SoundGroup::update() 303 * @param _loop Define whether this sound should be played in a loop. 304 */ play(bool loop=false)305 void play( bool loop = false ) { 306 _playing = true; _loop = loop; _changed = true; _static_changed = true; 307 } 308 309 /** 310 * Check if this audio sample is set to be continuous looping. 311 * @return Return true if this audio sample is set to looping. 312 */ is_looping()313 inline bool is_looping() { return _loop; } 314 315 /** 316 * Schedule this audio sample to stop playing. 317 */ stop()318 virtual void stop() { 319 _playing = false; _changed = true; 320 } 321 322 /** 323 * Schedule this audio sample to play once. 324 * @see #play 325 */ play_once()326 inline void play_once() { play(false); } 327 328 /** 329 * Schedule this audio sample to play looped. 330 * @see #play 331 */ play_looped()332 inline void play_looped() { play(true); } 333 334 /** 335 * Test if a audio sample is scheduled for playing. 336 * @return true if this audio sample is playing, false otherwise. 337 */ is_playing()338 inline bool is_playing() { return _playing; } 339 340 341 /** 342 * Set this sample to out-of-range (or not) and 343 * Schedule this audio sample to stop (or start) playing. 344 */ set_out_of_range(bool oor=true)345 inline void set_out_of_range(bool oor = true) { 346 _out_of_range = oor; _playing = (!oor && _loop); _changed = true; 347 } 348 349 /** 350 * Test if this sample to out-of-range or not. 351 */ test_out_of_range()352 inline bool test_out_of_range() { 353 return _out_of_range; 354 } 355 356 /** 357 * Set the data associated with this audio sample 358 * @param data Pointer to a memory block containg this audio sample data. 359 This pointer will be set to nullptr after calling this function. 360 */ set_data(std::unique_ptr<unsigned char,decltype(free)* > & data)361 inline void set_data( std::unique_ptr<unsigned char, decltype(free)*>& data ) { 362 _data = std::move(data); 363 } 364 365 /** 366 * Return the data associated with this audio sample. 367 * @return A pointer to this sound data of this audio sample. 368 */ get_data() const369 inline unsigned char* get_data() const { return _data.get(); } 370 371 /** 372 * Free the data associated with this audio sample 373 */ free_data()374 inline void free_data() { _data = nullptr; } 375 376 /** 377 * Set the source id of this source 378 * @param sid source-id 379 */ set_source(unsigned int sid)380 virtual void set_source(unsigned int sid) { 381 _source = sid; _valid_source = true; _changed = true; 382 } 383 384 /** 385 * Get the source id of this source 386 * @return source-id 387 */ get_source()388 virtual unsigned int get_source() { return _source; } 389 390 /** 391 * Test if the source-id of this audio sample is usable. 392 * @return true if the source-id is valid 393 */ is_valid_source() const394 virtual bool is_valid_source() const { return _valid_source; } 395 396 /** 397 * Set the source-id of this audio sample to invalid. 398 */ no_valid_source()399 virtual void no_valid_source() { _valid_source = false; } 400 401 /** 402 * Set the buffer-id of this source 403 * @param bid buffer-id 404 */ set_buffer(unsigned int bid)405 inline void set_buffer(unsigned int bid) { 406 _buffer = bid; _valid_buffer = true; _changed = true; 407 } 408 409 /** 410 * Get the buffer-id of this source 411 * @return buffer-id 412 */ get_buffer()413 inline unsigned int get_buffer() { return _buffer; } 414 415 /** 416 * Test if the buffer-id of this audio sample is usable. 417 * @return true if the buffer-id is valid 418 */ is_valid_buffer() const419 inline bool is_valid_buffer() const { return _valid_buffer; } 420 421 /** 422 * Set the buffer-id of this audio sample to invalid. 423 */ no_valid_buffer()424 inline void no_valid_buffer() { _valid_buffer = false; } 425 426 /** 427 * Set the playback pitch of this audio sample. 428 * Should be between 0.0 and 2.0 for maximum compatibility. 429 * @param p Pitch 430 */ set_pitch(float p)431 inline void set_pitch( float p ) { 432 if (p > 2.0) p = 2.0; else if (p < 0.01) p = 0.01; 433 _pitch = p; _changed = true; 434 } 435 436 /** 437 * Set the master volume of this sample. Should be between 0.0 and 1.0. 438 * The final volume is calculated by multiplying the master and audio sample 439 * volume. 440 * @param v Volume 441 */ set_master_volume(float v)442 inline void set_master_volume( float v ) { 443 if (v > 1.0) v = 1.0; else if (v < 0.0) v = 0.0; 444 _master_volume = v; _changed = true; 445 } 446 447 /** 448 * Set the volume of this audio sample. Should be between 0.0 and 1.0. 449 * The final volume is calculated by multiplying the master and audio sample 450 * volume. 451 * @param v Volume 452 */ set_volume(float v)453 inline void set_volume( float v ) { 454 if (v > 1.0) v = 1.0; else if (v < 0.0) v = 0.0; 455 _volume = v; _changed = true; 456 } 457 458 /** 459 * Set the SimGear format of this audio sample. 460 * @param format SimGear format-id 461 */ set_format(int fmt)462 inline void set_format( int fmt ) { 463 _tracks = fmt & 0x3; _bits = fmt & 0x1C; _compressed = fmt & 0x100; 464 } 465 set_bits_sample(unsigned int b)466 inline void set_bits_sample( unsigned int b ) { _bits = b; } set_no_tracks(unsigned int t)467 inline void set_no_tracks( unsigned int t ) { _tracks = t; } set_compressed(bool c)468 inline void set_compressed( bool c ) { _compressed = c; } 469 470 /** 471 * Set the block alignament for compressed audio. 472 * @param block the block alignment in bytes 473 */ set_block_align(int block)474 inline void set_block_align( int block ) { 475 _block_align = block; 476 } 477 478 /** 479 * Set the frequency (in Herz) of this audio sample. 480 * @param freq Frequency 481 */ set_frequency(int freq)482 inline void set_frequency( int freq ) { _frequency = freq; } 483 484 /** 485 * Sets the size (in bytes) of this audio sample. 486 * @param size Data size 487 */ set_size(size_t size)488 inline void set_size( size_t size ) { 489 _samples = size*8/(_bits*_tracks); 490 } set_no_samples(size_t samples)491 inline void set_no_samples(size_t samples) { _samples = samples; } 492 493 /** 494 * Set the position of this sound relative to the base position. 495 * This is in the same coordinate system as OpenGL; y=up, z=back, x=right. 496 * @param pos Relative position of this sound 497 */ set_relative_position(const SGVec3f & pos)498 inline void set_relative_position( const SGVec3f& pos ) { 499 _relative_pos = toVec3d(pos); _changed = true; 500 } 501 502 /** 503 * Set the base position in Cartesian coordinates 504 * @param pos position in Cartesian coordinates 505 */ set_position(const SGVec3d & pos)506 inline void set_position( const SGVec3d& pos ) { 507 _base_pos = pos; _changed = true; 508 } 509 set_position_properties(SGPropertyNode_ptr pos[3])510 inline void set_position_properties(SGPropertyNode_ptr pos[3]) { 511 _pos_prop[0] = pos[0]; _pos_prop[1] = pos[1]; _pos_prop[2] = pos[2]; 512 if (pos[0] || pos[1] || pos[2]) _use_pos_props = true; 513 _changed = true; 514 } 515 516 /** 517 * Set the orientation of this sound. 518 * @param ori Quaternation containing the orientation information 519 */ set_orientation(const SGQuatd & ori)520 inline void set_orientation( const SGQuatd& ori ) { 521 _orientation = ori; _changed = true; 522 } 523 set_rotation(const SGQuatd & ec2body)524 inline void set_rotation( const SGQuatd& ec2body ) { 525 _rotation = ec2body; _changed = true; 526 } 527 528 /** 529 * Set direction of this sound relative to the orientation. 530 * This is in the same coordinate system as OpenGL; y=up, z=back, x=right 531 * @param dir Sound emission direction 532 */ set_direction(const SGVec3f & dir)533 inline void set_direction( const SGVec3f& dir ) { 534 _direction = toVec3d(dir); _static_changed = true; 535 } 536 537 /** 538 * Define the audio cone parameters for directional audio. 539 * Note: setting it to 2 degree will result in 1 degree to both sides. 540 * @param inner Inner cone angle (0 - 360 degrees) 541 * @param outer Outer cone angle (0 - 360 degrees) 542 * @param gain Remaining gain at the edge of the outer cone (0.0 - 1.0) 543 */ set_audio_cone(float inner,float outer,float gain)544 void set_audio_cone( float inner, float outer, float gain ) { 545 _inner_angle = inner; _outer_angle = outer; _outer_gain = gain; 546 _static_changed = true; 547 } 548 549 /** 550 * Set the velocity vector (in meters per second) of this sound. 551 * This is in the local frame coordinate system; x=north, y=east, z=down 552 * @param Velocity vector 553 */ set_velocity(const SGVec3f & vel)554 inline void set_velocity( const SGVec3f& vel ) { 555 _velocity = vel; _changed = true; 556 } 557 558 /** 559 * Set both the temperature and relative humidity at the current altitude. 560 * @param t Temperature in degrees Celsius 561 * @param h Percent relative humidity (0.0 to 1.0) 562 * @param p Pressure in kPa; 563 */ set_atmosphere(float t,float h,float p)564 inline void set_atmosphere(float t, float h, float p) { 565 if (fabsf(_degC - t) > 1.0f || fabsf(_humidity - h) > 0.1f || 566 fabsf(_pressure - p) > 1.0f) 567 { 568 _degC = t, _humidity = h; _pressure = p; _static_changed = true; 569 } 570 } 571 572 /** 573 * Set reference distance (in meters) of this sound. 574 * This is the distance where the gain will be half. 575 * @param dist Reference distance 576 */ set_reference_dist(float dist)577 inline void set_reference_dist( float dist ) { 578 _reference_dist = dist; _static_changed = true; 579 } 580 581 /** 582 * Set maximum distance (in meters) of this sound. 583 * This is the distance where this sound is no longer audible. 584 * @param dist Maximum distance 585 */ set_max_dist(float dist)586 inline void set_max_dist( float dist ) { 587 _max_dist = dist; _static_changed = true; 588 } 589 is_queue() const590 inline virtual bool is_queue() const { return false; } 591 592 void update_pos_and_orientation(); 593 594 protected: 595 bool _is_file = false; 596 bool _changed = true; 597 598 // Sources are points emitting sound. 599 bool _valid_source = false; 600 unsigned int _source = SGSoundMgr::NO_SOURCE; 601 602 private: 603 std::unique_ptr<unsigned char, decltype(free)*> _data = { nullptr, free }; 604 605 // Buffers hold sound data. 606 bool _valid_buffer = false; 607 unsigned int _buffer = SGSoundMgr::NO_BUFFER; 608 }; 609 610 #endif // _SG_SAMPLE_HXX 611 612 613