audio.cc 83 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "audio/audio.h"
  23. #include "audio/audioDataBlock.h"
  24. #include "collection/vector.h"
  25. #include "console/console.h"
  26. #include "console/consoleTypes.h"
  27. #include "game/gameConnection.h"
  28. #include "io/fileStream.h"
  29. #include "audio/audioStreamSourceFactory.h"
  30. #ifdef TORQUE_OS_IOS
  31. #include "platformiOS/SoundEngine.h"
  32. #endif
  33. #ifdef TORQUE_OS_OSX
  34. //#define REL_WORKAROUND
  35. #endif
  36. //-------------------------------------------------------------------------
  37. #ifdef TORQUE_OS_IOS
  38. extern ALvoid alcMacOSXMixerOutputRateProc(const ALdouble value);
  39. #endif
  40. #define MAX_AUDIOSOURCES 16 // maximum number of concurrent sources
  41. #define MIN_GAIN 0.05f // anything with lower gain will not be started
  42. #define MIN_UNCULL_PERIOD 500 // time before buffer is checked to be unculled
  43. #define MIN_UNCULL_GAIN 0.1f // min gain of source to be unculled
  44. #define ALX_DEF_SAMPLE_RATE 44100 // default values for mixer
  45. #define ALX_DEF_SAMPLE_BITS 16
  46. #define ALX_DEF_CHANNELS 2
  47. #define FORCED_OUTER_FALLOFF 10000.f // forced falloff distance
  48. #ifdef TORQUE_OS_OSX
  49. static ALCdevice *mDevice = NULL; // active OpenAL device
  50. static ALCcontext *mContext = NULL; // active OpenAL context
  51. #elif TORQUE_OS_IOS
  52. static ALCdevice *mDevice = NULL; // active OpenAL device
  53. static ALCcontext *mContext = NULL; // active OpenAL context
  54. #else
  55. static ALCvoid *mDevice = NULL; // active OpenAL device
  56. static ALCvoid *mContext = NULL; // active OpenAL context
  57. #endif
  58. F32 mAudioChannelVolumes[Audio::AudioVolumeChannels]; // the attenuation for each of the channel types
  59. //-------------------------------------------------------------------------
  60. struct LoopingImage
  61. {
  62. AUDIOHANDLE mHandle;
  63. Resource<AudioBuffer> mBuffer;
  64. Audio::Description mDescription;
  65. AudioSampleEnvironment *mEnvironment;
  66. Point3F mPosition;
  67. Point3F mDirection;
  68. F32 mPitch;
  69. F32 mScore;
  70. U32 mCullTime;
  71. LoopingImage() { clear(); }
  72. void clear()
  73. {
  74. mHandle = NULL_AUDIOHANDLE;
  75. mBuffer = NULL;
  76. dMemset(&mDescription, 0, sizeof(Audio::Description));
  77. mEnvironment = 0;
  78. mPosition.set(0.f,0.f,0.f);
  79. mDirection.set(0.f,1.f,0.f);
  80. mPitch = 1.f;
  81. mScore = 0.f;
  82. mCullTime = 0;
  83. }
  84. };
  85. //-------------------------------------------------------------------------
  86. static F32 mMasterVolume = 1.f; // traped from AL_LISTENER gain (miles has difficulties with 3d sources)
  87. static ALuint mSource[MAX_AUDIOSOURCES]; // ALSources
  88. static ALint mResumePosition[MAX_AUDIOSOURCES]; // Ensures Pause resumes from the correct position
  89. static AUDIOHANDLE mHandle[MAX_AUDIOSOURCES]; // unique handles
  90. static Resource<AudioBuffer> mBuffer[MAX_AUDIOSOURCES]; // each of the playing buffers (needed for AudioThread)
  91. static F32 mScore[MAX_AUDIOSOURCES]; // for figuring out which sources to cull/uncull
  92. static F32 mSourceVolume[MAX_AUDIOSOURCES]; // the samples current un-attenuated gain (not scaled by master/channel gains)
  93. static U32 mType[MAX_AUDIOSOURCES]; // the channel which this source belongs
  94. static AudioSampleEnvironment* mSampleEnvironment[MAX_AUDIOSOURCES]; // currently playing sample environments
  95. static bool mEnvironmentEnabled = false; // environment enabled?
  96. static SimObjectPtr<AudioEnvironment> mCurrentEnvironment; // the last environment set
  97. struct LoopingList : VectorPtr<LoopingImage*>
  98. {
  99. LoopingList() : VectorPtr<LoopingImage*>(__FILE__, __LINE__) { }
  100. LoopingList::iterator findImage(AUDIOHANDLE handle);
  101. void sort();
  102. };
  103. struct StreamingList : VectorPtr<AudioStreamSource*>
  104. {
  105. StreamingList() : VectorPtr<AudioStreamSource*>(__FILE__, __LINE__) { }
  106. StreamingList::iterator findImage(AUDIOHANDLE handle);
  107. void sort();
  108. };
  109. // LoopingList and LoopingFreeList own the images
  110. static LoopingList mLoopingList; // all the looping sources
  111. static LoopingList mLoopingFreeList; // free store
  112. static LoopingList mLoopingInactiveList; // sources which have not been played yet
  113. static LoopingList mLoopingCulledList; // sources which have been culled (alxPlay called)
  114. // StreamingList and StreamingFreeList own the images
  115. static StreamingList mStreamingList; // all the streaming sources
  116. //static StreamingList mStreamingFreeList; // free store
  117. static StreamingList mStreamingInactiveList; // sources which have not been played yet
  118. static StreamingList mStreamingCulledList; // sources which have been culled (alxPlay called)
  119. #define AUDIOHANDLE_LOOPING_BIT (0x80000000)
  120. #define AUDIOHANDLE_STREAMING_BIT (0x40000000)
  121. #define AUDIOHANDLE_INACTIVE_BIT (0x20000000)
  122. #define AUDIOHANDLE_LOADING_BIT (0x10000000)
  123. #define HANDLE_MASK ~(AUDIOHANDLE_LOOPING_BIT | AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT)
  124. // keep the 'AUDIOHANDLE_LOOPING_BIT' on the handle returned to the caller so that
  125. // the handle can quickly be rejected from looping list queries
  126. #define RETURN_MASK ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT)
  127. static AUDIOHANDLE mLastHandle = NULL_AUDIOHANDLE;
  128. // force gain setting for 3d distances
  129. static U32 mNumSources = 0; // total number of sources to work with
  130. static U32 mRequestSources = MAX_AUDIOSOURCES; // number of sources to request from openAL
  131. #define INVALID_SOURCE 0xffffffff
  132. inline bool areEqualHandles(AUDIOHANDLE a, AUDIOHANDLE b)
  133. {
  134. return((a & HANDLE_MASK) == (b & HANDLE_MASK));
  135. }
  136. //-------------------------------------------------------------------------
  137. // Looping image
  138. //-------------------------------------------------------------------------
  139. inline LoopingList::iterator LoopingList::findImage(AUDIOHANDLE handle)
  140. {
  141. if(handle & AUDIOHANDLE_LOOPING_BIT)
  142. {
  143. LoopingList::iterator itr = begin();
  144. while(itr != end())
  145. {
  146. if(areEqualHandles((*itr)->mHandle, handle))
  147. return(itr);
  148. itr++;
  149. }
  150. }
  151. return(0);
  152. }
  153. inline S32 QSORT_CALLBACK loopingImageSort(const void * p1, const void * p2)
  154. {
  155. const LoopingImage * ip1 = *(const LoopingImage**)p1;
  156. const LoopingImage * ip2 = *(const LoopingImage**)p2;
  157. // min->max
  158. return (S32)(ip2->mScore - ip1->mScore);
  159. }
  160. void LoopingList::sort()
  161. {
  162. dQsort(address(), size(), sizeof(LoopingImage*), loopingImageSort);
  163. }
  164. //-------------------------------------------------------------------------
  165. // StreamingList
  166. //-------------------------------------------------------------------------
  167. inline StreamingList::iterator StreamingList::findImage(AUDIOHANDLE handle)
  168. {
  169. if(handle & AUDIOHANDLE_STREAMING_BIT)
  170. {
  171. StreamingList::iterator itr = begin();
  172. while(itr != end())
  173. {
  174. if(areEqualHandles((*itr)->mHandle, handle))
  175. return(itr);
  176. itr++;
  177. }
  178. }
  179. return(0);
  180. }
  181. inline S32 QSORT_CALLBACK streamingSourceSort(const void * p1, const void * p2)
  182. {
  183. const AudioStreamSource * ip1 = *(const AudioStreamSource**)p1;
  184. const AudioStreamSource * ip2 = *(const AudioStreamSource**)p2;
  185. // min->max
  186. return (S32)(ip2->mScore - ip1->mScore);
  187. }
  188. void StreamingList::sort()
  189. {
  190. dQsort(address(), size(), sizeof(AudioStreamSource*), streamingSourceSort);
  191. }
  192. //-------------------------------------------------------------------------
  193. LoopingImage * createLoopingImage()
  194. {
  195. LoopingImage *image;
  196. if (mLoopingFreeList.size())
  197. {
  198. image = mLoopingFreeList.last();
  199. mLoopingFreeList.pop_back();
  200. }
  201. else
  202. image = new LoopingImage;
  203. return(image);
  204. }
  205. //-------------------------------------------------------------------------
  206. AudioStreamSource * createStreamingSource(const char* filename)
  207. {
  208. AudioStreamSource *streamSource = AudioStreamSourceFactory::getNewInstance(filename);
  209. return(streamSource);
  210. }
  211. //-------------------------------------------------------------------------
  212. static AUDIOHANDLE getNewHandle()
  213. {
  214. mLastHandle++;
  215. mLastHandle &= HANDLE_MASK;
  216. if (mLastHandle == NULL_AUDIOHANDLE)
  217. mLastHandle++;
  218. return mLastHandle;
  219. }
  220. //-------------------------------------------------------------------------
  221. // function declarations
  222. void alxLoopingUpdate();
  223. void alxStreamingUpdate();
  224. void alxUpdateScores(bool);
  225. static bool findFreeSource(U32 *index)
  226. {
  227. for(U32 i = 0; i < mNumSources; i++)
  228. if(mHandle[i] == NULL_AUDIOHANDLE)
  229. {
  230. *index = i;
  231. return(true);
  232. }
  233. return(false);
  234. }
  235. //--------------------------------------------------------------------------
  236. // - cull out the min source that is below volume
  237. // - streams/voice/loading streams are all scored > 2
  238. // - volumes are attenuated by channel only
  239. static bool cullSource(U32 *index, F32 volume)
  240. {
  241. alGetError();
  242. F32 minVolume = volume;
  243. S32 best = -1;
  244. for(U32 i = 0; i < mNumSources; i++)
  245. {
  246. if(mScore[i] < minVolume)
  247. {
  248. minVolume = mScore[i];
  249. best = i;
  250. }
  251. }
  252. if(best == -1)
  253. return(false);
  254. // check if culling a looper
  255. LoopingList::iterator itr = mLoopingList.findImage(mHandle[best]);
  256. if(itr)
  257. {
  258. // check if culling an inactive looper
  259. if(mHandle[best] & AUDIOHANDLE_INACTIVE_BIT)
  260. {
  261. AssertFatal(!mLoopingInactiveList.findImage(mHandle[best]), "cullSource: image already in inactive list");
  262. AssertFatal(!mLoopingCulledList.findImage(mHandle[best]), "cullSource: image should not be in culled list");
  263. mLoopingInactiveList.push_back(*itr);
  264. }
  265. else
  266. {
  267. (*itr)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
  268. AssertFatal(!mLoopingCulledList.findImage(mHandle[best]), "cullSource: image already in culled list");
  269. AssertFatal(!mLoopingInactiveList.findImage(mHandle[best]), "cullSource: image should no be in inactive list");
  270. (*itr)->mCullTime = Platform::getRealMilliseconds();
  271. mLoopingCulledList.push_back(*itr);
  272. }
  273. }
  274. // check if culling a streamer
  275. StreamingList::iterator itr2 = mStreamingList.findImage(mHandle[best]);
  276. if(itr2)
  277. {
  278. // check if culling an inactive streamer
  279. if(mHandle[best] & AUDIOHANDLE_INACTIVE_BIT)
  280. {
  281. AssertFatal(!mStreamingInactiveList.findImage(mHandle[best]), "cullSource: image already in inactive list");
  282. AssertFatal(!mStreamingCulledList.findImage(mHandle[best]), "cullSource: image should not be in culled list");
  283. mStreamingInactiveList.push_back(*itr2);
  284. }
  285. else
  286. {
  287. (*itr2)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
  288. AssertFatal(!mStreamingCulledList.findImage(mHandle[best]), "cullSource: image already in culled list");
  289. AssertFatal(!mStreamingInactiveList.findImage(mHandle[best]), "cullSource: image should no be in inactive list");
  290. (*itr2)->freeStream();
  291. (*itr2)->mCullTime = Platform::getRealMilliseconds();
  292. mStreamingCulledList.push_back(*itr2);
  293. }
  294. }
  295. alSourceStop(mSource[best]);
  296. mHandle[best] = NULL_AUDIOHANDLE;
  297. mBuffer[best] = 0;
  298. *index = best;
  299. return(true);
  300. }
  301. //--------------------------------------------------------------------------
  302. /** Compute approximate max volume at a particular distance
  303. ignore cone volume influnces
  304. */
  305. static F32 approximate3DVolume(const Audio::Description& desc, const Point3F &position)
  306. {
  307. Point3F p1;
  308. alxGetListenerPoint3F(AL_POSITION, &p1);
  309. p1 -= position;
  310. F32 distance = p1.magnitudeSafe();
  311. if(distance >= desc.mMaxDistance)
  312. return(0.f);
  313. else if(distance < desc.mReferenceDistance)
  314. return 1.0f;
  315. else
  316. return 1.0f - (distance - desc.mReferenceDistance) / (desc.mMaxDistance - desc.mReferenceDistance);
  317. }
  318. //--------------------------------------------------------------------------
  319. inline U32 alxFindIndex(AUDIOHANDLE handle)
  320. {
  321. for (U32 i=0; i<mNumSources; i++)
  322. if(mHandle[i] && areEqualHandles(mHandle[i], handle))
  323. return i;
  324. return MAX_AUDIOSOURCES;
  325. }
  326. //--------------------------------------------------------------------------
  327. ALuint alxFindSource(AUDIOHANDLE handle)
  328. {
  329. for (U32 i=0; i<mNumSources; i++)
  330. if(mHandle[i] && areEqualHandles(mHandle[i], handle))
  331. return mSource[i];
  332. return(INVALID_SOURCE);
  333. }
  334. //--------------------------------------------------------------------------
  335. /** Determmine if an AUDIOHANDLE is valid.
  336. An AUDIOHANDLE is valid if it is a currently playing source, inactive source,
  337. or a looping source (basically anything where a alxSource??? call will succeed)
  338. */
  339. bool alxIsValidHandle(AUDIOHANDLE handle)
  340. {
  341. if(handle == NULL_AUDIOHANDLE)
  342. return(false);
  343. // inactive sources are valid
  344. U32 idx = alxFindIndex(handle);
  345. if(idx != MAX_AUDIOSOURCES)
  346. {
  347. if(mHandle[idx] & AUDIOHANDLE_INACTIVE_BIT)
  348. return(true);
  349. // if it is active but not playing then it has stopped...
  350. ALint state = AL_STOPPED;
  351. alGetSourcei(mSource[idx], AL_SOURCE_STATE, &state);
  352. return(state == AL_PLAYING);
  353. }
  354. if(mLoopingList.findImage(handle))
  355. return(true);
  356. if(mStreamingList.findImage(handle))
  357. return(true);
  358. return(false);
  359. }
  360. //--------------------------------------------------------------------------
  361. /** Determmine if an AUDIOHANDLE is currently playing
  362. */
  363. bool alxIsPlaying(AUDIOHANDLE handle)
  364. {
  365. if(handle == NULL_AUDIOHANDLE)
  366. return(false);
  367. U32 idx = alxFindIndex(handle);
  368. if(idx == MAX_AUDIOSOURCES)
  369. return(false);
  370. ALint state = 0;
  371. alGetSourcei(mSource[idx], AL_SOURCE_STATE, &state);
  372. return(state == AL_PLAYING);
  373. }
  374. //--------------------------------------------------------------------------
  375. void alxEnvironmentDestroy()
  376. {
  377. /* todo
  378. if(mEnvironment)
  379. {
  380. alDeleteEnvironmentIASIG(1, &mEnvironment);
  381. mEnvironment = 0;
  382. }
  383. */
  384. }
  385. void alxEnvironmentInit()
  386. {
  387. /* todo
  388. alxEnvironmentDestroy();
  389. if(alIsExtensionPresent((const ALubyte *)"AL_EXT_IASIG"))
  390. {
  391. alGenEnvironmentIASIG(1, &mEnvironment);
  392. if(alGetError() != AL_NO_ERROR)
  393. mEnvironment = 0;
  394. }
  395. */
  396. }
  397. //--------------------------------------------------------------------------
  398. // - setup a sources environmental effect settings
  399. static void alxSourceEnvironment(ALuint source, F32 environmentLevel, AudioSampleEnvironment * env)
  400. {
  401. // environment level is on the AudioDatablock
  402. /* todo
  403. alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, environmentLevel);
  404. */
  405. if(!env)
  406. return;
  407. /* todo
  408. alSourcei(source, AL_ENV_SAMPLE_DIRECT_EXT, env->mDirect);
  409. alSourcei(source, AL_ENV_SAMPLE_DIRECT_HF_EXT, env->mDirectHF);
  410. alSourcei(source, AL_ENV_SAMPLE_ROOM_EXT, env->mRoom);
  411. alSourcei(source, AL_ENV_SAMPLE_ROOM_HF_EXT, env->mRoomHF);
  412. alSourcei(source, AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, env->mOutsideVolumeHF);
  413. alSourcei(source, AL_ENV_SAMPLE_FLAGS_EXT, env->mFlags);
  414. alSourcef(source, AL_ENV_SAMPLE_OBSTRUCTION_EXT, env->mObstruction);
  415. alSourcef(source, AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, env->mObstructionLFRatio);
  416. alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_EXT, env->mOcclusion);
  417. alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, env->mOcclusionLFRatio);
  418. alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, env->mOcclusionRoomRatio);
  419. alSourcef(source, AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, env->mRoomRolloff);
  420. alSourcef(source, AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, env->mAirAbsorption);
  421. */
  422. }
  423. static void alxSourceEnvironment(ALuint source, LoopingImage * image)
  424. {
  425. AssertFatal(image, "alxSourceEnvironment: invalid looping image");
  426. if(image->mDescription.mIs3D)
  427. alxSourceEnvironment(source, image->mDescription.mEnvironmentLevel, image->mEnvironment);
  428. }
  429. static void alxSourceEnvironment(ALuint source, AudioStreamSource * image)
  430. {
  431. AssertFatal(image, "alxSourceEnvironment: invalid looping image");
  432. if(image->mDescription.mIs3D)
  433. alxSourceEnvironment(source, image->mDescription.mEnvironmentLevel, image->mEnvironment);
  434. }
  435. //--------------------------------------------------------------------------
  436. // setup a source to play... loopers have pitch cached
  437. // - by default, pitch is 1x (settings not defined in description)
  438. // - all the settings are cached by openAL (miles version), so no worries setting them here
  439. static void alxSourcePlay(ALuint source, Resource<AudioBuffer> buffer, const Audio::Description& desc, const MatrixF *transform)
  440. {
  441. alSourcei(source, AL_BUFFER, buffer->getALBuffer());
  442. alSourcef(source, AL_GAIN, Audio::linearToDB(desc.mVolume * mAudioChannelVolumes[desc.mVolumeChannel] * mMasterVolume));
  443. alSourcei(source, AL_LOOPING, desc.mIsLooping ? AL_TRUE : AL_FALSE);
  444. alSourcef(source, AL_PITCH, 1.f);
  445. alSourcei(source, AL_CONE_INNER_ANGLE, desc.mConeInsideAngle);
  446. alSourcei(source, AL_CONE_OUTER_ANGLE, desc.mConeOutsideAngle);
  447. alSourcef(source, AL_CONE_OUTER_GAIN, desc.mConeOutsideVolume);
  448. if(transform != NULL)
  449. {
  450. #ifdef REL_WORKAROUND
  451. alSourcei(source, AL_SOURCE_ABSOLUTE, AL_TRUE);
  452. #else
  453. alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
  454. #endif
  455. Point3F p;
  456. transform->getColumn(3, &p);
  457. alSource3f(source, AL_POSITION, p.x, p.y, p.z);
  458. //Always use ConeVector (which is tied to transform)
  459. alSource3f(source, AL_DIRECTION, desc.mConeVector.x, desc.mConeVector.y, desc.mConeVector.z);
  460. }
  461. else
  462. {
  463. // 2D sound
  464. alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
  465. alSource3f(source, AL_POSITION, 0.0f, 0.0f, 1.0f);
  466. }
  467. alSourcef(source, AL_REFERENCE_DISTANCE, desc.mReferenceDistance);
  468. alSourcef(source, AL_MAX_DISTANCE, desc.mMaxDistance);
  469. /* todo
  470. // environmental audio stuff:
  471. alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, desc.mEnvironmentLevel);
  472. if(desc.mEnvironmentLevel != 0.f)
  473. alSourceResetEnvironment_EXT(source);
  474. */
  475. }
  476. // helper for looping images
  477. static void alxSourcePlay(ALuint source, LoopingImage * image)
  478. {
  479. AssertFatal(image, "alxSourcePlay: invalid looping image");
  480. // 3d source? need position/direction
  481. if(image->mDescription.mIs3D)
  482. {
  483. MatrixF transform(true);
  484. transform.setColumn(3, image->mPosition);
  485. transform.setRow(1, image->mDirection);
  486. alxSourcePlay(source, image->mBuffer, image->mDescription, &transform);
  487. }
  488. else
  489. {
  490. // 2d source
  491. alxSourcePlay(source, image->mBuffer, image->mDescription, 0);
  492. }
  493. }
  494. //--------------------------------------------------------------------------
  495. // setup a streaming source to play
  496. static void alxSourcePlay(AudioStreamSource *streamSource)
  497. {
  498. ALuint source = streamSource->mSource;
  499. Audio::Description& desc = streamSource->mDescription;
  500. streamSource->initStream();
  501. alSourcef(source, AL_GAIN, Audio::linearToDB(desc.mVolume * mAudioChannelVolumes[desc.mVolumeChannel] * mMasterVolume));
  502. // alSourcei(source, AL_LOOPING, AL_FALSE);
  503. alSourcef(source, AL_PITCH, 1.f);
  504. alSourcei(source, AL_CONE_INNER_ANGLE, desc.mConeInsideAngle);
  505. alSourcei(source, AL_CONE_OUTER_ANGLE, desc.mConeOutsideAngle);
  506. alSourcef(source, AL_CONE_OUTER_GAIN, desc.mConeOutsideVolume);
  507. if(streamSource->mDescription.mIs3D)
  508. {
  509. MatrixF transform(true);
  510. transform.setColumn(3, streamSource->mPosition);
  511. transform.setRow(1, streamSource->mDirection);
  512. #ifdef REL_WORKAROUND
  513. alSourcei(source, AL_SOURCE_ABSOLUTE, AL_TRUE);
  514. #else
  515. alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
  516. #endif
  517. Point3F p;
  518. transform.getColumn(3, &p);
  519. alSource3f(source, AL_POSITION, p.x, p.y, p.z);
  520. //Always use ConeVector (which is tied to transform)
  521. alSource3f(source, AL_DIRECTION, desc.mConeVector.x, desc.mConeVector.y, desc.mConeVector.z);
  522. }
  523. else
  524. {
  525. // 2D sound
  526. // JMQ: slam the stream source's position to our desired value
  527. streamSource->mPosition = Point3F(0.0f, 0.0f, 1.0f);
  528. alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
  529. alSource3f(source, AL_POSITION,
  530. streamSource->mPosition.x,
  531. streamSource->mPosition.y,
  532. streamSource->mPosition.z);
  533. }
  534. alSourcef(source, AL_REFERENCE_DISTANCE, desc.mReferenceDistance);
  535. alSourcef(source, AL_MAX_DISTANCE, desc.mMaxDistance);
  536. /* todo
  537. // environmental audio stuff:
  538. alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, desc.mEnvironmentLevel);
  539. if(desc.mEnvironmentLevel != 0.f)
  540. alSourceResetEnvironment_EXT(source);
  541. */
  542. }
  543. //--------------------------------------------------------------------------
  544. AUDIOHANDLE alxCreateSource(const Audio::Description& desc,
  545. const char *filename,
  546. const MatrixF *transform,
  547. AudioSampleEnvironment *sampleEnvironment)
  548. {
  549. if (!mContext)
  550. return NULL_AUDIOHANDLE;
  551. if( filename == NULL || filename == StringTable->EmptyString )
  552. return NULL_AUDIOHANDLE;
  553. F32 volume = desc.mVolume;
  554. // calculate an approximate attenuation for 3d sounds
  555. if(transform && desc.mIs3D)
  556. {
  557. Point3F position;
  558. transform->getColumn(3, &position);
  559. volume *= approximate3DVolume(desc, position);
  560. }
  561. // check the type specific volume
  562. AssertFatal(desc.mVolumeChannel < Audio::AudioVolumeChannels, "alxCreateSource: invalid volume channel for source");
  563. if(desc.mVolumeChannel >= Audio::AudioVolumeChannels)
  564. return(NULL_AUDIOHANDLE);
  565. // done if channel is muted (and not a looper)
  566. if(!desc.mIsLooping && !desc.mIsStreaming && (mAudioChannelVolumes[desc.mVolumeChannel] == 0.f))
  567. return(NULL_AUDIOHANDLE);
  568. // scale volume by channel attenuation
  569. volume *= mAudioChannelVolumes[desc.mVolumeChannel];
  570. // non-loopers don't add if < minvolume
  571. if(!desc.mIsLooping && !desc.mIsStreaming && (volume <= MIN_GAIN))
  572. return(NULL_AUDIOHANDLE);
  573. U32 index = MAX_AUDIOSOURCES;
  574. // try and find an available source: 0 volume loopers get added to inactive list
  575. if(volume > MIN_GAIN)
  576. {
  577. if(!findFreeSource(&index))
  578. {
  579. alxUpdateScores(true);
  580. // scores do not include master volume
  581. if(!cullSource(&index, volume))
  582. index = MAX_AUDIOSOURCES;
  583. }
  584. }
  585. // make sure that loopers are added
  586. if(index == MAX_AUDIOSOURCES)
  587. {
  588. if(desc.mIsLooping && !(desc.mIsStreaming))
  589. {
  590. Resource<AudioBuffer> buffer = AudioBuffer::find(filename);
  591. if(!(bool)buffer)
  592. return(NULL_AUDIOHANDLE);
  593. // create the inactive looping image
  594. LoopingImage * image = createLoopingImage();
  595. image->mHandle = getNewHandle() | AUDIOHANDLE_LOOPING_BIT | AUDIOHANDLE_INACTIVE_BIT;
  596. image->mBuffer = buffer;
  597. image->mDescription = desc;
  598. image->mScore = volume;
  599. image->mEnvironment = sampleEnvironment;
  600. // grab position/direction if 3d source
  601. if(transform)
  602. {
  603. transform->getColumn(3, &image->mPosition);
  604. transform->getColumn(1, &image->mDirection);
  605. }
  606. AssertFatal(!mLoopingInactiveList.findImage(image->mHandle), "alxCreateSource: handle in inactive list");
  607. AssertFatal(!mLoopingCulledList.findImage(image->mHandle), "alxCreateSource: handle in culled list");
  608. // add to the looping and inactive lists
  609. mLoopingList.push_back(image);
  610. mLoopingInactiveList.push_back(image);
  611. return(image->mHandle & RETURN_MASK);
  612. }
  613. else
  614. return(NULL_AUDIOHANDLE);
  615. }
  616. // make sure that streamers are added
  617. if(index == MAX_AUDIOSOURCES)
  618. {
  619. if(desc.mIsStreaming)
  620. {
  621. // create the inactive audio stream
  622. AudioStreamSource * streamSource = createStreamingSource(filename);
  623. if (streamSource)
  624. {
  625. streamSource->mHandle = getNewHandle() | AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_INACTIVE_BIT;
  626. streamSource->mSource = NULL;
  627. streamSource->mDescription = desc;
  628. streamSource->mScore = volume;
  629. streamSource->mEnvironment = sampleEnvironment;
  630. // grab position/direction if 3d source
  631. if(transform)
  632. {
  633. transform->getColumn(3, &streamSource->mPosition);
  634. transform->getColumn(1, &streamSource->mDirection);
  635. }
  636. AssertFatal(!mStreamingInactiveList.findImage(streamSource->mHandle), "alxCreateSource: handle in inactive list");
  637. AssertFatal(!mStreamingCulledList.findImage(streamSource->mHandle), "alxCreateSource: handle in culled list");
  638. // add to the streaming and inactive lists
  639. mStreamingList.push_back(streamSource);
  640. mStreamingInactiveList.push_back(streamSource);
  641. return(streamSource->mHandle & RETURN_MASK);
  642. }
  643. else
  644. return NULL_AUDIOHANDLE;
  645. }
  646. else
  647. return(NULL_AUDIOHANDLE);
  648. }
  649. // clear the error state
  650. alGetError();
  651. // grab the buffer
  652. Resource<AudioBuffer> buffer;
  653. if(!(desc.mIsStreaming)) {
  654. buffer = AudioBuffer::find(filename);
  655. if((bool)buffer == false)
  656. return NULL_AUDIOHANDLE;
  657. }
  658. // init the source (created inactive) and store needed values
  659. mHandle[index] = getNewHandle() | AUDIOHANDLE_INACTIVE_BIT;
  660. mType[index] = desc.mVolumeChannel;
  661. if(!(desc.mIsStreaming)) {
  662. mBuffer[index] = buffer;
  663. }
  664. mScore[index] = volume;
  665. mSourceVolume[index] = desc.mVolume;
  666. mSampleEnvironment[index] = sampleEnvironment;
  667. ALuint source = mSource[index];
  668. // setup play info
  669. if(!desc.mIsStreaming)
  670. alxSourcePlay(source, buffer, desc, desc.mIs3D ? transform : 0);
  671. if(mEnvironmentEnabled)
  672. alxSourceEnvironment(source, desc.mEnvironmentLevel, sampleEnvironment);
  673. // setup a LoopingImage ONLY if the sound is a looper:
  674. if(desc.mIsLooping && !(desc.mIsStreaming))
  675. {
  676. mHandle[index] |= AUDIOHANDLE_LOOPING_BIT;
  677. LoopingImage * image = createLoopingImage();
  678. image->mHandle = mHandle[index];
  679. image->mBuffer = buffer;
  680. image->mDescription = desc;
  681. image->mScore = volume;
  682. image->mEnvironment = sampleEnvironment;
  683. // grab position/direction
  684. if(transform)
  685. {
  686. transform->getColumn(3, &image->mPosition);
  687. transform->getColumn(1, &image->mDirection);
  688. }
  689. AssertFatal(!mLoopingInactiveList.findImage(image->mHandle), "alxCreateSource: handle in inactive list");
  690. AssertFatal(!mLoopingCulledList.findImage(image->mHandle), "alxCreateSource: handle in culled list");
  691. // add to the looping list
  692. mLoopingList.push_back(image);
  693. }
  694. // setup a AudioStreamSource ONLY if the sound is a streamer:
  695. if(desc.mIsStreaming)
  696. {
  697. // Intangir> why is loading bit never used anywhere else?
  698. // comes in handy for my oggmixedstream
  699. // (prevents it from being deleted before it is loaded)
  700. mHandle[index] |= AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_LOADING_BIT;
  701. AudioStreamSource * streamSource = createStreamingSource(filename);
  702. if (streamSource)
  703. {
  704. streamSource->mHandle = mHandle[index];
  705. streamSource->mSource = mSource[index];
  706. streamSource->mDescription = desc;
  707. streamSource->mScore = volume;
  708. streamSource->mEnvironment = sampleEnvironment;
  709. // grab position/direction
  710. if(transform)
  711. {
  712. transform->getColumn(3, &streamSource->mPosition);
  713. transform->getColumn(1, &streamSource->mDirection);
  714. }
  715. AssertFatal(!mStreamingInactiveList.findImage(streamSource->mHandle), "alxCreateSource: handle in inactive list");
  716. AssertFatal(!mStreamingCulledList.findImage(streamSource->mHandle), "alxCreateSource: handle in culled list");
  717. alxSourcePlay(streamSource);
  718. // add to the looping list
  719. mStreamingList.push_back(streamSource);
  720. }
  721. else
  722. {
  723. mSampleEnvironment[index] = 0;
  724. mHandle[index] = NULL_AUDIOHANDLE;
  725. mBuffer[index] = 0;
  726. return NULL_AUDIOHANDLE;
  727. }
  728. }
  729. // clear off all but looping bit
  730. return(mHandle[index] & RETURN_MASK);
  731. }
  732. //--------------------------------------------------------------------------
  733. AUDIOHANDLE alxCreateSource(const AudioAsset *profile, const MatrixF *transform)
  734. {
  735. if (profile == NULL)
  736. return NULL_AUDIOHANDLE;
  737. return alxCreateSource(profile->getAudioDescription(), profile->getAudioFile(), transform, NULL );
  738. }
  739. //--------------------------------------------------------------------------
  740. extern void threadPlay(AudioBuffer * buffer, AUDIOHANDLE handle);
  741. AUDIOHANDLE alxPlay(AUDIOHANDLE handle)
  742. {
  743. U32 index = alxFindIndex(handle);
  744. if(index != MAX_AUDIOSOURCES)
  745. {
  746. // play if not already playing
  747. if(mHandle[index] & AUDIOHANDLE_INACTIVE_BIT)
  748. {
  749. mHandle[index] &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
  750. // make sure the looping image also clears it's inactive bit
  751. LoopingList::iterator itr = mLoopingList.findImage(handle);
  752. if(itr)
  753. (*itr)->mHandle &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
  754. // make sure the streaming image also clears it's inactive bit
  755. StreamingList::iterator itr2 = mStreamingList.findImage(handle);
  756. if(itr2)
  757. (*itr2)->mHandle &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
  758. alSourcePlay(mSource[index]);
  759. return(handle);
  760. }
  761. }
  762. else
  763. {
  764. // move inactive loopers to the culled list, try to start the sound
  765. LoopingList::iterator itr = mLoopingInactiveList.findImage(handle);
  766. if(itr)
  767. {
  768. AssertFatal(!mLoopingCulledList.findImage(handle), "alxPlay: image already in culled list");
  769. mLoopingCulledList.push_back(*itr);
  770. mLoopingInactiveList.erase_fast(itr);
  771. alxLoopingUpdate();
  772. return(handle);
  773. }
  774. else if(mLoopingCulledList.findImage(handle))
  775. {
  776. alxLoopingUpdate();
  777. return(handle);
  778. }
  779. else
  780. return(NULL_AUDIOHANDLE);
  781. #if 0
  782. // move inactive streamers to the culled list, try to start the sound
  783. StreamingList::iterator itr2 = mStreamingInactiveList.findImage(handle);
  784. if(itr2)
  785. {
  786. AssertFatal(!mStreamingCulledList.findImage(handle), "alxPlay: image already in culled list");
  787. (*itr2)->freeStream();
  788. mStreamingCulledList.push_back(*itr2);
  789. mStreamingInactiveList.erase_fast(itr2);
  790. alxStreamingUpdate();
  791. return(handle);
  792. }
  793. else if(mStreamingCulledList.findImage(handle))
  794. {
  795. alxStreamingUpdate();
  796. return(handle);
  797. }
  798. else
  799. return(NULL_AUDIOHANDLE);
  800. #endif
  801. }
  802. return(handle);
  803. }
  804. //--------------------------------------------------------------------------
  805. // helper function.. create a source and play it
  806. AUDIOHANDLE alxPlay(const AudioAsset *profile, const MatrixF *transform, const Point3F* /*velocity*/)
  807. {
  808. if(profile == NULL)
  809. return NULL_AUDIOHANDLE;
  810. AUDIOHANDLE handle = alxCreateSource(profile->getAudioDescription(), profile->getAudioFile(), transform, NULL);
  811. if(handle != NULL_AUDIOHANDLE)
  812. return(alxPlay(handle));
  813. return(handle);
  814. }
  815. bool alxPause( AUDIOHANDLE handle )
  816. {
  817. if(handle == NULL_AUDIOHANDLE)
  818. return false;
  819. U32 index = alxFindIndex( handle );
  820. alSourcePause( mSource[index] );
  821. ALint state;
  822. alGetSourcei(mSource[index], AL_SOURCE_STATE, &state);
  823. if( state==AL_PAUSED)
  824. {
  825. mResumePosition[index] = -1;
  826. return true;
  827. }
  828. alGetSourcei(mSource[index], AL_SAMPLE_OFFSET, &mResumePosition[index]);
  829. return alxCheckError("alxPause()","alGetSourcei");
  830. }
  831. void alxUnPause( AUDIOHANDLE handle )
  832. {
  833. if(handle == NULL_AUDIOHANDLE)
  834. return;
  835. U32 index = alxFindIndex(handle);
  836. ALuint source = mSource[index];
  837. if( mResumePosition[index] != -1 )
  838. {
  839. alSourcei( source, AL_SAMPLE_OFFSET, mResumePosition[index]);
  840. mResumePosition[index] = -1;
  841. }
  842. alxCheckError("alxUnPause()","alSourcei");
  843. alSourcePlay( source );
  844. alxCheckError("alxUnPause()","alSourcePlay");
  845. }
  846. //--------------------------------------------------------------------------
  847. void alxStop(AUDIOHANDLE handle)
  848. {
  849. U32 index = alxFindIndex(handle);
  850. // stop it
  851. if(index != MAX_AUDIOSOURCES)
  852. {
  853. if(!(mHandle[index] & AUDIOHANDLE_INACTIVE_BIT))
  854. {
  855. alSourceStop(mSource[index]);
  856. }
  857. alSourcei(mSource[index], AL_BUFFER, AL_NONE);
  858. mSampleEnvironment[index] = 0;
  859. mHandle[index] = NULL_AUDIOHANDLE;
  860. mBuffer[index] = 0;
  861. }
  862. // remove loopingImage and add it to the free list
  863. LoopingList::iterator itr = mLoopingList.findImage(handle);
  864. if(itr)
  865. {
  866. // remove from inactive/culled list
  867. if((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
  868. {
  869. LoopingList::iterator tmp = mLoopingInactiveList.findImage(handle);
  870. // inactive?
  871. if(tmp)
  872. mLoopingInactiveList.erase_fast(tmp);
  873. else
  874. {
  875. //culled?
  876. tmp = mLoopingCulledList.findImage(handle);
  877. AssertFatal(tmp, "alxStop: failed to find inactive looping source");
  878. mLoopingCulledList.erase_fast(tmp);
  879. }
  880. }
  881. AssertFatal(!mLoopingInactiveList.findImage((*itr)->mHandle), "alxStop: handle in inactive list");
  882. AssertFatal(!mLoopingCulledList.findImage((*itr)->mHandle), "alxStop: handle in culled list");
  883. // remove it
  884. (*itr)->clear();
  885. mLoopingFreeList.push_back(*itr);
  886. mLoopingList.erase_fast(itr);
  887. }
  888. // remove streamingImage and add it to the free list
  889. StreamingList::iterator itr2 = mStreamingList.findImage(handle);
  890. if(itr2)
  891. {
  892. // remove from inactive/culled list
  893. if((*itr2)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
  894. {
  895. StreamingList::iterator tmp = mStreamingInactiveList.findImage(handle);
  896. // inactive?
  897. if(tmp)
  898. mStreamingInactiveList.erase_fast(tmp);
  899. else
  900. {
  901. //culled?
  902. tmp = mStreamingCulledList.findImage(handle);
  903. AssertFatal(tmp, "alxStop: failed to find inactive looping source");
  904. mStreamingCulledList.erase_fast(tmp);
  905. }
  906. }
  907. AssertFatal(!mStreamingInactiveList.findImage((*itr2)->mHandle), "alxStop: handle in inactive list");
  908. AssertFatal(!mStreamingCulledList.findImage((*itr2)->mHandle), "alxStop: handle in culled list");
  909. // remove it
  910. (*itr2)->freeStream();
  911. delete(*itr2);
  912. mStreamingList.erase_fast(itr2);
  913. }
  914. }
  915. //--------------------------------------------------------------------------
  916. void alxStopAll()
  917. {
  918. // stop all open sources
  919. for(S32 i = mNumSources - 1; i >= 0; i--)
  920. if(mHandle[i] != NULL_AUDIOHANDLE)
  921. alxStop(mHandle[i]);
  922. // stop all looping sources
  923. while(mLoopingList.size())
  924. alxStop(mLoopingList.last()->mHandle);
  925. // stop all streaming sources
  926. while(mStreamingList.size())
  927. alxStop(mStreamingList.last()->mHandle);
  928. }
  929. void alxLoopSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
  930. {
  931. LoopingList::iterator itr = mLoopingList.findImage(handle);
  932. if(itr)
  933. {
  934. switch(pname)
  935. {
  936. case AL_GAIN:
  937. (*itr)->mDescription.mVolume = Audio::DBToLinear(value);
  938. break;
  939. case AL_GAIN_LINEAR:
  940. (*itr)->mDescription.mVolume = value;
  941. break;
  942. case AL_PITCH:
  943. (*itr)->mPitch = value;
  944. break;
  945. case AL_REFERENCE_DISTANCE:
  946. (*itr)->mDescription.mReferenceDistance = value;
  947. break;
  948. case AL_CONE_OUTER_GAIN:
  949. (*itr)->mDescription.mMaxDistance = value;
  950. break;
  951. }
  952. }
  953. }
  954. void alxLoopSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
  955. {
  956. LoopingList::iterator itr = mLoopingList.findImage(handle);
  957. if(itr)
  958. {
  959. switch(pname)
  960. {
  961. case AL_POSITION:
  962. (*itr)->mPosition.x = value1;
  963. (*itr)->mPosition.y = value2;
  964. (*itr)->mPosition.z = value3;
  965. break;
  966. case AL_DIRECTION:
  967. (*itr)->mDirection.x = value1;
  968. (*itr)->mDirection.y = value2;
  969. (*itr)->mDirection.z = value3;
  970. break;
  971. }
  972. }
  973. }
  974. void alxLoopSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
  975. {
  976. LoopingList::iterator itr = mLoopingList.findImage(handle);
  977. if(itr)
  978. {
  979. switch(pname)
  980. {
  981. //case AL_SOURCE_AMBIENT:
  982. // (*itr)->mDescription.mIs3D = value;
  983. // break;
  984. case AL_CONE_INNER_ANGLE:
  985. (*itr)->mDescription.mConeInsideAngle = value;
  986. break;
  987. case AL_CONE_OUTER_ANGLE:
  988. (*itr)->mDescription.mConeOutsideAngle = value;
  989. break;
  990. }
  991. }
  992. }
  993. void alxLoopGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
  994. {
  995. LoopingList::iterator itr = mLoopingList.findImage(handle);
  996. if(itr)
  997. {
  998. switch(pname)
  999. {
  1000. case AL_GAIN:
  1001. *value = Audio::linearToDB((*itr)->mDescription.mVolume);
  1002. break;
  1003. case AL_GAIN_LINEAR:
  1004. *value = (*itr)->mDescription.mVolume;
  1005. break;
  1006. case AL_PITCH:
  1007. *value = (*itr)->mPitch;
  1008. break;
  1009. case AL_REFERENCE_DISTANCE:
  1010. *value = (*itr)->mDescription.mReferenceDistance;
  1011. break;
  1012. case AL_CONE_OUTER_GAIN:
  1013. *value = (*itr)->mDescription.mMaxDistance;
  1014. break;
  1015. }
  1016. }
  1017. }
  1018. void alxLoopGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
  1019. {
  1020. LoopingList::iterator itr = mLoopingList.findImage(handle);
  1021. if(itr)
  1022. {
  1023. switch(pname)
  1024. {
  1025. case AL_POSITION:
  1026. *value1 = (*itr)->mPosition.x;
  1027. *value2 = (*itr)->mPosition.y;
  1028. *value3 = (*itr)->mPosition.z;
  1029. break;
  1030. case AL_DIRECTION:
  1031. *value1 = (*itr)->mDirection.x;
  1032. *value2 = (*itr)->mDirection.y;
  1033. *value3 = (*itr)->mDirection.z;
  1034. break;
  1035. }
  1036. }
  1037. }
  1038. void alxLoopGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
  1039. {
  1040. LoopingList::iterator itr = mLoopingList.findImage(handle);
  1041. if(itr)
  1042. {
  1043. switch(pname)
  1044. {
  1045. //case AL_SOURCE_AMBIENT:
  1046. // *value = (*itr)->mDescription.mIs3D;
  1047. // break;
  1048. case AL_LOOPING:
  1049. *value = true;
  1050. break;
  1051. case AL_CONE_INNER_ANGLE:
  1052. *value = (*itr)->mDescription.mConeInsideAngle;
  1053. break;
  1054. case AL_CONE_OUTER_ANGLE:
  1055. *value = (*itr)->mDescription.mConeOutsideAngle;
  1056. break;
  1057. }
  1058. }
  1059. }
  1060. //------------------------------------------------------
  1061. void alxStreamSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
  1062. {
  1063. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1064. if(itr)
  1065. {
  1066. switch(pname)
  1067. {
  1068. case AL_GAIN:
  1069. (*itr)->mDescription.mVolume = Audio::DBToLinear(value);
  1070. break;
  1071. case AL_GAIN_LINEAR:
  1072. (*itr)->mDescription.mVolume = value;
  1073. break;
  1074. case AL_PITCH:
  1075. (*itr)->mPitch = value;
  1076. break;
  1077. case AL_REFERENCE_DISTANCE:
  1078. (*itr)->mDescription.mReferenceDistance = value;
  1079. break;
  1080. case AL_CONE_OUTER_GAIN:
  1081. (*itr)->mDescription.mMaxDistance = value;
  1082. break;
  1083. }
  1084. }
  1085. }
  1086. void alxStreamSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
  1087. {
  1088. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1089. if(itr)
  1090. {
  1091. switch(pname)
  1092. {
  1093. case AL_POSITION:
  1094. (*itr)->mPosition.x = value1;
  1095. (*itr)->mPosition.y = value2;
  1096. (*itr)->mPosition.z = value3;
  1097. break;
  1098. case AL_DIRECTION:
  1099. (*itr)->mDirection.x = value1;
  1100. (*itr)->mDirection.y = value2;
  1101. (*itr)->mDirection.z = value3;
  1102. break;
  1103. }
  1104. }
  1105. }
  1106. void alxStreamSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
  1107. {
  1108. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1109. if(itr)
  1110. {
  1111. switch(pname)
  1112. {
  1113. //case AL_SOURCE_AMBIENT:
  1114. // (*itr)->mDescription.mIs3D = value;
  1115. // break;
  1116. case AL_CONE_INNER_ANGLE:
  1117. (*itr)->mDescription.mConeInsideAngle = value;
  1118. break;
  1119. case AL_CONE_OUTER_ANGLE:
  1120. (*itr)->mDescription.mConeOutsideAngle = value;
  1121. break;
  1122. }
  1123. }
  1124. }
  1125. void alxStreamGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
  1126. {
  1127. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1128. if(itr)
  1129. {
  1130. switch(pname)
  1131. {
  1132. case AL_GAIN:
  1133. *value = Audio::linearToDB((*itr)->mDescription.mVolume);
  1134. break;
  1135. case AL_GAIN_LINEAR:
  1136. *value = (*itr)->mDescription.mVolume;
  1137. break;
  1138. case AL_PITCH:
  1139. *value = (*itr)->mPitch;
  1140. break;
  1141. case AL_REFERENCE_DISTANCE:
  1142. *value = (*itr)->mDescription.mReferenceDistance;
  1143. break;
  1144. case AL_CONE_OUTER_GAIN:
  1145. *value = (*itr)->mDescription.mMaxDistance;
  1146. break;
  1147. }
  1148. }
  1149. }
  1150. void alxStreamGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
  1151. {
  1152. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1153. if(itr)
  1154. {
  1155. switch(pname)
  1156. {
  1157. case AL_POSITION:
  1158. *value1 = (*itr)->mPosition.x;
  1159. *value2 = (*itr)->mPosition.y;
  1160. *value3 = (*itr)->mPosition.z;
  1161. break;
  1162. case AL_DIRECTION:
  1163. *value1 = (*itr)->mDirection.x;
  1164. *value2 = (*itr)->mDirection.y;
  1165. *value3 = (*itr)->mDirection.z;
  1166. break;
  1167. }
  1168. }
  1169. }
  1170. void alxStreamGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
  1171. {
  1172. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1173. if(itr)
  1174. {
  1175. switch(pname)
  1176. {
  1177. //case AL_SOURCE_AMBIENT:
  1178. // *value = (*itr)->mDescription.mIs3D;
  1179. // break;
  1180. case AL_LOOPING:
  1181. *value = true;
  1182. break;
  1183. case AL_CONE_INNER_ANGLE:
  1184. *value = (*itr)->mDescription.mConeInsideAngle;
  1185. break;
  1186. case AL_CONE_OUTER_ANGLE:
  1187. *value = (*itr)->mDescription.mConeOutsideAngle;
  1188. break;
  1189. }
  1190. }
  1191. }
  1192. //--------------------------------------------------------------------------
  1193. // AL get/set methods: Source
  1194. //--------------------------------------------------------------------------
  1195. // - only need to worry about playing sources.. proper volume gets set on
  1196. // create source (so, could get out of sync if someone changes volume between
  1197. // a createSource and playSource call...)
  1198. void alxUpdateTypeGain(U32 type)
  1199. {
  1200. for(U32 i = 0; i < mNumSources; i++)
  1201. {
  1202. if(mHandle[i] == NULL_AUDIOHANDLE)
  1203. continue;
  1204. if(type != mType[i])
  1205. continue;
  1206. ALint state = AL_STOPPED;
  1207. alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
  1208. if(state == AL_PLAYING)
  1209. {
  1210. // volume = SourceVolume * ChannelVolume * MasterVolume
  1211. F32 vol = mClampF(mSourceVolume[i] * mAudioChannelVolumes[mType[i]] * mMasterVolume, 0.f, 1.f);
  1212. alSourcef(mSource[i], AL_GAIN, Audio::linearToDB(vol) );
  1213. }
  1214. }
  1215. }
  1216. void alxSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
  1217. {
  1218. ALuint source = alxFindSource(handle);
  1219. if(source != INVALID_SOURCE)
  1220. {
  1221. // ensure gain_linear
  1222. if(pname == AL_GAIN)
  1223. {
  1224. value = Audio::DBToLinear(value);
  1225. pname = AL_GAIN_LINEAR;
  1226. }
  1227. // need to process gain settings (so source can be affected by channel/master gains)
  1228. if(pname == AL_GAIN_LINEAR)
  1229. {
  1230. U32 idx = alxFindIndex(handle);
  1231. AssertFatal(idx != MAX_AUDIOSOURCES, "alxSourcef: handle not located for found source");
  1232. if(idx == MAX_AUDIOSOURCES)
  1233. return;
  1234. // update the stored value
  1235. mSourceVolume[idx] = value;
  1236. // volume = SourceVolume * ChannelVolume * MasterVolume
  1237. // #ifdef REL_WORKAROUND
  1238. // ALint val = AL_TRUE;
  1239. // alGetSourcei(source, AL_SOURCE_ABSOLUTE, &val);
  1240. // if(val == AL_FALSE)
  1241. // #else
  1242. // ALint val = AL_FALSE;
  1243. // alGetSourcei(source, AL_SOURCE_RELATIVE, &val);
  1244. // if(val == AL_TRUE)
  1245. // #endif
  1246. {
  1247. F32 vol = mClampF(mSourceVolume[idx] * mAudioChannelVolumes[mType[idx]] * mMasterVolume, 0.f, 1.f);
  1248. alSourcef(source, AL_GAIN, Audio::linearToDB(vol) );
  1249. }
  1250. }
  1251. else
  1252. alSourcef(source, pname, value);
  1253. }
  1254. alxLoopSourcef(handle, pname, value);
  1255. alxStreamSourcef(handle, pname, value);
  1256. }
  1257. void alxSourcefv(AUDIOHANDLE handle, ALenum pname, ALfloat *values)
  1258. {
  1259. ALuint source = alxFindSource(handle);
  1260. if(source != INVALID_SOURCE)
  1261. alSourcefv(source, pname, values);
  1262. if((pname == AL_POSITION) || (pname == AL_DIRECTION) || (pname == AL_VELOCITY)) {
  1263. alxLoopSource3f(handle, pname, values[0], values[1], values[2]);
  1264. alxStreamSource3f(handle, pname, values[0], values[1], values[2]);
  1265. }
  1266. }
  1267. void alxSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
  1268. {
  1269. ALuint source = alxFindSource(handle);
  1270. if(source != INVALID_SOURCE)
  1271. {
  1272. ALfloat values[3];
  1273. values[0] = value1;
  1274. values[1] = value2;
  1275. values[2] = value3;
  1276. alSourcefv(source, pname, values);
  1277. }
  1278. alxLoopSource3f(handle, pname, value1, value2, value3);
  1279. alxStreamSource3f(handle, pname, value1, value2, value3);
  1280. }
  1281. void alxSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
  1282. {
  1283. ALuint source = alxFindSource(handle);
  1284. if(source != INVALID_SOURCE)
  1285. alSourcei(source, pname, value);
  1286. alxLoopSourcei(handle, pname, value);
  1287. alxStreamSourcei(handle, pname, value);
  1288. }
  1289. // sets the position and direction of the source
  1290. void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform)
  1291. {
  1292. ALuint source = alxFindSource(handle);
  1293. Point3F pos;
  1294. transform->getColumn(3, &pos);
  1295. Point3F dir;
  1296. transform->getColumn(1, &dir);
  1297. if(source != INVALID_SOURCE)
  1298. {
  1299. // OpenAL uses a Right-Handed corrdinate system so flip the orientation vector
  1300. alSource3f(source, AL_POSITION, pos.x, pos.y, pos.z);
  1301. alSource3f(source, AL_DIRECTION, -dir.x, -dir.y, -dir.z);
  1302. }
  1303. alxLoopSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
  1304. alxLoopSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
  1305. alxStreamSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
  1306. alxStreamSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
  1307. }
  1308. //--------------------------------------------------------------------------
  1309. void alxGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
  1310. {
  1311. ALuint source = alxFindSource(handle);
  1312. if(source != INVALID_SOURCE)
  1313. {
  1314. // gain queries return unattenuated values
  1315. if((pname == AL_GAIN) || (pname == AL_GAIN_LINEAR))
  1316. {
  1317. U32 idx = alxFindIndex(handle);
  1318. AssertFatal(idx != MAX_AUDIOSOURCES, "alxGetSourcef: found source but handle is invalid");
  1319. if(idx == MAX_AUDIOSOURCES)
  1320. {
  1321. *value = 0.f;
  1322. return;
  1323. }
  1324. if(pname == AL_GAIN)
  1325. *value = Audio::linearToDB(mSourceVolume[idx]);
  1326. else
  1327. *value = mSourceVolume[idx];
  1328. }
  1329. else
  1330. alGetSourcef(source, pname, value);
  1331. }
  1332. else if(handle & AUDIOHANDLE_LOOPING_BIT)
  1333. alxLoopGetSourcef(handle, pname, value);
  1334. else
  1335. alxStreamGetSourcef(handle, pname, value);
  1336. }
  1337. void alxGetSourcefv(AUDIOHANDLE handle, ALenum pname, ALfloat *values)
  1338. {
  1339. if((pname == AL_POSITION) || (pname == AL_DIRECTION) || (pname == AL_VELOCITY))
  1340. alxGetSource3f(handle, pname, &values[0], &values[1], &values[2]);
  1341. }
  1342. void alxGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
  1343. {
  1344. ALuint source = alxFindSource(handle);
  1345. if(source != INVALID_SOURCE)
  1346. {
  1347. ALfloat values[3];
  1348. alGetSourcefv(source, pname, values);
  1349. *value1 = values[0];
  1350. *value2 = values[1];
  1351. *value3 = values[2];
  1352. }
  1353. else if(handle & AUDIOHANDLE_LOOPING_BIT)
  1354. alxLoopGetSource3f(handle, pname, value1, value2, value3);
  1355. else
  1356. alxStreamGetSource3f(handle, pname, value1, value2, value3);
  1357. }
  1358. void alxGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
  1359. {
  1360. ALuint source = alxFindSource(handle);
  1361. if(source != INVALID_SOURCE)
  1362. alGetSourcei(source, pname, value);
  1363. else if(handle & AUDIOHANDLE_LOOPING_BIT)
  1364. alxLoopGetSourcei(handle, pname, value);
  1365. else
  1366. alxStreamGetSourcei(handle, pname, value);
  1367. }
  1368. //--------------------------------------------------------------------------
  1369. /** alListenerfv extension for use with MatrixF's
  1370. Set the listener's position and orientation using a matrix
  1371. */
  1372. void alxListenerMatrixF(const MatrixF *transform)
  1373. {
  1374. Point3F p1, p2;
  1375. transform->getColumn(3, &p1);
  1376. alListener3f(AL_POSITION, p1.x, p1.y, p1.z);
  1377. transform->getColumn(2, &p1); // Up Vector
  1378. transform->getColumn(1, &p2); // Forward Vector
  1379. F32 orientation[6];
  1380. orientation[0] = -p1.x;
  1381. orientation[1] = -p1.y;
  1382. orientation[2] = -p1.z;
  1383. orientation[3] = p2.x;
  1384. orientation[4] = p2.y;
  1385. orientation[5] = p2.z;
  1386. alListenerfv(AL_ORIENTATION, orientation);
  1387. }
  1388. //--------------------------------------------------------------------------
  1389. /** alListenerf extension supporting linear gain
  1390. */
  1391. void alxListenerf(ALenum param, ALfloat value)
  1392. {
  1393. if (param == AL_GAIN_LINEAR)
  1394. {
  1395. value = Audio::linearToDB(value);
  1396. param = AL_GAIN;
  1397. }
  1398. alListenerf(param, value);
  1399. }
  1400. //--------------------------------------------------------------------------
  1401. /** alGetListenerf extension supporting linear gain
  1402. */
  1403. void alxGetListenerf(ALenum param, ALfloat *value)
  1404. {
  1405. if (param == AL_GAIN_LINEAR)
  1406. {
  1407. alGetListenerf(AL_GAIN, value);
  1408. *value = Audio::DBToLinear(*value);
  1409. }
  1410. else
  1411. alGetListenerf(param, value);
  1412. }
  1413. //--------------------------------------------------------------------------
  1414. // Simple metrics
  1415. //--------------------------------------------------------------------------
  1416. #ifdef TORQUE_GATHER_METRICS
  1417. static void alxGatherMetrics()
  1418. {
  1419. S32 mNumOpenHandles = 0;
  1420. S32 mNumOpenLoopingHandles = 0;
  1421. S32 mNumOpenStreamingHandles = 0;
  1422. S32 mNumActiveStreams = 0;
  1423. S32 mNumNullActiveStreams = 0;
  1424. S32 mNumActiveLoopingStreams = 0;
  1425. S32 mNumActiveStreamingStreams = 0;
  1426. S32 mNumLoopingStreams = 0;
  1427. S32 mNumInactiveLoopingStreams = 0;
  1428. S32 mNumCulledLoopingStreams = 0;
  1429. S32 mNumStreamingStreams = 0;
  1430. S32 mNumInactiveStreamingStreams = 0;
  1431. S32 mNumCulledStreamingStreams = 0;
  1432. // count installed streams and open handles
  1433. for(U32 i = 0; i < mNumSources; i++)
  1434. {
  1435. if(mHandle[i] != NULL_AUDIOHANDLE)
  1436. {
  1437. mNumOpenHandles++;
  1438. if(mHandle[i] & AUDIOHANDLE_LOOPING_BIT)
  1439. mNumOpenLoopingHandles++;
  1440. if(mHandle[i] & AUDIOHANDLE_STREAMING_BIT)
  1441. mNumOpenStreamingHandles++;
  1442. }
  1443. ALint state = AL_STOPPED;
  1444. alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
  1445. if(state == AL_PLAYING)
  1446. {
  1447. mNumActiveStreams++;
  1448. if(mHandle[i] == NULL_AUDIOHANDLE)
  1449. mNumNullActiveStreams++;
  1450. if(mHandle[i] & AUDIOHANDLE_LOOPING_BIT)
  1451. mNumActiveLoopingStreams++;
  1452. if(mHandle[i] & AUDIOHANDLE_STREAMING_BIT)
  1453. mNumActiveStreamingStreams++;
  1454. }
  1455. }
  1456. for(LoopingList::iterator itr = mLoopingList.begin(); itr != mLoopingList.end(); itr++)
  1457. mNumLoopingStreams++;
  1458. for(LoopingList::iterator itr = mLoopingInactiveList.begin(); itr != mLoopingInactiveList.end(); itr++)
  1459. mNumInactiveLoopingStreams++;
  1460. for(LoopingList::iterator itr = mLoopingCulledList.begin(); itr != mLoopingCulledList.end(); itr++)
  1461. mNumCulledLoopingStreams++;
  1462. for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
  1463. mNumStreamingStreams++;
  1464. for(StreamingList::iterator itr = mStreamingInactiveList.begin(); itr != mStreamingInactiveList.end(); itr++)
  1465. mNumInactiveStreamingStreams++;
  1466. for(StreamingList::iterator itr = mStreamingCulledList.begin(); itr != mStreamingCulledList.end(); itr++)
  1467. mNumCulledStreamingStreams++;
  1468. Con::setIntVariable("Audio::numOpenHandles", mNumOpenHandles);
  1469. Con::setIntVariable("Audio::numOpenLoopingHandles", mNumOpenLoopingHandles);
  1470. Con::setIntVariable("Audio::numOpenStreamingHandles", mNumOpenStreamingHandles);
  1471. Con::setIntVariable("Audio::numActiveStreams", mNumActiveStreams);
  1472. Con::setIntVariable("Audio::numNullActiveStreams", mNumNullActiveStreams);
  1473. Con::setIntVariable("Audio::numActiveLoopingStreams", mNumActiveLoopingStreams);
  1474. Con::setIntVariable("Audio::numActiveStreamingStreams", mNumActiveStreamingStreams);
  1475. Con::setIntVariable("Audio::numLoopingStreams", mNumLoopingStreams);
  1476. Con::setIntVariable("Audio::numInactiveLoopingStreams", mNumInactiveLoopingStreams);
  1477. Con::setIntVariable("Audio::numCulledLoopingStreams", mNumCulledLoopingStreams);
  1478. Con::setIntVariable("Audio::numStreamingStreams", mNumStreamingStreams);
  1479. Con::setIntVariable("Audio::numInactiveStreamingStreams", mNumInactiveStreamingStreams);
  1480. Con::setIntVariable("Audio::numCulledStreamingStreams", mNumCulledStreamingStreams);
  1481. }
  1482. #endif
  1483. //--------------------------------------------------------------------------
  1484. // Audio Update...
  1485. //--------------------------------------------------------------------------
  1486. void alxLoopingUpdate()
  1487. {
  1488. static LoopingList culledList;
  1489. U32 updateTime = Platform::getRealMilliseconds();
  1490. // check if can wakeup the inactive loopers
  1491. if(mLoopingCulledList.size())
  1492. {
  1493. Point3F listener;
  1494. alxGetListenerPoint3F(AL_POSITION, &listener);
  1495. // get the 'sort' value for this sound (could be based on time played...),
  1496. // and add to the culled list
  1497. LoopingList::iterator itr;
  1498. culledList.clear();
  1499. for(itr = mLoopingCulledList.begin(); itr != mLoopingCulledList.end(); itr++)
  1500. {
  1501. if((*itr)->mScore <= MIN_UNCULL_GAIN)
  1502. continue;
  1503. if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
  1504. continue;
  1505. culledList.push_back(*itr);
  1506. }
  1507. if(!culledList.size())
  1508. return;
  1509. U32 index = MAX_AUDIOSOURCES;
  1510. if(culledList.size() > 1)
  1511. culledList.sort();
  1512. for(itr = culledList.begin(); itr != culledList.end(); itr++)
  1513. {
  1514. if(!findFreeSource(&index))
  1515. {
  1516. // score does not include master volume
  1517. if(!cullSource(&index, (*itr)->mScore))
  1518. break;
  1519. // check buffer
  1520. if(!bool((*itr)->mBuffer))
  1521. {
  1522. // remove from culled list
  1523. LoopingList::iterator tmp;
  1524. tmp = mLoopingCulledList.findImage((*itr)->mHandle);
  1525. AssertFatal(tmp, "alxLoopingUpdate: failed to find culled source");
  1526. mLoopingCulledList.erase_fast(tmp);
  1527. // remove from looping list (and free)
  1528. tmp = mLoopingList.findImage((*itr)->mHandle);
  1529. if(tmp)
  1530. {
  1531. (*tmp)->clear();
  1532. mLoopingFreeList.push_back(*tmp);
  1533. mLoopingList.erase_fast(tmp);
  1534. }
  1535. continue;
  1536. }
  1537. }
  1538. // remove from culled list
  1539. LoopingList::iterator tmp = mLoopingCulledList.findImage((*itr)->mHandle);
  1540. AssertFatal(tmp, "alxLoopingUpdate: failed to find culled source");
  1541. mLoopingCulledList.erase_fast(tmp);
  1542. // restore all state data
  1543. mHandle[index] = (*itr)->mHandle;
  1544. mBuffer[index] = (*itr)->mBuffer;
  1545. mScore[index] = (*itr)->mScore;
  1546. mSourceVolume[index] = (*itr)->mDescription.mVolume;
  1547. mType[index] = (*itr)->mDescription.mVolumeChannel;
  1548. mSampleEnvironment[index] = (*itr)->mEnvironment;
  1549. ALuint source = mSource[index];
  1550. // setup play info
  1551. alGetError();
  1552. alxSourcePlay(source, *itr);
  1553. if(mEnvironmentEnabled)
  1554. alxSourceEnvironment(source, *itr);
  1555. alxPlay(mHandle[index]);
  1556. }
  1557. }
  1558. }
  1559. void alxStreamingUpdate()
  1560. {
  1561. // update buffer queues on active streamers
  1562. // update the loopers
  1563. for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
  1564. {
  1565. if((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
  1566. continue;
  1567. (*itr)->updateBuffers();
  1568. }
  1569. static StreamingList culledList;
  1570. U32 updateTime = Platform::getRealMilliseconds();
  1571. // check if can wakeup the inactive loopers
  1572. if(mStreamingCulledList.size())
  1573. {
  1574. Point3F listener;
  1575. alxGetListenerPoint3F(AL_POSITION, &listener);
  1576. // get the 'sort' value for this sound (could be based on time played...),
  1577. // and add to the culled list
  1578. StreamingList::iterator itr;
  1579. culledList.clear();
  1580. for(itr = mStreamingCulledList.begin(); itr != mStreamingCulledList.end(); itr++)
  1581. {
  1582. if((*itr)->mScore <= MIN_UNCULL_GAIN)
  1583. continue;
  1584. if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
  1585. continue;
  1586. culledList.push_back(*itr);
  1587. }
  1588. if(!culledList.size())
  1589. return;
  1590. U32 index = MAX_AUDIOSOURCES;
  1591. if(culledList.size() > 1)
  1592. culledList.sort();
  1593. for(itr = culledList.begin(); itr != culledList.end(); itr++)
  1594. {
  1595. if(!findFreeSource(&index))
  1596. {
  1597. // score does not include master volume
  1598. if(!cullSource(&index, (*itr)->mScore))
  1599. break;
  1600. // check buffer
  1601. //if(!bool((*itr)->mBuffer))
  1602. //{
  1603. // remove from culled list
  1604. StreamingList::iterator tmp;
  1605. tmp = mStreamingCulledList.findImage((*itr)->mHandle);
  1606. AssertFatal(tmp, "alxStreamingUpdate: failed to find culled source");
  1607. mStreamingCulledList.erase_fast(tmp);
  1608. // remove from streaming list (and free)
  1609. tmp = mStreamingList.findImage((*itr)->mHandle);
  1610. if(tmp)
  1611. {
  1612. delete(*tmp);
  1613. mStreamingList.erase_fast(tmp);
  1614. }
  1615. continue;
  1616. //}
  1617. }
  1618. // remove from culled list
  1619. StreamingList::iterator tmp = mStreamingCulledList.findImage((*itr)->mHandle);
  1620. AssertFatal(tmp, "alxStreamingUpdate: failed to find culled source");
  1621. mStreamingCulledList.erase_fast(tmp);
  1622. alxSourcePlay(*itr);
  1623. // restore all state data
  1624. mHandle[index] = (*itr)->mHandle;
  1625. mScore[index] = (*itr)->mScore;
  1626. mSourceVolume[index] = (*itr)->mDescription.mVolume;
  1627. mType[index] = (*itr)->mDescription.mVolumeChannel;
  1628. mSampleEnvironment[index] = (*itr)->mEnvironment;
  1629. ALuint source = mSource[index];
  1630. (*itr)->mSource = mSource[index];
  1631. // setup play info
  1632. alGetError();
  1633. if(mEnvironmentEnabled)
  1634. alxSourceEnvironment(source, *itr);
  1635. alxPlay(mHandle[index]);
  1636. }
  1637. }
  1638. }
  1639. //--------------------------------------------------------------------------
  1640. void alxCloseHandles()
  1641. {
  1642. for(U32 i = 0; i < mNumSources; i++)
  1643. {
  1644. if(mHandle[i] & AUDIOHANDLE_LOADING_BIT)
  1645. continue;
  1646. if(mHandle[i] == NULL_AUDIOHANDLE)
  1647. continue;
  1648. ALint state = 0;
  1649. alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
  1650. if(state == AL_PLAYING || state == AL_PAUSED)
  1651. continue;
  1652. if(!(mHandle[i] & AUDIOHANDLE_INACTIVE_BIT))
  1653. {
  1654. // should be playing? must have encounted an error.. remove
  1655. LoopingList::iterator itr = mLoopingList.findImage(mHandle[i]);
  1656. if(itr && !((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
  1657. {
  1658. AssertFatal(!mLoopingInactiveList.findImage((*itr)->mHandle), "alxCloseHandles: image incorrectly in inactive list");
  1659. AssertFatal(!mLoopingCulledList.findImage((*itr)->mHandle), "alxCloseHandles: image already in culled list");
  1660. mLoopingCulledList.push_back(*itr);
  1661. (*itr)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
  1662. mHandle[i] = NULL_AUDIOHANDLE;
  1663. mBuffer[i] = 0;
  1664. }
  1665. // should be playing? must have encounted an error.. remove
  1666. // StreamingList::iterator itr2 = mStreamingList.findImage(mHandle[i]);
  1667. // if(itr2 && !((*itr2)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
  1668. // {
  1669. // AssertFatal(!mStreamingInactiveList.findImage((*itr2)->mHandle), "alxCloseHandles: image incorrectly in inactive list");
  1670. // AssertFatal(!mStreamingCulledList.findImage((*itr2)->mHandle), "alxCloseHandles: image already in culled list");
  1671. // (*itr2)->freeStream();
  1672. //
  1673. // mStreamingCulledList.push_back(*itr2);
  1674. // (*itr2)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
  1675. //
  1676. // mHandle[i] = NULL_AUDIOHANDLE;
  1677. // mBuffer[i] = 0;
  1678. // }
  1679. }
  1680. alSourcei(mSource[i], AL_BUFFER, AL_NONE);
  1681. mHandle[i] = NULL_AUDIOHANDLE;
  1682. mBuffer[i] = 0;
  1683. }
  1684. }
  1685. //----------------------------------------------------------------------------------
  1686. // - update the score for each audio source. this is used for culing sources.
  1687. // normal ranges are between 0.f->1.f, voice/loading/music streams are scored
  1688. // outside this range so that they will not be culled
  1689. // - does not scale by attenuated volumes
  1690. void alxUpdateScores(bool sourcesOnly)
  1691. {
  1692. Point3F listener;
  1693. alxGetListenerPoint3F(AL_POSITION, &listener);
  1694. // do the base sources
  1695. for(U32 i = 0; i < mNumSources; i++)
  1696. {
  1697. if(mHandle[i] == NULL_AUDIOHANDLE)
  1698. {
  1699. mScore[i] = 0.f;
  1700. continue;
  1701. }
  1702. ALint state = 0;
  1703. alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
  1704. if(state==AL_PAUSED)
  1705. continue;
  1706. // grab the volume.. (not attenuated by master for score)
  1707. F32 volume = mSourceVolume[i] * mAudioChannelVolumes[mType[i]];
  1708. // 3d?
  1709. mScore[i] = volume;
  1710. #ifdef REL_WORKAROUND
  1711. ALint val = AL_FALSE;
  1712. alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
  1713. if(val == AL_TRUE)
  1714. #else
  1715. ALint val = AL_FALSE;
  1716. alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
  1717. if(val == AL_FALSE)
  1718. #endif
  1719. {
  1720. // approximate 3d volume
  1721. Point3F pos;
  1722. alGetSourcefv(mSource[i], AL_POSITION, (ALfloat*)((F32*)pos) );
  1723. ALfloat min=0, max=1;
  1724. alGetSourcef(mSource[i], AL_REFERENCE_DISTANCE, &min);
  1725. alGetSourcef(mSource[i], AL_MAX_DISTANCE, &max);
  1726. pos -= listener;
  1727. F32 dist = pos.magnitudeSafe();
  1728. if(dist >= max)
  1729. mScore[i] = 0.f;
  1730. else if(dist > min)
  1731. mScore[i] *= (max-dist) / (max-min);
  1732. }
  1733. }
  1734. if(sourcesOnly)
  1735. return;
  1736. U32 updateTime = Platform::getRealMilliseconds();
  1737. // update the loopers
  1738. for(LoopingList::iterator itr = mLoopingList.begin(); itr != mLoopingList.end(); itr++)
  1739. {
  1740. if(!((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
  1741. continue;
  1742. if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
  1743. continue;
  1744. (*itr)->mScore = (*itr)->mDescription.mVolume;
  1745. if((*itr)->mDescription.mIs3D)
  1746. {
  1747. Point3F pos = (*itr)->mPosition - listener;
  1748. F32 dist = pos.magnitudeSafe();
  1749. F32 min = (*itr)->mDescription.mReferenceDistance;
  1750. F32 max = (*itr)->mDescription.mMaxDistance;
  1751. if(dist >= max)
  1752. (*itr)->mScore = 0.f;
  1753. else if(dist > min)
  1754. (*itr)->mScore *= (max-dist) / (max-min);
  1755. }
  1756. // attenuate by the channel gain
  1757. (*itr)->mScore *= mAudioChannelVolumes[(*itr)->mDescription.mVolumeChannel];
  1758. }
  1759. // update the streamers
  1760. for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
  1761. {
  1762. if(!((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
  1763. continue;
  1764. if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
  1765. continue;
  1766. (*itr)->mScore = (*itr)->mDescription.mVolume;
  1767. if((*itr)->mDescription.mIs3D)
  1768. {
  1769. Point3F pos = (*itr)->mPosition - listener;
  1770. F32 dist = pos.magnitudeSafe();
  1771. F32 min = (*itr)->mDescription.mReferenceDistance;
  1772. F32 max = (*itr)->mDescription.mMaxDistance;
  1773. if(dist >= max)
  1774. (*itr)->mScore = 0.f;
  1775. else if(dist > min)
  1776. (*itr)->mScore *= (max-dist) / (max-min);
  1777. }
  1778. // attenuate by the channel gain
  1779. (*itr)->mScore *= mAudioChannelVolumes[(*itr)->mDescription.mVolumeChannel];
  1780. }
  1781. }
  1782. // the directx buffers are set to mute at max distance, but many of the providers seem to
  1783. // ignore this flag... that is why this is here
  1784. void alxUpdateMaxDistance()
  1785. {
  1786. Point3F listener;
  1787. alxGetListenerPoint3F(AL_POSITION, &listener);
  1788. for(U32 i = 0; i < mNumSources; i++)
  1789. {
  1790. if(mHandle[i] == NULL_AUDIOHANDLE)
  1791. continue;
  1792. #ifdef REL_WORKAROUND
  1793. ALint val = AL_FALSE;
  1794. alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
  1795. if(val == AL_FALSE)
  1796. #else
  1797. ALint val = AL_FALSE;
  1798. alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
  1799. if(val == AL_TRUE)
  1800. #endif
  1801. continue;
  1802. Point3F pos;
  1803. alGetSourcefv(mSource[i], AL_POSITION, (F32*)pos);
  1804. F32 dist = 0.f;
  1805. alGetSourcef(mSource[i], AL_MAX_DISTANCE, &dist);
  1806. pos -= listener;
  1807. dist -= pos.len();
  1808. F32 gain = (dist < 0.f) ? 0.f : mSourceVolume[i] * mAudioChannelVolumes[mType[i]] * mMasterVolume;
  1809. alSourcef(mSource[i], AL_GAIN, Audio::linearToDB(gain));
  1810. }
  1811. }
  1812. //--------------------------------------------------------------------------
  1813. // Called to update alx system
  1814. //--------------------------------------------------------------------------
  1815. void alxUpdate()
  1816. {
  1817. //if(mForceMaxDistanceUpdate)
  1818. alxUpdateMaxDistance();
  1819. alxCloseHandles();
  1820. alxUpdateScores(false);
  1821. alxLoopingUpdate();
  1822. alxStreamingUpdate();
  1823. #ifdef TORQUE_GATHER_METRICS
  1824. alxGatherMetrics();
  1825. #endif
  1826. }
  1827. //--------------------------------------------------------------------------
  1828. // Misc
  1829. //--------------------------------------------------------------------------
  1830. // client-side function only
  1831. ALuint alxGetWaveLen(ALuint buffer)
  1832. {
  1833. if(buffer == AL_INVALID)
  1834. return(0);
  1835. ALint frequency = 0;
  1836. ALint bits = 0;
  1837. ALint channels = 0;
  1838. ALint size;
  1839. alGetBufferi(buffer, AL_FREQUENCY, &frequency);
  1840. alGetBufferi(buffer, AL_BITS, &bits);
  1841. alGetBufferi(buffer, AL_CHANNELS, &channels);
  1842. alGetBufferi(buffer, AL_SIZE, &size);
  1843. if(!frequency || !bits || !channels)
  1844. {
  1845. Con::errorf(ConsoleLogEntry::General, "alxGetWaveLen: invalid buffer");
  1846. return(0);
  1847. }
  1848. ALuint len = (ALuint)((F64(size) * 8000.f) / F64(frequency * bits * channels));
  1849. return(len);
  1850. }
  1851. bool alxCheckError(const char* sourceFuncName, const char* alFuncName)
  1852. {
  1853. ALenum errorVal = alGetError();
  1854. switch (errorVal)
  1855. {
  1856. case AL_NO_ERROR:
  1857. break;
  1858. case AL_INVALID_NAME:
  1859. Con::errorf("%s - %s OpenAL AL_INVALID_NAME error code returned", sourceFuncName, alFuncName);
  1860. break;
  1861. case AL_INVALID_ENUM:
  1862. Con::errorf("%s - %s OpenAL AL_INVALID_ENUM error code returned", sourceFuncName, alFuncName);
  1863. break;
  1864. case AL_INVALID_VALUE:
  1865. Con::errorf("%s - %s OpenAL AL_INVALID_VALUE error code returned", sourceFuncName, alFuncName);
  1866. break;
  1867. case AL_INVALID_OPERATION:
  1868. Con::errorf("%s - %s OpenAL AL_INVALID_OPERATION error code returned", sourceFuncName, alFuncName);
  1869. break;
  1870. case AL_OUT_OF_MEMORY:
  1871. Con::errorf("%s - %s OpenAL AL_OUT_OF_MEMORY error code returned", sourceFuncName, alFuncName);
  1872. break;
  1873. default:
  1874. Con::errorf("%s - %s OpenAL has encountered a problem and won't tell us what it is. %d", errorVal, sourceFuncName, alFuncName);
  1875. };
  1876. if (errorVal == AL_NO_ERROR)
  1877. return true;
  1878. else
  1879. return false;
  1880. }
  1881. //--------------------------------------------------------------------------
  1882. // Environment:
  1883. //--------------------------------------------------------------------------
  1884. void alxEnvironmenti(ALenum pname, ALint value)
  1885. {
  1886. /* todo
  1887. alEnvironmentiIASIG(mEnvironment, pname, value);
  1888. */
  1889. }
  1890. void alxEnvironmentf(ALenum pname, ALfloat value)
  1891. {
  1892. /* todo
  1893. alEnvironmentfIASIG(mEnvironment, pname, value);
  1894. */
  1895. }
  1896. void alxGetEnvironmenti(ALenum pname, ALint * value)
  1897. {
  1898. /* todo
  1899. alGetEnvironmentiIASIG_EXT(mEnvironment, pname, value);
  1900. */
  1901. }
  1902. void alxGetEnvironmentf(ALenum pname, ALfloat * value)
  1903. {
  1904. /* todo
  1905. alGetEnvironmentfIASIG_EXT(mEnvironment, pname, value);
  1906. */
  1907. }
  1908. void alxEnableEnvironmental(bool enable)
  1909. {
  1910. if(mEnvironmentEnabled == enable)
  1911. return;
  1912. // go through the playing sources and update their reverb mix
  1913. // - only 3d samples get environmental fx
  1914. // - only loopers can reenable fx
  1915. for(U32 i = 0; i < mNumSources; i++)
  1916. {
  1917. if(mHandle[i] == NULL_AUDIOHANDLE)
  1918. continue;
  1919. ALint val = AL_FALSE;
  1920. // 3d?
  1921. #ifdef REL_WORKAROUND
  1922. alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
  1923. if(val == AL_FALSE)
  1924. #else
  1925. alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
  1926. if(val == AL_TRUE)
  1927. #endif
  1928. continue;
  1929. // stopped?
  1930. val = AL_STOPPED;
  1931. alGetSourcei(mSource[i], AL_SOURCE_STATE, &val);
  1932. // only looping sources can reenable environmental effects (no description around
  1933. // for the non-loopers)
  1934. if(enable)
  1935. {
  1936. LoopingList::iterator itr = mLoopingList.findImage(mHandle[i]);
  1937. if(!itr)
  1938. continue;
  1939. /* todo
  1940. alSourcef(mSource[i], AL_ENV_SAMPLE_REVERB_MIX_EXT, (*itr)->mDescription.mEnvironmentLevel);
  1941. */
  1942. }
  1943. /* todo
  1944. else
  1945. alSourcef(mSource[i], AL_ENV_SAMPLE_REVERB_MIX_EXT, 0.f);
  1946. */
  1947. }
  1948. mEnvironmentEnabled = enable;
  1949. }
  1950. void alxSetEnvironment(const AudioEnvironment * env)
  1951. {
  1952. /* todo
  1953. mCurrentEnvironment = const_cast<AudioEnvironment*>(env);
  1954. // reset environmental audio?
  1955. if(!env)
  1956. {
  1957. alxEnvironmenti(AL_ENV_ROOM_IASIG, AL_ENVIRONMENT_GENERIC);
  1958. return;
  1959. }
  1960. // room trashes all the values
  1961. if(env->mUseRoom)
  1962. {
  1963. alxEnvironmenti(AL_ENV_ROOM_IASIG, env->mRoom);
  1964. return;
  1965. }
  1966. // set all the params
  1967. alxEnvironmenti(AL_ENV_ROOM_HIGH_FREQUENCY_IASIG, env->mRoomHF);
  1968. alxEnvironmenti(AL_ENV_REFLECTIONS_IASIG, env->mReflections);
  1969. alxEnvironmenti(AL_ENV_REVERB_IASIG, env->mReverb);
  1970. alxEnvironmentf(AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG, env->mRoomRolloffFactor);
  1971. alxEnvironmentf(AL_ENV_DECAY_TIME_IASIG, env->mDecayTime);
  1972. alxEnvironmentf(AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG, env->mDecayTime);
  1973. alxEnvironmentf(AL_ENV_REFLECTIONS_DELAY_IASIG, env->mReflectionsDelay);
  1974. alxEnvironmentf(AL_ENV_REVERB_DELAY_IASIG, env->mReverbDelay);
  1975. alxEnvironmentf(AL_ENV_DENSITY_IASIG, env->mAirAbsorption);
  1976. alxEnvironmentf(AL_ENV_DIFFUSION_IASIG, env->mEnvironmentDiffusion);
  1977. // ext:
  1978. alxEnvironmenti(AL_ENV_ROOM_VOLUME_EXT, env->mRoomVolume);
  1979. alxEnvironmenti(AL_ENV_FLAGS_EXT, env->mFlags);
  1980. alxEnvironmentf(AL_ENV_EFFECT_VOLUME_EXT, env->mEffectVolume);
  1981. alxEnvironmentf(AL_ENV_DAMPING_EXT, env->mDamping);
  1982. alxEnvironmentf(AL_ENV_ENVIRONMENT_SIZE_EXT, env->mEnvironmentSize);
  1983. */
  1984. }
  1985. const AudioEnvironment * alxGetEnvironment()
  1986. {
  1987. return(mCurrentEnvironment);
  1988. }
  1989. F32 alxGetStreamPosition( AUDIOHANDLE handle )
  1990. {
  1991. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1992. if( !itr )
  1993. return -1.f;
  1994. return (*itr)->getElapsedTime();
  1995. }
  1996. F32 alxGetStreamDuration( AUDIOHANDLE handle )
  1997. {
  1998. StreamingList::iterator itr = mStreamingList.findImage(handle);
  1999. if( !itr )
  2000. return -1.f;
  2001. return (*itr)->getTotalTime();
  2002. }
  2003. // Namespace: Audio ---------------------------------------------------------
  2004. namespace Audio
  2005. {
  2006. //---------------------------------------------------------------------------
  2007. // the following db<->linear conversion functions come from Loki openAL linux driver
  2008. // code, here more for completeness than anything else (all current audio code
  2009. // uses AL_GAIN_LINEAR)... in Audio:: so that looping updates and audio channel updates
  2010. // can convert gain types and to give the miles driver access
  2011. static const F32 logtab[] = {
  2012. 0.00f, 0.001f, 0.002f, 0.003f, 0.004f,
  2013. 0.005f, 0.01f, 0.011f, 0.012f, 0.013f,
  2014. 0.014f, 0.015f, 0.016f, 0.02f, 0.021f,
  2015. 0.022f, 0.023f, 0.024f, 0.025f, 0.03f,
  2016. 0.031f, 0.032f, 0.033f, 0.034f, 0.04f,
  2017. 0.041f, 0.042f, 0.043f, 0.044f, 0.05f,
  2018. 0.051f, 0.052f, 0.053f, 0.054f, 0.06f,
  2019. 0.061f, 0.062f, 0.063f, 0.064f, 0.07f,
  2020. 0.071f, 0.072f, 0.073f, 0.08f, 0.081f,
  2021. 0.082f, 0.083f, 0.084f, 0.09f, 0.091f,
  2022. 0.092f, 0.093f, 0.094f, 0.10f, 0.101f,
  2023. 0.102f, 0.103f, 0.11f, 0.111f, 0.112f,
  2024. 0.113f, 0.12f, 0.121f, 0.122f, 0.123f,
  2025. 0.124f, 0.13f, 0.131f, 0.132f, 0.14f,
  2026. 0.141f, 0.142f, 0.143f, 0.15f, 0.151f,
  2027. 0.152f, 0.16f, 0.161f, 0.162f, 0.17f,
  2028. 0.171f, 0.172f, 0.18f, 0.181f, 0.19f,
  2029. 0.191f, 0.192f, 0.20f, 0.201f, 0.21f,
  2030. 0.211f, 0.22f, 0.221f, 0.23f, 0.231f,
  2031. 0.24f, 0.25f, 0.251f, 0.26f, 0.27f,
  2032. 0.271f, 0.28f, 0.29f, 0.30f, 0.301f,
  2033. 0.31f, 0.32f, 0.33f, 0.34f, 0.35f,
  2034. 0.36f, 0.37f, 0.38f, 0.39f, 0.40f,
  2035. 0.41f, 0.43f, 0.50f, 0.60f, 0.65f,
  2036. 0.70f, 0.75f, 0.80f, 0.85f, 0.90f,
  2037. 0.95f, 0.97f, 0.99f };
  2038. const int logmax = sizeof logtab / sizeof *logtab;
  2039. F32 DBToLinear(F32 value)
  2040. {
  2041. if(value <= 0.f)
  2042. return(0.f);
  2043. if(value >= 1.f)
  2044. return(1.f);
  2045. S32 max = logmax;
  2046. S32 min = 0;
  2047. S32 mid;
  2048. S32 last = -1;
  2049. mid = (max - min) / 2;
  2050. do {
  2051. last = mid;
  2052. if(logtab[mid] == value)
  2053. break;
  2054. if(logtab[mid] < value)
  2055. min = mid;
  2056. else
  2057. max = mid;
  2058. mid = min + ((max - min) / 2);
  2059. } while(last != mid);
  2060. return((F32)mid / logmax);
  2061. }
  2062. F32 linearToDB(F32 value)
  2063. {
  2064. if(value <= 0.f)
  2065. return(0.f);
  2066. if(value >= 1.f)
  2067. return(1.f);
  2068. return(logtab[(U32)(logmax * value)]);
  2069. }
  2070. //---------------------------------------------------------------------------
  2071. static ALvoid errorCallback(ALbyte *msg)
  2072. {
  2073. // used to allow our OpenAL implementation to display info on the console
  2074. Con::errorf(ConsoleLogEntry::General, (const char *)msg);
  2075. }
  2076. void shutdownContext()
  2077. {
  2078. // invalidate active handles
  2079. dMemset(mSource, 0, sizeof(mSource));
  2080. }
  2081. //--------------------------------------------------------------------------
  2082. bool OpenALInit()
  2083. {
  2084. OpenALShutdown();
  2085. if(!OpenALDLLInit())
  2086. return false;
  2087. // Open a device
  2088. #ifdef TORQUE_OS_IOS
  2089. ALenum result = AL_NO_ERROR;
  2090. mDevice = alcOpenDevice(NULL);
  2091. result = alGetError();
  2092. AssertNoOALError("Error opening output device");
  2093. //-Mat for streaming mp3
  2094. //-Sven Moved up to allow for music and audio to run with iPod Music
  2095. SoundEngine::SoundEngine_Initialize( DEFAULT_SOUND_OUTPUT_RATE );
  2096. // PUAP -Mat output rate must be set before calling alcCreateContext()
  2097. alcMacOSXMixerOutputRateProc(DEFAULT_SOUND_OUTPUT_RATE);
  2098. // Create an openAL context
  2099. mContext = alcCreateContext(mDevice,NULL);
  2100. // Make this context the active context
  2101. alcMakeContextCurrent(mContext);
  2102. AssertNoOALError("Error setting current OpenAL context");
  2103. //now request the number of audio sources we want, if we can't get that many decrement until we have a number we can get
  2104. #elif defined(TORQUE_OS_OSX)
  2105. mDevice = alcOpenDevice((const ALCchar*)NULL);
  2106. #elif defined(TORQUE_OS_ANDROID)
  2107. mDevice = alcOpenDevice("openal-soft");
  2108. #else
  2109. mDevice = (ALCvoid *)alcOpenDevice((const ALCchar*)NULL);
  2110. #endif
  2111. if (mDevice == (ALCvoid *)NULL)
  2112. return false;
  2113. // Create an openAL context
  2114. #ifdef TORQUE_OS_LINUX
  2115. int freq = Con::getIntVariable("Pref::Unix::OpenALFrequency");
  2116. if (freq == 0)
  2117. freq = 22050;
  2118. Con::printf(" Setting OpenAL output frequency to %d", freq);
  2119. // some versions of openal have bugs converting between 22050 and 44100
  2120. // samples when the lib is in 44100 mode.
  2121. int attrlist[] = {
  2122. // this 0x100 is "ALC_FREQUENCY" in the linux openal implementation.
  2123. // it doesn't match the value of the creative headers, so we can't use
  2124. // that define. seems like linux torque really shouldn't be using the
  2125. // creative headers.
  2126. 0x100, freq,
  2127. 0
  2128. };
  2129. mContext = alcCreateContext((ALCdevice*)mDevice,attrlist);
  2130. #elif defined(TORQUE_OS_ANDROID)
  2131. mContext = alcCreateContext((ALCdevice*)mDevice, NULL);
  2132. #elif defined(TORQUE_OS_EMSCRIPTEN)
  2133. mContext = alcCreateContext((ALCdevice*)mDevice, NULL);;
  2134. #elif defined(TORQUE_OS_IOS)
  2135. #else
  2136. mContext = alcCreateContext(mDevice,NULL);
  2137. #endif
  2138. if (mContext == NULL)
  2139. return false;
  2140. // Make this context the active context
  2141. #if defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_EMSCRIPTEN)
  2142. alcMakeContextCurrent((ALCcontext*)mContext);
  2143. #else
  2144. alcMakeContextCurrent(mContext);
  2145. #endif
  2146. ALenum err = alGetError();
  2147. mRequestSources = MAX_AUDIOSOURCES;
  2148. while(true)
  2149. {
  2150. alGenSources(mRequestSources, mSource);
  2151. err = alGetError();
  2152. if (err == AL_NO_ERROR)
  2153. break;
  2154. mRequestSources--;
  2155. if (mRequestSources == 0)
  2156. {
  2157. OpenALShutdown();
  2158. return (false);
  2159. }
  2160. }
  2161. mNumSources = mRequestSources;
  2162. // invalidate all existing handles
  2163. dMemset(mHandle, NULL_AUDIOHANDLE, sizeof(mHandle));
  2164. // default all channels to full gain
  2165. for(U32 i = 0; i < Audio::AudioVolumeChannels; i++)
  2166. mAudioChannelVolumes[i] = 1.0f;
  2167. // Clear Error Code
  2168. alGetError();
  2169. // Similiar to DSound Model w/o min distance clamping
  2170. alEnable(AL_DISTANCE_MODEL);
  2171. alDistanceModel(AL_INVERSE_DISTANCE);
  2172. alListenerf(AL_GAIN_LINEAR, 1.f);
  2173. return true;
  2174. }
  2175. //--------------------------------------------------------------------------
  2176. void OpenALShutdown()
  2177. {
  2178. alxStopAll();
  2179. //if(mInitialized)
  2180. {
  2181. alxEnvironmentDestroy();
  2182. }
  2183. while(mLoopingList.size())
  2184. {
  2185. mLoopingList.last()->mBuffer.purge();
  2186. delete mLoopingList.last();
  2187. mLoopingList.pop_back();
  2188. }
  2189. while(mLoopingFreeList.size())
  2190. {
  2191. mLoopingFreeList.last()->mBuffer.purge();
  2192. delete mLoopingFreeList.last();
  2193. mLoopingFreeList.pop_back();
  2194. }
  2195. //clear error buffer
  2196. alGetError();
  2197. for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
  2198. {
  2199. ALint tempbuff = 0;
  2200. alGetSourcei( mSource[i], AL_BUFFER, &tempbuff);
  2201. if (alIsBuffer(tempbuff) && tempbuff !=0)
  2202. {
  2203. ALuint buffer = tempbuff;
  2204. alSourceUnqueueBuffers( mSource[i], 1, &buffer);
  2205. alxCheckError("OpenALShutdown()","alSourceUnqueueBuffers");
  2206. }
  2207. }
  2208. alDeleteSources(mNumSources, mSource);
  2209. if (mContext)
  2210. {
  2211. #if defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_LINUX)
  2212. alcDestroyContext((ALCcontext*)mContext);
  2213. #elif defined(TORQUE_OS_EMSCRIPTEN)
  2214. alcDestroyContext((ALCcontext*)mContext);
  2215. #else
  2216. alcDestroyContext(mContext);
  2217. #endif
  2218. mContext = NULL;
  2219. }
  2220. if (mDevice)
  2221. {
  2222. #if defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_LINUX)
  2223. alcCloseDevice((ALCdevice*)mDevice);
  2224. #elif defined(TORQUE_OS_EMSCRIPTEN)
  2225. alcCloseDevice((ALCdevice*)mDevice);
  2226. #else
  2227. alcCloseDevice(mDevice);
  2228. #endif
  2229. mDevice = NULL;
  2230. }
  2231. OpenALDLLShutdown();
  2232. }
  2233. } // end OpenAL namespace
  2234. AudioStreamSource* alxFindAudioStreamSource(AUDIOHANDLE handle)
  2235. {
  2236. StreamingList::iterator itr2 = mStreamingList.findImage(handle);
  2237. if(itr2)
  2238. return *itr2;
  2239. return NULL;
  2240. }