a munch adventure
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

291 wiersze
5.4 KiB

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