Sound.cpp 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. 'SoundCallback' needs to have its 'del' method called.
  6. When accessing 'Sound.sound' member always check 'SoundFunc' too,
  7. because after 'ShutSound' all '_Sound' have been deleted, and pointers are no longer valid.
  8. If "_Sound.stream_loaded==false" then '_Sound._stream' may be loaded from any thread using '_Sound.stream()' method.
  9. If "_Sound.stream_loaded==true" then '_Sound._stream' can be read+write on sound threads and read (but only simple methods) on other threads.
  10. '_Sound._buffer' can be used only on sound threads.
  11. '_Sound._buffer._par' can be different than '_Sound._stream._par'
  12. -channels can be different for OpenAL 3D (needs downmixing)
  13. -size for _buffer means buffer size, while size for _stream means stream size
  14. '_Sound._name' cannot be deleted because it can be accessed on other threads.
  15. 'SoundMemx' can be used on all threads so it needs 'SoundMemxLock', however on non-sound threads only New elements can be created.
  16. 'SoundMemxPlaying' can be used only on sound threads.
  17. Methods with "!! requires 'SoundAPILock' !!" comment need to be surrounded by a 'SoundAPILock' lock.
  18. Main Thread operates on Sound.time (main), raw , frac. Time was chosen to be the main value because it's most commonly used on the user side, its usage does not require knowning about the SoundStream.
  19. Sound Threads operate on _Sound.raw (main), time, frac. Raw was chosen to be the main value because sound buffer data operate on it.
  20. However, when _Sound is not playing ("_buffer_playing==false"), then _Sound operates on '_Sound._time'.
  21. At the moment, 'VOLUME_GROUP' and 'SoundVolume' multipliers don't contribute to Sound priority,
  22. to avoid extra calculations.
  23. /******************************************************************************/
  24. ALIGN_ASSERT(_Sound, _callback); // must have native alignment because we use it in atomic operations for get/set on multiple threads
  25. #define DEL_BUFFER_WHEN_NOT_PLAYING 1 // 0 is not fully implemented so don't use
  26. #define SOUND_API_THREAD_SAFE (DIRECT_SOUND || XAUDIO || OPEN_AL) // DirectSound, XAudio, OpenAL are thread-safe, OpenSL is thread-safe only with SL_ENGINEOPTION_THREADSAFE flag enabled which however is not used in Esenthel since thread-safety is handled manually
  27. #define UPDATE_2X WINDOWS_NEW // on WINDOWS_NEW we have to operate 2x faster due to low-latency sound recording, TODO: can this be improved so we don't have to call 2x more frequently? perhaps do sound recording on a separate dedicated thread
  28. #if SOUND_API_THREAD_SAFE
  29. #define SOUND_API_LOCK
  30. #define SOUND_API_LOCK_COND
  31. #define SOUND_API_LOCK_OFF
  32. #define SOUND_API_LOCK_SET
  33. #else
  34. static Bool SoundAPILockDo; // if locking should be done
  35. #define SOUND_API_LOCK SyncLockerEx locker(SoundAPILock)
  36. #define SOUND_API_LOCK_COND SyncLockerEx locker(SoundAPILock, SoundAPILockDo)
  37. #define SOUND_API_LOCK_OFF locker.off()
  38. #define SOUND_API_LOCK_SET locker.set(SoundAPILockDo)
  39. #endif
  40. #if XAUDIO
  41. static Int ListenerChanged;
  42. #elif OPEN_AL
  43. static Mems< Mems<Byte> > SoundThreadBuffer;
  44. #endif
  45. enum
  46. {
  47. SOUND_PAUSED =1<<0,
  48. SOUND_WANT_PAUSED=1<<1,
  49. SOUND_WAIT =1<<2, // temporarily wait (special request used when wanting to pause sound thread until finished some task)
  50. };
  51. enum
  52. {
  53. SOUND_LOAD_OK,
  54. SOUND_LOAD_EMPTY,
  55. SOUND_LOAD_ERROR,
  56. };
  57. static Memx<_Sound > SoundMemx;
  58. static Memc<_Sound*> SoundMemxPlaying;
  59. static Thread SoundThread;
  60. static Threads SoundThreads;
  61. static SyncEvent SoundEvent, SoundWaiting;
  62. static Bool SoundWaited;
  63. static Int SoundPause, SoundMaxConc=16, SoundMaxThread=1;
  64. static Flt SoundMinVol=0.020f, // min volume required for a sound to play, this value was chosen by playing music at half volume, and then playing a sound with different volumes, the value was chosen at which the sound couldn't be heard
  65. SoundPlayPriority=0.012f, // if a sound is already playing, then increase its priority to avoid playing/stopping due to minor priority differences which causes overhead
  66. SoundGroupPriority[VOLUME_NUM];
  67. static Dbl SoundTime; // time of the last sound update
  68. SoundVolumeClass SoundVolume;
  69. SyncLock SoundAPILock, SoundMemxLock, SoundStreamLock, SoundWait;
  70. Flt SoundMinVolume () {return SoundMinVol ;} void SoundMinVolume (Flt volume) {SoundMinVol =Sat(volume);}
  71. Int SoundMaxConcurrent() {return SoundMaxConc ;} void SoundMaxConcurrent(Int max ) {SoundMaxConc = max ;}
  72. Int SoundMaxThreads () {return SoundMaxThread;} void SoundMaxThreads (Int max ) {SoundMaxThread= max ;}
  73. /******************************************************************************/
  74. // SOUND VOLUME CONTROL
  75. /******************************************************************************/
  76. SoundVolumeClass::SoundVolumeClass() {REPAO(_v)=1;}
  77. void SoundVolumeClass::fx (Flt v) {SAT(v); if(_v[VOLUME_FX ]!=v){_v[VOLUME_FX ]=v; update();}}
  78. void SoundVolumeClass::music (Flt v) {SAT(v); if(_v[VOLUME_MUSIC ]!=v){_v[VOLUME_MUSIC ]=v; update();}}
  79. void SoundVolumeClass::ambient(Flt v) {SAT(v); if(_v[VOLUME_AMBIENT]!=v){_v[VOLUME_AMBIENT]=v; update();}}
  80. void SoundVolumeClass::voice (Flt v) {SAT(v); if(_v[VOLUME_VOICE ]!=v){_v[VOLUME_VOICE ]=v; update();}}
  81. void SoundVolumeClass::ui (Flt v) {SAT(v); if(_v[VOLUME_UI ]!=v){_v[VOLUME_UI ]=v; update();}}
  82. void SoundVolumeClass::global (Flt v) {SAT(v); if(_v[VOLUME_NUM ]!=v){_v[VOLUME_NUM ]=v; update();}}
  83. void SoundVolumeClass::update ( ) {VolumeSound();}
  84. /******************************************************************************/
  85. // _SOUND
  86. /******************************************************************************/
  87. void _Sound::zero()
  88. {
  89. //stream_loaded=false; this is set only in constructor, don't clear it here to avoid unnecessary stream reloads
  90. //deleted=false; this is set only in constructor, don't clear it here because we need to know if it got deleted
  91. //flag=0; this is set only in constructor, don't clear it here in case 'Sound' has set 'SOUND_REMOVE', if this would get called due to an error during sound update then it would get cleared, we also need to keep SOUND_NO_REF
  92. _playing=_buffer_playing=_loop=_is3D=false;
  93. last_buffer=0xFF;
  94. kill=0;
  95. volume_group=VOLUME_FX;
  96. _fade_curve=FADE_LINEAR;
  97. raw_pos=0;
  98. _volume=_speed=_actual_speed=_range=_fade=1;
  99. _fade_d=_time=0;
  100. priority=-1;
  101. _pos=_vel.zero();
  102. _callback=null;
  103. }
  104. void _Sound::del() // !! requires 'SoundAPILock' !!
  105. {
  106. deleted=true; // set this first in case other threads access this '_Sound'
  107. _buffer.del(); // !! requires 'SoundAPILock' !! delete the buffer first
  108. // we can delete the stream only because we're accessing simple members from it
  109. if(stream_loaded)_stream.del();else // if 'stream_loaded' then we can access it on this thread
  110. { // otherwise we need to use 'SoundStreamLock' in case it's being loaded on secondary thread
  111. SyncLocker locker(SoundStreamLock); _stream.del(); // delete always to clear '_stream._callback'
  112. }
  113. // don't delete the '_name' because we may be accessing it on a secondary thread (for both get 'name' method and stream load)
  114. zero();
  115. if(noRef())flag|=SOUND_REMOVE; // if there are no references to this sound then we can remove it, no need for 'AtomicOr' since it won't be processed by other thread
  116. }
  117. /******************************************************************************/
  118. void _Sound::init(C Str &name, SoundCallback *call, Bool is3D, VOLUME_GROUP volume_group) // !! warning: 'name' can be 'T._name' !!
  119. {
  120. T._name=name;
  121. T._is3D=is3D;
  122. T._stream._callback=call;
  123. T.volume_group=(VOLUME_GROUP)Mid(volume_group, 0, VOLUME_NUM-1); // clamp it just in case because later we're using it as array index
  124. setSpeed(); // !! call this after setting 'volume_group' because that affects the result !!
  125. }
  126. Bool _Sound::init(C _Sound &src)
  127. {
  128. init(src.name(), src._stream._callback, src.is3D(), src.volume_group);
  129. _fade =src._fade;
  130. _fade_d =src._fade_d;
  131. _fade_curve=src._fade_curve;
  132. _callback=src._callback;
  133. loop (src.loop ());
  134. time (src.time ());
  135. volume(src.volume());
  136. speed (src.speed ());
  137. range (src.range ());
  138. pos (src.pos ());
  139. vel (src.vel ());
  140. Bool playing=src.playing(); // copy before checking if deleted
  141. if(src.deleted)return false; // !! it is important to check if the sound got deleted after copying all parameters, which may mean that some of them were not set fully !!
  142. if(playing)play(); // call this at the end
  143. return true;
  144. }
  145. /******************************************************************************/
  146. // OPERATIONS
  147. /******************************************************************************/
  148. Flt _Sound::actualSpeed()C
  149. {
  150. Flt speed=T._speed;
  151. if(volume_group!=VOLUME_MUSIC && volume_group!=VOLUME_AMBIENT && volume_group!=VOLUME_UI)speed*=Time.speed();
  152. return speed;
  153. }
  154. void _Sound::setVolume() // !! requires 'SoundAPILock' !!
  155. {
  156. Flt volume=T._volume*SoundVolume._v[volume_group]*SoundVolume.global(); // adjust the volume by fade and global modifiers
  157. switch(_fade_curve)
  158. {
  159. case FADE_LINEAR: volume*= _fade ; break;
  160. case FADE_SQRT : volume*=SqrtFast(_fade); break; // can use sqrt fast because '_fade' always guaranteed to be 0..1
  161. }
  162. #if !WINDOWS_OLD
  163. if(AppVolume.muteFinal())volume=0;else volume*=AppVolume.volume(); // non Windows platforms don't support 'AppVolume' directly, so let's simulate it here
  164. #endif
  165. _buffer.volume(volume); // !! requires 'SoundAPILock' !!
  166. }
  167. void _Sound::setSpeed()
  168. {
  169. _actual_speed=actualSpeed();
  170. }
  171. /******************************************************************************/
  172. void _Sound::stop () {_playing=false; time(0);}
  173. void _Sound::pause() {_playing=false;}
  174. void _Sound::play () {_playing=true ;}
  175. /******************************************************************************/
  176. // GET / SET
  177. /******************************************************************************/
  178. void _Sound::memAddressChanged()
  179. {
  180. {
  181. SyncLocker locker(SoundStreamLock);
  182. _stream.memAddressChanged();
  183. }
  184. _buffer.memAddressChanged(); // this handles locking on its own
  185. }
  186. SoundStream& _Sound::stream() // !! this may be called on the main thread !!
  187. {
  188. if(!stream_loaded && !deleted) // don't load if already deleted
  189. {
  190. if(_name.is())
  191. {
  192. SoundStream temp; temp.create(_name); // load to temporary first to minimize time needed for Lock (in worst case we'll just load # times on # threads, but most likely this won't happen)
  193. temp.fastSeek(); // for Sound playback enable fast seeking
  194. if(!stream_loaded)
  195. {
  196. {
  197. SyncLocker locker(SoundStreamLock); if(!stream_loaded)
  198. {
  199. Swap(temp, _stream);
  200. _stream.memAddressChanged();
  201. stream_loaded=true; // set as last
  202. }
  203. }
  204. temp.memAddressChanged(); // 'temp' may have been swapped above, but call this here outside of the lock to minimize the lock time, call this in case it's needed for proper shut down
  205. }
  206. }else // for callback we always need to use Lock because we're storing it in '_stream._callback' for simplicity
  207. {
  208. SyncLocker locker(SoundStreamLock); if(!stream_loaded)
  209. {
  210. if(_stream._callback)_stream.create(*_stream._callback);
  211. stream_loaded=true;
  212. }
  213. }
  214. }
  215. return _stream;
  216. }
  217. C Str& _Sound::name()C {return deleted ? S : _name;} // !! this is also important for '_Sound.save' !! since we're not deleting '_name' to avoid synchronization then return no name if it already got deleted
  218. UID _Sound::id ()C {UID id; if(!deleted && DecodeFileName(_name, id))return id; return UIDZero;}
  219. Bool _Sound::is ()C {return !deleted;}
  220. Int _Sound::channels ()C {return stream().channels ();}
  221. Int _Sound::frequency()C {return stream().frequency();}
  222. Int _Sound::bitRate ()C {return stream().bitRate ();}
  223. Long _Sound::size ()C {return stream().size ();}
  224. Long _Sound::samples ()C {return stream().samples ();}
  225. Flt _Sound::length ()C {return stream().length ();}
  226. SOUND_CODEC _Sound::codec ()C {return stream().codec ();}
  227. void _Sound::loop ( Bool loop ) {T._loop=loop;}
  228. void _Sound::volume( Flt volume) {SAT(volume ); if(T._volume!=volume){T._volume=volume; AtomicOr(flag, SOUND_CHANGED_VOLUME);}}
  229. void _Sound::range ( Flt range ) {MAX(range, 0); if(T._range !=range ){T._range =range ; AtomicOr(flag, SOUND_CHANGED_RANGE );}}
  230. void _Sound::pos (C Vec &pos ) { if(T._pos !=pos ){T._pos =pos ; AtomicOr(flag, SOUND_CHANGED_POS );}}
  231. void _Sound::vel (C Vec &vel ) { if(T._vel !=vel ){T._vel =vel ; AtomicOr(flag, SOUND_CHANGED_VEL );}}
  232. void _Sound::speed ( Flt speed ) {MAX(speed, 0); if(T._speed !=speed ){T._speed =speed ; AtomicOr(flag, SOUND_CHANGED_SPEED );}}
  233. void _Sound::raw (Long raw ) {C SoundStream &stream=T.stream(); if(Long size =stream.size ())time(raw *(Dbl(stream.length ())/size ));else time(0);} // use Dbl because index can be huge (more than INT_MAX)
  234. void _Sound::sample (Long sample) {C SoundStream &stream=T.stream(); if(Long samples=stream.samples())time(sample*(Dbl(stream.length ())/samples));else time(0);} // use Dbl because index can be huge (more than INT_MAX)
  235. Long _Sound::raw ( )C {C SoundStream &stream=T.stream(); if(Dbl length =stream.length ())return time()*(stream.size () /length ); return 0 ;} // use Dbl because index can be huge (more than INT_MAX)
  236. Long _Sound::sample ( )C {C SoundStream &stream=T.stream(); if(Dbl length =stream.length ())return time()*(stream.samples() /length ); return 0 ;} // use Dbl because index can be huge (more than INT_MAX)
  237. void _Sound::frac (Flt f ) {time(f*length());}
  238. void _Sound::time (Flt t ) {if(T._time!=t){AtomicOr(flag, SOUND_CHANGING_TIME); T._time=t; AtomicOr(flag, SOUND_CHANGED_TIME);}} // changing time value needs to be surrounded by flags because it gets modified on the sound thread as well
  239. Flt _Sound::frac ( )C {if(Flt l=length())return time()/l; return 0;}
  240. Flt _Sound::timeLeft( )C {return length()-time();}
  241. /******************************************************************************/
  242. // 'precise*' functions have '_stream' already available
  243. void _Sound::preciseRaw(Long raw)
  244. {
  245. if(Int block=_stream.block()) // copy to temp in case the value is changed in another thread
  246. {
  247. Long old_pos=raw_pos;
  248. raw_pos=(raw/block)*block;
  249. if(kill // if started killing
  250. && _stream.size()>=0 // this can be negative for unlimited audio (such as callbacks)
  251. && old_pos>=_stream.size() // because the sound reached the end
  252. && raw_pos< _stream.size())kill=0; // and new position is before the end, then cancel killing
  253. }
  254. }
  255. Long _Sound::preciseRaw()C // !! requires 'SoundAPILock' !!
  256. {
  257. if(last_buffer==0xFF)return raw_pos;
  258. // buffer is for a short time (not entire sound) so it can operate on 32-bit Int
  259. Int buffer_pos=_buffer.raw(), // !! requires 'SoundAPILock' !!
  260. pos = buffer_pos-_buffer._par.size; // go back because 'raw_pos' is already ahead
  261. #if DIRECT_SOUND || XAUDIO
  262. if(last_buffer==0)
  263. {
  264. Int buffer_half=_buffer._par.size/2;
  265. pos-=buffer_half; // go back half the buffer, because after 'buffer_pos' crossed the half, new buffer data was set, advancing 'raw_pos' by half buffer, however 'buffer_pos' remains the same
  266. if(buffer_pos<buffer_half)pos+=_buffer._par.size; // if 'buffer_pos' got back to the start due to wrapping, then we need to add by entire buffer (what was wrapped), because 'raw_pos' wasn't changed during wrapping
  267. }
  268. #elif OPEN_AL // OPEN_AL doesn't need this, because its '_buffer.raw' is always for a single buffer and not the total
  269. #elif OPEN_SL // OPEN_SL '_buffer.raw' is very imprecise and inconsistent with '_buffer._processed', because of that, a custom implementation had to be done, based on commented code below
  270. Int buffer_half=_buffer._par.size/2;
  271. if(last_buffer==0)
  272. {
  273. pos-=buffer_half; // go back half the buffer, because after 'buffer_pos' crossed the half, new buffer data was set, advancing 'raw_pos' by half buffer, however 'buffer_pos' remains the same
  274. }else
  275. {
  276. if(buffer_pos>buffer_half)pos-=_buffer._par.size;
  277. }
  278. //static Int last; LogName(S); LogN(S+last_buffer+' '+buffer_pos+'/'+_buffer._par.size+", rp:"+raw_pos+", pos:"+pos+", d:"+(pos-last)); last=pos;
  279. #endif
  280. // this won't be needed if we would operate on samples
  281. #if OPEN_AL
  282. pos=pos*_stream.block()/_buffer._par.block; // OpenAL may have buffer/stream block values different due to downmixed 3D stereo to mono
  283. #endif
  284. Long total_pos=raw_pos+pos; // !! need to use Long now !!
  285. if(loop())
  286. {
  287. Long size=_stream.size(); if(size>=0)total_pos=Mod(total_pos, size); // size can be negative for unlimited audio
  288. }
  289. return total_pos;
  290. }
  291. void _Sound::preciseFrac(Flt f) {preciseRaw(RoundL(Dbl(f)* _stream.size () ));} // use Dbl because 'preciseRaw' can be huge (more than INT_MAX)
  292. void _Sound::preciseTime(Flt t) {preciseRaw(RoundL(Dbl(t)*(_stream.block()*_stream.frequency())));} // use Dbl because 'preciseRaw' can be huge (more than INT_MAX)
  293. Flt _Sound::preciseTime( )C { Int div=_stream.block()*_stream.frequency() ; return div ? Dbl(preciseRaw())/div : 0;} // !! requires 'SoundAPILock' !! use Dbl because 'preciseRaw' can be huge (more than INT_MAX)
  294. Flt _Sound::preciseFrac( )C { Long div=_stream.size () ; return div ? Dbl(preciseRaw())/div : 0;} // !! requires 'SoundAPILock' !! use Dbl because 'preciseRaw' can be huge (more than INT_MAX)
  295. /******************************************************************************/
  296. // FADE
  297. /******************************************************************************/
  298. void _Sound::fadeCurve(FADE_CURVE curve)
  299. {
  300. //if(_fade_curve!=curve)
  301. {
  302. _fade_curve=curve;
  303. //AtomicOr(flag, SOUND_CHANGED_VOLUME); skip this because any fade curve changes should be set before actual fading
  304. }
  305. }
  306. #define EPS_TIME 0.01f
  307. void _Sound::fadeInFromSilence(Flt fade_duration) // !! this may be called on the main thread !!
  308. {
  309. if(fade_duration>EPS_TIME) // fade over time
  310. {
  311. Flt d=1.0f/fade_duration;
  312. _fade_d=d; // first set the new delta in case we're fading out slowly, and setting "_fade=0" could immediately delete the sound
  313. _fade =0; // set fade start to zero
  314. _fade_d=d; // in case the fade got cleared before we've set the "_fade=0" then reset delta
  315. //AtomicOr(flag, SOUND_CHANGED_VOLUME); // need to refresh volume due to '_fade' change HOWEVER this is not needed since we're enabling '_fade_d' which will trigger adjusting volume on its own
  316. }else // fade immediately
  317. {
  318. _fade_d=0; // clear this first to disable fading
  319. _fade =1;
  320. AtomicOr(flag, SOUND_CHANGED_VOLUME); // need to refresh volume due to '_fade' change
  321. }
  322. }
  323. void _Sound::fadeIn(Flt fade_duration) // !! this may be called on the main thread !!
  324. {
  325. if(fade_duration>EPS_TIME) // fade over time
  326. {
  327. _fade_d=1.0f/fade_duration;
  328. }else // fade immediately
  329. {
  330. _fade_d=0; // clear this first to disable fading
  331. _fade =1;
  332. AtomicOr(flag, SOUND_CHANGED_VOLUME); // need to refresh volume due to '_fade' change
  333. }
  334. }
  335. void _Sound::fadeOut(Flt fade_duration) // !! this may be called on the main thread !!
  336. {
  337. if(fade_duration>EPS_TIME) // fade over time
  338. {
  339. _fade_d=-1.0f/fade_duration;
  340. }else // fade immediately
  341. {
  342. _fade_d=-1; // set negative value so that '_fade' goes below zero and gets deleted
  343. _fade = 0; // set zero and not a negative to avoid having invalid ranges, it will be decreased either way due to '_fade_d' and value <0 will be processed in the update loop
  344. //AtomicOr(flag, SOUND_CHANGED_VOLUME); no need to refresh volume because the sound will get deleted
  345. }
  346. }
  347. /******************************************************************************/
  348. // BUFFERING
  349. /******************************************************************************/
  350. void _Sound::setBuffer(Byte *buffer, Int size) // no extra care needed
  351. {
  352. SoundStream &stream=T.stream();
  353. Int size_start=size;
  354. Long raw_pos_start=raw_pos, raw_size=stream.size(); // this can be negative for unlimited audio (such as callbacks)
  355. Byte *buffer_start=buffer;
  356. for(; size>0; )
  357. {
  358. Long set, clear=0;
  359. if(raw_size>=0)
  360. {
  361. if(_loop && (raw_pos<0 || raw_pos>=raw_size) )raw_pos=Mod(raw_pos, raw_size); // adjust 'raw_pos' if it got out of range
  362. if( raw_pos<0 )clear =-raw_pos ;else // if data position is before the available data then clear
  363. if( raw_pos>=raw_size && !kill)kill =2 ; // if data position is after the available data then kill
  364. }
  365. if(kill)clear=size; // if killing then clear
  366. if(clear)
  367. {
  368. set=Min(size, clear);
  369. SetMem(buffer, (stream.bytes()==1) ? 0x80 : 0, set);
  370. }else
  371. {
  372. set=size; if(raw_size>=0)MIN(set, raw_size-raw_pos); // at once process only up to the allowed data range if specified
  373. set=(stream.pos(raw_pos) ? stream.set(buffer, set) : 0);
  374. if(set<=0)kill=2;
  375. }
  376. buffer +=set;
  377. raw_pos+=set;
  378. size -=set;
  379. }
  380. if(SoundDataCallback *callback=_callback)callback->data(buffer_start, size_start, stream, raw_pos_start); // first copy to temp var to avoid multi-threading issues
  381. if(raw_size>=0)
  382. if(_loop && (raw_pos<0 || raw_pos>=raw_size))raw_pos=Mod(raw_pos, raw_size); // adjust 'raw_pos' if it got out of range
  383. }
  384. /******************************************************************************/
  385. Bool _Sound::setBuffer(Bool buffer, Int thread_index) // this manages locking on its own
  386. {
  387. if(kill && !--kill)return false;
  388. last_buffer=buffer;
  389. #if DIRECT_SOUND
  390. Int size=_buffer._par.size/2; // we're setting only half of the buffer
  391. SOUND_API_LOCK_COND; if(_buffer.lock(buffer ? size : 0, size)) // !! requires 'SoundAPILock' !!
  392. {
  393. SOUND_API_LOCK_OFF; setBuffer((Byte*)_buffer._lock_data, _buffer._lock_size);
  394. SOUND_API_LOCK_SET; _buffer.unlock(); // !! requires 'SoundAPILock' !!
  395. return true;
  396. }
  397. return false;
  398. #elif XAUDIO
  399. if(_buffer._sv)
  400. {
  401. Int size=_buffer._data.elms()/2; // we're setting only half of the buffer
  402. Byte *data=_buffer._data.data(); if(buffer)data+=size;
  403. setBuffer(data, size);
  404. XAUDIO2_BUFFER ab;
  405. ab.Flags =0;
  406. ab.AudioBytes=size;
  407. ab.pAudioData=data;
  408. ab.PlayBegin =0;
  409. ab.PlayLength=0;
  410. ab.LoopBegin =0;
  411. ab.LoopLength=0;
  412. ab.LoopCount =0;
  413. ab.pContext =this;
  414. SOUND_API_LOCK_COND;
  415. return OK(_buffer._sv->SubmitSourceBuffer(&ab, null)); // !! requires 'SoundAPILock' !!
  416. }
  417. return false;
  418. #elif OPEN_AL
  419. Bool downmix=(_buffer._par.channels==1 && _stream.channels()==2),
  420. stereo=(_buffer._par.channels==2),
  421. bit16 =(_buffer._par.bytes >=2); // use >= to include 24-bit which will be downsampled
  422. Int size = _buffer._par.size; if(!downmix)size/=2; // we're setting only half of the buffer (don't do this for downmixing, because for that we need initially 2x more)
  423. Byte *buffer_data, temp[512*1024]; // 512 KB
  424. if(size<=SIZE(temp))buffer_data=temp;else // if we can fit all data in stack then use it
  425. {
  426. Mems<Byte> &buffer=SoundThreadBuffer[thread_index];
  427. if(size>buffer.elms())buffer.clear().setNum(size); // 'clear' first to avoid unnecessary copy of old elements
  428. buffer_data=buffer.data();
  429. }
  430. setBuffer(buffer_data, size);
  431. if(_buffer._par.bytes==3) // convert 24-bit to 16-bit
  432. {
  433. size=size/3*2; // we now have less data (div by 3 first to avoid overflow, it's OK because size is always divisible by 3 here)
  434. I16 *d=(I16*)buffer_data, *s=(I16*)(buffer_data+1); // start source from 1 byte higher because we have to ignore first low 8-bits, and just get higher 16-bits
  435. Int samples=size/SIZE(*d); FREP(samples){d[i]=*s; s=(I16*)(((Byte*)s)+3);} // advance source by 3 bytes
  436. }
  437. if(downmix)
  438. {
  439. size/=2; // changing from stereo to mono cuts size in half
  440. if(bit16)
  441. {
  442. I16 *d=(I16*)buffer_data; Int samples=size/SIZE(*d); FREP(samples)d[i]=(d[i*2]+d[i*2+1])/2;
  443. }else
  444. {
  445. U8 *d=(U8*)buffer_data; Int samples=size/SIZE(*d); FREP(samples)d[i]=(d[i*2]+d[i*2+1])/2;
  446. }
  447. }
  448. SOUND_API_LOCK_COND;
  449. alBufferData ( _buffer._buffer[buffer], bit16 ? (stereo ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16) : (stereo ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8), buffer_data, size, _buffer._par.frequency); // !! requires 'SoundAPILock' !! 'alBufferData' copies the buffer into its internal memory
  450. alSourceQueueBuffers(_buffer._source, 1, &_buffer._buffer[buffer]);
  451. return true;
  452. #elif OPEN_SL
  453. if(_buffer.player_buffer_queue)
  454. {
  455. Int size=_buffer._data.elms()/2; // we're setting only half of the buffer
  456. Byte *data=_buffer._data.data(); if(buffer)data+=size;
  457. setBuffer(data, size);
  458. SOUND_API_LOCK_COND;
  459. return (*_buffer.player_buffer_queue)->Enqueue(_buffer.player_buffer_queue, data, size)==SL_RESULT_SUCCESS; // !! requires 'SoundAPILock' !!
  460. }
  461. return false;
  462. #else
  463. return false;
  464. #endif
  465. }
  466. /******************************************************************************/
  467. Bool _Sound::testBuffer(Int thread_index) // this manages locking on its own
  468. {
  469. #if DIRECT_SOUND
  470. SOUND_API_LOCK_COND; if((_buffer.raw()>=_buffer._par.size/2)==last_buffer) // remember that 'last_buffer' can be 0xFF
  471. {
  472. SOUND_API_LOCK_OFF; return setNextBuffer(thread_index);
  473. }
  474. #elif XAUDIO
  475. if(_buffer._sv)
  476. {
  477. SOUND_API_LOCK_COND;
  478. XAUDIO2_VOICE_STATE state;
  479. #if WINDOWS_OLD
  480. _buffer._sv->GetState(&state);
  481. #else
  482. _buffer._sv->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
  483. #endif
  484. REP(2-state.BuffersQueued)if(!setNextBuffer(thread_index))return false;
  485. // unlike OpenAL we don't need to check if buffer is no longer playing due to running out of buffers, because XAudio will auto-play when adding new buffers if it's in play mode - "If the voice is started and has no buffers queued, the new buffer will start playing immediately" https://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.ixaudio2sourcevoice.ixaudio2sourcevoice.submitsourcebuffer(v=vs.85).aspx
  486. }
  487. #elif OPEN_AL
  488. SOUND_API_LOCK_COND;
  489. Int processed=0; if(_buffer._source)alGetSourcei(_buffer._source, AL_BUFFERS_PROCESSED, &processed); // !! requires 'SoundAPILock' !!
  490. if( processed>0)
  491. {
  492. REP(processed)
  493. {
  494. UInt buffer=0; alSourceUnqueueBuffers(_buffer._source, 1, &buffer); // !! requires 'SoundAPILock' !!
  495. SOUND_API_LOCK_OFF;
  496. if(buffer==_buffer._buffer[0]){if(!setBuffer(false, thread_index))return false;}else
  497. if(buffer==_buffer._buffer[1]){if(!setBuffer(true , thread_index))return false;}else
  498. return false; // unknown buffer
  499. SOUND_API_LOCK_SET; // reset lock, needed for both 'alSourceUnqueueBuffers' at the top and '_buffer.playing', '_buffer.play' below
  500. }
  501. if(!_buffer.playing())_buffer.play(true); // !! requires 'SoundAPILock' !! continue playing if it stopped (for example ran out of buffers - processed==2)
  502. }
  503. #elif OPEN_SL
  504. if(Int processed=AtomicGet(_buffer._processed))
  505. {
  506. REP(processed)if(!setNextBuffer(thread_index))return false;
  507. AtomicSub(_buffer._processed, processed);
  508. // unlike OpenAL we don't need to check if buffer is no longer playing due to running out of buffers, because OpenSL will auto-play when adding new buffers if it's in play mode, check comments on 'Enqueue' function - https://www.khronos.org/registry/sles/specs/OpenSL_ES_Specification_1.0.1.pdf
  509. }
  510. #endif
  511. return true;
  512. }
  513. /******************************************************************************/
  514. Bool _Sound::update(Flt dt) // !! requires 'SoundAPILock' !!
  515. {
  516. // process fade
  517. if(Any(_fade_d))
  518. {
  519. _fade+=_fade_d*dt;
  520. if(_fade>1){ _fade=1; _fade_d=0; }else // don't check for >=1 in case '_fade_d' is <0 (want to fade out) however 'dt'=0
  521. if(_fade<0){/*_fade=0; _fade_d=0;*/ del();} // !! requires 'SoundAPILock' !! don't clear because members are already cleared in 'del', don't check for <0 in case '_fade_d' is >0 (want to fade in) however 'dt'=0
  522. AtomicOr(flag, SOUND_CHANGED_VOLUME);
  523. }
  524. // remove
  525. if(AtomicGet(flag)&SOUND_REMOVE)
  526. {
  527. del(); // !! requires 'SoundAPILock' !!
  528. return false;
  529. }
  530. // calculate priority
  531. if(playing())
  532. {
  533. // first calculate based on actual volume
  534. priority=_volume;
  535. if(is3D())
  536. {
  537. Flt dist=Dist(_pos, Listener.pos());
  538. if( dist>_range)priority*=_range/dist;
  539. }
  540. // add modifiers
  541. priority+=SoundGroupPriority[volume_group];
  542. if(_buffer_playing)priority+=SoundPlayPriority; // increase priority for sounds that are already playing
  543. }else priority=-1; // if it's not playing then set priority below zero (zero is lowest possible 'SoundMinVolume' value, so it will never be played)
  544. return true;
  545. }
  546. void _Sound::updatePlaying(Int thread_index)
  547. {
  548. { // brackets to avoid compile errors on GCC
  549. // create
  550. if(!_buffer.is())
  551. {
  552. SoundStream &stream=T.stream();
  553. SOUND_API_LOCK_COND;
  554. if(!_buffer.create(stream.frequency(), stream.bits(), stream.channels(), Ceil2(stream.frequency()*SOUND_TIME/1000), is3D())) // !! requires 'SoundAPILock' !! we need to use 'Ceil2' because we're operating on half buffers everywhere, so we need to make sure that their sizes will be the same
  555. goto error;
  556. #if DEL_BUFFER_WHEN_NOT_PLAYING
  557. AtomicOr(T.flag, SOUND_CHANGED_POS|SOUND_CHANGED_VEL|SOUND_CHANGED_RANGE|SOUND_CHANGED_VOLUME|SOUND_CHANGED_SPEED|SOUND_CHANGED_TIME); // after creating buffer we need to apply: pos/vel/range (in case they were set before but buffer got deleted and need to be reset again), volume and speed because they are affected by global parameters, also reset the time because it's modified when sound is paused
  558. #endif
  559. }
  560. Flt old_time=_time; // remember current '_time' value before checking "SOUND_CHANGED_TIME|SOUND_CHANGING_TIME" flags, this is important to know the state of value before checking for 'SOUND_CHANGING_TIME', to properly detect if it's being changed by the user on another thread
  561. // process changes
  562. #if !DEL_BUFFER_WHEN_NOT_PLAYING
  563. if(!_buffer_playing)AtomicOr(T.flag, SOUND_CHANGED_VOLUME|SOUND_CHANGED_SPEED|SOUND_CHANGED_TIME); // if not playing yet then we need to apply volume and speed because they are affected by global parameters, also reset the time because it's modified when sound is paused
  564. #endif
  565. UInt flag=AtomicGet(T.flag), handled=SOUND_CHANGED_POS|SOUND_CHANGED_VEL|SOUND_CHANGED_ORN|SOUND_CHANGED_RANGE|SOUND_CHANGED_VOLUME|SOUND_CHANGED_SPEED|SOUND_CHANGED_TIME|SOUND_CHANGING_TIME; // flags handled here, need to check for SOUND_CHANGED_ORN because of Listener changes
  566. #if XAUDIO
  567. flag|=ListenerChanged;
  568. #endif
  569. if(flag&handled)
  570. {
  571. flag=AtomicDisable(T.flag, handled); // disable these flags first, so in case the user modifies parameters while this function is running, it will be activated again, don't surround this with another 'if' (to avoid extra branching) because most likely they will return handled flags
  572. #if XAUDIO
  573. flag|=ListenerChanged;
  574. #endif
  575. SOUND_API_LOCK_COND; // below !! requires 'SoundAPILock' !!
  576. #if XAUDIO // XAudio processes changes together
  577. if(_is3D)
  578. {
  579. if(flag&SOUND_CHANGED_SPEED)setSpeed(); // !! call this first before 'setParams' !!
  580. if(flag&(SOUND_CHANGED_POS|SOUND_CHANGED_ORN|SOUND_CHANGED_RANGE|SOUND_CHANGED_VEL|SOUND_CHANGED_SPEED)) // need to check for SOUND_CHANGED_ORN because of Listener changes
  581. _buffer.setParams(T, FlagTest(flag, SOUND_CHANGED_POS|SOUND_CHANGED_ORN|SOUND_CHANGED_RANGE), FlagTest(flag, SOUND_CHANGED_POS|SOUND_CHANGED_VEL|SOUND_CHANGED_SPEED));
  582. }else
  583. {
  584. if(flag&SOUND_CHANGED_SPEED){setSpeed(); _buffer.speed(SoundSpeed(_actual_speed));}
  585. }
  586. #else
  587. if(flag&SOUND_CHANGED_POS )_buffer.pos (_pos);
  588. if(flag&SOUND_CHANGED_VEL )_buffer.vel (_vel);
  589. if(flag&SOUND_CHANGED_RANGE )_buffer.range(_range);
  590. if(flag&SOUND_CHANGED_SPEED ){setSpeed(); _buffer.speed(SoundSpeed(_actual_speed));}
  591. #endif
  592. if(flag&SOUND_CHANGED_VOLUME)setVolume();
  593. if(flag&SOUND_CHANGED_TIME )preciseTime(time());
  594. }
  595. // buffer data
  596. if(_buffer_playing) // if already playing
  597. {
  598. if(!testBuffer(thread_index))goto error; // check if we need to update playback buffers
  599. if(!(flag&(SOUND_CHANGING_TIME|SOUND_CHANGED_TIME))) // update time position only if it wasn't modified by the user
  600. {
  601. Flt precise_time; {SOUND_API_LOCK_COND; precise_time=preciseTime();} // !! requires 'SoundAPILock' !!
  602. AtomicCAS(_time, old_time, precise_time);
  603. }
  604. }else // start playback
  605. {
  606. // set buffer data for both halfs
  607. if(!setBuffer(false, thread_index) || !setBuffer(true, thread_index))goto error;
  608. _buffer_playing=true;
  609. SOUND_API_LOCK_COND; _buffer.play(true); // !! requires 'SoundAPILock' !!
  610. }
  611. }
  612. return;
  613. error:;
  614. SOUND_API_LOCK_COND; del(); // !! requires 'SoundAPILock' !!
  615. }
  616. /******************************************************************************/
  617. // IO
  618. /******************************************************************************/
  619. #pragma pack(push, 1)
  620. enum
  621. {
  622. SD_3D =1<<0,
  623. SD_LOOP =1<<1,
  624. SD_PLAYING=1<<2,
  625. };
  626. struct SoundDesc // 'priority' does not need to be saved because it's always calculated when needed
  627. {
  628. Byte flag;
  629. VOLUME_GROUP volume_group;
  630. Flt time, volume, speed, range, fade, fade_d; // use 'time' instead of 'raw' in case sound file format changes (for example frequency or bits which would make 'raw' invalid)
  631. Vec pos, vel;
  632. };
  633. #pragma pack(pop)
  634. /******************************************************************************/
  635. Bool _Sound::save(File &f, CChar *path)C // !! Warning: Fade Curve is not saved !!
  636. {
  637. // copy params to temp object first
  638. SoundDesc desc;
  639. _Unaligned(desc.flag , (is3D() ? SD_3D : 0) | (loop() ? SD_LOOP : 0) | (playing() ? SD_PLAYING : 0));
  640. Unaligned(desc.volume_group, volume_group);
  641. Unaligned(desc.time , time());
  642. Unaligned(desc.volume , volume());
  643. Unaligned(desc.speed , speed());
  644. Unaligned(desc.range , range());
  645. Unaligned(desc.fade , _fade);
  646. Unaligned(desc.fade_d , _fade_d);
  647. Unaligned(desc.pos , pos());
  648. Unaligned(desc.vel , vel());
  649. f.cmpUIntV(0); // version
  650. C Str &name=T.name(); // !! it is important to check if the sound is being/got deleted after copying all parameters !!
  651. f.putAsset(name); if(name.is())f<<desc;
  652. return f.ok();
  653. }
  654. Int _Sound::loadResult(File &f, CChar *path) // this does not call delete on its own, it expects the caller to delete if necessary
  655. {
  656. switch(f.decUIntV()) // version
  657. {
  658. case 0:
  659. {
  660. f.getAsset(_name); if(_name.is())
  661. {
  662. SoundDesc desc; if(f.getFast(desc) && f.ok())
  663. {
  664. Byte flag=Unaligned(desc.flag);
  665. init(_name, null, FlagTest(flag, SD_3D), Unaligned(desc.volume_group));
  666. Unaligned(_fade , desc.fade );
  667. Unaligned(_fade_d, desc.fade_d);
  668. loop (FlagTest(flag, SD_LOOP));
  669. time (Unaligned(desc.time ));
  670. volume(Unaligned(desc.volume));
  671. speed (Unaligned(desc.speed ));
  672. range (Unaligned(desc.range ));
  673. pos (Unaligned(desc.pos ));
  674. vel (Unaligned(desc.vel ));
  675. if(FlagTest(flag, SD_PLAYING))play(); // call this last after all parameters have been set
  676. return SOUND_LOAD_OK;
  677. }
  678. }else if(f.ok())return SOUND_LOAD_EMPTY;
  679. }break;
  680. }
  681. return SOUND_LOAD_ERROR;
  682. }
  683. /******************************************************************************/
  684. // SOUND
  685. /******************************************************************************/
  686. Sound::Sound(C Sound &src) : Sound() {T=src;}
  687. Sound& Sound::del()
  688. {
  689. if(sound)
  690. {
  691. if(SoundFunc)AtomicOr(sound->flag, SOUND_REMOVE);
  692. sound=null;
  693. }
  694. return T;
  695. }
  696. Sound& Sound::close()
  697. {
  698. if(sound)
  699. {
  700. if(SoundFunc)
  701. {
  702. SyncLocker lock(SoundWait);
  703. if(!SoundWaited) // check if we already know that the sound is waiting, can check this only after lock, this is important in case there are 2 or more 'close' calls on main thread, for example first call is processed OK, and if next call is made right after the first one, then 'SoundWait' may get locked before the sound thread gets it, which means that it won't be able to signal 'SoundWaiting' event for the 2nd time, because it's still locked due to the first call
  704. {
  705. AtomicOr(SoundPause, SOUND_WAIT); // request waiting
  706. SoundEvent.on(); // wake up sound thread
  707. SoundWaiting.wait(); // wait until sound thread is waiting
  708. SoundWaited=true; // we now know that the sound thread is waiting
  709. }
  710. {
  711. SyncLocker locker_memx(SoundMemxLock);
  712. SOUND_API_LOCK;
  713. SoundMemx.removeData(sound, true); // !! requires 'SoundAPILock' !!
  714. }
  715. AtomicDisable(SoundPause, SOUND_WAIT); // disable waiting request
  716. }
  717. sound=null;
  718. }
  719. return T;
  720. }
  721. Sound& Sound::_create(C Str &name, SoundCallback *call, Bool is3D, VOLUME_GROUP volume_group) // !! call 'call.del' if not passed down !!
  722. {
  723. del();
  724. if((name.is() || call) && SoundFunc)
  725. {
  726. SyncLocker locker(SoundMemxLock);
  727. sound=&SoundMemx.New();
  728. sound->init(name, call, is3D, volume_group); call=null; // 'call' will be processed inside, don't modify it anymore
  729. }
  730. if(call){call->del(); call=null;}
  731. return T;
  732. }
  733. void Sound::operator=(C Sound &src)
  734. {
  735. if(this!=&src)
  736. {
  737. del();
  738. if(SoundFunc)
  739. {
  740. SyncLocker locker(SoundMemxLock);
  741. if(src.is())
  742. {
  743. sound=&SoundMemx.New();
  744. if(!sound->init(*src.sound))del();
  745. }
  746. }
  747. }
  748. }
  749. Sound& Sound::create(C Str &name, Bool loop, Flt volume, VOLUME_GROUP volume_group) {return name.is() ? _create(name, null, false, volume_group). loop(loop).volume(volume) : del();}
  750. Sound& Sound::create(C Str &name, C Vec &pos, Flt range, Bool loop, Flt volume, VOLUME_GROUP volume_group) {return name.is() ? _create(name, null, true , volume_group).pos(pos).range(range).loop(loop).volume(volume) : del();}
  751. Sound& Sound::create(C UID &id, Bool loop, Flt volume, VOLUME_GROUP volume_group) {return id.valid() ? _create(_EncodeFileName(id), null, false, volume_group). loop(loop).volume(volume) : del();}
  752. Sound& Sound::create(C UID &id, C Vec &pos, Flt range, Bool loop, Flt volume, VOLUME_GROUP volume_group) {return id.valid() ? _create(_EncodeFileName(id), null, true , volume_group).pos(pos).range(range).loop(loop).volume(volume) : del();}
  753. Sound& Sound::create(SoundCallback &call, Bool loop, Flt volume, VOLUME_GROUP volume_group) {return _create(S, &call, false, volume_group). loop(loop).volume(volume);} // !! call 'call.del' if not passed down !!
  754. Sound& Sound::create(SoundCallback &call, C Vec &pos, Flt range, Bool loop, Flt volume, VOLUME_GROUP volume_group) {return _create(S, &call, true , volume_group).pos(pos).range(range).loop(loop).volume(volume);} // !! call 'call.del' if not passed down !!
  755. /******************************************************************************/
  756. Sound& Sound::stop () {if(sound && SoundFunc)sound->stop (); return T;}
  757. Sound& Sound::pause() {if(sound && SoundFunc)sound->pause(); return T;}
  758. Sound& Sound::play () {if(sound && SoundFunc)sound->play (); return T;}
  759. CChar8* Sound::codecName()C {return CodecName(codec());}
  760. SOUND_CODEC Sound::codec ()C {return (sound && SoundFunc) ? sound->codec () : SOUND_NONE;}
  761. Bool Sound::is ()C {return (sound && SoundFunc) ? sound->is () : false ;}
  762. Bool Sound::playing ()C {return (sound && SoundFunc) ? sound->playing () : false ;}
  763. C Str& Sound::name ()C {return (sound && SoundFunc) ? sound->name () : S ;}
  764. UID Sound::id ()C {return (sound && SoundFunc) ? sound->id () : UIDZero;}
  765. Long Sound::size ()C {return (sound && SoundFunc) ? sound->size () : 0 ;}
  766. Long Sound::samples ()C {return (sound && SoundFunc) ? sound->samples () : 0 ;}
  767. Flt Sound::length ()C {return (sound && SoundFunc) ? sound->length () : 0 ;}
  768. Int Sound::channels ()C {return (sound && SoundFunc) ? sound->channels () : 0 ;}
  769. Int Sound::frequency()C {return (sound && SoundFunc) ? sound->frequency() : 0 ;}
  770. Int Sound::bitRate ()C {return (sound && SoundFunc) ? sound->bitRate () : 0 ;}
  771. Long Sound::raw ()C {return (sound && SoundFunc) ? sound->raw () : 0 ;}
  772. Long Sound::sample ()C {return (sound && SoundFunc) ? sound->sample () : 0 ;}
  773. Flt Sound::timeLeft ()C {return (sound && SoundFunc) ? sound->timeLeft () : 0 ;}
  774. Flt Sound::time ()C {return (sound && SoundFunc) ? sound->time () : 0 ;}
  775. Flt Sound::frac ()C {return (sound && SoundFunc) ? sound->frac () : 0 ;}
  776. Bool Sound::loop ()C {return (sound && SoundFunc) ? sound->loop () : false;}
  777. Flt Sound::volume ()C {return (sound && SoundFunc) ? sound->volume () : 0;}
  778. Flt Sound::fade ()C {return (sound && SoundFunc) ? sound->fade () : 0;}
  779. FADE_CURVE Sound::fadeCurve()C {return (sound && SoundFunc) ? sound->fadeCurve() : FADE_LINEAR;}
  780. Flt Sound::speed ()C {return (sound && SoundFunc) ? sound->speed () : 0;}
  781. Flt Sound::range ()C {return (sound && SoundFunc) ? sound->range () : 0;}
  782. C Vec& Sound::pos ()C {return (sound && SoundFunc) ? sound->pos () : VecZero;}
  783. C Vec& Sound::vel ()C {return (sound && SoundFunc) ? sound->vel () : VecZero;}
  784. Sound& Sound::raw ( Long raw ) {if(sound && SoundFunc)sound->raw (raw ); return T;}
  785. Sound& Sound::sample( Long sample) {if(sound && SoundFunc)sound->sample(sample); return T;}
  786. Sound& Sound::time ( Flt t ) {if(sound && SoundFunc)sound->time (t ); return T;}
  787. Sound& Sound::frac ( Flt f ) {if(sound && SoundFunc)sound->frac (f ); return T;}
  788. Sound& Sound::loop ( Bool loop ) {if(sound && SoundFunc)sound->loop (loop ); return T;}
  789. Sound& Sound::volume( Flt volume) {if(sound && SoundFunc)sound->volume(volume); return T;}
  790. Sound& Sound::speed ( Flt speed ) {if(sound && SoundFunc)sound->speed (speed ); return T;}
  791. Sound& Sound::range ( Flt range ) {if(sound && SoundFunc)sound->range (range ); return T;}
  792. Sound& Sound::pos (C Vec &pos ) {if(sound && SoundFunc)sound->pos (pos ); return T;}
  793. Sound& Sound::vel (C Vec &vel ) {if(sound && SoundFunc)sound->vel (vel ); return T;}
  794. Sound& Sound::callback(SoundDataCallback *callback) { if(sound && SoundFunc) sound->_callback=callback; return T;}
  795. SoundDataCallback* Sound::callback( )C {return (sound && SoundFunc) ? sound->_callback : null;}
  796. Sound& Sound::fadeCurve (FADE_CURVE curve ) {if(sound && SoundFunc)sound->fadeCurve (curve ); return T;}
  797. Sound& Sound::fadeInFromSilence(Flt fade_duration) {if(sound && SoundFunc)sound->fadeInFromSilence(fade_duration); return T;}
  798. Sound& Sound::fadeIn (Flt fade_duration) {if(sound && SoundFunc)sound->fadeIn (fade_duration); return T;}
  799. Sound& Sound::fadeOut (Flt fade_duration) {if(sound && SoundFunc)sound->fadeOut (fade_duration); return T;}
  800. /******************************************************************************/
  801. // IO
  802. /******************************************************************************/
  803. Bool Sound::save(File &f, CChar *path)C
  804. {
  805. SyncLocker locker_memx(SoundMemxLock); // use lock even though we don't need it, to minimize the risk of data being modified during saving
  806. SOUND_API_LOCK; // use lock even though we don't need it, to minimize the risk of data being modified during saving
  807. if(is())
  808. {
  809. f.putBool(true);
  810. if(!sound->save(f, path))return false;
  811. }else
  812. {
  813. f.putBool(false);
  814. }
  815. return f.ok();
  816. }
  817. /******************************************************************************/
  818. Bool Sound::load(File &f, CChar *path)
  819. {
  820. del(); // always delete, because for loading we need to modify a new '_Sound'
  821. if(!f.getBool())return f.ok(); // no sound
  822. if(SoundFunc) // can load sounds only with 'SoundMemx' available
  823. {
  824. SyncLocker locker(SoundMemxLock);
  825. sound=&SoundMemx.New();
  826. switch(sound->loadResult(f, path))
  827. {
  828. case SOUND_LOAD_OK : return true;
  829. case SOUND_LOAD_EMPTY: del(); return true;
  830. default : del(); break; // SOUND_LOAD_ERROR
  831. }
  832. }
  833. return false;
  834. }
  835. /******************************************************************************/
  836. #if OPEN_SL
  837. // !! this can be called only inside 'Listener' on sound thread !! because it's called only there, it already had 'SoundAPILock' lock applied so we don't need to call it again, since it's the sound thread then we can use 'SoundMemxPlaying'
  838. void EmulateSound3D() {REPAO(SoundMemxPlaying)->_buffer.emulate3D();} // !! requires 'SoundAPILock' !!
  839. #endif
  840. // following can be called on the main thread, because of that we can't use 'SoundMemxPlaying'
  841. void VolumeSound() {SyncLocker locker_memx(SoundMemxLock); REPA(SoundMemx)AtomicOr(SoundMemx[i].flag, SOUND_CHANGED_VOLUME);}
  842. void SpeedSound() {SyncLocker locker_memx(SoundMemxLock); REPA(SoundMemx)AtomicOr(SoundMemx[i].flag, SOUND_CHANGED_SPEED );}
  843. static Int CompareSound(C _Sound &a, C _Sound &b)
  844. {
  845. return Compare(b.priority, a.priority); // compare reversed so that we start with highest priority
  846. }
  847. /******************************************************************************/
  848. static void UpdatePlaying(_Sound* &sound, Ptr user, Int thread_index) {sound->updatePlaying(thread_index);}
  849. #if UPDATE_2X
  850. static Bool UpdateStep;
  851. #endif
  852. static Bool UpdateSound2(Thread &thread)
  853. {
  854. SoundEvent.wait(SOUND_TIMER/(UPDATE_2X ? 2 : 1));
  855. again:
  856. if(AtomicGet(SoundPause))
  857. {
  858. Int state=AtomicGet(SoundPause);
  859. Bool want_paused=FlagTest(state, SOUND_WANT_PAUSED),
  860. paused=FlagTest(state, SOUND_PAUSED );
  861. if( want_paused!=paused)
  862. {
  863. SOUND_API_LOCK;
  864. if(want_paused){REPAO(SoundMemxPlaying)->_buffer.pause( ); AtomicOr (SoundPause, SOUND_PAUSED); } // !! requires 'SoundAPILock' !! pause all playing sounds
  865. else {REPAO(SoundMemxPlaying)->_buffer.play (true); AtomicDisable(SoundPause, SOUND_PAUSED); SoundTime=Time.curTime();} // !! requires 'SoundAPILock' !! unpause all playing sounds
  866. }
  867. if(state&SOUND_WAIT) // if want to wait temporarily
  868. {
  869. SoundWaiting.on(); // message other thread that we will wait now
  870. SyncLocker lock(SoundWait); // since other thread already is inside 'SoundWait', this will continue only after other thread finished
  871. SoundWaited=false; // sound thread is no longer waiting, can clear this only after lock
  872. }
  873. if(want_paused)
  874. {
  875. SoundEvent.wait(); // wait for unpause as long as it takes
  876. #if HAS_THREADS
  877. goto again; // if we have threads then it means we have waited and we can just check again
  878. #else
  879. return true; // if we haven't waited then we need to return so we don't update anything since we're paused
  880. #endif
  881. }
  882. }
  883. #if UPDATE_2X
  884. if(UpdateStep^=1) // we have to update every 2nd step, because this function is called 2x more frequently
  885. #endif
  886. {
  887. UpdateMusic(); // !! do not surround music by any locks, because it doesn't need any, it operates only on 'Sound' which was designed to don't require any locks !!
  888. SoundMemxPlaying.clear();
  889. Int max_concurrent=SoundMaxConcurrent();
  890. Flt min_vol =SoundMinVolume ();
  891. Dbl time=Time.curTime(); Flt dt=time-SoundTime; SoundTime=time; // get time that passed since the last update
  892. SyncLockerEx locker_memx(SoundMemxLock);
  893. #if !SOUND_API_THREAD_SAFE
  894. SyncLockerEx locker_api (SoundAPILock); SoundAPILockDo=false; // disable 'SoundAPILock' because we're already covered by the lock here
  895. #endif
  896. REPA(SoundMemx)if(!SoundMemx[i].update(dt))SoundMemx.removeValid(i, true); // !! requires 'SoundAPILock' !!
  897. SoundMemx.sort(CompareSound);
  898. // detect sounds which should be playing
  899. if(!SoundAPI)max_concurrent=0;else if(max_concurrent<0)max_concurrent=SoundMemx.elms();else MIN(max_concurrent, SoundMemx.elms()); // needs to be done after 'SoundMemxLock' and after removing sounds from 'SoundMemx'
  900. Int i=0; for(; i<max_concurrent; i++) // go from the start with highest priority
  901. {
  902. _Sound &sound=SoundMemx[i];
  903. if(sound.priority<=min_vol)break;
  904. SoundMemxPlaying.add(&sound);
  905. }
  906. for(; i<SoundMemx.elms(); i++) // all remaining sounds should not be played
  907. {
  908. _Sound &sound=SoundMemx[i]; if(!sound.deleted)
  909. {
  910. if(sound._buffer_playing)
  911. {
  912. // remember time position to restart it later, do this before adjusting other members in case they affect the result
  913. Flt time=sound._time; // remember this before checking flags
  914. if(!(AtomicGet(sound.flag)&(SOUND_CHANGED_TIME|SOUND_CHANGING_TIME)))
  915. AtomicCAS(sound._time, time, sound.preciseTime()); // !! requires 'SoundAPILock' !!
  916. sound._buffer_playing=false;
  917. #if DEL_BUFFER_WHEN_NOT_PLAYING
  918. sound.last_buffer=0xFF;
  919. sound._buffer.del(); // !! requires 'SoundAPILock' !! delete the buffer to free up memory
  920. #else
  921. ..
  922. #endif
  923. }else
  924. if(sound.playing() && !sound.loop()) // update time but only if the sound is not looped, if it's looped then for simplicity we skip this
  925. { // update time over here because above it is already set from precise value
  926. if(AtomicGet(sound.flag)&SOUND_CHANGED_SPEED) // check if speed was changed, which affects time updates
  927. {
  928. AtomicDisable(sound.flag, SOUND_CHANGED_SPEED); // disable flag first, so in case the user modifies parameters while this function is running, it will be activated again
  929. sound.setSpeed();
  930. }
  931. Flt time=sound._time; AtomicCAS(sound._time, time, time+sound._actual_speed*dt); // update time only if it wasn't changed on another thread, normally we should also set SOUND_CHANGED_TIME flag, however to avoid doing it everytime, since the sound is paused, then we just apply that flag once when the sound is unpaused. This is a safe change since it's a small relative change using 'AtomicCAS' which can be performed even if time was modified on another thread.
  932. if(sound.time()>=sound.length())sound.del(); // !! requires 'SoundAPILock' !! if reached the end, then delete it
  933. }
  934. }
  935. }
  936. locker_memx.off(); // finished operating on 'SoundMemx', further sound update will be done using 'SoundMemxPlaying'
  937. if(SoundMemxPlaying.elms())
  938. {
  939. #if XAUDIO
  940. ListenerChanged=
  941. #endif
  942. Listener.updateNoLock(); // !! requires 'SoundAPILock' !!
  943. #if HAS_THREADS
  944. Int threads =Mid(SoundMaxThreads(), 1, Cpu.threads())-1; // since we always create 'SoundThread' (this thread) then we need one less for 'SoundThreads', because we will use this one too
  945. if( threads!=SoundThreads.threads())SoundThreads.create(false, threads, SoundThread.priority(), "EE.Sound #");
  946. #endif
  947. Int max_threads=SoundThreads.threads1(); // add 1 extra thread to process on this one too !! do not minimize here with 'SoundMemxPlaying.elms()' because we need to allocate 'SoundThreadBuffer' below for all threads, as we don't know which ones are going to wake up !!
  948. #if OPEN_AL
  949. if( max_threads>SoundThreadBuffer.elms())SoundThreadBuffer.setNum(max_threads);
  950. #endif
  951. #if !SOUND_API_THREAD_SAFE
  952. SoundAPILockDo=(SoundMemxPlaying.elms()>1 && max_threads>1); // we need to do locking if we're going to process multiple sounds on multiple threads
  953. if(SoundAPILockDo)locker_api.off(); // release from this thread so other threads can capture it
  954. #endif
  955. SoundThreads.process1(SoundMemxPlaying, UpdatePlaying, null, max_threads);
  956. #if !SOUND_API_THREAD_SAFE
  957. if(SoundAPILockDo)locker_api.on(); // re-enable for this thread because it's needed for calls done below (Listener)
  958. #endif
  959. }
  960. Listener.commitNoLock(); // !! requires 'SoundAPILock' !! call this always, not only when 'SoundMemxPlaying.elms' because on XAudio some operations are deferred
  961. }
  962. #if WINDOWS_NEW || !APPLE && !ANDROID && (DIRECT_SOUND || OPEN_AL)
  963. if(SoundRecords.elms())
  964. {
  965. SyncLocker locker(SoundAPILock); // sound record methods always require lock, read why in the sound record class
  966. REPAO(SoundRecords)->updateNoLock(); // !! requires 'SoundAPILock' !!
  967. }
  968. #endif
  969. return true;
  970. }
  971. void ShutSound2()
  972. {
  973. SoundThread.stop(); ResumeSound(); SoundEvent.on(); // resume to avoid waiting forever, enable event so the 'SoundThread' can wake up immediately
  974. SoundThread.del ();
  975. #if OPEN_AL
  976. SoundThreadBuffer.del(); // delete this after deleting the thread
  977. #endif
  978. SyncLocker locker_memx(SoundMemxLock);
  979. SyncLocker locker_api (SoundAPILock );
  980. SoundMemxPlaying.del();
  981. SoundMemx .del();
  982. }
  983. void InitSound2()
  984. {
  985. SoundGroupPriority[VOLUME_MUSIC]=1; // increase priority for Music
  986. SoundTime=Time.curTime();
  987. SoundThread.create(UpdateSound2, null, 2, false, "EE.Sound"); // here parameter "2" is thread priority
  988. }
  989. void UpdateSound()
  990. {
  991. SoundEvent.on();
  992. }
  993. /******************************************************************************/
  994. void PauseSound() // in order to work on 'SoundMemxPlaying' we would need to delete the thread
  995. {
  996. #if XAUDIO
  997. if(XAudio)XAudio->StopEngine();
  998. #else
  999. AtomicOr(SoundPause, SOUND_WANT_PAUSED); SoundEvent.on(); // wake up the thread immediately to perform the changes
  1000. #endif
  1001. }
  1002. void ResumeSound()
  1003. {
  1004. #if XAUDIO
  1005. if(XAudio)XAudio->StartEngine();
  1006. #else
  1007. AtomicDisable(SoundPause, SOUND_WANT_PAUSED); SoundEvent.on(); // wake up the thread immediately to perform the changes
  1008. #endif
  1009. }
  1010. /******************************************************************************/
  1011. Bool PlayingAnySound() {return SoundMemxPlaying.elms()>0;}
  1012. /******************************************************************************/
  1013. static void SoundPlay2D(C Str &name, SoundCallback *call, Flt volume, VOLUME_GROUP volume_group, Flt speed) // !! call 'call.del' if not passed down !!
  1014. {
  1015. if(SoundAPI) // test for 'SoundAPI' because there's no point in creating dummy sounds if they won't be played
  1016. {
  1017. SyncLocker locker(SoundMemxLock);
  1018. _Sound &sound=SoundMemx.New(); FlagEnable(sound.flag, SOUND_NO_REF);
  1019. sound.init(name, call, false, volume_group); call=null; // 'call' will be processed inside, don't modify it anymore
  1020. sound.volume(volume); sound.speed(speed);
  1021. sound.play();
  1022. }
  1023. if(call){call->del(); call=null;}
  1024. }
  1025. static void SoundPlay3D(C Str &name, SoundCallback *call, C Vec &pos, Flt range, Flt volume, VOLUME_GROUP volume_group, Flt speed) // !! call 'call.del' if not passed down !!
  1026. {
  1027. if(SoundAPI) // test for 'SoundAPI' because there's no point in creating dummy sounds if they won't be played
  1028. {
  1029. SyncLocker locker(SoundMemxLock);
  1030. _Sound &sound=SoundMemx.New(); FlagEnable(sound.flag, SOUND_NO_REF);
  1031. sound.init(name, call, true, volume_group); call=null; // 'call' will be processed inside, don't modify it anymore
  1032. sound.volume(volume); sound.speed(speed); sound.pos(pos); sound.range(range);
  1033. sound.play();
  1034. }
  1035. if(call){call->del(); call=null;}
  1036. }
  1037. void SoundPlay(C Str &name, Flt volume, VOLUME_GROUP volume_group, Flt speed) {if(name .is())SoundPlay2D(name , null, volume, volume_group, speed);}
  1038. void SoundPlay(C Str &name, C Vec &pos, Flt range, Flt volume, VOLUME_GROUP volume_group, Flt speed) {if(name .is())SoundPlay3D(name , null, pos, range, volume, volume_group, speed);}
  1039. void SoundPlay(C UID &id , Flt volume, VOLUME_GROUP volume_group, Flt speed) {if(id.valid())SoundPlay2D(_EncodeFileName(id), null, volume, volume_group, speed);}
  1040. void SoundPlay(C UID &id , C Vec &pos, Flt range, Flt volume, VOLUME_GROUP volume_group, Flt speed) {if(id.valid())SoundPlay3D(_EncodeFileName(id), null, pos, range, volume, volume_group, speed);}
  1041. void SoundPlay(SoundCallback &call, Flt volume, VOLUME_GROUP volume_group, Flt speed) { SoundPlay2D(S , &call, volume, volume_group, speed);}
  1042. void SoundPlay(SoundCallback &call, C Vec &pos, Flt range, Flt volume, VOLUME_GROUP volume_group, Flt speed) { SoundPlay3D(S , &call, pos, range, volume, volume_group, speed);}
  1043. /******************************************************************************/
  1044. }
  1045. /******************************************************************************/