a munch adventure
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

284 řádky
5.2 KiB

  1. let playing = [];
  2. let looping = {};
  3. let waiting = {};
  4. let audioContext;
  5. let gainControl;
  6. let audioBaseUrl;
  7. let audioDict = {};
  8. function setVolume(vol) {
  9. gainControl.gain.value = vol;
  10. }
  11. // play some sound
  12. function playSfx(name) {
  13. if (waiting[name]) {
  14. waiting[name].push({
  15. type: "sfx",
  16. name: name
  17. });
  18. console.error(name + " isn't ready yet");
  19. return;
  20. }
  21. if (audioDict[name] == undefined) {
  22. console.error(name + " is not loaded yet, dingus");
  23. return;
  24. }
  25. let src = audioContext.createBufferSource();
  26. src.buffer = audioDict[name];
  27. src.connect(gainControl);
  28. playing.push(src);
  29. src.name = name;
  30. src.onended = (event) => src.done = true;
  31. src.start(0);
  32. }
  33. function playLoop(name) {
  34. if (waiting[name]) {
  35. waiting[name].push({
  36. type: "loop",
  37. name: name
  38. });
  39. console.error(name + " isn't ready yet");
  40. return;
  41. }
  42. if (audioDict[name] == undefined) {
  43. console.error(name + " is not loaded yet, dingus");
  44. return;
  45. }
  46. // if already playing, just keep going
  47. if (looping[name] && !looping[name].done) {
  48. console.warn(name + " is already looping");
  49. return;
  50. }
  51. let src = audioContext.createBufferSource();
  52. src.buffer = audioDict[name];
  53. src.connect(audioContext.destination);
  54. looping[name] = src;
  55. src.name = name;
  56. src.onended = (event) => src.done = true;
  57. src.loop = true;
  58. src.start(0);
  59. }
  60. function stopSfx(name) {
  61. playing.map(item => {
  62. if (item.name == name)
  63. item.stop();
  64. } );
  65. cleanPlaying();
  66. }
  67. function stopAllSfx() {
  68. playing.map(item => item.stop());
  69. cleanPlaying();
  70. }
  71. function stopLoop(name) {
  72. if (looping[name]) {
  73. looping[name].stop();
  74. delete looping[name];
  75. }
  76. }
  77. function stopAllLoops() {
  78. Object.entries(looping).forEach(([key, val]) => {
  79. val.stop();
  80. delete looping[key];
  81. });
  82. }
  83. function stopAllSound() {
  84. stopAllSfx();
  85. stopAllLoops();
  86. }
  87. function cleanPlaying() {
  88. playing = playing.filter(item => !item.done);
  89. }
  90. // asynchronously load an audio file
  91. function loadAudio(name, flush=false) {
  92. // are we already trying to get the audio?
  93. if (waiting[name]) {
  94. return;
  95. }
  96. // do we already have the audio?
  97. if (audioDict[name] && !flush) {
  98. return;
  99. }
  100. waiting[name] = [];
  101. // is the audio already stored locally?
  102. if (!flush) {
  103. checkCache(
  104. "audio",
  105. name,
  106. (data) => parseAudioData(name, data),
  107. () => loadRemoteAudio(name)
  108. );
  109. } else {
  110. loadRemoteAudio(name);
  111. }
  112. }
  113. function cacheAndParse(name, data) {
  114. storeCache("audio", name, data.slice(0));
  115. parseAudioData(name, data);
  116. }
  117. function parseAudioData(name, data) {
  118. audioContext.decodeAudioData(data, function(buffer) {
  119. audioDict[name] = buffer;
  120. waiting[name].forEach(queued => {
  121. if (queued.type == "sfx") {
  122. playSfx(name);
  123. }
  124. if (queued.type == "loop") {
  125. playLoop(name);
  126. }
  127. delete waiting[name];
  128. })
  129. }, function(e){
  130. console.log("Error with decoding audio data" + e.err);
  131. delete waiting[name];
  132. });
  133. }
  134. function loadRemoteAudio(name) {
  135. let xhr = new XMLHttpRequest();
  136. xhr.open("GET", audioBaseUrl + name, true);
  137. xhr.responseType = "arraybuffer";
  138. xhr.onload = (res) => {
  139. if (xhr.status == 200)
  140. cacheAndParse(name, xhr.response);
  141. else {
  142. console.log("Couldn't load " + name);
  143. delete waiting[name];
  144. }
  145. }
  146. xhr.onerror = (xhr) => {
  147. console.log("Couldn't load " + name);
  148. }
  149. xhr.send();
  150. }
  151. // check if the content is cached
  152. function checkCache(type, name, hit, miss) {
  153. const req = window.indexedDB.open("cache", 1);
  154. req.onsuccess = () => {
  155. const db = req.result;
  156. const tx = db.transaction([type], "readonly");
  157. const audio = tx.objectStore(type);
  158. const read = audio.get(name);
  159. read.onsuccess = (event) => {
  160. const res = event.target.result;
  161. if (res) {
  162. console.log("cache hit on " + name);
  163. hit(res.content);
  164. } else {
  165. console.log("cache miss on " + name);
  166. miss();
  167. }
  168. }
  169. tx.oncomplete = () => {
  170. db.close();
  171. }
  172. }
  173. }
  174. function initAudio(story, state) {
  175. if (!audioContext)
  176. audioContext = new (window.AudioContext || window.webkitAudioContext)();
  177. if (!gainControl) {
  178. gainControl = audioContext.createGain();
  179. gainControl.gain.value = 0.5;
  180. gainControl.connect(audioContext.destination);
  181. }
  182. createCache();
  183. audioBaseUrl = "./media/" + story.id + "/audio/";
  184. story.sounds.forEach(sound => {
  185. loadAudio(sound);
  186. });
  187. }
  188. // caching stuff here
  189. function storeCache(type, name, blob) {
  190. const req = window.indexedDB.open("cache", 1);
  191. req.onsuccess = () => {
  192. const db = req.result;
  193. const tx = db.transaction([type], "readwrite");
  194. const audio = tx.objectStore(type);
  195. const update = audio.put({
  196. name: name,
  197. content: blob
  198. });
  199. tx.oncomplete = () => {
  200. db.close();
  201. }
  202. }
  203. }
  204. // if the indexedDB table doesn't exist at all, make it
  205. function createCache() {
  206. let idb = window.indexedDB;
  207. let req = idb.open("cache", 1);
  208. req.onupgradeneeded = event => {
  209. const db = event.target.result;
  210. const audio = db.createObjectStore("audio", { keyPath: "name" });
  211. }
  212. req.onerror = event => {
  213. alert("Couldn't open the database?");
  214. }
  215. }