library_godot_audio.js 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263
  1. /**************************************************************************/
  2. /* library_godot_audio.js */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. /**
  31. * @typedef { "disabled" | "forward" | "backward" | "pingpong" } LoopMode
  32. */
  33. /**
  34. * @typedef {{
  35. * id: string
  36. * audioBuffer: AudioBuffer
  37. * }} SampleParams
  38. * @typedef {{
  39. * numberOfChannels?: number
  40. * sampleRate?: number
  41. * loopMode?: LoopMode
  42. * loopBegin?: number
  43. * loopEnd?: number
  44. * }} SampleOptions
  45. */
  46. /**
  47. * Represents a sample, memory-wise.
  48. * @class
  49. */
  50. class Sample {
  51. /**
  52. * Returns a `Sample`.
  53. * @param {string} id Id of the `Sample` to get.
  54. * @returns {Sample}
  55. * @throws {ReferenceError} When no `Sample` is found
  56. */
  57. static getSample(id) {
  58. if (!GodotAudio.samples.has(id)) {
  59. throw new ReferenceError(`Could not find sample "${id}"`);
  60. }
  61. return GodotAudio.samples.get(id);
  62. }
  63. /**
  64. * Returns a `Sample` or `null`, if it doesn't exist.
  65. * @param {string} id Id of the `Sample` to get.
  66. * @returns {Sample?}
  67. */
  68. static getSampleOrNull(id) {
  69. return GodotAudio.samples.get(id) ?? null;
  70. }
  71. /**
  72. * Creates a `Sample` based on the params. Will register it to the
  73. * `GodotAudio.samples` registry.
  74. * @param {SampleParams} params Base params
  75. * @param {SampleOptions} [options={{}}] Optional params
  76. * @returns {Sample}
  77. */
  78. static create(params, options = {}) {
  79. const sample = new GodotAudio.Sample(params, options);
  80. GodotAudio.samples.set(params.id, sample);
  81. return sample;
  82. }
  83. /**
  84. * Deletes a `Sample` based on the id.
  85. * @param {string} id `Sample` id to delete
  86. * @returns {void}
  87. */
  88. static delete(id) {
  89. GodotAudio.samples.delete(id);
  90. }
  91. /**
  92. * `Sample` constructor.
  93. * @param {SampleParams} params Base params
  94. * @param {SampleOptions} [options={{}}] Optional params
  95. */
  96. constructor(params, options = {}) {
  97. /** @type {string} */
  98. this.id = params.id;
  99. /** @type {AudioBuffer} */
  100. this._audioBuffer = null;
  101. /** @type {number} */
  102. this.numberOfChannels = options.numberOfChannels ?? 2;
  103. /** @type {number} */
  104. this.sampleRate = options.sampleRate ?? 44100;
  105. /** @type {LoopMode} */
  106. this.loopMode = options.loopMode ?? 'disabled';
  107. /** @type {number} */
  108. this.loopBegin = options.loopBegin ?? 0;
  109. /** @type {number} */
  110. this.loopEnd = options.loopEnd ?? 0;
  111. this.setAudioBuffer(params.audioBuffer);
  112. }
  113. /**
  114. * Gets the audio buffer of the sample.
  115. * @returns {AudioBuffer}
  116. */
  117. getAudioBuffer() {
  118. return this._duplicateAudioBuffer();
  119. }
  120. /**
  121. * Sets the audio buffer of the sample.
  122. * @param {AudioBuffer} val The audio buffer to set.
  123. * @returns {void}
  124. */
  125. setAudioBuffer(val) {
  126. this._audioBuffer = val;
  127. }
  128. /**
  129. * Clears the current sample.
  130. * @returns {void}
  131. */
  132. clear() {
  133. this.setAudioBuffer(null);
  134. GodotAudio.Sample.delete(this.id);
  135. }
  136. /**
  137. * Returns a duplicate of the stored audio buffer.
  138. * @returns {AudioBuffer}
  139. */
  140. _duplicateAudioBuffer() {
  141. if (this._audioBuffer == null) {
  142. throw new Error('couldn\'t duplicate a null audioBuffer');
  143. }
  144. /** @type {Array<Float32Array>} */
  145. const channels = new Array(this._audioBuffer.numberOfChannels);
  146. for (let i = 0; i < this._audioBuffer.numberOfChannels; i++) {
  147. const channel = new Float32Array(this._audioBuffer.getChannelData(i));
  148. channels[i] = channel;
  149. }
  150. const buffer = GodotAudio.ctx.createBuffer(
  151. this.numberOfChannels,
  152. this._audioBuffer.length,
  153. this._audioBuffer.sampleRate
  154. );
  155. for (let i = 0; i < channels.length; i++) {
  156. buffer.copyToChannel(channels[i], i, 0);
  157. }
  158. return buffer;
  159. }
  160. }
  161. /**
  162. * Represents a `SampleNode` linked to a `Bus`.
  163. * @class
  164. */
  165. class SampleNodeBus {
  166. /**
  167. * Creates a new `SampleNodeBus`.
  168. * @param {Bus} bus The bus related to the new `SampleNodeBus`.
  169. * @returns {SampleNodeBus}
  170. */
  171. static create(bus) {
  172. return new GodotAudio.SampleNodeBus(bus);
  173. }
  174. /**
  175. * `SampleNodeBus` constructor.
  176. * @param {Bus} bus The bus related to the new `SampleNodeBus`.
  177. */
  178. constructor(bus) {
  179. const NUMBER_OF_WEB_CHANNELS = 6;
  180. /** @type {Bus} */
  181. this._bus = bus;
  182. /** @type {ChannelSplitterNode} */
  183. this._channelSplitter = GodotAudio.ctx.createChannelSplitter(NUMBER_OF_WEB_CHANNELS);
  184. /** @type {GainNode} */
  185. this._l = GodotAudio.ctx.createGain();
  186. /** @type {GainNode} */
  187. this._r = GodotAudio.ctx.createGain();
  188. /** @type {GainNode} */
  189. this._sl = GodotAudio.ctx.createGain();
  190. /** @type {GainNode} */
  191. this._sr = GodotAudio.ctx.createGain();
  192. /** @type {GainNode} */
  193. this._c = GodotAudio.ctx.createGain();
  194. /** @type {GainNode} */
  195. this._lfe = GodotAudio.ctx.createGain();
  196. /** @type {ChannelMergerNode} */
  197. this._channelMerger = GodotAudio.ctx.createChannelMerger(NUMBER_OF_WEB_CHANNELS);
  198. this._channelSplitter
  199. .connect(this._l, GodotAudio.WebChannel.CHANNEL_L)
  200. .connect(
  201. this._channelMerger,
  202. GodotAudio.WebChannel.CHANNEL_L,
  203. GodotAudio.WebChannel.CHANNEL_L
  204. );
  205. this._channelSplitter
  206. .connect(this._r, GodotAudio.WebChannel.CHANNEL_R)
  207. .connect(
  208. this._channelMerger,
  209. GodotAudio.WebChannel.CHANNEL_L,
  210. GodotAudio.WebChannel.CHANNEL_R
  211. );
  212. this._channelSplitter
  213. .connect(this._sl, GodotAudio.WebChannel.CHANNEL_SL)
  214. .connect(
  215. this._channelMerger,
  216. GodotAudio.WebChannel.CHANNEL_L,
  217. GodotAudio.WebChannel.CHANNEL_SL
  218. );
  219. this._channelSplitter
  220. .connect(this._sr, GodotAudio.WebChannel.CHANNEL_SR)
  221. .connect(
  222. this._channelMerger,
  223. GodotAudio.WebChannel.CHANNEL_L,
  224. GodotAudio.WebChannel.CHANNEL_SR
  225. );
  226. this._channelSplitter
  227. .connect(this._c, GodotAudio.WebChannel.CHANNEL_C)
  228. .connect(
  229. this._channelMerger,
  230. GodotAudio.WebChannel.CHANNEL_L,
  231. GodotAudio.WebChannel.CHANNEL_C
  232. );
  233. this._channelSplitter
  234. .connect(this._lfe, GodotAudio.WebChannel.CHANNEL_L)
  235. .connect(
  236. this._channelMerger,
  237. GodotAudio.WebChannel.CHANNEL_L,
  238. GodotAudio.WebChannel.CHANNEL_LFE
  239. );
  240. this._channelMerger.connect(this._bus.getInputNode());
  241. }
  242. /**
  243. * Returns the input node.
  244. * @returns {AudioNode}
  245. */
  246. getInputNode() {
  247. return this._channelSplitter;
  248. }
  249. /**
  250. * Returns the output node.
  251. * @returns {AudioNode}
  252. */
  253. getOutputNode() {
  254. return this._channelMerger;
  255. }
  256. /**
  257. * Sets the volume for each (split) channel.
  258. * @param {Float32Array} volume Volume array from the engine for each channel.
  259. * @returns {void}
  260. */
  261. setVolume(volume) {
  262. if (volume.length !== GodotAudio.MAX_VOLUME_CHANNELS) {
  263. throw new Error(
  264. `Volume length isn't "${GodotAudio.MAX_VOLUME_CHANNELS}", is ${volume.length} instead`
  265. );
  266. }
  267. this._l.gain.value = volume[GodotAudio.GodotChannel.CHANNEL_L] ?? 0;
  268. this._r.gain.value = volume[GodotAudio.GodotChannel.CHANNEL_R] ?? 0;
  269. this._sl.gain.value = volume[GodotAudio.GodotChannel.CHANNEL_SL] ?? 0;
  270. this._sr.gain.value = volume[GodotAudio.GodotChannel.CHANNEL_SR] ?? 0;
  271. this._c.gain.value = volume[GodotAudio.GodotChannel.CHANNEL_C] ?? 0;
  272. this._lfe.gain.value = volume[GodotAudio.GodotChannel.CHANNEL_LFE] ?? 0;
  273. }
  274. /**
  275. * Clears the current `SampleNodeBus` instance.
  276. * @returns {void}
  277. */
  278. clear() {
  279. this._bus = null;
  280. this._channelSplitter.disconnect();
  281. this._channelSplitter = null;
  282. this._l.disconnect();
  283. this._l = null;
  284. this._r.disconnect();
  285. this._r = null;
  286. this._sl.disconnect();
  287. this._sl = null;
  288. this._sr.disconnect();
  289. this._sr = null;
  290. this._c.disconnect();
  291. this._c = null;
  292. this._lfe.disconnect();
  293. this._lfe = null;
  294. this._channelMerger.disconnect();
  295. this._channelMerger = null;
  296. }
  297. }
  298. /**
  299. * @typedef {{
  300. * id: string
  301. * streamObjectId: string
  302. * busIndex: number
  303. * }} SampleNodeParams
  304. * @typedef {{
  305. * offset?: number
  306. * playbackRate?: number
  307. * startTime?: number
  308. * pitchScale?: number
  309. * loopMode?: LoopMode
  310. * volume?: Float32Array
  311. * start?: boolean
  312. * }} SampleNodeOptions
  313. */
  314. /**
  315. * Represents an `AudioNode` of a `Sample`.
  316. * @class
  317. */
  318. class SampleNode {
  319. /**
  320. * Returns a `SampleNode`.
  321. * @param {string} id Id of the `SampleNode`.
  322. * @returns {SampleNode}
  323. * @throws {ReferenceError} When no `SampleNode` is not found
  324. */
  325. static getSampleNode(id) {
  326. if (!GodotAudio.sampleNodes.has(id)) {
  327. throw new ReferenceError(`Could not find sample node "${id}"`);
  328. }
  329. return GodotAudio.sampleNodes.get(id);
  330. }
  331. /**
  332. * Returns a `SampleNode`, returns null if not found.
  333. * @param {string} id Id of the SampleNode.
  334. * @returns {SampleNode?}
  335. */
  336. static getSampleNodeOrNull(id) {
  337. return GodotAudio.sampleNodes.get(id) ?? null;
  338. }
  339. /**
  340. * Stops a `SampleNode` by id.
  341. * @param {string} id Id of the `SampleNode` to stop.
  342. * @returns {void}
  343. */
  344. static stopSampleNode(id) {
  345. const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(id);
  346. if (sampleNode == null) {
  347. return;
  348. }
  349. sampleNode.stop();
  350. }
  351. /**
  352. * Pauses the `SampleNode` by id.
  353. * @param {string} id Id of the `SampleNode` to pause.
  354. * @param {boolean} enable State of the pause
  355. * @returns {void}
  356. */
  357. static pauseSampleNode(id, enable) {
  358. const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(id);
  359. if (sampleNode == null) {
  360. return;
  361. }
  362. sampleNode.pause(enable);
  363. }
  364. /**
  365. * Creates a `SampleNode` based on the params. Will register the `SampleNode` to
  366. * the `GodotAudio.sampleNodes` regisery.
  367. * @param {SampleNodeParams} params Base params.
  368. * @param {SampleNodeOptions} options Optional params.
  369. * @returns {SampleNode}
  370. */
  371. static create(params, options = {}) {
  372. const sampleNode = new GodotAudio.SampleNode(params, options);
  373. GodotAudio.sampleNodes.set(params.id, sampleNode);
  374. return sampleNode;
  375. }
  376. /**
  377. * Deletes a `SampleNode` based on the id.
  378. * @param {string} id Id of the `SampleNode` to delete.
  379. * @returns {void}
  380. */
  381. static delete(id) {
  382. GodotAudio.sampleNodes.delete(id);
  383. }
  384. /**
  385. * @param {SampleNodeParams} params Base params
  386. * @param {SampleNodeOptions} [options={{}}] Optional params
  387. */
  388. constructor(params, options = {}) {
  389. /** @type {string} */
  390. this.id = params.id;
  391. /** @type {string} */
  392. this.streamObjectId = params.streamObjectId;
  393. /** @type {number} */
  394. this.offset = options.offset ?? 0;
  395. /** @type {number} */
  396. this._playbackPosition = options.offset;
  397. /** @type {number} */
  398. this.startTime = options.startTime ?? 0;
  399. /** @type {boolean} */
  400. this.isPaused = false;
  401. /** @type {boolean} */
  402. this.isStarted = false;
  403. /** @type {boolean} */
  404. this.isCanceled = false;
  405. /** @type {number} */
  406. this.pauseTime = 0;
  407. /** @type {number} */
  408. this._playbackRate = 44100;
  409. /** @type {LoopMode} */
  410. this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled';
  411. /** @type {number} */
  412. this._pitchScale = options.pitchScale ?? 1;
  413. /** @type {number} */
  414. this._sourceStartTime = 0;
  415. /** @type {Map<Bus, SampleNodeBus>} */
  416. this._sampleNodeBuses = new Map();
  417. /** @type {AudioBufferSourceNode | null} */
  418. this._source = GodotAudio.ctx.createBufferSource();
  419. this._onended = null;
  420. /** @type {AudioWorkletNode | null} */
  421. this._positionWorklet = null;
  422. this.setPlaybackRate(options.playbackRate ?? 44100);
  423. this._source.buffer = this.getSample().getAudioBuffer();
  424. this._addEndedListener();
  425. const bus = GodotAudio.Bus.getBus(params.busIndex);
  426. const sampleNodeBus = this.getSampleNodeBus(bus);
  427. sampleNodeBus.setVolume(options.volume);
  428. this.connectPositionWorklet(options.start);
  429. }
  430. /**
  431. * Gets the playback rate.
  432. * @returns {number}
  433. */
  434. getPlaybackRate() {
  435. return this._playbackRate;
  436. }
  437. /**
  438. * Gets the playback position.
  439. * @returns {number}
  440. */
  441. getPlaybackPosition() {
  442. return this._playbackPosition;
  443. }
  444. /**
  445. * Sets the playback rate.
  446. * @param {number} val Value to set.
  447. * @returns {void}
  448. */
  449. setPlaybackRate(val) {
  450. this._playbackRate = val;
  451. this._syncPlaybackRate();
  452. }
  453. /**
  454. * Gets the pitch scale.
  455. * @returns {number}
  456. */
  457. getPitchScale() {
  458. return this._pitchScale;
  459. }
  460. /**
  461. * Sets the pitch scale.
  462. * @param {number} val Value to set.
  463. * @returns {void}
  464. */
  465. setPitchScale(val) {
  466. this._pitchScale = val;
  467. this._syncPlaybackRate();
  468. }
  469. /**
  470. * Returns the linked `Sample`.
  471. * @returns {Sample}
  472. */
  473. getSample() {
  474. return GodotAudio.Sample.getSample(this.streamObjectId);
  475. }
  476. /**
  477. * Returns the output node.
  478. * @returns {AudioNode}
  479. */
  480. getOutputNode() {
  481. return this._source;
  482. }
  483. /**
  484. * Starts the `SampleNode`.
  485. * @returns {void}
  486. */
  487. start() {
  488. if (this.isStarted) {
  489. return;
  490. }
  491. this._resetSourceStartTime();
  492. this._source.start(this.startTime, this.offset);
  493. this.isStarted = true;
  494. }
  495. /**
  496. * Stops the `SampleNode`.
  497. * @returns {void}
  498. */
  499. stop() {
  500. this.clear();
  501. }
  502. /**
  503. * Restarts the `SampleNode`.
  504. */
  505. restart() {
  506. this.isPaused = false;
  507. this.pauseTime = 0;
  508. this._resetSourceStartTime();
  509. this._restart();
  510. }
  511. /**
  512. * Pauses the `SampleNode`.
  513. * @param {boolean} [enable=true] State of the pause.
  514. * @returns {void}
  515. */
  516. pause(enable = true) {
  517. if (enable) {
  518. this._pause();
  519. return;
  520. }
  521. this._unpause();
  522. }
  523. /**
  524. * Connects an AudioNode to the output node of this `SampleNode`.
  525. * @param {AudioNode} node AudioNode to connect.
  526. * @returns {void}
  527. */
  528. connect(node) {
  529. return this.getOutputNode().connect(node);
  530. }
  531. /**
  532. * Sets the volumes of the `SampleNode` for each buses passed in parameters.
  533. * @param {Array<Bus>} buses
  534. * @param {Float32Array} volumes
  535. */
  536. setVolumes(buses, volumes) {
  537. for (let busIdx = 0; busIdx < buses.length; busIdx++) {
  538. const sampleNodeBus = this.getSampleNodeBus(buses[busIdx]);
  539. sampleNodeBus.setVolume(
  540. volumes.slice(
  541. busIdx * GodotAudio.MAX_VOLUME_CHANNELS,
  542. (busIdx * GodotAudio.MAX_VOLUME_CHANNELS) + GodotAudio.MAX_VOLUME_CHANNELS
  543. )
  544. );
  545. }
  546. }
  547. /**
  548. * Returns the SampleNodeBus based on the bus in parameters.
  549. * @param {Bus} bus Bus to get the SampleNodeBus from.
  550. * @returns {SampleNodeBus}
  551. */
  552. getSampleNodeBus(bus) {
  553. if (!this._sampleNodeBuses.has(bus)) {
  554. const sampleNodeBus = GodotAudio.SampleNodeBus.create(bus);
  555. this._sampleNodeBuses.set(bus, sampleNodeBus);
  556. this._source.connect(sampleNodeBus.getInputNode());
  557. }
  558. return this._sampleNodeBuses.get(bus);
  559. }
  560. /**
  561. * Sets up and connects the source to the GodotPositionReportingProcessor
  562. * If the worklet module is not loaded in, it will be added
  563. */
  564. connectPositionWorklet(start) {
  565. try {
  566. this._positionWorklet = this.createPositionWorklet();
  567. this._source.connect(this._positionWorklet);
  568. if (start) {
  569. this.start();
  570. }
  571. } catch (error) {
  572. if (error?.name !== 'InvalidStateError') {
  573. throw error;
  574. }
  575. const path = GodotConfig.locate_file('godot.audio.position.worklet.js');
  576. GodotAudio.ctx.audioWorklet
  577. .addModule(path)
  578. .then(() => {
  579. if (!this.isCanceled) {
  580. this._positionWorklet = this.createPositionWorklet();
  581. this._source.connect(this._positionWorklet);
  582. if (start) {
  583. this.start();
  584. }
  585. }
  586. }).catch((addModuleError) => {
  587. GodotRuntime.error('Failed to create PositionWorklet.', addModuleError);
  588. });
  589. }
  590. }
  591. /**
  592. * Creates the AudioWorkletProcessor used to track playback position.
  593. * @returns {AudioWorkletNode}
  594. */
  595. createPositionWorklet() {
  596. const worklet = new AudioWorkletNode(
  597. GodotAudio.ctx,
  598. 'godot-position-reporting-processor'
  599. );
  600. worklet.port.onmessage = (event) => {
  601. switch (event.data['type']) {
  602. case 'position':
  603. this._playbackPosition = (parseInt(event.data.data, 10) / this.getSample().sampleRate) + this.offset;
  604. break;
  605. default:
  606. // Do nothing.
  607. }
  608. };
  609. return worklet;
  610. }
  611. /**
  612. * Clears the `SampleNode`.
  613. * @returns {void}
  614. */
  615. clear() {
  616. this.isCanceled = true;
  617. this.isPaused = false;
  618. this.pauseTime = 0;
  619. if (this._source != null) {
  620. this._source.removeEventListener('ended', this._onended);
  621. this._onended = null;
  622. if (this.isStarted) {
  623. this._source.stop();
  624. }
  625. this._source.disconnect();
  626. this._source = null;
  627. }
  628. for (const sampleNodeBus of this._sampleNodeBuses.values()) {
  629. sampleNodeBus.clear();
  630. }
  631. this._sampleNodeBuses.clear();
  632. if (this._positionWorklet) {
  633. this._positionWorklet.disconnect();
  634. this._positionWorklet.port.onmessage = null;
  635. this._positionWorklet = null;
  636. }
  637. GodotAudio.SampleNode.delete(this.id);
  638. }
  639. /**
  640. * Resets the source start time
  641. * @returns {void}
  642. */
  643. _resetSourceStartTime() {
  644. this._sourceStartTime = GodotAudio.ctx.currentTime;
  645. }
  646. /**
  647. * Syncs the `AudioNode` playback rate based on the `SampleNode` playback rate and pitch scale.
  648. * @returns {void}
  649. */
  650. _syncPlaybackRate() {
  651. this._source.playbackRate.value = this.getPlaybackRate() * this.getPitchScale();
  652. }
  653. /**
  654. * Restarts the `SampleNode`.
  655. * Honors `isPaused` and `pauseTime`.
  656. * @returns {void}
  657. */
  658. _restart() {
  659. if (this._source != null) {
  660. this._source.disconnect();
  661. }
  662. this._source = GodotAudio.ctx.createBufferSource();
  663. this._source.buffer = this.getSample().getAudioBuffer();
  664. // Make sure that we connect the new source to the sample node bus.
  665. for (const sampleNodeBus of this._sampleNodeBuses.values()) {
  666. this.connect(sampleNodeBus.getInputNode());
  667. }
  668. this._addEndedListener();
  669. const pauseTime = this.isPaused
  670. ? this.pauseTime
  671. : 0;
  672. this.connectPositionWorklet();
  673. this._source.start(this.startTime, this.offset + pauseTime);
  674. this.isStarted = true;
  675. }
  676. /**
  677. * Pauses the `SampleNode`.
  678. * @returns {void}
  679. */
  680. _pause() {
  681. this.isPaused = true;
  682. this.pauseTime = (GodotAudio.ctx.currentTime - this._sourceStartTime) / this.getPlaybackRate();
  683. this._source.stop();
  684. }
  685. /**
  686. * Unpauses the `SampleNode`.
  687. * @returns {void}
  688. */
  689. _unpause() {
  690. this._restart();
  691. this.isPaused = false;
  692. this.pauseTime = 0;
  693. }
  694. /**
  695. * Adds an "ended" listener to the source node to repeat it if necessary.
  696. * @returns {void}
  697. */
  698. _addEndedListener() {
  699. if (this._onended != null) {
  700. this._source.removeEventListener('ended', this._onended);
  701. }
  702. /** @type {SampleNode} */
  703. // eslint-disable-next-line consistent-this
  704. const self = this;
  705. this._onended = (_) => {
  706. if (self.isPaused) {
  707. return;
  708. }
  709. switch (self.getSample().loopMode) {
  710. case 'disabled': {
  711. const id = this.id;
  712. self.stop();
  713. if (GodotAudio.sampleFinishedCallback != null) {
  714. const idCharPtr = GodotRuntime.allocString(id);
  715. GodotAudio.sampleFinishedCallback(idCharPtr);
  716. GodotRuntime.free(idCharPtr);
  717. }
  718. } break;
  719. case 'forward':
  720. case 'backward':
  721. self.restart();
  722. break;
  723. default:
  724. // do nothing
  725. }
  726. };
  727. this._source.addEventListener('ended', this._onended);
  728. }
  729. }
  730. /**
  731. * Collection of nodes to represents a Godot Engine audio bus.
  732. * @class
  733. */
  734. class Bus {
  735. /**
  736. * Returns the number of registered buses.
  737. * @returns {number}
  738. */
  739. static getCount() {
  740. return GodotAudio.buses.length;
  741. }
  742. /**
  743. * Sets the number of registered buses.
  744. * Will delete buses if lower than the current number.
  745. * @param {number} val Count of registered buses.
  746. * @returns {void}
  747. */
  748. static setCount(val) {
  749. const buses = GodotAudio.buses;
  750. if (val === buses.length) {
  751. return;
  752. }
  753. if (val < buses.length) {
  754. // TODO: what to do with nodes connected to the deleted buses?
  755. const deletedBuses = buses.slice(val);
  756. for (let i = 0; i < deletedBuses.length; i++) {
  757. const deletedBus = deletedBuses[i];
  758. deletedBus.clear();
  759. }
  760. GodotAudio.buses = buses.slice(0, val);
  761. return;
  762. }
  763. for (let i = GodotAudio.buses.length; i < val; i++) {
  764. GodotAudio.Bus.create();
  765. }
  766. }
  767. /**
  768. * Returns a `Bus` based on it's index number.
  769. * @param {number} index
  770. * @returns {Bus}
  771. * @throws {ReferenceError} If the index value is outside the registry.
  772. */
  773. static getBus(index) {
  774. if (index < 0 || index >= GodotAudio.buses.length) {
  775. throw new ReferenceError(`invalid bus index "${index}"`);
  776. }
  777. return GodotAudio.buses[index];
  778. }
  779. /**
  780. * Returns a `Bus` based on it's index number. Returns null if it doesn't exist.
  781. * @param {number} index
  782. * @returns {Bus?}
  783. */
  784. static getBusOrNull(index) {
  785. if (index < 0 || index >= GodotAudio.buses.length) {
  786. return null;
  787. }
  788. return GodotAudio.buses[index];
  789. }
  790. /**
  791. * Move a bus from an index to another.
  792. * @param {number} fromIndex From index
  793. * @param {number} toIndex To index
  794. * @returns {void}
  795. */
  796. static move(fromIndex, toIndex) {
  797. const movedBus = GodotAudio.Bus.getBusOrNull(fromIndex);
  798. if (movedBus == null) {
  799. return;
  800. }
  801. const buses = GodotAudio.buses.filter((_, i) => i !== fromIndex);
  802. // Inserts at index.
  803. buses.splice(toIndex - 1, 0, movedBus);
  804. GodotAudio.buses = buses;
  805. }
  806. /**
  807. * Adds a new bus at the specified index.
  808. * @param {number} index Index to add a new bus.
  809. * @returns {void}
  810. */
  811. static addAt(index) {
  812. const newBus = GodotAudio.Bus.create();
  813. if (index !== newBus.getId()) {
  814. GodotAudio.Bus.move(newBus.getId(), index);
  815. }
  816. }
  817. /**
  818. * Creates a `Bus` and registers it.
  819. * @returns {Bus}
  820. */
  821. static create() {
  822. const newBus = new GodotAudio.Bus();
  823. const isFirstBus = GodotAudio.buses.length === 0;
  824. GodotAudio.buses.push(newBus);
  825. if (isFirstBus) {
  826. newBus.setSend(null);
  827. } else {
  828. newBus.setSend(GodotAudio.Bus.getBus(0));
  829. }
  830. return newBus;
  831. }
  832. /**
  833. * `Bus` constructor.
  834. */
  835. constructor() {
  836. /** @type {Set<SampleNode>} */
  837. this._sampleNodes = new Set();
  838. /** @type {boolean} */
  839. this.isSolo = false;
  840. /** @type {Bus?} */
  841. this._send = null;
  842. /** @type {GainNode} */
  843. this._gainNode = GodotAudio.ctx.createGain();
  844. /** @type {GainNode} */
  845. this._soloNode = GodotAudio.ctx.createGain();
  846. /** @type {GainNode} */
  847. this._muteNode = GodotAudio.ctx.createGain();
  848. this._gainNode
  849. .connect(this._soloNode)
  850. .connect(this._muteNode);
  851. }
  852. /**
  853. * Returns the current id of the bus (its index).
  854. * @returns {number}
  855. */
  856. getId() {
  857. return GodotAudio.buses.indexOf(this);
  858. }
  859. /**
  860. * Returns the bus volume db value.
  861. * @returns {number}
  862. */
  863. getVolumeDb() {
  864. return GodotAudio.linear_to_db(this._gainNode.gain.value);
  865. }
  866. /**
  867. * Sets the bus volume db value.
  868. * @param {number} val Value to set
  869. * @returns {void}
  870. */
  871. setVolumeDb(val) {
  872. const linear = GodotAudio.db_to_linear(val);
  873. if (isFinite(linear)) {
  874. this._gainNode.gain.value = linear;
  875. }
  876. }
  877. /**
  878. * Returns the "send" bus.
  879. * If null, this bus sends its contents directly to the output.
  880. * If not null, this bus sends its contents to another bus.
  881. * @returns {Bus?}
  882. */
  883. getSend() {
  884. return this._send;
  885. }
  886. /**
  887. * Sets the "send" bus.
  888. * If null, this bus sends its contents directly to the output.
  889. * If not null, this bus sends its contents to another bus.
  890. *
  891. * **Note:** if null, `getId()` must be equal to 0. Otherwise, it will throw.
  892. * @param {Bus?} val
  893. * @returns {void}
  894. * @throws {Error} When val is `null` and `getId()` isn't equal to 0
  895. */
  896. setSend(val) {
  897. this._send = val;
  898. if (val == null) {
  899. if (this.getId() == 0) {
  900. this.getOutputNode().connect(GodotAudio.ctx.destination);
  901. return;
  902. }
  903. throw new Error(
  904. `Cannot send to "${val}" without the bus being at index 0 (current index: ${this.getId()})`
  905. );
  906. }
  907. this.connect(val);
  908. }
  909. /**
  910. * Returns the input node of the bus.
  911. * @returns {AudioNode}
  912. */
  913. getInputNode() {
  914. return this._gainNode;
  915. }
  916. /**
  917. * Returns the output node of the bus.
  918. * @returns {AudioNode}
  919. */
  920. getOutputNode() {
  921. return this._muteNode;
  922. }
  923. /**
  924. * Sets the mute status of the bus.
  925. * @param {boolean} enable
  926. */
  927. mute(enable) {
  928. this._muteNode.gain.value = enable ? 0 : 1;
  929. }
  930. /**
  931. * Sets the solo status of the bus.
  932. * @param {boolean} enable
  933. */
  934. solo(enable) {
  935. if (this.isSolo === enable) {
  936. return;
  937. }
  938. if (enable) {
  939. if (GodotAudio.busSolo != null && GodotAudio.busSolo !== this) {
  940. GodotAudio.busSolo._disableSolo();
  941. }
  942. this._enableSolo();
  943. return;
  944. }
  945. this._disableSolo();
  946. }
  947. /**
  948. * Wrapper to simply add a sample node to the bus.
  949. * @param {SampleNode} sampleNode `SampleNode` to remove
  950. * @returns {void}
  951. */
  952. addSampleNode(sampleNode) {
  953. this._sampleNodes.add(sampleNode);
  954. sampleNode.getOutputNode().connect(this.getInputNode());
  955. }
  956. /**
  957. * Wrapper to simply remove a sample node from the bus.
  958. * @param {SampleNode} sampleNode `SampleNode` to remove
  959. * @returns {void}
  960. */
  961. removeSampleNode(sampleNode) {
  962. this._sampleNodes.delete(sampleNode);
  963. sampleNode.getOutputNode().disconnect();
  964. }
  965. /**
  966. * Wrapper to simply connect to another bus.
  967. * @param {Bus} bus
  968. * @returns {void}
  969. */
  970. connect(bus) {
  971. if (bus == null) {
  972. throw new Error('cannot connect to null bus');
  973. }
  974. this.getOutputNode().disconnect();
  975. this.getOutputNode().connect(bus.getInputNode());
  976. return bus;
  977. }
  978. /**
  979. * Clears the current bus.
  980. * @returns {void}
  981. */
  982. clear() {
  983. GodotAudio.buses = GodotAudio.buses.filter((v) => v !== this);
  984. }
  985. _syncSampleNodes() {
  986. const sampleNodes = Array.from(this._sampleNodes);
  987. for (let i = 0; i < sampleNodes.length; i++) {
  988. const sampleNode = sampleNodes[i];
  989. sampleNode.getOutputNode().disconnect();
  990. sampleNode.getOutputNode().connect(this.getInputNode());
  991. }
  992. }
  993. /**
  994. * Process to enable solo.
  995. * @returns {void}
  996. */
  997. _enableSolo() {
  998. this.isSolo = true;
  999. GodotAudio.busSolo = this;
  1000. this._soloNode.gain.value = 1;
  1001. const otherBuses = GodotAudio.buses.filter(
  1002. (otherBus) => otherBus !== this
  1003. );
  1004. for (let i = 0; i < otherBuses.length; i++) {
  1005. const otherBus = otherBuses[i];
  1006. otherBus._soloNode.gain.value = 0;
  1007. }
  1008. }
  1009. /**
  1010. * Process to disable solo.
  1011. * @returns {void}
  1012. */
  1013. _disableSolo() {
  1014. this.isSolo = false;
  1015. GodotAudio.busSolo = null;
  1016. this._soloNode.gain.value = 1;
  1017. const otherBuses = GodotAudio.buses.filter(
  1018. (otherBus) => otherBus !== this
  1019. );
  1020. for (let i = 0; i < otherBuses.length; i++) {
  1021. const otherBus = otherBuses[i];
  1022. otherBus._soloNode.gain.value = 1;
  1023. }
  1024. }
  1025. }
  1026. const _GodotAudio = {
  1027. $GodotAudio__deps: ['$GodotRuntime', '$GodotOS'],
  1028. $GodotAudio: {
  1029. /**
  1030. * Max number of volume channels.
  1031. */
  1032. MAX_VOLUME_CHANNELS: 8,
  1033. /**
  1034. * Represents the index of each sound channel relative to the engine.
  1035. */
  1036. GodotChannel: Object.freeze({
  1037. CHANNEL_L: 0,
  1038. CHANNEL_R: 1,
  1039. CHANNEL_C: 3,
  1040. CHANNEL_LFE: 4,
  1041. CHANNEL_RL: 5,
  1042. CHANNEL_RR: 6,
  1043. CHANNEL_SL: 7,
  1044. CHANNEL_SR: 8,
  1045. }),
  1046. /**
  1047. * Represents the index of each sound channel relative to the Web Audio API.
  1048. */
  1049. WebChannel: Object.freeze({
  1050. CHANNEL_L: 0,
  1051. CHANNEL_R: 1,
  1052. CHANNEL_SL: 2,
  1053. CHANNEL_SR: 3,
  1054. CHANNEL_C: 4,
  1055. CHANNEL_LFE: 5,
  1056. }),
  1057. // `Sample` class
  1058. /**
  1059. * Registry of `Sample`s.
  1060. * @type {Map<string, Sample>}
  1061. */
  1062. samples: null,
  1063. Sample,
  1064. // `SampleNodeBus` class
  1065. SampleNodeBus,
  1066. // `SampleNode` class
  1067. /**
  1068. * Registry of `SampleNode`s.
  1069. * @type {Map<string, SampleNode>}
  1070. */
  1071. sampleNodes: null,
  1072. SampleNode,
  1073. // `Bus` class
  1074. /**
  1075. * Registry of `Bus`es.
  1076. * @type {Array<Bus>}
  1077. */
  1078. buses: null,
  1079. /**
  1080. * Reference to the current bus in solo mode.
  1081. * @type {Bus | null}
  1082. */
  1083. busSolo: null,
  1084. Bus,
  1085. /**
  1086. * Callback to signal that a sample has finished.
  1087. * @type {(playbackObjectIdPtr: number) => void | null}
  1088. */
  1089. sampleFinishedCallback: null,
  1090. /** @type {AudioContext} */
  1091. ctx: null,
  1092. input: null,
  1093. driver: null,
  1094. interval: 0,
  1095. /**
  1096. * Converts linear volume to Db.
  1097. * @param {number} linear Linear value to convert.
  1098. * @returns {number}
  1099. */
  1100. linear_to_db: function (linear) {
  1101. // eslint-disable-next-line no-loss-of-precision
  1102. return Math.log(linear) * 8.6858896380650365530225783783321;
  1103. },
  1104. /**
  1105. * Converts Db volume to linear.
  1106. * @param {number} db Db value to convert.
  1107. * @returns {number}
  1108. */
  1109. db_to_linear: function (db) {
  1110. // eslint-disable-next-line no-loss-of-precision
  1111. return Math.exp(db * 0.11512925464970228420089957273422);
  1112. },
  1113. init: function (mix_rate, latency, onstatechange, onlatencyupdate) {
  1114. // Initialize classes static values.
  1115. GodotAudio.samples = new Map();
  1116. GodotAudio.sampleNodes = new Map();
  1117. GodotAudio.buses = [];
  1118. GodotAudio.busSolo = null;
  1119. const opts = {};
  1120. // If mix_rate is 0, let the browser choose.
  1121. if (mix_rate) {
  1122. GodotAudio.sampleRate = mix_rate;
  1123. opts['sampleRate'] = mix_rate;
  1124. }
  1125. // Do not specify, leave 'interactive' for good performance.
  1126. // opts['latencyHint'] = latency / 1000;
  1127. const ctx = new (window.AudioContext || window.webkitAudioContext)(opts);
  1128. GodotAudio.ctx = ctx;
  1129. ctx.onstatechange = function () {
  1130. let state = 0;
  1131. switch (ctx.state) {
  1132. case 'suspended':
  1133. state = 0;
  1134. break;
  1135. case 'running':
  1136. state = 1;
  1137. break;
  1138. case 'closed':
  1139. state = 2;
  1140. break;
  1141. default:
  1142. // Do nothing.
  1143. }
  1144. onstatechange(state);
  1145. };
  1146. ctx.onstatechange(); // Immediately notify state.
  1147. // Update computed latency
  1148. GodotAudio.interval = setInterval(function () {
  1149. let computed_latency = 0;
  1150. if (ctx.baseLatency) {
  1151. computed_latency += GodotAudio.ctx.baseLatency;
  1152. }
  1153. if (ctx.outputLatency) {
  1154. computed_latency += GodotAudio.ctx.outputLatency;
  1155. }
  1156. onlatencyupdate(computed_latency);
  1157. }, 1000);
  1158. GodotOS.atexit(GodotAudio.close_async);
  1159. return ctx.destination.channelCount;
  1160. },
  1161. create_input: function (callback) {
  1162. if (GodotAudio.input) {
  1163. return 0; // Already started.
  1164. }
  1165. function gotMediaInput(stream) {
  1166. try {
  1167. GodotAudio.input = GodotAudio.ctx.createMediaStreamSource(stream);
  1168. callback(GodotAudio.input);
  1169. } catch (e) {
  1170. GodotRuntime.error('Failed creating input.', e);
  1171. }
  1172. }
  1173. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  1174. navigator.mediaDevices.getUserMedia({
  1175. 'audio': true,
  1176. }).then(gotMediaInput, function (e) {
  1177. GodotRuntime.error('Error getting user media.', e);
  1178. });
  1179. } else {
  1180. if (!navigator.getUserMedia) {
  1181. navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
  1182. }
  1183. if (!navigator.getUserMedia) {
  1184. GodotRuntime.error('getUserMedia not available.');
  1185. return 1;
  1186. }
  1187. navigator.getUserMedia({
  1188. 'audio': true,
  1189. }, gotMediaInput, function (e) {
  1190. GodotRuntime.print(e);
  1191. });
  1192. }
  1193. return 0;
  1194. },
  1195. close_async: function (resolve, reject) {
  1196. const ctx = GodotAudio.ctx;
  1197. GodotAudio.ctx = null;
  1198. // Audio was not initialized.
  1199. if (!ctx) {
  1200. resolve();
  1201. return;
  1202. }
  1203. // Remove latency callback
  1204. if (GodotAudio.interval) {
  1205. clearInterval(GodotAudio.interval);
  1206. GodotAudio.interval = 0;
  1207. }
  1208. // Disconnect input, if it was started.
  1209. if (GodotAudio.input) {
  1210. GodotAudio.input.disconnect();
  1211. GodotAudio.input = null;
  1212. }
  1213. // Disconnect output
  1214. let closed = Promise.resolve();
  1215. if (GodotAudio.driver) {
  1216. closed = GodotAudio.driver.close();
  1217. }
  1218. closed.then(function () {
  1219. return ctx.close();
  1220. }).then(function () {
  1221. ctx.onstatechange = null;
  1222. resolve();
  1223. }).catch(function (e) {
  1224. ctx.onstatechange = null;
  1225. GodotRuntime.error('Error closing AudioContext', e);
  1226. resolve();
  1227. });
  1228. },
  1229. /**
  1230. * Triggered when a sample node needs to start.
  1231. * @param {string} playbackObjectId The unique id of the sample playback
  1232. * @param {string} streamObjectId The unique id of the stream
  1233. * @param {number} busIndex Index of the bus currently binded to the sample playback
  1234. * @param {SampleNodeOptions} startOptions Optional params
  1235. * @returns {void}
  1236. */
  1237. start_sample: function (
  1238. playbackObjectId,
  1239. streamObjectId,
  1240. busIndex,
  1241. startOptions
  1242. ) {
  1243. GodotAudio.SampleNode.stopSampleNode(playbackObjectId);
  1244. GodotAudio.SampleNode.create(
  1245. {
  1246. busIndex,
  1247. id: playbackObjectId,
  1248. streamObjectId,
  1249. },
  1250. startOptions
  1251. );
  1252. },
  1253. /**
  1254. * Triggered when a sample node needs to be stopped.
  1255. * @param {string} playbackObjectId Id of the sample playback
  1256. * @returns {void}
  1257. */
  1258. stop_sample: function (playbackObjectId) {
  1259. GodotAudio.SampleNode.stopSampleNode(playbackObjectId);
  1260. },
  1261. /**
  1262. * Triggered when a sample node needs to be paused or unpaused.
  1263. * @param {string} playbackObjectId Id of the sample playback
  1264. * @param {boolean} pause State of the pause
  1265. * @returns {void}
  1266. */
  1267. sample_set_pause: function (playbackObjectId, pause) {
  1268. GodotAudio.SampleNode.pauseSampleNode(playbackObjectId, pause);
  1269. },
  1270. /**
  1271. * Triggered when a sample node needs its pitch scale to be updated.
  1272. * @param {string} playbackObjectId Id of the sample playback
  1273. * @param {number} pitchScale Pitch scale of the sample playback
  1274. * @returns {void}
  1275. */
  1276. update_sample_pitch_scale: function (playbackObjectId, pitchScale) {
  1277. const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);
  1278. if (sampleNode == null) {
  1279. return;
  1280. }
  1281. sampleNode.setPitchScale(pitchScale);
  1282. },
  1283. /**
  1284. * Triggered when a sample node volumes need to be updated.
  1285. * @param {string} playbackObjectId Id of the sample playback
  1286. * @param {Array<number>} busIndexes Indexes of the buses that need to be updated
  1287. * @param {Float32Array} volumes Array of the volumes
  1288. * @returns {void}
  1289. */
  1290. sample_set_volumes_linear: function (playbackObjectId, busIndexes, volumes) {
  1291. const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);
  1292. if (sampleNode == null) {
  1293. return;
  1294. }
  1295. const buses = busIndexes.map((busIndex) => GodotAudio.Bus.getBus(busIndex));
  1296. sampleNode.setVolumes(buses, volumes);
  1297. },
  1298. /**
  1299. * Triggered when the bus count changes.
  1300. * @param {number} count Number of buses
  1301. * @returns {void}
  1302. */
  1303. set_sample_bus_count: function (count) {
  1304. GodotAudio.Bus.setCount(count);
  1305. },
  1306. /**
  1307. * Triggered when a bus needs to be removed.
  1308. * @param {number} index Bus index
  1309. * @returns {void}
  1310. */
  1311. remove_sample_bus: function (index) {
  1312. const bus = GodotAudio.Bus.getBusOrNull(index);
  1313. if (bus == null) {
  1314. return;
  1315. }
  1316. bus.clear();
  1317. },
  1318. /**
  1319. * Triggered when a bus needs to be at the desired position.
  1320. * @param {number} atPos Position to add the bus
  1321. * @returns {void}
  1322. */
  1323. add_sample_bus: function (atPos) {
  1324. GodotAudio.Bus.addAt(atPos);
  1325. },
  1326. /**
  1327. * Triggered when a bus needs to be moved.
  1328. * @param {number} busIndex Index of the bus to move
  1329. * @param {number} toPos Index of the new position of the bus
  1330. * @returns {void}
  1331. */
  1332. move_sample_bus: function (busIndex, toPos) {
  1333. GodotAudio.Bus.move(busIndex, toPos);
  1334. },
  1335. /**
  1336. * Triggered when the "send" value of a bus changes.
  1337. * @param {number} busIndex Index of the bus to update the "send" value
  1338. * @param {number} sendIndex Index of the bus that is the new "send"
  1339. * @returns {void}
  1340. */
  1341. set_sample_bus_send: function (busIndex, sendIndex) {
  1342. const bus = GodotAudio.Bus.getBusOrNull(busIndex);
  1343. if (bus == null) {
  1344. // Cannot send from an invalid bus.
  1345. return;
  1346. }
  1347. let targetBus = GodotAudio.Bus.getBusOrNull(sendIndex);
  1348. if (targetBus == null) {
  1349. // Send to master.
  1350. targetBus = GodotAudio.Bus.getBus(0);
  1351. }
  1352. bus.setSend(targetBus);
  1353. },
  1354. /**
  1355. * Triggered when a bus needs its volume db to be updated.
  1356. * @param {number} busIndex Index of the bus to update its volume db
  1357. * @param {number} volumeDb Volume of the bus
  1358. * @returns {void}
  1359. */
  1360. set_sample_bus_volume_db: function (busIndex, volumeDb) {
  1361. const bus = GodotAudio.Bus.getBusOrNull(busIndex);
  1362. if (bus == null) {
  1363. return;
  1364. }
  1365. bus.setVolumeDb(volumeDb);
  1366. },
  1367. /**
  1368. * Triggered when a bus needs to update its solo status
  1369. * @param {number} busIndex Index of the bus to update its solo status
  1370. * @param {boolean} enable Status of the solo
  1371. * @returns {void}
  1372. */
  1373. set_sample_bus_solo: function (busIndex, enable) {
  1374. const bus = GodotAudio.Bus.getBusOrNull(busIndex);
  1375. if (bus == null) {
  1376. return;
  1377. }
  1378. bus.solo(enable);
  1379. },
  1380. /**
  1381. * Triggered when a bus needs to update its mute status
  1382. * @param {number} busIndex Index of the bus to update its mute status
  1383. * @param {boolean} enable Status of the mute
  1384. * @returns {void}
  1385. */
  1386. set_sample_bus_mute: function (busIndex, enable) {
  1387. const bus = GodotAudio.Bus.getBusOrNull(busIndex);
  1388. if (bus == null) {
  1389. return;
  1390. }
  1391. bus.mute(enable);
  1392. },
  1393. },
  1394. godot_audio_is_available__sig: 'i',
  1395. godot_audio_is_available__proxy: 'sync',
  1396. godot_audio_is_available: function () {
  1397. if (!(window.AudioContext || window.webkitAudioContext)) {
  1398. return 0;
  1399. }
  1400. return 1;
  1401. },
  1402. godot_audio_has_worklet__proxy: 'sync',
  1403. godot_audio_has_worklet__sig: 'i',
  1404. godot_audio_has_worklet: function () {
  1405. return GodotAudio.ctx && GodotAudio.ctx.audioWorklet ? 1 : 0;
  1406. },
  1407. godot_audio_has_script_processor__proxy: 'sync',
  1408. godot_audio_has_script_processor__sig: 'i',
  1409. godot_audio_has_script_processor: function () {
  1410. return GodotAudio.ctx && GodotAudio.ctx.createScriptProcessor ? 1 : 0;
  1411. },
  1412. godot_audio_init__proxy: 'sync',
  1413. godot_audio_init__sig: 'iiiii',
  1414. godot_audio_init: function (
  1415. p_mix_rate,
  1416. p_latency,
  1417. p_state_change,
  1418. p_latency_update
  1419. ) {
  1420. const statechange = GodotRuntime.get_func(p_state_change);
  1421. const latencyupdate = GodotRuntime.get_func(p_latency_update);
  1422. const mix_rate = GodotRuntime.getHeapValue(p_mix_rate, 'i32');
  1423. const channels = GodotAudio.init(
  1424. mix_rate,
  1425. p_latency,
  1426. statechange,
  1427. latencyupdate
  1428. );
  1429. GodotRuntime.setHeapValue(p_mix_rate, GodotAudio.ctx.sampleRate, 'i32');
  1430. return channels;
  1431. },
  1432. godot_audio_resume__proxy: 'sync',
  1433. godot_audio_resume__sig: 'v',
  1434. godot_audio_resume: function () {
  1435. if (GodotAudio.ctx && GodotAudio.ctx.state !== 'running') {
  1436. GodotAudio.ctx.resume();
  1437. }
  1438. },
  1439. godot_audio_input_start__proxy: 'sync',
  1440. godot_audio_input_start__sig: 'i',
  1441. godot_audio_input_start: function () {
  1442. return GodotAudio.create_input(function (input) {
  1443. input.connect(GodotAudio.driver.get_node());
  1444. });
  1445. },
  1446. godot_audio_input_stop__proxy: 'sync',
  1447. godot_audio_input_stop__sig: 'v',
  1448. godot_audio_input_stop: function () {
  1449. if (GodotAudio.input) {
  1450. const tracks = GodotAudio.input['mediaStream']['getTracks']();
  1451. for (let i = 0; i < tracks.length; i++) {
  1452. tracks[i]['stop']();
  1453. }
  1454. GodotAudio.input.disconnect();
  1455. GodotAudio.input = null;
  1456. }
  1457. },
  1458. godot_audio_sample_stream_is_registered__proxy: 'sync',
  1459. godot_audio_sample_stream_is_registered__sig: 'ii',
  1460. /**
  1461. * Returns if the sample stream is registered
  1462. * @param {number} streamObjectIdStrPtr Pointer of the streamObjectId
  1463. * @returns {number}
  1464. */
  1465. godot_audio_sample_stream_is_registered: function (streamObjectIdStrPtr) {
  1466. const streamObjectId = GodotRuntime.parseString(streamObjectIdStrPtr);
  1467. return Number(GodotAudio.Sample.getSampleOrNull(streamObjectId) != null);
  1468. },
  1469. godot_audio_sample_register_stream__proxy: 'sync',
  1470. godot_audio_sample_register_stream__sig: 'viiiiiii',
  1471. /**
  1472. * Registers a stream.
  1473. * @param {number} streamObjectIdStrPtr StreamObjectId pointer
  1474. * @param {number} framesPtr Frames pointer
  1475. * @param {number} framesTotal Frames total value
  1476. * @param {number} loopModeStrPtr Loop mode pointer
  1477. * @param {number} loopBegin Loop begin value
  1478. * @param {number} loopEnd Loop end value
  1479. * @returns {void}
  1480. */
  1481. godot_audio_sample_register_stream: function (
  1482. streamObjectIdStrPtr,
  1483. framesPtr,
  1484. framesTotal,
  1485. loopModeStrPtr,
  1486. loopBegin,
  1487. loopEnd
  1488. ) {
  1489. const BYTES_PER_FLOAT32 = 4;
  1490. const streamObjectId = GodotRuntime.parseString(streamObjectIdStrPtr);
  1491. const loopMode = GodotRuntime.parseString(loopModeStrPtr);
  1492. const numberOfChannels = 2;
  1493. const sampleRate = GodotAudio.ctx.sampleRate;
  1494. /** @type {Float32Array} */
  1495. const subLeft = GodotRuntime.heapSub(HEAPF32, framesPtr, framesTotal);
  1496. /** @type {Float32Array} */
  1497. const subRight = GodotRuntime.heapSub(
  1498. HEAPF32,
  1499. framesPtr + framesTotal * BYTES_PER_FLOAT32,
  1500. framesTotal
  1501. );
  1502. const audioBuffer = GodotAudio.ctx.createBuffer(
  1503. numberOfChannels,
  1504. framesTotal,
  1505. sampleRate
  1506. );
  1507. audioBuffer.copyToChannel(new Float32Array(subLeft), 0, 0);
  1508. audioBuffer.copyToChannel(new Float32Array(subRight), 1, 0);
  1509. GodotAudio.Sample.create(
  1510. {
  1511. id: streamObjectId,
  1512. audioBuffer,
  1513. },
  1514. {
  1515. loopBegin,
  1516. loopEnd,
  1517. loopMode,
  1518. numberOfChannels,
  1519. sampleRate,
  1520. }
  1521. );
  1522. },
  1523. godot_audio_sample_unregister_stream__proxy: 'sync',
  1524. godot_audio_sample_unregister_stream__sig: 'vi',
  1525. /**
  1526. * Unregisters a stream.
  1527. * @param {number} streamObjectIdStrPtr StreamObjectId pointer
  1528. * @returns {void}
  1529. */
  1530. godot_audio_sample_unregister_stream: function (streamObjectIdStrPtr) {
  1531. const streamObjectId = GodotRuntime.parseString(streamObjectIdStrPtr);
  1532. const sample = GodotAudio.Sample.getSampleOrNull(streamObjectId);
  1533. if (sample != null) {
  1534. sample.clear();
  1535. }
  1536. },
  1537. godot_audio_sample_start__proxy: 'sync',
  1538. godot_audio_sample_start__sig: 'viiiifi',
  1539. /**
  1540. * Starts a sample.
  1541. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1542. * @param {number} streamObjectIdStrPtr Stream object id pointer
  1543. * @param {number} busIndex Bus index
  1544. * @param {number} offset Sample offset
  1545. * @param {number} pitchScale Pitch scale
  1546. * @param {number} volumePtr Volume pointer
  1547. * @returns {void}
  1548. */
  1549. godot_audio_sample_start: function (
  1550. playbackObjectIdStrPtr,
  1551. streamObjectIdStrPtr,
  1552. busIndex,
  1553. offset,
  1554. pitchScale,
  1555. volumePtr
  1556. ) {
  1557. /** @type {string} */
  1558. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1559. /** @type {string} */
  1560. const streamObjectId = GodotRuntime.parseString(streamObjectIdStrPtr);
  1561. /** @type {Float32Array} */
  1562. const volume = GodotRuntime.heapSub(HEAPF32, volumePtr, 8);
  1563. /** @type {SampleNodeOptions} */
  1564. const startOptions = {
  1565. offset,
  1566. volume,
  1567. playbackRate: 1,
  1568. pitchScale,
  1569. start: true,
  1570. };
  1571. GodotAudio.start_sample(
  1572. playbackObjectId,
  1573. streamObjectId,
  1574. busIndex,
  1575. startOptions
  1576. );
  1577. },
  1578. godot_audio_sample_stop__proxy: 'sync',
  1579. godot_audio_sample_stop__sig: 'vi',
  1580. /**
  1581. * Stops a sample from playing.
  1582. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1583. * @returns {void}
  1584. */
  1585. godot_audio_sample_stop: function (playbackObjectIdStrPtr) {
  1586. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1587. GodotAudio.stop_sample(playbackObjectId);
  1588. },
  1589. godot_audio_sample_set_pause__proxy: 'sync',
  1590. godot_audio_sample_set_pause__sig: 'vii',
  1591. /**
  1592. * Sets the pause state of a sample.
  1593. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1594. * @param {number} pause Pause state
  1595. */
  1596. godot_audio_sample_set_pause: function (playbackObjectIdStrPtr, pause) {
  1597. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1598. GodotAudio.sample_set_pause(playbackObjectId, Boolean(pause));
  1599. },
  1600. godot_audio_sample_is_active__proxy: 'sync',
  1601. godot_audio_sample_is_active__sig: 'ii',
  1602. /**
  1603. * Returns if the sample is active.
  1604. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1605. * @returns {number}
  1606. */
  1607. godot_audio_sample_is_active: function (playbackObjectIdStrPtr) {
  1608. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1609. return Number(GodotAudio.sampleNodes.has(playbackObjectId));
  1610. },
  1611. godot_audio_get_sample_playback_position__proxy: 'sync',
  1612. godot_audio_get_sample_playback_position__sig: 'di',
  1613. /**
  1614. * Returns the position of the playback position.
  1615. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1616. * @returns {number}
  1617. */
  1618. godot_audio_get_sample_playback_position: function (playbackObjectIdStrPtr) {
  1619. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1620. const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);
  1621. if (sampleNode == null) {
  1622. return 0;
  1623. }
  1624. return sampleNode.getPlaybackPosition();
  1625. },
  1626. godot_audio_sample_update_pitch_scale__proxy: 'sync',
  1627. godot_audio_sample_update_pitch_scale__sig: 'vii',
  1628. /**
  1629. * Updates the pitch scale of a sample.
  1630. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1631. * @param {number} pitchScale Pitch scale value
  1632. * @returns {void}
  1633. */
  1634. godot_audio_sample_update_pitch_scale: function (
  1635. playbackObjectIdStrPtr,
  1636. pitchScale
  1637. ) {
  1638. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1639. GodotAudio.update_sample_pitch_scale(playbackObjectId, pitchScale);
  1640. },
  1641. godot_audio_sample_set_volumes_linear__proxy: 'sync',
  1642. godot_audio_sample_set_volumes_linear__sig: 'vii',
  1643. /**
  1644. * Sets the volumes linear of each mentioned bus for the sample.
  1645. * @param {number} playbackObjectIdStrPtr Playback object id pointer
  1646. * @param {number} busesPtr Buses array pointer
  1647. * @param {number} busesSize Buses array size
  1648. * @param {number} volumesPtr Volumes array pointer
  1649. * @param {number} volumesSize Volumes array size
  1650. * @returns {void}
  1651. */
  1652. godot_audio_sample_set_volumes_linear: function (
  1653. playbackObjectIdStrPtr,
  1654. busesPtr,
  1655. busesSize,
  1656. volumesPtr,
  1657. volumesSize
  1658. ) {
  1659. /** @type {string} */
  1660. const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
  1661. /** @type {Uint32Array} */
  1662. const buses = GodotRuntime.heapSub(HEAP32, busesPtr, busesSize);
  1663. /** @type {Float32Array} */
  1664. const volumes = GodotRuntime.heapSub(HEAPF32, volumesPtr, volumesSize);
  1665. GodotAudio.sample_set_volumes_linear(
  1666. playbackObjectId,
  1667. Array.from(buses),
  1668. volumes
  1669. );
  1670. },
  1671. godot_audio_sample_bus_set_count__proxy: 'sync',
  1672. godot_audio_sample_bus_set_count__sig: 'vi',
  1673. /**
  1674. * Sets the bus count.
  1675. * @param {number} count Bus count
  1676. * @returns {void}
  1677. */
  1678. godot_audio_sample_bus_set_count: function (count) {
  1679. GodotAudio.set_sample_bus_count(count);
  1680. },
  1681. godot_audio_sample_bus_remove__proxy: 'sync',
  1682. godot_audio_sample_bus_remove__sig: 'vi',
  1683. /**
  1684. * Removes a bus.
  1685. * @param {number} index Index of the bus to remove
  1686. * @returns {void}
  1687. */
  1688. godot_audio_sample_bus_remove: function (index) {
  1689. GodotAudio.remove_sample_bus(index);
  1690. },
  1691. godot_audio_sample_bus_add__proxy: 'sync',
  1692. godot_audio_sample_bus_add__sig: 'vi',
  1693. /**
  1694. * Adds a bus at the defined position.
  1695. * @param {number} atPos Position to add the bus
  1696. * @returns {void}
  1697. */
  1698. godot_audio_sample_bus_add: function (atPos) {
  1699. GodotAudio.add_sample_bus(atPos);
  1700. },
  1701. godot_audio_sample_bus_move__proxy: 'sync',
  1702. godot_audio_sample_bus_move__sig: 'vii',
  1703. /**
  1704. * Moves the bus from a position to another.
  1705. * @param {number} fromPos Position of the bus to move
  1706. * @param {number} toPos Final position of the bus
  1707. * @returns {void}
  1708. */
  1709. godot_audio_sample_bus_move: function (fromPos, toPos) {
  1710. GodotAudio.move_sample_bus(fromPos, toPos);
  1711. },
  1712. godot_audio_sample_bus_set_send__proxy: 'sync',
  1713. godot_audio_sample_bus_set_send__sig: 'vii',
  1714. /**
  1715. * Sets the "send" of a bus.
  1716. * @param {number} bus Position of the bus to set the send
  1717. * @param {number} sendIndex Position of the "send" bus
  1718. * @returns {void}
  1719. */
  1720. godot_audio_sample_bus_set_send: function (bus, sendIndex) {
  1721. GodotAudio.set_sample_bus_send(bus, sendIndex);
  1722. },
  1723. godot_audio_sample_bus_set_volume_db__proxy: 'sync',
  1724. godot_audio_sample_bus_set_volume_db__sig: 'vii',
  1725. /**
  1726. * Sets the volume db of a bus.
  1727. * @param {number} bus Position of the bus to set the volume db
  1728. * @param {number} volumeDb Volume db to set
  1729. * @returns {void}
  1730. */
  1731. godot_audio_sample_bus_set_volume_db: function (bus, volumeDb) {
  1732. GodotAudio.set_sample_bus_volume_db(bus, volumeDb);
  1733. },
  1734. godot_audio_sample_bus_set_solo__proxy: 'sync',
  1735. godot_audio_sample_bus_set_solo__sig: 'vii',
  1736. /**
  1737. * Sets the state of solo for a bus
  1738. * @param {number} bus Position of the bus to set the solo state
  1739. * @param {number} enable State of the solo
  1740. * @returns {void}
  1741. */
  1742. godot_audio_sample_bus_set_solo: function (bus, enable) {
  1743. GodotAudio.set_sample_bus_solo(bus, Boolean(enable));
  1744. },
  1745. godot_audio_sample_bus_set_mute__proxy: 'sync',
  1746. godot_audio_sample_bus_set_mute__sig: 'vii',
  1747. /**
  1748. * Sets the state of mute for a bus
  1749. * @param {number} bus Position of the bus to set the mute state
  1750. * @param {number} enable State of the mute
  1751. * @returns {void}
  1752. */
  1753. godot_audio_sample_bus_set_mute: function (bus, enable) {
  1754. GodotAudio.set_sample_bus_mute(bus, Boolean(enable));
  1755. },
  1756. godot_audio_sample_set_finished_callback__proxy: 'sync',
  1757. godot_audio_sample_set_finished_callback__sig: 'vi',
  1758. /**
  1759. * Sets the finished callback
  1760. * @param {Number} callbackPtr Finished callback pointer
  1761. * @returns {void}
  1762. */
  1763. godot_audio_sample_set_finished_callback: function (callbackPtr) {
  1764. GodotAudio.sampleFinishedCallback = GodotRuntime.get_func(callbackPtr);
  1765. },
  1766. };
  1767. autoAddDeps(_GodotAudio, '$GodotAudio');
  1768. mergeInto(LibraryManager.library, _GodotAudio);
  1769. /**
  1770. * The AudioWorklet API driver, used when threads are available.
  1771. */
  1772. const GodotAudioWorklet = {
  1773. $GodotAudioWorklet__deps: ['$GodotAudio', '$GodotConfig'],
  1774. $GodotAudioWorklet: {
  1775. promise: null,
  1776. worklet: null,
  1777. ring_buffer: null,
  1778. create: function (channels) {
  1779. const path = GodotConfig.locate_file('godot.audio.worklet.js');
  1780. GodotAudioWorklet.promise = GodotAudio.ctx.audioWorklet
  1781. .addModule(path)
  1782. .then(function () {
  1783. GodotAudioWorklet.worklet = new AudioWorkletNode(
  1784. GodotAudio.ctx,
  1785. 'godot-processor',
  1786. {
  1787. outputChannelCount: [channels],
  1788. }
  1789. );
  1790. return Promise.resolve();
  1791. });
  1792. GodotAudio.driver = GodotAudioWorklet;
  1793. },
  1794. start: function (in_buf, out_buf, state) {
  1795. GodotAudioWorklet.promise.then(function () {
  1796. const node = GodotAudioWorklet.worklet;
  1797. node.connect(GodotAudio.ctx.destination);
  1798. node.port.postMessage({
  1799. 'cmd': 'start',
  1800. 'data': [state, in_buf, out_buf],
  1801. });
  1802. node.port.onmessage = function (event) {
  1803. GodotRuntime.error(event.data);
  1804. };
  1805. });
  1806. },
  1807. start_no_threads: function (
  1808. p_out_buf,
  1809. p_out_size,
  1810. out_callback,
  1811. p_in_buf,
  1812. p_in_size,
  1813. in_callback
  1814. ) {
  1815. function RingBuffer() {
  1816. let wpos = 0;
  1817. let rpos = 0;
  1818. let pending_samples = 0;
  1819. const wbuf = new Float32Array(p_out_size);
  1820. function send(port) {
  1821. if (pending_samples === 0) {
  1822. return;
  1823. }
  1824. const buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
  1825. const size = buffer.length;
  1826. const tot_sent = pending_samples;
  1827. out_callback(wpos, pending_samples);
  1828. if (wpos + pending_samples >= size) {
  1829. const high = size - wpos;
  1830. wbuf.set(buffer.subarray(wpos, size));
  1831. pending_samples -= high;
  1832. wpos = 0;
  1833. }
  1834. if (pending_samples > 0) {
  1835. wbuf.set(
  1836. buffer.subarray(wpos, wpos + pending_samples),
  1837. tot_sent - pending_samples
  1838. );
  1839. }
  1840. port.postMessage({ 'cmd': 'chunk', 'data': wbuf.subarray(0, tot_sent) });
  1841. wpos += pending_samples;
  1842. pending_samples = 0;
  1843. }
  1844. this.receive = function (recv_buf) {
  1845. const buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
  1846. const from = rpos;
  1847. let to_write = recv_buf.length;
  1848. let high = 0;
  1849. if (rpos + to_write >= p_in_size) {
  1850. high = p_in_size - rpos;
  1851. buffer.set(recv_buf.subarray(0, high), rpos);
  1852. to_write -= high;
  1853. rpos = 0;
  1854. }
  1855. if (to_write) {
  1856. buffer.set(recv_buf.subarray(high, to_write), rpos);
  1857. }
  1858. in_callback(from, recv_buf.length);
  1859. rpos += to_write;
  1860. };
  1861. this.consumed = function (size, port) {
  1862. pending_samples += size;
  1863. send(port);
  1864. };
  1865. }
  1866. GodotAudioWorklet.ring_buffer = new RingBuffer();
  1867. GodotAudioWorklet.promise.then(function () {
  1868. const node = GodotAudioWorklet.worklet;
  1869. const buffer = GodotRuntime.heapSlice(HEAPF32, p_out_buf, p_out_size);
  1870. node.connect(GodotAudio.ctx.destination);
  1871. node.port.postMessage({
  1872. 'cmd': 'start_nothreads',
  1873. 'data': [buffer, p_in_size],
  1874. });
  1875. node.port.onmessage = function (event) {
  1876. if (!GodotAudioWorklet.worklet) {
  1877. return;
  1878. }
  1879. if (event.data['cmd'] === 'read') {
  1880. const read = event.data['data'];
  1881. GodotAudioWorklet.ring_buffer.consumed(
  1882. read,
  1883. GodotAudioWorklet.worklet.port
  1884. );
  1885. } else if (event.data['cmd'] === 'input') {
  1886. const buf = event.data['data'];
  1887. if (buf.length > p_in_size) {
  1888. GodotRuntime.error('Input chunk is too big');
  1889. return;
  1890. }
  1891. GodotAudioWorklet.ring_buffer.receive(buf);
  1892. } else {
  1893. GodotRuntime.error(event.data);
  1894. }
  1895. };
  1896. });
  1897. },
  1898. get_node: function () {
  1899. return GodotAudioWorklet.worklet;
  1900. },
  1901. close: function () {
  1902. return new Promise(function (resolve, reject) {
  1903. if (GodotAudioWorklet.promise === null) {
  1904. return;
  1905. }
  1906. const p = GodotAudioWorklet.promise;
  1907. p.then(function () {
  1908. GodotAudioWorklet.worklet.port.postMessage({
  1909. 'cmd': 'stop',
  1910. 'data': null,
  1911. });
  1912. GodotAudioWorklet.worklet.disconnect();
  1913. GodotAudioWorklet.worklet.port.onmessage = null;
  1914. GodotAudioWorklet.worklet = null;
  1915. GodotAudioWorklet.promise = null;
  1916. resolve();
  1917. }).catch(function (err) {
  1918. // Aborted?
  1919. GodotRuntime.error(err);
  1920. });
  1921. });
  1922. },
  1923. },
  1924. godot_audio_worklet_create__proxy: 'sync',
  1925. godot_audio_worklet_create__sig: 'ii',
  1926. godot_audio_worklet_create: function (channels) {
  1927. try {
  1928. GodotAudioWorklet.create(channels);
  1929. } catch (e) {
  1930. GodotRuntime.error('Error starting AudioDriverWorklet', e);
  1931. return 1;
  1932. }
  1933. return 0;
  1934. },
  1935. godot_audio_worklet_start__proxy: 'sync',
  1936. godot_audio_worklet_start__sig: 'viiiii',
  1937. godot_audio_worklet_start: function (
  1938. p_in_buf,
  1939. p_in_size,
  1940. p_out_buf,
  1941. p_out_size,
  1942. p_state
  1943. ) {
  1944. const out_buffer = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
  1945. const in_buffer = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
  1946. const state = GodotRuntime.heapSub(HEAP32, p_state, 4);
  1947. GodotAudioWorklet.start(in_buffer, out_buffer, state);
  1948. },
  1949. godot_audio_worklet_start_no_threads__proxy: 'sync',
  1950. godot_audio_worklet_start_no_threads__sig: 'viiiiii',
  1951. godot_audio_worklet_start_no_threads: function (
  1952. p_out_buf,
  1953. p_out_size,
  1954. p_out_callback,
  1955. p_in_buf,
  1956. p_in_size,
  1957. p_in_callback
  1958. ) {
  1959. const out_callback = GodotRuntime.get_func(p_out_callback);
  1960. const in_callback = GodotRuntime.get_func(p_in_callback);
  1961. GodotAudioWorklet.start_no_threads(
  1962. p_out_buf,
  1963. p_out_size,
  1964. out_callback,
  1965. p_in_buf,
  1966. p_in_size,
  1967. in_callback
  1968. );
  1969. },
  1970. godot_audio_worklet_state_wait__sig: 'iiii',
  1971. godot_audio_worklet_state_wait: function (
  1972. p_state,
  1973. p_idx,
  1974. p_expected,
  1975. p_timeout
  1976. ) {
  1977. Atomics.wait(HEAP32, (p_state >> 2) + p_idx, p_expected, p_timeout);
  1978. return Atomics.load(HEAP32, (p_state >> 2) + p_idx);
  1979. },
  1980. godot_audio_worklet_state_add__sig: 'iiii',
  1981. godot_audio_worklet_state_add: function (p_state, p_idx, p_value) {
  1982. return Atomics.add(HEAP32, (p_state >> 2) + p_idx, p_value);
  1983. },
  1984. godot_audio_worklet_state_get__sig: 'iii',
  1985. godot_audio_worklet_state_get: function (p_state, p_idx) {
  1986. return Atomics.load(HEAP32, (p_state >> 2) + p_idx);
  1987. },
  1988. };
  1989. autoAddDeps(GodotAudioWorklet, '$GodotAudioWorklet');
  1990. mergeInto(LibraryManager.library, GodotAudioWorklet);
  1991. /*
  1992. * The ScriptProcessorNode API, used when threads are disabled.
  1993. */
  1994. const GodotAudioScript = {
  1995. $GodotAudioScript__deps: ['$GodotAudio'],
  1996. $GodotAudioScript: {
  1997. script: null,
  1998. create: function (buffer_length, channel_count) {
  1999. GodotAudioScript.script = GodotAudio.ctx.createScriptProcessor(
  2000. buffer_length,
  2001. 2,
  2002. channel_count
  2003. );
  2004. GodotAudio.driver = GodotAudioScript;
  2005. return GodotAudioScript.script.bufferSize;
  2006. },
  2007. start: function (p_in_buf, p_in_size, p_out_buf, p_out_size, onprocess) {
  2008. GodotAudioScript.script.onaudioprocess = function (event) {
  2009. // Read input
  2010. const inb = GodotRuntime.heapSub(HEAPF32, p_in_buf, p_in_size);
  2011. const input = event.inputBuffer;
  2012. if (GodotAudio.input) {
  2013. const inlen = input.getChannelData(0).length;
  2014. for (let ch = 0; ch < 2; ch++) {
  2015. const data = input.getChannelData(ch);
  2016. for (let s = 0; s < inlen; s++) {
  2017. inb[s * 2 + ch] = data[s];
  2018. }
  2019. }
  2020. }
  2021. // Let Godot process the input/output.
  2022. onprocess();
  2023. // Write the output.
  2024. const outb = GodotRuntime.heapSub(HEAPF32, p_out_buf, p_out_size);
  2025. const output = event.outputBuffer;
  2026. const channels = output.numberOfChannels;
  2027. for (let ch = 0; ch < channels; ch++) {
  2028. const data = output.getChannelData(ch);
  2029. // Loop through samples and assign computed values.
  2030. for (let sample = 0; sample < data.length; sample++) {
  2031. data[sample] = outb[sample * channels + ch];
  2032. }
  2033. }
  2034. };
  2035. GodotAudioScript.script.connect(GodotAudio.ctx.destination);
  2036. },
  2037. get_node: function () {
  2038. return GodotAudioScript.script;
  2039. },
  2040. close: function () {
  2041. return new Promise(function (resolve, reject) {
  2042. GodotAudioScript.script.disconnect();
  2043. GodotAudioScript.script.onaudioprocess = null;
  2044. GodotAudioScript.script = null;
  2045. resolve();
  2046. });
  2047. },
  2048. },
  2049. godot_audio_script_create__proxy: 'sync',
  2050. godot_audio_script_create__sig: 'iii',
  2051. godot_audio_script_create: function (buffer_length, channel_count) {
  2052. const buf_len = GodotRuntime.getHeapValue(buffer_length, 'i32');
  2053. try {
  2054. const out_len = GodotAudioScript.create(buf_len, channel_count);
  2055. GodotRuntime.setHeapValue(buffer_length, out_len, 'i32');
  2056. } catch (e) {
  2057. GodotRuntime.error('Error starting AudioDriverScriptProcessor', e);
  2058. return 1;
  2059. }
  2060. return 0;
  2061. },
  2062. godot_audio_script_start__proxy: 'sync',
  2063. godot_audio_script_start__sig: 'viiiii',
  2064. godot_audio_script_start: function (
  2065. p_in_buf,
  2066. p_in_size,
  2067. p_out_buf,
  2068. p_out_size,
  2069. p_cb
  2070. ) {
  2071. const onprocess = GodotRuntime.get_func(p_cb);
  2072. GodotAudioScript.start(
  2073. p_in_buf,
  2074. p_in_size,
  2075. p_out_buf,
  2076. p_out_size,
  2077. onprocess
  2078. );
  2079. },
  2080. };
  2081. autoAddDeps(GodotAudioScript, '$GodotAudioScript');
  2082. mergeInto(LibraryManager.library, GodotAudioScript);