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