@adatechnology/game-audio 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1067) hide show
  1. package/README.md +20 -0
  2. package/assets/sfx/CREDITOS.md +1057 -0
  3. package/assets/sfx/ambiente/ambience_city_day_01.ogg +0 -0
  4. package/assets/sfx/ambiente/ambience_city_day_02.ogg +0 -0
  5. package/assets/sfx/ambiente/ambience_construction_01.ogg +0 -0
  6. package/assets/sfx/ambiente/ambience_night_01.ogg +0 -0
  7. package/assets/sfx/ambiente/ambience_room_tone_01.ogg +0 -0
  8. package/assets/sfx/ambiente/ambience_room_tone_02.ogg +0 -0
  9. package/assets/sfx/ambiente/ambience_rural_day_01.ogg +0 -0
  10. package/assets/sfx/ambiente/ambience_rural_day_02.ogg +0 -0
  11. package/assets/sfx/ambiente/rain_loop_01.ogg +0 -0
  12. package/assets/sfx/ambiente/water_river_loop_01.ogg +0 -0
  13. package/assets/sfx/ambiente/water_river_loop_02.ogg +0 -0
  14. package/assets/sfx/ambiente/water_river_loop_03.ogg +0 -0
  15. package/assets/sfx/ambiente/water_splash_01.ogg +0 -0
  16. package/assets/sfx/ambiente/water_splash_02.ogg +0 -0
  17. package/assets/sfx/ambiente/water_splash_03.ogg +0 -0
  18. package/assets/sfx/ambiente/water_splash_04.ogg +0 -0
  19. package/assets/sfx/ambiente/water_splash_05.ogg +0 -0
  20. package/assets/sfx/ambiente/water_splash_06.ogg +0 -0
  21. package/assets/sfx/ambiente/water_splash_07.ogg +0 -0
  22. package/assets/sfx/ambiente/water_splash_08.ogg +0 -0
  23. package/assets/sfx/ambiente/water_splash_09.ogg +0 -0
  24. package/assets/sfx/ambiente/water_splash_10.ogg +0 -0
  25. package/assets/sfx/ambiente/water_splash_11.ogg +0 -0
  26. package/assets/sfx/ambiente/water_splash_12.ogg +0 -0
  27. package/assets/sfx/ambiente/water_splash_13.ogg +0 -0
  28. package/assets/sfx/ambiente/water_splash_14.ogg +0 -0
  29. package/assets/sfx/ambiente/water_splash_15.ogg +0 -0
  30. package/assets/sfx/armas/bolt_lift_medium_01.ogg +0 -0
  31. package/assets/sfx/armas/bolt_lift_medium_02.ogg +0 -0
  32. package/assets/sfx/armas/bolt_lift_medium_03.ogg +0 -0
  33. package/assets/sfx/armas/bolt_lift_medium_04.ogg +0 -0
  34. package/assets/sfx/armas/bolt_lift_medium_05.ogg +0 -0
  35. package/assets/sfx/armas/bolt_lock_medium_01.ogg +0 -0
  36. package/assets/sfx/armas/bolt_lock_medium_02.ogg +0 -0
  37. package/assets/sfx/armas/bolt_lock_medium_03.ogg +0 -0
  38. package/assets/sfx/armas/bolt_lock_medium_04.ogg +0 -0
  39. package/assets/sfx/armas/bolt_lock_medium_05.ogg +0 -0
  40. package/assets/sfx/armas/bolt_pull_heavy_01.ogg +0 -0
  41. package/assets/sfx/armas/bolt_pull_medium_01.ogg +0 -0
  42. package/assets/sfx/armas/bolt_pull_medium_02.ogg +0 -0
  43. package/assets/sfx/armas/bolt_pull_medium_03.ogg +0 -0
  44. package/assets/sfx/armas/bolt_pull_medium_04.ogg +0 -0
  45. package/assets/sfx/armas/bolt_pull_medium_05.ogg +0 -0
  46. package/assets/sfx/armas/bolt_pull_medium_06.ogg +0 -0
  47. package/assets/sfx/armas/bolt_push_heavy_01.ogg +0 -0
  48. package/assets/sfx/armas/bolt_push_medium_01.ogg +0 -0
  49. package/assets/sfx/armas/bolt_push_medium_02.ogg +0 -0
  50. package/assets/sfx/armas/bolt_push_medium_03.ogg +0 -0
  51. package/assets/sfx/armas/bolt_push_medium_04.ogg +0 -0
  52. package/assets/sfx/armas/bolt_push_medium_05.ogg +0 -0
  53. package/assets/sfx/armas/bolt_push_medium_06.ogg +0 -0
  54. package/assets/sfx/armas/casing_drop_concreto_01.ogg +0 -0
  55. package/assets/sfx/armas/casing_drop_concreto_02.ogg +0 -0
  56. package/assets/sfx/armas/casing_drop_concreto_03.ogg +0 -0
  57. package/assets/sfx/armas/casing_drop_metal_01.ogg +0 -0
  58. package/assets/sfx/armas/casing_drop_metal_02.ogg +0 -0
  59. package/assets/sfx/armas/casing_drop_metal_03.ogg +0 -0
  60. package/assets/sfx/armas/casing_drop_terra_01.ogg +0 -0
  61. package/assets/sfx/armas/casing_drop_terra_02.ogg +0 -0
  62. package/assets/sfx/armas/casing_drop_terra_03.ogg +0 -0
  63. package/assets/sfx/armas/charging_handle_pull_light_01.ogg +0 -0
  64. package/assets/sfx/armas/charging_handle_pull_light_02.ogg +0 -0
  65. package/assets/sfx/armas/charging_handle_pull_light_03.ogg +0 -0
  66. package/assets/sfx/armas/charging_handle_pull_light_04.ogg +0 -0
  67. package/assets/sfx/armas/charging_handle_pull_medium_01.ogg +0 -0
  68. package/assets/sfx/armas/charging_handle_pull_medium_02.ogg +0 -0
  69. package/assets/sfx/armas/charging_handle_pull_medium_03.ogg +0 -0
  70. package/assets/sfx/armas/charging_handle_pull_medium_04.ogg +0 -0
  71. package/assets/sfx/armas/charging_handle_pull_medium_05.ogg +0 -0
  72. package/assets/sfx/armas/charging_handle_release_medium_01.ogg +0 -0
  73. package/assets/sfx/armas/charging_handle_release_medium_02.ogg +0 -0
  74. package/assets/sfx/armas/charging_handle_release_medium_03.ogg +0 -0
  75. package/assets/sfx/armas/charging_handle_release_medium_04.ogg +0 -0
  76. package/assets/sfx/armas/charging_handle_release_medium_05.ogg +0 -0
  77. package/assets/sfx/armas/cylinder_close_light_01.ogg +0 -0
  78. package/assets/sfx/armas/cylinder_close_light_02.ogg +0 -0
  79. package/assets/sfx/armas/cylinder_close_light_03.ogg +0 -0
  80. package/assets/sfx/armas/cylinder_close_light_04.ogg +0 -0
  81. package/assets/sfx/armas/cylinder_close_light_05.ogg +0 -0
  82. package/assets/sfx/armas/cylinder_eject_light_01.ogg +0 -0
  83. package/assets/sfx/armas/cylinder_eject_light_02.ogg +0 -0
  84. package/assets/sfx/armas/cylinder_eject_light_03.ogg +0 -0
  85. package/assets/sfx/armas/cylinder_open_light_01.ogg +0 -0
  86. package/assets/sfx/armas/cylinder_open_light_02.ogg +0 -0
  87. package/assets/sfx/armas/cylinder_open_light_03.ogg +0 -0
  88. package/assets/sfx/armas/cylinder_open_light_04.ogg +0 -0
  89. package/assets/sfx/armas/cylinder_open_light_05.ogg +0 -0
  90. package/assets/sfx/armas/lever_close_medium_01.ogg +0 -0
  91. package/assets/sfx/armas/lever_close_medium_02.ogg +0 -0
  92. package/assets/sfx/armas/lever_open_medium_01.ogg +0 -0
  93. package/assets/sfx/armas/lever_open_medium_02.ogg +0 -0
  94. package/assets/sfx/armas/mag_out_heavy_01.ogg +0 -0
  95. package/assets/sfx/armas/mag_out_heavy_02.ogg +0 -0
  96. package/assets/sfx/armas/mag_out_light_01.ogg +0 -0
  97. package/assets/sfx/armas/mag_out_light_02.ogg +0 -0
  98. package/assets/sfx/armas/mag_out_light_03.ogg +0 -0
  99. package/assets/sfx/armas/mag_out_light_04.ogg +0 -0
  100. package/assets/sfx/armas/mag_out_medium_01.ogg +0 -0
  101. package/assets/sfx/armas/mag_out_medium_02.ogg +0 -0
  102. package/assets/sfx/armas/mag_out_medium_03.ogg +0 -0
  103. package/assets/sfx/armas/mag_seat_heavy_01.ogg +0 -0
  104. package/assets/sfx/armas/mag_seat_heavy_02.ogg +0 -0
  105. package/assets/sfx/armas/mag_seat_light_01.ogg +0 -0
  106. package/assets/sfx/armas/mag_seat_light_02.ogg +0 -0
  107. package/assets/sfx/armas/mag_seat_light_03.ogg +0 -0
  108. package/assets/sfx/armas/mag_seat_light_04.ogg +0 -0
  109. package/assets/sfx/armas/mag_seat_light_05.ogg +0 -0
  110. package/assets/sfx/armas/mag_seat_light_06.ogg +0 -0
  111. package/assets/sfx/armas/mag_seat_medium_01.ogg +0 -0
  112. package/assets/sfx/armas/mag_seat_medium_02.ogg +0 -0
  113. package/assets/sfx/armas/mag_seat_medium_03.ogg +0 -0
  114. package/assets/sfx/armas/pistol_22lr_close_01.ogg +0 -0
  115. package/assets/sfx/armas/pistol_22lr_close_02.ogg +0 -0
  116. package/assets/sfx/armas/pistol_22lr_far_01.ogg +0 -0
  117. package/assets/sfx/armas/pistol_22lr_far_02.ogg +0 -0
  118. package/assets/sfx/armas/pistol_22lr_far_03.ogg +0 -0
  119. package/assets/sfx/armas/pistol_22lr_far_04.ogg +0 -0
  120. package/assets/sfx/armas/pistol_45_close_01.ogg +0 -0
  121. package/assets/sfx/armas/pistol_45_close_02.ogg +0 -0
  122. package/assets/sfx/armas/pistol_45_close_03.ogg +0 -0
  123. package/assets/sfx/armas/pistol_45_close_04.ogg +0 -0
  124. package/assets/sfx/armas/pistol_45_close_05.ogg +0 -0
  125. package/assets/sfx/armas/pistol_45_far_01.ogg +0 -0
  126. package/assets/sfx/armas/pistol_45_far_02.ogg +0 -0
  127. package/assets/sfx/armas/pistol_45_far_03.ogg +0 -0
  128. package/assets/sfx/armas/pistol_45_far_04.ogg +0 -0
  129. package/assets/sfx/armas/pistol_9mm_close_01.ogg +0 -0
  130. package/assets/sfx/armas/pistol_9mm_close_02.ogg +0 -0
  131. package/assets/sfx/armas/pistol_9mm_close_03.ogg +0 -0
  132. package/assets/sfx/armas/pistol_9mm_close_04.ogg +0 -0
  133. package/assets/sfx/armas/pistol_9mm_close_05.ogg +0 -0
  134. package/assets/sfx/armas/pistol_9mm_close_06.ogg +0 -0
  135. package/assets/sfx/armas/pistol_9mm_far_01.ogg +0 -0
  136. package/assets/sfx/armas/pistol_9mm_far_02.ogg +0 -0
  137. package/assets/sfx/armas/pistol_9mm_far_03.ogg +0 -0
  138. package/assets/sfx/armas/pistol_9mm_far_04.ogg +0 -0
  139. package/assets/sfx/armas/pump_back_heavy_01.ogg +0 -0
  140. package/assets/sfx/armas/pump_back_heavy_02.ogg +0 -0
  141. package/assets/sfx/armas/pump_back_heavy_03.ogg +0 -0
  142. package/assets/sfx/armas/pump_back_heavy_04.ogg +0 -0
  143. package/assets/sfx/armas/pump_back_heavy_05.ogg +0 -0
  144. package/assets/sfx/armas/pump_forward_heavy_01.ogg +0 -0
  145. package/assets/sfx/armas/pump_forward_heavy_02.ogg +0 -0
  146. package/assets/sfx/armas/pump_forward_heavy_03.ogg +0 -0
  147. package/assets/sfx/armas/revolver_38_357_close_01.ogg +0 -0
  148. package/assets/sfx/armas/revolver_38_357_close_02.ogg +0 -0
  149. package/assets/sfx/armas/revolver_38_357_close_03.ogg +0 -0
  150. package/assets/sfx/armas/revolver_38_357_close_04.ogg +0 -0
  151. package/assets/sfx/armas/revolver_38_357_far_01.ogg +0 -0
  152. package/assets/sfx/armas/revolver_38_357_far_02.ogg +0 -0
  153. package/assets/sfx/armas/revolver_38_357_far_03.ogg +0 -0
  154. package/assets/sfx/armas/revolver_38_357_far_04.ogg +0 -0
  155. package/assets/sfx/armas/rifle_308_3006_close_01.ogg +0 -0
  156. package/assets/sfx/armas/rifle_308_3006_close_02.ogg +0 -0
  157. package/assets/sfx/armas/rifle_308_3006_close_03.ogg +0 -0
  158. package/assets/sfx/armas/rifle_308_3006_close_04.ogg +0 -0
  159. package/assets/sfx/armas/rifle_308_3006_close_05.ogg +0 -0
  160. package/assets/sfx/armas/rifle_308_3006_close_06.ogg +0 -0
  161. package/assets/sfx/armas/rifle_308_3006_far_01.ogg +0 -0
  162. package/assets/sfx/armas/rifle_308_3006_far_02.ogg +0 -0
  163. package/assets/sfx/armas/rifle_308_3006_far_03.ogg +0 -0
  164. package/assets/sfx/armas/rifle_308_3006_far_04.ogg +0 -0
  165. package/assets/sfx/armas/rifle_308_3006_far_05.ogg +0 -0
  166. package/assets/sfx/armas/rifle_308_3006_far_06.ogg +0 -0
  167. package/assets/sfx/armas/rifle_5_56_close_01.ogg +0 -0
  168. package/assets/sfx/armas/rifle_5_56_close_02.ogg +0 -0
  169. package/assets/sfx/armas/rifle_5_56_close_03.ogg +0 -0
  170. package/assets/sfx/armas/rifle_5_56_close_04.ogg +0 -0
  171. package/assets/sfx/armas/rifle_5_56_close_05.ogg +0 -0
  172. package/assets/sfx/armas/rifle_5_56_far_01.ogg +0 -0
  173. package/assets/sfx/armas/rifle_5_56_far_02.ogg +0 -0
  174. package/assets/sfx/armas/rifle_5_56_far_03.ogg +0 -0
  175. package/assets/sfx/armas/rifle_5_56_far_04.ogg +0 -0
  176. package/assets/sfx/armas/rifle_7_62x39_close_01.ogg +0 -0
  177. package/assets/sfx/armas/rifle_7_62x39_close_02.ogg +0 -0
  178. package/assets/sfx/armas/rifle_7_62x39_close_03.ogg +0 -0
  179. package/assets/sfx/armas/rifle_7_62x39_close_04.ogg +0 -0
  180. package/assets/sfx/armas/rifle_7_62x39_close_05.ogg +0 -0
  181. package/assets/sfx/armas/rifle_7_62x39_close_06.ogg +0 -0
  182. package/assets/sfx/armas/rifle_7_62x39_close_07.ogg +0 -0
  183. package/assets/sfx/armas/rifle_7_62x39_close_08.ogg +0 -0
  184. package/assets/sfx/armas/rifle_7_62x39_close_09.ogg +0 -0
  185. package/assets/sfx/armas/rifle_7_62x39_close_10.ogg +0 -0
  186. package/assets/sfx/armas/rifle_7_62x39_close_11.ogg +0 -0
  187. package/assets/sfx/armas/rifle_7_62x39_close_12.ogg +0 -0
  188. package/assets/sfx/armas/rifle_7_62x39_far_01.ogg +0 -0
  189. package/assets/sfx/armas/rifle_7_62x39_far_02.ogg +0 -0
  190. package/assets/sfx/armas/rifle_7_62x39_far_03.ogg +0 -0
  191. package/assets/sfx/armas/shell_insert_heavy_01.ogg +0 -0
  192. package/assets/sfx/armas/shell_insert_heavy_02.ogg +0 -0
  193. package/assets/sfx/armas/shell_insert_heavy_03.ogg +0 -0
  194. package/assets/sfx/armas/shell_insert_heavy_04.ogg +0 -0
  195. package/assets/sfx/armas/shell_insert_heavy_05.ogg +0 -0
  196. package/assets/sfx/armas/shell_insert_heavy_06.ogg +0 -0
  197. package/assets/sfx/armas/shell_insert_heavy_07.ogg +0 -0
  198. package/assets/sfx/armas/shell_insert_heavy_08.ogg +0 -0
  199. package/assets/sfx/armas/shell_insert_heavy_09.ogg +0 -0
  200. package/assets/sfx/armas/shotgun_12_close_01.ogg +0 -0
  201. package/assets/sfx/armas/shotgun_12_close_02.ogg +0 -0
  202. package/assets/sfx/armas/shotgun_12_close_03.ogg +0 -0
  203. package/assets/sfx/armas/shotgun_12_close_04.ogg +0 -0
  204. package/assets/sfx/armas/shotgun_12_close_05.ogg +0 -0
  205. package/assets/sfx/armas/shotgun_12_close_06.ogg +0 -0
  206. package/assets/sfx/armas/shotgun_12_far_01.ogg +0 -0
  207. package/assets/sfx/armas/shotgun_12_far_02.ogg +0 -0
  208. package/assets/sfx/armas/shotgun_12_far_03.ogg +0 -0
  209. package/assets/sfx/armas/shotgun_12_far_04.ogg +0 -0
  210. package/assets/sfx/armas/shotgun_12_far_05.ogg +0 -0
  211. package/assets/sfx/armas/shotgun_12_far_06.ogg +0 -0
  212. package/assets/sfx/armas/slide_release_light_01.ogg +0 -0
  213. package/assets/sfx/armas/slide_release_light_02.ogg +0 -0
  214. package/assets/sfx/armas/slide_release_light_03.ogg +0 -0
  215. package/assets/sfx/armas/slide_release_light_04.ogg +0 -0
  216. package/assets/sfx/armas/slide_release_light_05.ogg +0 -0
  217. package/assets/sfx/armas/slide_release_light_06.ogg +0 -0
  218. package/assets/sfx/armas/slide_release_medium_01.ogg +0 -0
  219. package/assets/sfx/armas/slide_release_medium_02.ogg +0 -0
  220. package/assets/sfx/armas/slide_release_medium_03.ogg +0 -0
  221. package/assets/sfx/armas/slide_release_medium_04.ogg +0 -0
  222. package/assets/sfx/armas/sniper_338_300wm_close_01.ogg +0 -0
  223. package/assets/sfx/armas/sniper_338_300wm_close_02.ogg +0 -0
  224. package/assets/sfx/armas/sniper_338_300wm_close_03.ogg +0 -0
  225. package/assets/sfx/armas/sniper_338_300wm_close_04.ogg +0 -0
  226. package/assets/sfx/armas/sniper_338_300wm_close_05.ogg +0 -0
  227. package/assets/sfx/armas/sniper_338_300wm_close_06.ogg +0 -0
  228. package/assets/sfx/armas/sniper_338_300wm_close_07.ogg +0 -0
  229. package/assets/sfx/armas/sniper_338_300wm_far_01.ogg +0 -0
  230. package/assets/sfx/armas/sniper_338_300wm_far_02.ogg +0 -0
  231. package/assets/sfx/armas/sniper_338_300wm_far_03.ogg +0 -0
  232. package/assets/sfx/armas/sniper_338_300wm_far_04.ogg +0 -0
  233. package/assets/sfx/armas/sniper_338_300wm_far_05.ogg +0 -0
  234. package/assets/sfx/armas/sniper_338_300wm_far_06.ogg +0 -0
  235. package/assets/sfx/armas/speedloader_light_01.ogg +0 -0
  236. package/assets/sfx/armas/speedloader_light_02.ogg +0 -0
  237. package/assets/sfx/armas/speedloader_light_03.ogg +0 -0
  238. package/assets/sfx/armas/speedloader_light_04.ogg +0 -0
  239. package/assets/sfx/armas/speedloader_light_05.ogg +0 -0
  240. package/assets/sfx/armas/speedloader_light_06.ogg +0 -0
  241. package/assets/sfx/armas/speedloader_light_07.ogg +0 -0
  242. package/assets/sfx/armas/speedloader_light_08.ogg +0 -0
  243. package/assets/sfx/armas/suppressed_pistol_close_01.ogg +0 -0
  244. package/assets/sfx/armas/suppressed_pistol_close_02.ogg +0 -0
  245. package/assets/sfx/armas/suppressed_pistol_close_03.ogg +0 -0
  246. package/assets/sfx/armas/suppressed_pistol_close_04.ogg +0 -0
  247. package/assets/sfx/armas/suppressed_pistol_close_05.ogg +0 -0
  248. package/assets/sfx/armas/suppressed_pistol_far_01.ogg +0 -0
  249. package/assets/sfx/armas/suppressed_pistol_far_02.ogg +0 -0
  250. package/assets/sfx/armas/suppressed_pistol_far_03.ogg +0 -0
  251. package/assets/sfx/armas/suppressed_pistol_far_04.ogg +0 -0
  252. package/assets/sfx/armas/suppressed_pistol_far_05.ogg +0 -0
  253. package/assets/sfx/armas/suppressed_rifle_close_01.ogg +0 -0
  254. package/assets/sfx/armas/suppressed_rifle_close_02.ogg +0 -0
  255. package/assets/sfx/armas/suppressed_rifle_close_03.ogg +0 -0
  256. package/assets/sfx/armas/suppressed_rifle_close_04.ogg +0 -0
  257. package/assets/sfx/armas/suppressed_rifle_far_01.ogg +0 -0
  258. package/assets/sfx/armas/suppressed_rifle_far_02.ogg +0 -0
  259. package/assets/sfx/armas/suppressed_rifle_far_03.ogg +0 -0
  260. package/assets/sfx/armas/suppressed_rifle_far_04.ogg +0 -0
  261. package/assets/sfx/explosoes-fogo/debris_rain_mixed_01.ogg +0 -0
  262. package/assets/sfx/explosoes-fogo/debris_rain_mixed_02.ogg +0 -0
  263. package/assets/sfx/explosoes-fogo/debris_rain_mixed_03.ogg +0 -0
  264. package/assets/sfx/explosoes-fogo/debris_rain_mixed_04.ogg +0 -0
  265. package/assets/sfx/explosoes-fogo/debris_rain_mixed_05.ogg +0 -0
  266. package/assets/sfx/explosoes-fogo/debris_rain_mixed_06.ogg +0 -0
  267. package/assets/sfx/explosoes-fogo/debris_rain_mixed_07.ogg +0 -0
  268. package/assets/sfx/explosoes-fogo/debris_rain_stone_01.ogg +0 -0
  269. package/assets/sfx/explosoes-fogo/debris_rain_stone_02.ogg +0 -0
  270. package/assets/sfx/explosoes-fogo/debris_rain_stone_03.ogg +0 -0
  271. package/assets/sfx/explosoes-fogo/debris_rain_stone_04.ogg +0 -0
  272. package/assets/sfx/explosoes-fogo/debris_rain_stone_05.ogg +0 -0
  273. package/assets/sfx/explosoes-fogo/debris_rain_stone_06.ogg +0 -0
  274. package/assets/sfx/explosoes-fogo/debris_rain_stone_07.ogg +0 -0
  275. package/assets/sfx/explosoes-fogo/debris_rain_stone_08.ogg +0 -0
  276. package/assets/sfx/explosoes-fogo/debris_rain_stone_09.ogg +0 -0
  277. package/assets/sfx/explosoes-fogo/explosion_big_01.ogg +0 -0
  278. package/assets/sfx/explosoes-fogo/explosion_big_02.ogg +0 -0
  279. package/assets/sfx/explosoes-fogo/explosion_big_03.ogg +0 -0
  280. package/assets/sfx/explosoes-fogo/explosion_big_04.ogg +0 -0
  281. package/assets/sfx/explosoes-fogo/explosion_big_05.ogg +0 -0
  282. package/assets/sfx/explosoes-fogo/explosion_car_01.ogg +0 -0
  283. package/assets/sfx/explosoes-fogo/explosion_car_02.ogg +0 -0
  284. package/assets/sfx/explosoes-fogo/explosion_car_03.ogg +0 -0
  285. package/assets/sfx/explosoes-fogo/explosion_car_04.ogg +0 -0
  286. package/assets/sfx/explosoes-fogo/explosion_far_close_01.ogg +0 -0
  287. package/assets/sfx/explosoes-fogo/explosion_far_close_02.ogg +0 -0
  288. package/assets/sfx/explosoes-fogo/explosion_far_close_03.ogg +0 -0
  289. package/assets/sfx/explosoes-fogo/explosion_far_close_04.ogg +0 -0
  290. package/assets/sfx/explosoes-fogo/explosion_flashbang_01.ogg +0 -0
  291. package/assets/sfx/explosoes-fogo/explosion_flashbang_02.ogg +0 -0
  292. package/assets/sfx/explosoes-fogo/explosion_grenade_01.ogg +0 -0
  293. package/assets/sfx/explosoes-fogo/explosion_grenade_02.ogg +0 -0
  294. package/assets/sfx/explosoes-fogo/explosion_grenade_03.ogg +0 -0
  295. package/assets/sfx/explosoes-fogo/explosion_grenade_04.ogg +0 -0
  296. package/assets/sfx/explosoes-fogo/explosion_grenade_05.ogg +0 -0
  297. package/assets/sfx/explosoes-fogo/explosion_grenade_06.ogg +0 -0
  298. package/assets/sfx/explosoes-fogo/explosion_small_bang_01.ogg +0 -0
  299. package/assets/sfx/explosoes-fogo/explosion_small_bang_02.ogg +0 -0
  300. package/assets/sfx/explosoes-fogo/explosion_small_bang_03.ogg +0 -0
  301. package/assets/sfx/explosoes-fogo/explosion_small_bang_04.ogg +0 -0
  302. package/assets/sfx/explosoes-fogo/explosion_small_bang_05.ogg +0 -0
  303. package/assets/sfx/explosoes-fogo/explosion_small_bang_06.ogg +0 -0
  304. package/assets/sfx/explosoes-fogo/explosion_small_bang_07.ogg +0 -0
  305. package/assets/sfx/explosoes-fogo/explosion_small_bang_08.ogg +0 -0
  306. package/assets/sfx/explosoes-fogo/explosion_small_bang_09.ogg +0 -0
  307. package/assets/sfx/explosoes-fogo/explosion_small_bang_10.ogg +0 -0
  308. package/assets/sfx/explosoes-fogo/fire_crackle_01.ogg +0 -0
  309. package/assets/sfx/explosoes-fogo/fire_crackle_02.ogg +0 -0
  310. package/assets/sfx/explosoes-fogo/fire_crackle_03.ogg +0 -0
  311. package/assets/sfx/explosoes-fogo/fire_crackle_05.ogg +0 -0
  312. package/assets/sfx/explosoes-fogo/fire_crackle_07.ogg +0 -0
  313. package/assets/sfx/explosoes-fogo/fire_extinguish_01.ogg +0 -0
  314. package/assets/sfx/explosoes-fogo/fire_extinguish_02.ogg +0 -0
  315. package/assets/sfx/explosoes-fogo/fire_ignite_01.ogg +0 -0
  316. package/assets/sfx/explosoes-fogo/fire_ignite_02.ogg +0 -0
  317. package/assets/sfx/explosoes-fogo/fire_ignite_03.ogg +0 -0
  318. package/assets/sfx/explosoes-fogo/fire_ignite_04.ogg +0 -0
  319. package/assets/sfx/explosoes-fogo/fire_loop_01.ogg +0 -0
  320. package/assets/sfx/explosoes-fogo/fire_loop_03.ogg +0 -0
  321. package/assets/sfx/explosoes-fogo/fire_loop_04.ogg +0 -0
  322. package/assets/sfx/explosoes-fogo/fire_loop_gas_01.ogg +0 -0
  323. package/assets/sfx/explosoes-fogo/fire_loop_gas_02.ogg +0 -0
  324. package/assets/sfx/explosoes-fogo/fire_loop_gas_03.ogg +0 -0
  325. package/assets/sfx/explosoes-fogo/fire_loop_gas_04.ogg +0 -0
  326. package/assets/sfx/explosoes-fogo/fire_loop_liquid_01.ogg +0 -0
  327. package/assets/sfx/explosoes-fogo/fire_loop_liquid_02.ogg +0 -0
  328. package/assets/sfx/explosoes-fogo/fire_loop_liquid_03.ogg +0 -0
  329. package/assets/sfx/explosoes-fogo/fire_loop_plastic_01.ogg +0 -0
  330. package/assets/sfx/explosoes-fogo/fire_loop_plastic_02.ogg +0 -0
  331. package/assets/sfx/explosoes-fogo/fire_loop_plastic_03.ogg +0 -0
  332. package/assets/sfx/explosoes-fogo/fire_loop_plastic_04.ogg +0 -0
  333. package/assets/sfx/explosoes-fogo/fire_loop_small_01.ogg +0 -0
  334. package/assets/sfx/explosoes-fogo/fire_pop_01.ogg +0 -0
  335. package/assets/sfx/explosoes-fogo/fire_pop_02.ogg +0 -0
  336. package/assets/sfx/explosoes-fogo/fire_pop_03.ogg +0 -0
  337. package/assets/sfx/explosoes-fogo/flamethrower_loop_01.ogg +0 -0
  338. package/assets/sfx/explosoes-fogo/gas_leak_01.ogg +0 -0
  339. package/assets/sfx/explosoes-fogo/molotov_break_ignite_01.ogg +0 -0
  340. package/assets/sfx/explosoes-fogo/molotov_break_ignite_02.ogg +0 -0
  341. package/assets/sfx/impactos/ceramic_hit_close_01.ogg +0 -0
  342. package/assets/sfx/impactos/concrete_block_drag_close_01.ogg +0 -0
  343. package/assets/sfx/impactos/concrete_block_drag_close_02.ogg +0 -0
  344. package/assets/sfx/impactos/concrete_block_drag_close_03.ogg +0 -0
  345. package/assets/sfx/impactos/concrete_block_drag_close_04.ogg +0 -0
  346. package/assets/sfx/impactos/concrete_block_drag_close_05.ogg +0 -0
  347. package/assets/sfx/impactos/concrete_block_drag_close_06.ogg +0 -0
  348. package/assets/sfx/impactos/concrete_block_drag_close_07.ogg +0 -0
  349. package/assets/sfx/impactos/crack_close_01.ogg +0 -0
  350. package/assets/sfx/impactos/crack_close_02.ogg +0 -0
  351. package/assets/sfx/impactos/crack_close_03.ogg +0 -0
  352. package/assets/sfx/impactos/crack_close_04.ogg +0 -0
  353. package/assets/sfx/impactos/crack_close_05.ogg +0 -0
  354. package/assets/sfx/impactos/crack_close_06.ogg +0 -0
  355. package/assets/sfx/impactos/crack_close_07.ogg +0 -0
  356. package/assets/sfx/impactos/crack_close_08.ogg +0 -0
  357. package/assets/sfx/impactos/crack_close_09.ogg +0 -0
  358. package/assets/sfx/impactos/crack_close_10.ogg +0 -0
  359. package/assets/sfx/impactos/crumble_close_01.ogg +0 -0
  360. package/assets/sfx/impactos/crumble_close_02.ogg +0 -0
  361. package/assets/sfx/impactos/crumble_close_03.ogg +0 -0
  362. package/assets/sfx/impactos/crumble_close_04.ogg +0 -0
  363. package/assets/sfx/impactos/crumble_close_05.ogg +0 -0
  364. package/assets/sfx/impactos/flesh_fruit_splat_close_01.ogg +0 -0
  365. package/assets/sfx/impactos/flesh_fruit_splat_close_02.ogg +0 -0
  366. package/assets/sfx/impactos/flesh_fruit_splat_close_03.ogg +0 -0
  367. package/assets/sfx/impactos/furniture_push_wood_close_01.ogg +0 -0
  368. package/assets/sfx/impactos/glass_blunt_heavy_close_01.ogg +0 -0
  369. package/assets/sfx/impactos/glass_blunt_heavy_close_02.ogg +0 -0
  370. package/assets/sfx/impactos/glass_blunt_heavy_close_03.ogg +0 -0
  371. package/assets/sfx/impactos/glass_blunt_heavy_close_04.ogg +0 -0
  372. package/assets/sfx/impactos/glass_blunt_heavy_close_05.ogg +0 -0
  373. package/assets/sfx/impactos/glass_blunt_light_close_01.ogg +0 -0
  374. package/assets/sfx/impactos/glass_blunt_light_close_02.ogg +0 -0
  375. package/assets/sfx/impactos/glass_blunt_light_close_03.ogg +0 -0
  376. package/assets/sfx/impactos/glass_blunt_light_close_04.ogg +0 -0
  377. package/assets/sfx/impactos/glass_blunt_light_close_05.ogg +0 -0
  378. package/assets/sfx/impactos/glass_blunt_medium_close_01.ogg +0 -0
  379. package/assets/sfx/impactos/glass_blunt_medium_close_02.ogg +0 -0
  380. package/assets/sfx/impactos/glass_blunt_medium_close_03.ogg +0 -0
  381. package/assets/sfx/impactos/glass_blunt_medium_close_04.ogg +0 -0
  382. package/assets/sfx/impactos/glass_blunt_medium_close_05.ogg +0 -0
  383. package/assets/sfx/impactos/glass_bottle_break_close_01.ogg +0 -0
  384. package/assets/sfx/impactos/glass_debris_fall_close_01.ogg +0 -0
  385. package/assets/sfx/impactos/glass_debris_fall_close_02.ogg +0 -0
  386. package/assets/sfx/impactos/glass_debris_fall_close_03.ogg +0 -0
  387. package/assets/sfx/impactos/glass_debris_fall_close_04.ogg +0 -0
  388. package/assets/sfx/impactos/glass_shatter_close_01.ogg +0 -0
  389. package/assets/sfx/impactos/glass_shatter_close_02.ogg +0 -0
  390. package/assets/sfx/impactos/glass_shatter_close_03.ogg +0 -0
  391. package/assets/sfx/impactos/glass_shatter_close_04.ogg +0 -0
  392. package/assets/sfx/impactos/glass_shatter_close_05.ogg +0 -0
  393. package/assets/sfx/impactos/glass_shatter_close_06.ogg +0 -0
  394. package/assets/sfx/impactos/glass_shatter_close_07.ogg +0 -0
  395. package/assets/sfx/impactos/glass_shatter_close_08.ogg +0 -0
  396. package/assets/sfx/impactos/glass_shatter_close_09.ogg +0 -0
  397. package/assets/sfx/impactos/gravel_hit_close_21.ogg +0 -0
  398. package/assets/sfx/impactos/gravel_hit_close_22.ogg +0 -0
  399. package/assets/sfx/impactos/gravel_hit_close_23.ogg +0 -0
  400. package/assets/sfx/impactos/gravel_hit_close_24.ogg +0 -0
  401. package/assets/sfx/impactos/gravel_hit_close_25.ogg +0 -0
  402. package/assets/sfx/impactos/gravel_hit_close_26.ogg +0 -0
  403. package/assets/sfx/impactos/gravel_hit_close_27.ogg +0 -0
  404. package/assets/sfx/impactos/gravel_hit_close_28.ogg +0 -0
  405. package/assets/sfx/impactos/gravel_hit_close_29.ogg +0 -0
  406. package/assets/sfx/impactos/impact_generic_light_close_01.ogg +0 -0
  407. package/assets/sfx/impactos/impact_generic_light_close_02.ogg +0 -0
  408. package/assets/sfx/impactos/impact_generic_light_close_03.ogg +0 -0
  409. package/assets/sfx/impactos/impact_generic_light_close_04.ogg +0 -0
  410. package/assets/sfx/impactos/impact_generic_light_close_05.ogg +0 -0
  411. package/assets/sfx/impactos/masonry_blunt_close_01.ogg +0 -0
  412. package/assets/sfx/impactos/masonry_blunt_close_02.ogg +0 -0
  413. package/assets/sfx/impactos/masonry_blunt_close_03.ogg +0 -0
  414. package/assets/sfx/impactos/masonry_blunt_close_04.ogg +0 -0
  415. package/assets/sfx/impactos/masonry_blunt_close_05.ogg +0 -0
  416. package/assets/sfx/impactos/masonry_blunt_close_06.ogg +0 -0
  417. package/assets/sfx/impactos/masonry_blunt_close_07.ogg +0 -0
  418. package/assets/sfx/impactos/masonry_bullet_close_01.ogg +0 -0
  419. package/assets/sfx/impactos/masonry_bullet_close_02.ogg +0 -0
  420. package/assets/sfx/impactos/masonry_bullet_close_03.ogg +0 -0
  421. package/assets/sfx/impactos/masonry_bullet_close_04.ogg +0 -0
  422. package/assets/sfx/impactos/masonry_bullet_close_05.ogg +0 -0
  423. package/assets/sfx/impactos/masonry_bullet_close_21.ogg +0 -0
  424. package/assets/sfx/impactos/masonry_bullet_close_22.ogg +0 -0
  425. package/assets/sfx/impactos/masonry_bullet_close_23.ogg +0 -0
  426. package/assets/sfx/impactos/masonry_bullet_close_24.ogg +0 -0
  427. package/assets/sfx/impactos/masonry_bullet_close_25.ogg +0 -0
  428. package/assets/sfx/impactos/masonry_bullet_close_26.ogg +0 -0
  429. package/assets/sfx/impactos/masonry_bullet_close_27.ogg +0 -0
  430. package/assets/sfx/impactos/masonry_bullet_close_28.ogg +0 -0
  431. package/assets/sfx/impactos/metal_blunt_close_01.ogg +0 -0
  432. package/assets/sfx/impactos/metal_blunt_close_02.ogg +0 -0
  433. package/assets/sfx/impactos/metal_blunt_close_03.ogg +0 -0
  434. package/assets/sfx/impactos/metal_blunt_close_04.ogg +0 -0
  435. package/assets/sfx/impactos/metal_blunt_close_05.ogg +0 -0
  436. package/assets/sfx/impactos/metal_blunt_heavy_close_01.ogg +0 -0
  437. package/assets/sfx/impactos/metal_blunt_heavy_close_02.ogg +0 -0
  438. package/assets/sfx/impactos/metal_blunt_heavy_close_03.ogg +0 -0
  439. package/assets/sfx/impactos/metal_blunt_heavy_close_04.ogg +0 -0
  440. package/assets/sfx/impactos/metal_blunt_heavy_close_05.ogg +0 -0
  441. package/assets/sfx/impactos/metal_blunt_light_close_01.ogg +0 -0
  442. package/assets/sfx/impactos/metal_blunt_light_close_02.ogg +0 -0
  443. package/assets/sfx/impactos/metal_blunt_light_close_03.ogg +0 -0
  444. package/assets/sfx/impactos/metal_blunt_light_close_04.ogg +0 -0
  445. package/assets/sfx/impactos/metal_blunt_light_close_05.ogg +0 -0
  446. package/assets/sfx/impactos/metal_blunt_medium_close_01.ogg +0 -0
  447. package/assets/sfx/impactos/metal_blunt_medium_close_02.ogg +0 -0
  448. package/assets/sfx/impactos/metal_blunt_medium_close_03.ogg +0 -0
  449. package/assets/sfx/impactos/metal_blunt_medium_close_04.ogg +0 -0
  450. package/assets/sfx/impactos/metal_blunt_medium_close_05.ogg +0 -0
  451. package/assets/sfx/impactos/metal_bullet_close_01.ogg +0 -0
  452. package/assets/sfx/impactos/metal_bullet_close_02.ogg +0 -0
  453. package/assets/sfx/impactos/metal_bullet_close_03.ogg +0 -0
  454. package/assets/sfx/impactos/metal_bullet_close_04.ogg +0 -0
  455. package/assets/sfx/impactos/metal_bullet_close_05.ogg +0 -0
  456. package/assets/sfx/impactos/metal_bullet_close_06.ogg +0 -0
  457. package/assets/sfx/impactos/metal_bullet_close_21.ogg +0 -0
  458. package/assets/sfx/impactos/metal_bullet_close_22.ogg +0 -0
  459. package/assets/sfx/impactos/metal_bullet_close_23.ogg +0 -0
  460. package/assets/sfx/impactos/metal_bullet_close_24.ogg +0 -0
  461. package/assets/sfx/impactos/metal_bullet_close_25.ogg +0 -0
  462. package/assets/sfx/impactos/metal_bullet_close_26.ogg +0 -0
  463. package/assets/sfx/impactos/metal_bullet_close_27.ogg +0 -0
  464. package/assets/sfx/impactos/metal_bullet_close_28.ogg +0 -0
  465. package/assets/sfx/impactos/metal_bullet_close_29.ogg +0 -0
  466. package/assets/sfx/impactos/metal_bullet_close_30.ogg +0 -0
  467. package/assets/sfx/impactos/metal_bullet_close_31.ogg +0 -0
  468. package/assets/sfx/impactos/metal_bullet_close_32.ogg +0 -0
  469. package/assets/sfx/impactos/metal_bullet_close_33.ogg +0 -0
  470. package/assets/sfx/impactos/metal_bullet_close_34.ogg +0 -0
  471. package/assets/sfx/impactos/metal_fall_close_01.ogg +0 -0
  472. package/assets/sfx/impactos/metal_fall_close_02.ogg +0 -0
  473. package/assets/sfx/impactos/metal_fall_close_03.ogg +0 -0
  474. package/assets/sfx/impactos/metal_fall_close_04.ogg +0 -0
  475. package/assets/sfx/impactos/metal_fall_close_05.ogg +0 -0
  476. package/assets/sfx/impactos/metal_fall_close_06.ogg +0 -0
  477. package/assets/sfx/impactos/metal_fall_close_07.ogg +0 -0
  478. package/assets/sfx/impactos/metal_fall_close_08.ogg +0 -0
  479. package/assets/sfx/impactos/metal_fall_close_09.ogg +0 -0
  480. package/assets/sfx/impactos/metal_fall_close_10.ogg +0 -0
  481. package/assets/sfx/impactos/metal_thin_close_01.ogg +0 -0
  482. package/assets/sfx/impactos/metal_thin_close_02.ogg +0 -0
  483. package/assets/sfx/impactos/metal_thin_close_03.ogg +0 -0
  484. package/assets/sfx/impactos/metal_thin_close_04.ogg +0 -0
  485. package/assets/sfx/impactos/metal_thin_close_05.ogg +0 -0
  486. package/assets/sfx/impactos/metal_thin_close_06.ogg +0 -0
  487. package/assets/sfx/impactos/metal_thin_close_07.ogg +0 -0
  488. package/assets/sfx/impactos/metal_thin_close_08.ogg +0 -0
  489. package/assets/sfx/impactos/metal_thin_close_09.ogg +0 -0
  490. package/assets/sfx/impactos/metal_thin_close_10.ogg +0 -0
  491. package/assets/sfx/impactos/metal_thin_close_11.ogg +0 -0
  492. package/assets/sfx/impactos/metal_thin_close_12.ogg +0 -0
  493. package/assets/sfx/impactos/metal_thin_close_13.ogg +0 -0
  494. package/assets/sfx/impactos/metal_thin_close_14.ogg +0 -0
  495. package/assets/sfx/impactos/metal_thin_close_15.ogg +0 -0
  496. package/assets/sfx/impactos/metal_thin_close_16.ogg +0 -0
  497. package/assets/sfx/impactos/metal_thin_close_17.ogg +0 -0
  498. package/assets/sfx/impactos/metal_thin_close_18.ogg +0 -0
  499. package/assets/sfx/impactos/metal_thin_close_19.ogg +0 -0
  500. package/assets/sfx/impactos/metal_thin_close_20.ogg +0 -0
  501. package/assets/sfx/impactos/metal_thin_close_21.ogg +0 -0
  502. package/assets/sfx/impactos/metal_thin_close_22.ogg +0 -0
  503. package/assets/sfx/impactos/metal_thin_close_23.ogg +0 -0
  504. package/assets/sfx/impactos/metal_thin_close_24.ogg +0 -0
  505. package/assets/sfx/impactos/metal_thin_close_25.ogg +0 -0
  506. package/assets/sfx/impactos/metal_thin_close_26.ogg +0 -0
  507. package/assets/sfx/impactos/paper_tear_close_01.ogg +0 -0
  508. package/assets/sfx/impactos/paper_tear_close_02.ogg +0 -0
  509. package/assets/sfx/impactos/plastic_hit_close_01.ogg +0 -0
  510. package/assets/sfx/impactos/plastic_hit_close_02.ogg +0 -0
  511. package/assets/sfx/impactos/plastic_hit_close_03.ogg +0 -0
  512. package/assets/sfx/impactos/plastic_hit_close_04.ogg +0 -0
  513. package/assets/sfx/impactos/ricochet_metal_close_21.ogg +0 -0
  514. package/assets/sfx/impactos/ricochet_metal_close_22.ogg +0 -0
  515. package/assets/sfx/impactos/ricochet_metal_close_23.ogg +0 -0
  516. package/assets/sfx/impactos/ricochet_metal_close_24.ogg +0 -0
  517. package/assets/sfx/impactos/ricochet_metal_close_25.ogg +0 -0
  518. package/assets/sfx/impactos/ricochet_metal_close_26.ogg +0 -0
  519. package/assets/sfx/impactos/ricochet_metal_close_27.ogg +0 -0
  520. package/assets/sfx/impactos/ricochet_metal_close_28.ogg +0 -0
  521. package/assets/sfx/impactos/rubble_falling_close_01.ogg +0 -0
  522. package/assets/sfx/impactos/rubble_falling_close_02.ogg +0 -0
  523. package/assets/sfx/impactos/rubble_falling_close_03.ogg +0 -0
  524. package/assets/sfx/impactos/rubble_falling_close_04.ogg +0 -0
  525. package/assets/sfx/impactos/rubble_falling_close_05.ogg +0 -0
  526. package/assets/sfx/impactos/rubble_falling_close_06.ogg +0 -0
  527. package/assets/sfx/impactos/rubble_falling_close_07.ogg +0 -0
  528. package/assets/sfx/impactos/rubble_falling_close_08.ogg +0 -0
  529. package/assets/sfx/impactos/rubble_falling_close_09.ogg +0 -0
  530. package/assets/sfx/impactos/soft_bullet_close_01.ogg +0 -0
  531. package/assets/sfx/impactos/soft_bullet_close_02.ogg +0 -0
  532. package/assets/sfx/impactos/soft_bullet_close_03.ogg +0 -0
  533. package/assets/sfx/impactos/soft_bullet_close_04.ogg +0 -0
  534. package/assets/sfx/impactos/soft_bullet_close_05.ogg +0 -0
  535. package/assets/sfx/impactos/soft_bullet_close_06.ogg +0 -0
  536. package/assets/sfx/impactos/soft_bullet_close_07.ogg +0 -0
  537. package/assets/sfx/impactos/soft_bullet_close_08.ogg +0 -0
  538. package/assets/sfx/impactos/soft_bullet_close_09.ogg +0 -0
  539. package/assets/sfx/impactos/soft_bullet_close_10.ogg +0 -0
  540. package/assets/sfx/impactos/soil_bullet_close_21.ogg +0 -0
  541. package/assets/sfx/impactos/soil_bullet_close_22.ogg +0 -0
  542. package/assets/sfx/impactos/soil_bullet_close_23.ogg +0 -0
  543. package/assets/sfx/impactos/wood_blunt_heavy_close_01.ogg +0 -0
  544. package/assets/sfx/impactos/wood_blunt_heavy_close_02.ogg +0 -0
  545. package/assets/sfx/impactos/wood_blunt_heavy_close_03.ogg +0 -0
  546. package/assets/sfx/impactos/wood_blunt_heavy_close_04.ogg +0 -0
  547. package/assets/sfx/impactos/wood_blunt_heavy_close_05.ogg +0 -0
  548. package/assets/sfx/impactos/wood_blunt_light_close_01.ogg +0 -0
  549. package/assets/sfx/impactos/wood_blunt_light_close_02.ogg +0 -0
  550. package/assets/sfx/impactos/wood_blunt_light_close_03.ogg +0 -0
  551. package/assets/sfx/impactos/wood_blunt_light_close_04.ogg +0 -0
  552. package/assets/sfx/impactos/wood_blunt_light_close_05.ogg +0 -0
  553. package/assets/sfx/impactos/wood_blunt_medium_close_01.ogg +0 -0
  554. package/assets/sfx/impactos/wood_blunt_medium_close_02.ogg +0 -0
  555. package/assets/sfx/impactos/wood_blunt_medium_close_03.ogg +0 -0
  556. package/assets/sfx/impactos/wood_blunt_medium_close_04.ogg +0 -0
  557. package/assets/sfx/impactos/wood_blunt_medium_close_05.ogg +0 -0
  558. package/assets/sfx/impactos/wood_fall_close_01.ogg +0 -0
  559. package/assets/sfx/impactos/wood_fall_close_02.ogg +0 -0
  560. package/assets/sfx/impactos/wood_fall_close_03.ogg +0 -0
  561. package/assets/sfx/impactos/wood_fall_close_04.ogg +0 -0
  562. package/assets/sfx/impactos/wood_fall_close_05.ogg +0 -0
  563. package/assets/sfx/impactos/wood_fall_close_06.ogg +0 -0
  564. package/assets/sfx/impactos/wood_fall_close_07.ogg +0 -0
  565. package/assets/sfx/impactos/wood_fall_close_08.ogg +0 -0
  566. package/assets/sfx/impactos/wood_fall_close_09.ogg +0 -0
  567. package/assets/sfx/impactos/wood_hit_close_01.ogg +0 -0
  568. package/assets/sfx/impactos/wood_hit_close_02.ogg +0 -0
  569. package/assets/sfx/impactos/wood_hit_close_03.ogg +0 -0
  570. package/assets/sfx/impactos/wood_hit_close_04.ogg +0 -0
  571. package/assets/sfx/impactos/wood_hit_close_05.ogg +0 -0
  572. package/assets/sfx/impactos/wood_hit_close_06.ogg +0 -0
  573. package/assets/sfx/impactos/wood_hit_close_07.ogg +0 -0
  574. package/assets/sfx/impactos/wood_hit_close_08.ogg +0 -0
  575. package/assets/sfx/impactos/wood_hit_close_09.ogg +0 -0
  576. package/assets/sfx/impactos/wood_hit_close_10.ogg +0 -0
  577. package/assets/sfx/impactos/wood_hit_close_11.ogg +0 -0
  578. package/assets/sfx/impactos/wood_hit_close_12.ogg +0 -0
  579. package/assets/sfx/impactos/wood_hit_close_13.ogg +0 -0
  580. package/assets/sfx/impactos/wood_hit_close_14.ogg +0 -0
  581. package/assets/sfx/impactos/wood_hit_close_21.ogg +0 -0
  582. package/assets/sfx/impactos/wood_hit_close_22.ogg +0 -0
  583. package/assets/sfx/impactos/wood_hit_close_23.ogg +0 -0
  584. package/assets/sfx/impactos/wood_hit_close_24.ogg +0 -0
  585. package/assets/sfx/impactos/wood_hit_close_25.ogg +0 -0
  586. package/assets/sfx/impactos/wood_hit_close_26.ogg +0 -0
  587. package/assets/sfx/impactos/wood_hit_close_27.ogg +0 -0
  588. package/assets/sfx/itens-portas/backpack_open_shut_01.ogg +0 -0
  589. package/assets/sfx/itens-portas/backpack_open_shut_02.ogg +0 -0
  590. package/assets/sfx/itens-portas/backpack_open_shut_03.ogg +0 -0
  591. package/assets/sfx/itens-portas/backpack_open_shut_04.ogg +0 -0
  592. package/assets/sfx/itens-portas/backpack_open_shut_05.ogg +0 -0
  593. package/assets/sfx/itens-portas/backpack_open_shut_06.ogg +0 -0
  594. package/assets/sfx/itens-portas/book_open_shut_01.ogg +0 -0
  595. package/assets/sfx/itens-portas/book_open_shut_02.ogg +0 -0
  596. package/assets/sfx/itens-portas/book_open_shut_03.ogg +0 -0
  597. package/assets/sfx/itens-portas/book_open_shut_04.ogg +0 -0
  598. package/assets/sfx/itens-portas/book_open_shut_05.ogg +0 -0
  599. package/assets/sfx/itens-portas/book_open_shut_06.ogg +0 -0
  600. package/assets/sfx/itens-portas/book_open_shut_07.ogg +0 -0
  601. package/assets/sfx/itens-portas/book_open_shut_08.ogg +0 -0
  602. package/assets/sfx/itens-portas/book_open_shut_09.ogg +0 -0
  603. package/assets/sfx/itens-portas/book_open_shut_10.ogg +0 -0
  604. package/assets/sfx/itens-portas/book_open_shut_11.ogg +0 -0
  605. package/assets/sfx/itens-portas/door_creak_01.ogg +0 -0
  606. package/assets/sfx/itens-portas/door_creak_02.ogg +0 -0
  607. package/assets/sfx/itens-portas/door_creak_03.ogg +0 -0
  608. package/assets/sfx/itens-portas/door_creak_06.ogg +0 -0
  609. package/assets/sfx/itens-portas/door_creak_07.ogg +0 -0
  610. package/assets/sfx/itens-portas/door_glass_sliding_01.ogg +0 -0
  611. package/assets/sfx/itens-portas/door_glass_sliding_02.ogg +0 -0
  612. package/assets/sfx/itens-portas/door_glass_sliding_03.ogg +0 -0
  613. package/assets/sfx/itens-portas/door_glass_sliding_04.ogg +0 -0
  614. package/assets/sfx/itens-portas/door_knock_01.ogg +0 -0
  615. package/assets/sfx/itens-portas/door_lock_unlock_01.ogg +0 -0
  616. package/assets/sfx/itens-portas/door_lock_unlock_02.ogg +0 -0
  617. package/assets/sfx/itens-portas/door_lock_unlock_03.ogg +0 -0
  618. package/assets/sfx/itens-portas/door_lock_unlock_04.ogg +0 -0
  619. package/assets/sfx/itens-portas/door_lock_unlock_05.ogg +0 -0
  620. package/assets/sfx/itens-portas/door_lock_unlock_06.ogg +0 -0
  621. package/assets/sfx/itens-portas/door_lock_unlock_07.ogg +0 -0
  622. package/assets/sfx/itens-portas/door_lock_unlock_08.ogg +0 -0
  623. package/assets/sfx/itens-portas/door_lock_unlock_09.ogg +0 -0
  624. package/assets/sfx/itens-portas/door_lock_unlock_10.ogg +0 -0
  625. package/assets/sfx/itens-portas/door_lock_unlock_11.ogg +0 -0
  626. package/assets/sfx/itens-portas/door_locked_rattle_01.ogg +0 -0
  627. package/assets/sfx/itens-portas/door_locked_rattle_02.ogg +0 -0
  628. package/assets/sfx/itens-portas/door_locked_rattle_03.ogg +0 -0
  629. package/assets/sfx/itens-portas/door_metal_open_shut_01.ogg +0 -0
  630. package/assets/sfx/itens-portas/door_metal_open_shut_02.ogg +0 -0
  631. package/assets/sfx/itens-portas/door_metal_open_shut_03.ogg +0 -0
  632. package/assets/sfx/itens-portas/door_metal_open_shut_04.ogg +0 -0
  633. package/assets/sfx/itens-portas/door_metal_open_shut_05.ogg +0 -0
  634. package/assets/sfx/itens-portas/door_metal_open_shut_06.ogg +0 -0
  635. package/assets/sfx/itens-portas/door_metal_open_shut_07.ogg +0 -0
  636. package/assets/sfx/itens-portas/door_slam_wood_01.ogg +0 -0
  637. package/assets/sfx/itens-portas/door_slam_wood_02.ogg +0 -0
  638. package/assets/sfx/itens-portas/door_slam_wood_03.ogg +0 -0
  639. package/assets/sfx/itens-portas/door_slam_wood_04.ogg +0 -0
  640. package/assets/sfx/itens-portas/door_wood_open_01.ogg +0 -0
  641. package/assets/sfx/itens-portas/door_wood_open_02.ogg +0 -0
  642. package/assets/sfx/itens-portas/door_wood_open_03.ogg +0 -0
  643. package/assets/sfx/itens-portas/door_wood_open_04.ogg +0 -0
  644. package/assets/sfx/itens-portas/door_wood_shut_01.ogg +0 -0
  645. package/assets/sfx/itens-portas/door_wood_shut_02.ogg +0 -0
  646. package/assets/sfx/itens-portas/door_wood_shut_03.ogg +0 -0
  647. package/assets/sfx/itens-portas/door_wood_shut_04.ogg +0 -0
  648. package/assets/sfx/itens-portas/door_wood_shut_05.ogg +0 -0
  649. package/assets/sfx/itens-portas/door_wood_shut_06.ogg +0 -0
  650. package/assets/sfx/itens-portas/door_wood_shut_07.ogg +0 -0
  651. package/assets/sfx/itens-portas/door_wood_shut_08.ogg +0 -0
  652. package/assets/sfx/itens-portas/drawer_open_01.ogg +0 -0
  653. package/assets/sfx/itens-portas/drawer_open_02.ogg +0 -0
  654. package/assets/sfx/itens-portas/drawer_shut_01.ogg +0 -0
  655. package/assets/sfx/itens-portas/drawer_shut_02.ogg +0 -0
  656. package/assets/sfx/itens-portas/item_drop_bag_01.ogg +0 -0
  657. package/assets/sfx/itens-portas/item_drop_bag_02.ogg +0 -0
  658. package/assets/sfx/itens-portas/item_drop_glass_01.ogg +0 -0
  659. package/assets/sfx/itens-portas/item_drop_metal_01.ogg +0 -0
  660. package/assets/sfx/itens-portas/item_drop_metal_02.ogg +0 -0
  661. package/assets/sfx/itens-portas/item_drop_metal_03.ogg +0 -0
  662. package/assets/sfx/itens-portas/item_drop_metal_04.ogg +0 -0
  663. package/assets/sfx/itens-portas/item_drop_metal_05.ogg +0 -0
  664. package/assets/sfx/itens-portas/item_drop_paper_01.ogg +0 -0
  665. package/assets/sfx/itens-portas/item_pickup_coins_01.ogg +0 -0
  666. package/assets/sfx/itens-portas/item_pickup_coins_02.ogg +0 -0
  667. package/assets/sfx/itens-portas/item_pickup_generic_01.ogg +0 -0
  668. package/assets/sfx/itens-portas/item_pickup_generic_02.ogg +0 -0
  669. package/assets/sfx/itens-portas/item_pickup_generic_03.ogg +0 -0
  670. package/assets/sfx/itens-portas/item_pickup_generic_04.ogg +0 -0
  671. package/assets/sfx/itens-portas/item_pickup_leather_01.ogg +0 -0
  672. package/assets/sfx/itens-portas/item_pickup_leather_02.ogg +0 -0
  673. package/assets/sfx/itens-portas/item_pickup_leather_03.ogg +0 -0
  674. package/assets/sfx/itens-portas/item_pickup_leather_04.ogg +0 -0
  675. package/assets/sfx/itens-portas/item_pickup_leather_05.ogg +0 -0
  676. package/assets/sfx/itens-portas/item_setdown_01.ogg +0 -0
  677. package/assets/sfx/itens-portas/item_setdown_02.ogg +0 -0
  678. package/assets/sfx/itens-portas/keys_jingle_01.ogg +0 -0
  679. package/assets/sfx/itens-portas/keys_jingle_02.ogg +0 -0
  680. package/assets/sfx/itens-portas/keys_jingle_03.ogg +0 -0
  681. package/assets/sfx/itens-portas/keys_jingle_04.ogg +0 -0
  682. package/assets/sfx/itens-portas/keys_jingle_05.ogg +0 -0
  683. package/assets/sfx/itens-portas/keys_jingle_06.ogg +0 -0
  684. package/assets/sfx/itens-portas/keys_jingle_07.ogg +0 -0
  685. package/assets/sfx/itens-portas/lever_pull_01.ogg +0 -0
  686. package/assets/sfx/itens-portas/lever_pull_02.ogg +0 -0
  687. package/assets/sfx/itens-portas/lever_pull_03.ogg +0 -0
  688. package/assets/sfx/itens-portas/lever_pull_04.ogg +0 -0
  689. package/assets/sfx/itens-portas/lever_pull_05.ogg +0 -0
  690. package/assets/sfx/itens-portas/lever_pull_06.ogg +0 -0
  691. package/assets/sfx/itens-portas/metal_latch_01.ogg +0 -0
  692. package/assets/sfx/itens-portas/metal_latch_02.ogg +0 -0
  693. package/assets/sfx/itens-portas/plates_hit_01.ogg +0 -0
  694. package/assets/sfx/itens-portas/plates_hit_02.ogg +0 -0
  695. package/assets/sfx/itens-portas/plates_hit_03.ogg +0 -0
  696. package/assets/sfx/itens-portas/plates_hit_04.ogg +0 -0
  697. package/assets/sfx/itens-portas/plates_hit_05.ogg +0 -0
  698. package/assets/sfx/itens-portas/plates_hit_06.ogg +0 -0
  699. package/assets/sfx/itens-portas/plates_hit_07.ogg +0 -0
  700. package/assets/sfx/itens-portas/plates_hit_08.ogg +0 -0
  701. package/assets/sfx/itens-portas/plates_hit_09.ogg +0 -0
  702. package/assets/sfx/itens-portas/plates_hit_10.ogg +0 -0
  703. package/assets/sfx/itens-portas/plates_slide_01.ogg +0 -0
  704. package/assets/sfx/itens-portas/plates_slide_02.ogg +0 -0
  705. package/assets/sfx/itens-portas/plates_slide_03.ogg +0 -0
  706. package/assets/sfx/itens-portas/plates_slide_04.ogg +0 -0
  707. package/assets/sfx/itens-portas/saw_wood_01.ogg +0 -0
  708. package/assets/sfx/itens-portas/switch_click_01.ogg +0 -0
  709. package/assets/sfx/itens-portas/switch_click_02.ogg +0 -0
  710. package/assets/sfx/itens-portas/switch_click_03.ogg +0 -0
  711. package/assets/sfx/itens-portas/switch_click_04.ogg +0 -0
  712. package/assets/sfx/itens-portas/toilet_flush_01.ogg +0 -0
  713. package/assets/sfx/itens-portas/tools_handling_01.ogg +0 -0
  714. package/assets/sfx/itens-portas/tools_handling_02.ogg +0 -0
  715. package/assets/sfx/itens-portas/tools_handling_03.ogg +0 -0
  716. package/assets/sfx/itens-portas/tools_handling_04.ogg +0 -0
  717. package/assets/sfx/itens-portas/tools_handling_05.ogg +0 -0
  718. package/assets/sfx/itens-portas/tools_handling_06.ogg +0 -0
  719. package/assets/sfx/itens-portas/tools_handling_07.ogg +0 -0
  720. package/assets/sfx/itens-portas/tools_handling_08.ogg +0 -0
  721. package/assets/sfx/itens-portas/tools_handling_09.ogg +0 -0
  722. package/assets/sfx/itens-portas/tools_handling_10.ogg +0 -0
  723. package/assets/sfx/itens-portas/tools_handling_11.ogg +0 -0
  724. package/assets/sfx/itens-portas/tools_handling_12.ogg +0 -0
  725. package/assets/sfx/itens-portas/tools_handling_13.ogg +0 -0
  726. package/assets/sfx/itens-portas/tools_handling_14.ogg +0 -0
  727. package/assets/sfx/itens-portas/tools_handling_15.ogg +0 -0
  728. package/assets/sfx/itens-portas/tools_handling_16.ogg +0 -0
  729. package/assets/sfx/itens-portas/water_drip_01.ogg +0 -0
  730. package/assets/sfx/itens-portas/water_drip_02.ogg +0 -0
  731. package/assets/sfx/itens-portas/water_drip_03.ogg +0 -0
  732. package/assets/sfx/itens-portas/water_drip_04.ogg +0 -0
  733. package/assets/sfx/itens-portas/water_faucet_01.ogg +0 -0
  734. package/assets/sfx/itens-portas/water_faucet_02.ogg +0 -0
  735. package/assets/sfx/itens-portas/water_faucet_03.ogg +0 -0
  736. package/assets/sfx/itens-portas/water_faucet_04.ogg +0 -0
  737. package/assets/sfx/itens-portas/water_faucet_05.ogg +0 -0
  738. package/assets/sfx/itens-portas/water_faucet_06.ogg +0 -0
  739. package/assets/sfx/itens-portas/water_faucet_07.ogg +0 -0
  740. package/assets/sfx/itens-portas/water_faucet_08.ogg +0 -0
  741. package/assets/sfx/itens-portas/water_faucet_09.ogg +0 -0
  742. package/assets/sfx/itens-portas/water_faucet_10.ogg +0 -0
  743. package/assets/sfx/manifest.json +12093 -0
  744. package/assets/sfx/personagem/axe_chop_01.ogg +0 -0
  745. package/assets/sfx/personagem/axe_chop_02.ogg +0 -0
  746. package/assets/sfx/personagem/blunt_hit_metal_01.ogg +0 -0
  747. package/assets/sfx/personagem/blunt_hit_metal_02.ogg +0 -0
  748. package/assets/sfx/personagem/blunt_hit_metal_03.ogg +0 -0
  749. package/assets/sfx/personagem/blunt_hit_metal_04.ogg +0 -0
  750. package/assets/sfx/personagem/blunt_hit_metal_05.ogg +0 -0
  751. package/assets/sfx/personagem/body_fall_01.ogg +0 -0
  752. package/assets/sfx/personagem/body_fall_02.ogg +0 -0
  753. package/assets/sfx/personagem/body_fall_03.ogg +0 -0
  754. package/assets/sfx/personagem/body_fall_04.ogg +0 -0
  755. package/assets/sfx/personagem/body_fall_05.ogg +0 -0
  756. package/assets/sfx/personagem/cloth_rustle_01.ogg +0 -0
  757. package/assets/sfx/personagem/cloth_rustle_02.ogg +0 -0
  758. package/assets/sfx/personagem/cloth_rustle_03.ogg +0 -0
  759. package/assets/sfx/personagem/cloth_rustle_04.ogg +0 -0
  760. package/assets/sfx/personagem/cloth_rustle_05.ogg +0 -0
  761. package/assets/sfx/personagem/cloth_rustle_06.ogg +0 -0
  762. package/assets/sfx/personagem/cloth_rustle_07.ogg +0 -0
  763. package/assets/sfx/personagem/cloth_rustle_08.ogg +0 -0
  764. package/assets/sfx/personagem/cloth_rustle_09.ogg +0 -0
  765. package/assets/sfx/personagem/cloth_rustle_10.ogg +0 -0
  766. package/assets/sfx/personagem/cloth_rustle_11.ogg +0 -0
  767. package/assets/sfx/personagem/cloth_rustle_12.ogg +0 -0
  768. package/assets/sfx/personagem/cloth_rustle_13.ogg +0 -0
  769. package/assets/sfx/personagem/cloth_rustle_14.ogg +0 -0
  770. package/assets/sfx/personagem/drink_gulp_01.ogg +0 -0
  771. package/assets/sfx/personagem/drink_gulp_02.ogg +0 -0
  772. package/assets/sfx/personagem/drink_gulp_03.ogg +0 -0
  773. package/assets/sfx/personagem/footstep_carpet_alt_01.ogg +0 -0
  774. package/assets/sfx/personagem/footstep_carpet_alt_02.ogg +0 -0
  775. package/assets/sfx/personagem/footstep_carpet_alt_03.ogg +0 -0
  776. package/assets/sfx/personagem/footstep_carpet_alt_04.ogg +0 -0
  777. package/assets/sfx/personagem/footstep_carpet_alt_05.ogg +0 -0
  778. package/assets/sfx/personagem/footstep_concrete_alt_01.ogg +0 -0
  779. package/assets/sfx/personagem/footstep_concrete_alt_02.ogg +0 -0
  780. package/assets/sfx/personagem/footstep_concrete_alt_03.ogg +0 -0
  781. package/assets/sfx/personagem/footstep_concrete_alt_04.ogg +0 -0
  782. package/assets/sfx/personagem/footstep_concrete_alt_05.ogg +0 -0
  783. package/assets/sfx/personagem/footstep_concrete_run_01.ogg +0 -0
  784. package/assets/sfx/personagem/footstep_concrete_run_02.ogg +0 -0
  785. package/assets/sfx/personagem/footstep_concrete_run_03.ogg +0 -0
  786. package/assets/sfx/personagem/footstep_concrete_run_04.ogg +0 -0
  787. package/assets/sfx/personagem/footstep_concrete_run_05.ogg +0 -0
  788. package/assets/sfx/personagem/footstep_concrete_walk_01.ogg +0 -0
  789. package/assets/sfx/personagem/footstep_concrete_walk_02.ogg +0 -0
  790. package/assets/sfx/personagem/footstep_concrete_walk_03.ogg +0 -0
  791. package/assets/sfx/personagem/footstep_concrete_walk_04.ogg +0 -0
  792. package/assets/sfx/personagem/footstep_concrete_walk_05.ogg +0 -0
  793. package/assets/sfx/personagem/footstep_dirt_run_01.ogg +0 -0
  794. package/assets/sfx/personagem/footstep_dirt_run_02.ogg +0 -0
  795. package/assets/sfx/personagem/footstep_dirt_run_03.ogg +0 -0
  796. package/assets/sfx/personagem/footstep_dirt_run_04.ogg +0 -0
  797. package/assets/sfx/personagem/footstep_dirt_run_05.ogg +0 -0
  798. package/assets/sfx/personagem/footstep_dirt_walk_01.ogg +0 -0
  799. package/assets/sfx/personagem/footstep_dirt_walk_02.ogg +0 -0
  800. package/assets/sfx/personagem/footstep_dirt_walk_03.ogg +0 -0
  801. package/assets/sfx/personagem/footstep_dirt_walk_04.ogg +0 -0
  802. package/assets/sfx/personagem/footstep_dirt_walk_05.ogg +0 -0
  803. package/assets/sfx/personagem/footstep_generic_alt_01.ogg +0 -0
  804. package/assets/sfx/personagem/footstep_generic_alt_02.ogg +0 -0
  805. package/assets/sfx/personagem/footstep_generic_alt_03.ogg +0 -0
  806. package/assets/sfx/personagem/footstep_generic_alt_04.ogg +0 -0
  807. package/assets/sfx/personagem/footstep_generic_alt_05.ogg +0 -0
  808. package/assets/sfx/personagem/footstep_generic_alt_06.ogg +0 -0
  809. package/assets/sfx/personagem/footstep_generic_alt_07.ogg +0 -0
  810. package/assets/sfx/personagem/footstep_generic_alt_08.ogg +0 -0
  811. package/assets/sfx/personagem/footstep_generic_alt_09.ogg +0 -0
  812. package/assets/sfx/personagem/footstep_generic_alt_10.ogg +0 -0
  813. package/assets/sfx/personagem/footstep_grass_alt2_01.ogg +0 -0
  814. package/assets/sfx/personagem/footstep_grass_alt2_02.ogg +0 -0
  815. package/assets/sfx/personagem/footstep_grass_alt2_03.ogg +0 -0
  816. package/assets/sfx/personagem/footstep_grass_alt2_04.ogg +0 -0
  817. package/assets/sfx/personagem/footstep_grass_alt_01.ogg +0 -0
  818. package/assets/sfx/personagem/footstep_grass_alt_02.ogg +0 -0
  819. package/assets/sfx/personagem/footstep_grass_alt_03.ogg +0 -0
  820. package/assets/sfx/personagem/footstep_grass_alt_04.ogg +0 -0
  821. package/assets/sfx/personagem/footstep_grass_alt_05.ogg +0 -0
  822. package/assets/sfx/personagem/footstep_grass_run_01.ogg +0 -0
  823. package/assets/sfx/personagem/footstep_grass_run_02.ogg +0 -0
  824. package/assets/sfx/personagem/footstep_grass_run_03.ogg +0 -0
  825. package/assets/sfx/personagem/footstep_grass_run_04.ogg +0 -0
  826. package/assets/sfx/personagem/footstep_grass_run_05.ogg +0 -0
  827. package/assets/sfx/personagem/footstep_grass_walk_01.ogg +0 -0
  828. package/assets/sfx/personagem/footstep_grass_walk_02.ogg +0 -0
  829. package/assets/sfx/personagem/footstep_grass_walk_03.ogg +0 -0
  830. package/assets/sfx/personagem/footstep_grass_walk_04.ogg +0 -0
  831. package/assets/sfx/personagem/footstep_grass_walk_05.ogg +0 -0
  832. package/assets/sfx/personagem/footstep_hard_alt_01.ogg +0 -0
  833. package/assets/sfx/personagem/footstep_hard_alt_02.ogg +0 -0
  834. package/assets/sfx/personagem/footstep_hard_alt_03.ogg +0 -0
  835. package/assets/sfx/personagem/footstep_hard_alt_04.ogg +0 -0
  836. package/assets/sfx/personagem/footstep_hard_alt_05.ogg +0 -0
  837. package/assets/sfx/personagem/footstep_hard_alt_06.ogg +0 -0
  838. package/assets/sfx/personagem/footstep_hard_alt_07.ogg +0 -0
  839. package/assets/sfx/personagem/footstep_hard_alt_08.ogg +0 -0
  840. package/assets/sfx/personagem/footstep_hard_alt_09.ogg +0 -0
  841. package/assets/sfx/personagem/footstep_metal_run_01.ogg +0 -0
  842. package/assets/sfx/personagem/footstep_metal_run_02.ogg +0 -0
  843. package/assets/sfx/personagem/footstep_metal_run_03.ogg +0 -0
  844. package/assets/sfx/personagem/footstep_metal_run_04.ogg +0 -0
  845. package/assets/sfx/personagem/footstep_metal_run_05.ogg +0 -0
  846. package/assets/sfx/personagem/footstep_metal_walk_01.ogg +0 -0
  847. package/assets/sfx/personagem/footstep_metal_walk_02.ogg +0 -0
  848. package/assets/sfx/personagem/footstep_metal_walk_03.ogg +0 -0
  849. package/assets/sfx/personagem/footstep_metal_walk_04.ogg +0 -0
  850. package/assets/sfx/personagem/footstep_metal_walk_05.ogg +0 -0
  851. package/assets/sfx/personagem/footstep_snow_alt_01.ogg +0 -0
  852. package/assets/sfx/personagem/footstep_snow_alt_02.ogg +0 -0
  853. package/assets/sfx/personagem/footstep_snow_alt_03.ogg +0 -0
  854. package/assets/sfx/personagem/footstep_snow_alt_04.ogg +0 -0
  855. package/assets/sfx/personagem/footstep_snow_alt_05.ogg +0 -0
  856. package/assets/sfx/personagem/footstep_snow_run_01.ogg +0 -0
  857. package/assets/sfx/personagem/footstep_snow_run_02.ogg +0 -0
  858. package/assets/sfx/personagem/footstep_snow_run_03.ogg +0 -0
  859. package/assets/sfx/personagem/footstep_snow_run_04.ogg +0 -0
  860. package/assets/sfx/personagem/footstep_snow_run_05.ogg +0 -0
  861. package/assets/sfx/personagem/footstep_snow_walk_01.ogg +0 -0
  862. package/assets/sfx/personagem/footstep_snow_walk_02.ogg +0 -0
  863. package/assets/sfx/personagem/footstep_snow_walk_03.ogg +0 -0
  864. package/assets/sfx/personagem/footstep_snow_walk_04.ogg +0 -0
  865. package/assets/sfx/personagem/footstep_snow_walk_05.ogg +0 -0
  866. package/assets/sfx/personagem/footstep_wood_alt_01.ogg +0 -0
  867. package/assets/sfx/personagem/footstep_wood_alt_02.ogg +0 -0
  868. package/assets/sfx/personagem/footstep_wood_alt_03.ogg +0 -0
  869. package/assets/sfx/personagem/footstep_wood_alt_04.ogg +0 -0
  870. package/assets/sfx/personagem/footstep_wood_alt_05.ogg +0 -0
  871. package/assets/sfx/personagem/footstep_wood_run_01.ogg +0 -0
  872. package/assets/sfx/personagem/footstep_wood_run_02.ogg +0 -0
  873. package/assets/sfx/personagem/footstep_wood_run_03.ogg +0 -0
  874. package/assets/sfx/personagem/footstep_wood_run_04.ogg +0 -0
  875. package/assets/sfx/personagem/footstep_wood_run_05.ogg +0 -0
  876. package/assets/sfx/personagem/footstep_wood_walk_01.ogg +0 -0
  877. package/assets/sfx/personagem/footstep_wood_walk_02.ogg +0 -0
  878. package/assets/sfx/personagem/footstep_wood_walk_03.ogg +0 -0
  879. package/assets/sfx/personagem/footstep_wood_walk_04.ogg +0 -0
  880. package/assets/sfx/personagem/footstep_wood_walk_05.ogg +0 -0
  881. package/assets/sfx/personagem/frying_pan_hit_01.ogg +0 -0
  882. package/assets/sfx/personagem/frying_pan_hit_02.ogg +0 -0
  883. package/assets/sfx/personagem/frying_pan_hit_03.ogg +0 -0
  884. package/assets/sfx/personagem/grab_01.ogg +0 -0
  885. package/assets/sfx/personagem/kick_hit_01.ogg +0 -0
  886. package/assets/sfx/personagem/kick_hit_02.ogg +0 -0
  887. package/assets/sfx/personagem/kick_hit_03.ogg +0 -0
  888. package/assets/sfx/personagem/kick_hit_04.ogg +0 -0
  889. package/assets/sfx/personagem/kick_hit_05.ogg +0 -0
  890. package/assets/sfx/personagem/knife_draw_01.ogg +0 -0
  891. package/assets/sfx/personagem/knife_draw_02.ogg +0 -0
  892. package/assets/sfx/personagem/knife_draw_03.ogg +0 -0
  893. package/assets/sfx/personagem/knife_scrape_01.ogg +0 -0
  894. package/assets/sfx/personagem/knife_slash_01.ogg +0 -0
  895. package/assets/sfx/personagem/knife_slash_02.ogg +0 -0
  896. package/assets/sfx/personagem/land_concrete_01.ogg +0 -0
  897. package/assets/sfx/personagem/land_concrete_02.ogg +0 -0
  898. package/assets/sfx/personagem/land_dirt_01.ogg +0 -0
  899. package/assets/sfx/personagem/land_dirt_02.ogg +0 -0
  900. package/assets/sfx/personagem/land_grass_01.ogg +0 -0
  901. package/assets/sfx/personagem/land_grass_02.ogg +0 -0
  902. package/assets/sfx/personagem/land_metal_01.ogg +0 -0
  903. package/assets/sfx/personagem/land_metal_02.ogg +0 -0
  904. package/assets/sfx/personagem/land_snow_01.ogg +0 -0
  905. package/assets/sfx/personagem/land_snow_02.ogg +0 -0
  906. package/assets/sfx/personagem/land_wood_01.ogg +0 -0
  907. package/assets/sfx/personagem/land_wood_02.ogg +0 -0
  908. package/assets/sfx/personagem/player_breath_run_01.ogg +0 -0
  909. package/assets/sfx/personagem/player_breath_run_02.ogg +0 -0
  910. package/assets/sfx/personagem/player_breath_run_03.ogg +0 -0
  911. package/assets/sfx/personagem/player_breath_run_04.ogg +0 -0
  912. package/assets/sfx/personagem/player_breath_run_05.ogg +0 -0
  913. package/assets/sfx/personagem/player_death_female_01.ogg +0 -0
  914. package/assets/sfx/personagem/player_death_female_02.ogg +0 -0
  915. package/assets/sfx/personagem/player_death_female_03.ogg +0 -0
  916. package/assets/sfx/personagem/player_death_female_04.ogg +0 -0
  917. package/assets/sfx/personagem/player_death_female_05.ogg +0 -0
  918. package/assets/sfx/personagem/player_death_female_06.ogg +0 -0
  919. package/assets/sfx/personagem/player_death_female_07.ogg +0 -0
  920. package/assets/sfx/personagem/player_death_female_08.ogg +0 -0
  921. package/assets/sfx/personagem/player_death_male_01.ogg +0 -0
  922. package/assets/sfx/personagem/player_death_male_02.ogg +0 -0
  923. package/assets/sfx/personagem/player_death_male_03.ogg +0 -0
  924. package/assets/sfx/personagem/player_death_male_04.ogg +0 -0
  925. package/assets/sfx/personagem/player_death_male_05.ogg +0 -0
  926. package/assets/sfx/personagem/player_death_male_06.ogg +0 -0
  927. package/assets/sfx/personagem/player_death_male_07.ogg +0 -0
  928. package/assets/sfx/personagem/player_death_male_08.ogg +0 -0
  929. package/assets/sfx/personagem/player_effort_01.ogg +0 -0
  930. package/assets/sfx/personagem/player_effort_02.ogg +0 -0
  931. package/assets/sfx/personagem/player_effort_03.ogg +0 -0
  932. package/assets/sfx/personagem/player_effort_04.ogg +0 -0
  933. package/assets/sfx/personagem/player_hurt_female_01.ogg +0 -0
  934. package/assets/sfx/personagem/player_hurt_female_02.ogg +0 -0
  935. package/assets/sfx/personagem/player_hurt_female_03.ogg +0 -0
  936. package/assets/sfx/personagem/player_hurt_female_04.ogg +0 -0
  937. package/assets/sfx/personagem/player_hurt_female_05.ogg +0 -0
  938. package/assets/sfx/personagem/player_hurt_female_06.ogg +0 -0
  939. package/assets/sfx/personagem/player_hurt_male_01.ogg +0 -0
  940. package/assets/sfx/personagem/player_hurt_male_02.ogg +0 -0
  941. package/assets/sfx/personagem/player_hurt_male_03.ogg +0 -0
  942. package/assets/sfx/personagem/player_hurt_male_04.ogg +0 -0
  943. package/assets/sfx/personagem/player_hurt_male_05.ogg +0 -0
  944. package/assets/sfx/personagem/player_hurt_male_06.ogg +0 -0
  945. package/assets/sfx/personagem/punch_hit_alt_01.ogg +0 -0
  946. package/assets/sfx/personagem/punch_hit_alt_02.ogg +0 -0
  947. package/assets/sfx/personagem/punch_hit_alt_03.ogg +0 -0
  948. package/assets/sfx/personagem/punch_hit_alt_04.ogg +0 -0
  949. package/assets/sfx/personagem/punch_hit_alt_05.ogg +0 -0
  950. package/assets/sfx/personagem/punch_hit_alt_06.ogg +0 -0
  951. package/assets/sfx/personagem/punch_hit_alt_07.ogg +0 -0
  952. package/assets/sfx/personagem/punch_hit_alt_08.ogg +0 -0
  953. package/assets/sfx/personagem/punch_hit_alt_09.ogg +0 -0
  954. package/assets/sfx/personagem/punch_hit_alt_10.ogg +0 -0
  955. package/assets/sfx/personagem/punch_hit_body_01.ogg +0 -0
  956. package/assets/sfx/personagem/punch_hit_body_02.ogg +0 -0
  957. package/assets/sfx/personagem/punch_hit_body_03.ogg +0 -0
  958. package/assets/sfx/personagem/punch_hit_body_04.ogg +0 -0
  959. package/assets/sfx/personagem/punch_hit_body_05.ogg +0 -0
  960. package/assets/sfx/personagem/punch_hit_body_06.ogg +0 -0
  961. package/assets/sfx/personagem/punch_hit_face_01.ogg +0 -0
  962. package/assets/sfx/personagem/punch_hit_face_02.ogg +0 -0
  963. package/assets/sfx/personagem/punch_hit_face_03.ogg +0 -0
  964. package/assets/sfx/personagem/punch_hit_face_04.ogg +0 -0
  965. package/assets/sfx/personagem/punch_hit_face_05.ogg +0 -0
  966. package/assets/sfx/personagem/punch_swing_01.ogg +0 -0
  967. package/assets/sfx/personagem/punch_swing_02.ogg +0 -0
  968. package/assets/sfx/personagem/punch_swing_03.ogg +0 -0
  969. package/assets/sfx/personagem/punch_swing_04.ogg +0 -0
  970. package/assets/sfx/personagem/punch_swing_05.ogg +0 -0
  971. package/assets/sfx/personagem/punch_swing_06.ogg +0 -0
  972. package/assets/sfx/personagem/punch_swing_07.ogg +0 -0
  973. package/assets/sfx/personagem/punch_swing_08.ogg +0 -0
  974. package/assets/sfx/personagem/scream_01.ogg +0 -0
  975. package/assets/sfx/personagem/scream_02.ogg +0 -0
  976. package/assets/sfx/personagem/scream_03.ogg +0 -0
  977. package/assets/sfx/personagem/scream_04.ogg +0 -0
  978. package/assets/sfx/personagem/slap_01.ogg +0 -0
  979. package/assets/sfx/personagem/swing_whoosh_alt_01.ogg +0 -0
  980. package/assets/sfx/personagem/swing_whoosh_alt_02.ogg +0 -0
  981. package/assets/sfx/personagem/swing_whoosh_alt_03.ogg +0 -0
  982. package/assets/sfx/personagem/swing_whoosh_alt_04.ogg +0 -0
  983. package/assets/sfx/personagem/swing_whoosh_alt_05.ogg +0 -0
  984. package/assets/sfx/personagem/swing_whoosh_alt_06.ogg +0 -0
  985. package/assets/sfx/personagem/swing_whoosh_alt_07.ogg +0 -0
  986. package/assets/sfx/personagem/swing_whoosh_alt_08.ogg +0 -0
  987. package/assets/sfx/personagem/swing_whoosh_alt_09.ogg +0 -0
  988. package/assets/sfx/personagem/swing_whoosh_alt_10.ogg +0 -0
  989. package/assets/sfx/personagem/swing_whoosh_alt_11.ogg +0 -0
  990. package/assets/sfx/personagem/swing_whoosh_alt_12.ogg +0 -0
  991. package/assets/sfx/personagem/swing_whoosh_alt_13.ogg +0 -0
  992. package/assets/sfx/personagem/swing_whoosh_alt_14.ogg +0 -0
  993. package/assets/sfx/personagem/swing_whoosh_alt_15.ogg +0 -0
  994. package/assets/sfx/personagem/swing_whoosh_alt_16.ogg +0 -0
  995. package/assets/sfx/personagem/swing_whoosh_alt_17.ogg +0 -0
  996. package/assets/sfx/personagem/swing_whoosh_alt_18.ogg +0 -0
  997. package/assets/sfx/personagem/swing_whoosh_alt_19.ogg +0 -0
  998. package/assets/sfx/personagem/swing_whoosh_alt_20.ogg +0 -0
  999. package/assets/sfx/personagem/swing_whoosh_alt_21.ogg +0 -0
  1000. package/assets/sfx/personagem/swing_whoosh_alt_22.ogg +0 -0
  1001. package/assets/sfx/personagem/swing_whoosh_alt_23.ogg +0 -0
  1002. package/assets/sfx/personagem/swing_whoosh_alt_24.ogg +0 -0
  1003. package/assets/sfx/personagem/swing_whoosh_alt_25.ogg +0 -0
  1004. package/assets/sfx/personagem/swing_whoosh_alt_26.ogg +0 -0
  1005. package/assets/sfx/personagem/swing_whoosh_alt_27.ogg +0 -0
  1006. package/assets/sfx/personagem/swing_whoosh_alt_28.ogg +0 -0
  1007. package/assets/sfx/personagem/swing_whoosh_alt_29.ogg +0 -0
  1008. package/assets/sfx/personagem/swing_whoosh_alt_30.ogg +0 -0
  1009. package/assets/sfx/personagem/swing_whoosh_alt_31.ogg +0 -0
  1010. package/assets/sfx/personagem/swing_whoosh_alt_32.ogg +0 -0
  1011. package/assets/sfx/personagem/swing_whoosh_alt_33.ogg +0 -0
  1012. package/assets/sfx/personagem/swing_whoosh_alt_34.ogg +0 -0
  1013. package/assets/sfx/personagem/swing_whoosh_alt_35.ogg +0 -0
  1014. package/assets/sfx/personagem/swing_whoosh_alt_36.ogg +0 -0
  1015. package/assets/sfx/personagem/swing_whoosh_alt_37.ogg +0 -0
  1016. package/assets/sfx/personagem/swing_whoosh_alt_38.ogg +0 -0
  1017. package/assets/sfx/personagem/swing_whoosh_alt_39.ogg +0 -0
  1018. package/assets/sfx/personagem/swing_whoosh_alt_40.ogg +0 -0
  1019. package/assets/sfx/personagem/swing_whoosh_alt_41.ogg +0 -0
  1020. package/assets/sfx/personagem/swing_whoosh_alt_42.ogg +0 -0
  1021. package/assets/sfx/personagem/swing_whoosh_alt_43.ogg +0 -0
  1022. package/assets/sfx/personagem/zombie_creature_01.ogg +0 -0
  1023. package/assets/sfx/personagem/zombie_creature_02.ogg +0 -0
  1024. package/assets/sfx/personagem/zombie_creature_03.ogg +0 -0
  1025. package/assets/sfx/personagem/zombie_death_01.ogg +0 -0
  1026. package/assets/sfx/personagem/zombie_death_02.ogg +0 -0
  1027. package/assets/sfx/personagem/zombie_death_03.ogg +0 -0
  1028. package/assets/sfx/personagem/zombie_death_04.ogg +0 -0
  1029. package/assets/sfx/personagem/zombie_death_05.ogg +0 -0
  1030. package/assets/sfx/personagem/zombie_death_06.ogg +0 -0
  1031. package/assets/sfx/personagem/zombie_death_07.ogg +0 -0
  1032. package/assets/sfx/personagem/zombie_death_08.ogg +0 -0
  1033. package/assets/sfx/personagem/zombie_groan_01.ogg +0 -0
  1034. package/assets/sfx/personagem/zombie_groan_02.ogg +0 -0
  1035. package/assets/sfx/personagem/zombie_groan_03.ogg +0 -0
  1036. package/assets/sfx/personagem/zombie_groan_04.ogg +0 -0
  1037. package/assets/sfx/personagem/zombie_groan_05.ogg +0 -0
  1038. package/assets/sfx/personagem/zombie_groan_06.ogg +0 -0
  1039. package/assets/sfx/personagem/zombie_groan_07.ogg +0 -0
  1040. package/assets/sfx/personagem/zombie_groan_08.ogg +0 -0
  1041. package/assets/sfx/personagem/zombie_groan_09.ogg +0 -0
  1042. package/assets/sfx/personagem/zombie_groan_10.ogg +0 -0
  1043. package/assets/sfx/personagem/zombie_groan_11.ogg +0 -0
  1044. package/assets/sfx/veiculos/car_door_open_01.ogg +0 -0
  1045. package/assets/sfx/veiculos/car_door_shut_01.ogg +0 -0
  1046. package/assets/sfx/veiculos/car_gear_shift_01.ogg +0 -0
  1047. package/assets/sfx/veiculos/car_trunk_shut_01.ogg +0 -0
  1048. package/assets/sfx/veiculos/car_window_01.ogg +0 -0
  1049. package/assets/sfx/veiculos/car_window_02.ogg +0 -0
  1050. package/assets/sfx/veiculos/engine_loop_car_high_01.ogg +0 -0
  1051. package/assets/sfx/veiculos/engine_loop_car_idle_01.ogg +0 -0
  1052. package/assets/sfx/veiculos/engine_start_run_stop_01.ogg +0 -0
  1053. package/assets/sfx/veiculos/engine_start_run_stop_02.ogg +0 -0
  1054. package/dist/chunk-KBTUANNG.js +4429 -0
  1055. package/dist/chunk-KBTUANNG.js.map +1 -0
  1056. package/dist/index.d.ts +153 -3
  1057. package/dist/index.global.js +3423 -1112
  1058. package/dist/index.global.js.map +1 -1
  1059. package/dist/index.js +842 -6
  1060. package/dist/index.js.map +1 -1
  1061. package/dist/locale-CJfDpWnI.d.ts +1250 -0
  1062. package/dist/testing.d.ts +84 -2
  1063. package/dist/testing.js +11 -3
  1064. package/package.json +10 -6
  1065. package/dist/chunk-DZITVFMJ.js +0 -2235
  1066. package/dist/chunk-DZITVFMJ.js.map +0 -1
  1067. package/dist/testing-BZQid7h0.d.ts +0 -653
@@ -29,58 +29,137 @@ var GameAudio = (() => {
29
29
  BUILD_ORDERS: () => BUILD_ORDERS,
30
30
  BUS_IDS: () => BUS_IDS,
31
31
  BUS_PRIORITY: () => BUS_PRIORITY,
32
+ CABIN_BANK: () => CABIN_BANK,
33
+ CABIN_SOUNDS: () => CABIN_SOUNDS,
34
+ CALIBRATION_ID: () => CALIBRATION_ID,
35
+ CALIBRATION_LOOP_ID: () => CALIBRATION_LOOP_ID,
36
+ CALIBRATION_LOOP_SOUND: () => CALIBRATION_LOOP_SOUND,
37
+ CALIBRATION_SOUND: () => CALIBRATION_SOUND,
38
+ CRITERIA: () => CRITERIA,
39
+ CRITERIA_TARGETS: () => CRITERIA_TARGETS,
40
+ CROSSFADE_STEPS: () => CROSSFADE_STEPS,
41
+ DEFAULT_CROSSOVER: () => DEFAULT_CROSSOVER,
32
42
  DEFAULT_LISTENER: () => DEFAULT_LISTENER,
43
+ DEFAULT_LOOP_CROSSFADE: () => DEFAULT_LOOP_CROSSFADE,
33
44
  DEFAULT_MAX_VOICES: () => DEFAULT_MAX_VOICES,
34
45
  DEFAULT_MIX: () => DEFAULT_MIX,
46
+ DEFAULT_SAMPLE_CUTOFF_K: () => DEFAULT_SAMPLE_CUTOFF_K,
47
+ DEFAULT_SAMPLE_FALLOFF: () => DEFAULT_SAMPLE_FALLOFF,
48
+ DEFAULT_SAMPLE_PITCH: () => DEFAULT_SAMPLE_PITCH,
49
+ DEFAULT_TAIL_GAIN: () => DEFAULT_TAIL_GAIN,
35
50
  DISTANCE_CURVES: () => DISTANCE_CURVES,
51
+ FOLEY_BANK: () => FOLEY_BANK,
52
+ FOLEY_FALLOFF: () => FOLEY_FALLOFF,
36
53
  FOOTSTEP_FALLOFF: () => FOOTSTEP_FALLOFF,
37
54
  GAME_SFX_BANK: () => GAME_SFX_BANK,
38
55
  GAME_SFX_CLASSES: () => GAME_SFX_CLASSES,
39
56
  GAME_SFX_FALLBACK: () => GAME_SFX_FALLBACK,
57
+ ITEM_FOLEY: () => ITEM_FOLEY,
58
+ KIT_CATEGORY_BUS: () => KIT_CATEGORY_BUS,
59
+ KIT_SAMPLES_BANK: () => KIT_SAMPLES_BANK,
60
+ KIT_SAMPLE_ALIASES: () => KIT_SAMPLE_ALIASES,
61
+ LAYER_FLOOR: () => LAYER_FLOOR,
62
+ LEAF_FOLEY: () => LEAF_FOLEY,
63
+ LEAF_FOLEY_SETS: () => LEAF_FOLEY_SETS,
40
64
  LIBRARY_BANK: () => LIBRARY_BANK,
41
65
  LIBRARY_LOOPS: () => LIBRARY_LOOPS,
42
66
  LIBRARY_SOUNDS: () => LIBRARY_SOUNDS,
43
67
  LOOP_SMOOTHING: () => LOOP_SMOOTHING,
44
68
  LOOP_START_ABOVE: () => LOOP_START_ABOVE,
45
69
  MIX_PROFILES: () => MIX_PROFILES,
70
+ OPEN_CUTOFF: () => OPEN_CUTOFF,
46
71
  PAN_LIMIT: () => PAN_LIMIT,
47
72
  PAN_MIN_DISTANCE: () => PAN_MIN_DISTANCE,
73
+ PICKUP_CLASSES: () => PICKUP_CLASSES,
74
+ SAMPLE_ENVIRONMENTS: () => SAMPLE_ENVIRONMENTS,
75
+ SDK_LICENSES: () => SDK_LICENSES,
76
+ SFX_BLOWS: () => SFX_BLOWS,
77
+ SFX_CATALOG: () => SFX_CATALOG,
78
+ SFX_CATEGORIES: () => SFX_CATEGORIES,
79
+ SFX_IMPACTS: () => SFX_IMPACTS,
80
+ SFX_LAYERS: () => SFX_LAYERS,
81
+ SFX_MATERIALS: () => SFX_MATERIALS,
82
+ SFX_TIERS: () => SFX_TIERS,
83
+ USER_SAMPLES_BANK: () => USER_SAMPLES_BANK,
84
+ USER_SOUND_PREFIX: () => USER_SOUND_PREFIX,
48
85
  VOICE_GROUPS: () => VOICE_GROUPS,
49
86
  audioBenchTextOf: () => audioBenchTextOf,
87
+ balanceOf: () => balanceOf,
50
88
  bufferOf: () => bufferOf,
51
89
  buildChain: () => buildChain,
52
90
  busGain: () => busGain,
53
91
  busPriority: () => busPriority,
92
+ calibrationLoader: () => calibrationLoader,
93
+ centroidHz: () => centroidHz,
94
+ countHits: () => countHits,
54
95
  createAudioEngine: () => createAudioEngine,
55
96
  createAudioSink: () => createAudioSink,
56
97
  createAudioTestModule: () => createAudioTestModule,
57
98
  createLoopState: () => createLoopState,
58
99
  createRng: () => createRng,
100
+ createSampleStore: () => createSampleStore,
59
101
  createSetPicker: () => createSetPicker,
60
102
  createSoundRegistry: () => createSoundRegistry,
61
103
  createVoiceManager: () => createVoiceManager,
104
+ crossGains: () => crossGains,
62
105
  cutoffDivisor: () => cutoffDivisor,
106
+ dbOf: () => dbOf,
63
107
  drawBetween: () => drawBetween,
108
+ encodeWav: () => encodeWav,
109
+ endDropDb: () => endDropDb,
110
+ equalPowerIn: () => equalPowerIn,
64
111
  falloffGain: () => falloffGain,
112
+ farMixAt: () => farMixAt,
113
+ fetchLoader: () => fetchLoader,
65
114
  fillImpulse: () => fillImpulse,
66
115
  fillNoise: () => fillNoise,
67
116
  globalContext: () => globalContext,
117
+ impactSoundId: () => impactSoundId,
118
+ inlineLoader: () => inlineLoader,
119
+ isCalibrationUrl: () => isCalibrationUrl,
68
120
  isSilent: () => isSilent,
121
+ kitSampleBank: () => kitSampleBank,
122
+ kitSampleDefaults: () => kitSampleDefaults,
123
+ limiterCurve: () => limiterCurve2,
69
124
  listenerDistance: () => listenerDistance,
125
+ loadKitSamples: () => loadKitSamples,
126
+ loadUserSamples: () => loadUserSamples,
127
+ loopCrossfade: () => loopCrossfade,
128
+ measureCriteria: () => measureCriteria,
70
129
  noiseClassRecipe: () => noiseClassRecipe,
130
+ onsetIndex: () => onsetIndex,
71
131
  panFor: () => panFor,
132
+ parseSfxName: () => parseSfxName,
133
+ peakIndex: () => peakIndex,
134
+ peakOf: () => peakOf,
135
+ pickTake: () => pickTake,
136
+ planSample: () => planSample,
72
137
  planSound: () => planSound,
73
138
  realize: () => realize,
139
+ realizeSample: () => realizeSample,
140
+ recordOutput: () => recordOutput,
74
141
  resolveMix: () => resolveMix,
75
142
  retunePlan: () => retunePlan,
143
+ ringSeconds: () => ringSeconds,
144
+ rmsDb: () => rmsDb,
145
+ sampleBankFromManifest: () => sampleBankFromManifest,
146
+ sampleCutoff: () => sampleCutoff,
147
+ sampleLength: () => sampleLength,
148
+ sampleUrls: () => sampleUrls,
76
149
  sceneMany: () => sceneMany,
77
150
  sceneNoise: () => sceneNoise,
78
151
  sceneRecipe: () => sceneRecipe,
79
152
  sceneTone: () => sceneTone,
80
153
  sceneVoice: () => sceneVoice,
154
+ sfxState: () => sfxState,
155
+ soundSourceOf: () => soundSourceOf,
156
+ splitTier: () => splitTier,
81
157
  streamFor: () => streamFor,
82
158
  streamSeed: () => streamSeed,
83
- unlockContext: () => unlockContext
159
+ tierFor: () => tierFor,
160
+ unlockContext: () => unlockContext,
161
+ userSampleBank: () => userSampleBank,
162
+ withAliases: () => withAliases
84
163
  });
85
164
 
86
165
  // src/rng.ts
@@ -177,6 +256,19 @@ var GameAudio = (() => {
177
256
  "jogo:L66986 one groan every 0.25 s (`groanBudget`), only nearer than 40 m",
178
257
  "jogo:L70472 the budget counts down by the frame time"
179
258
  ]
259
+ },
260
+ fire_ignite: {
261
+ maxConcurrent: 2,
262
+ cooldown: 0.15,
263
+ sources: [
264
+ "sdk: many parts catching at once (a blast sets a shelf alight) are one or two ignitions, not forty"
265
+ ]
266
+ },
267
+ fire_crackle: {
268
+ maxConcurrent: 4,
269
+ sources: [
270
+ "sdk: the crackles of every fire share four voices"
271
+ ]
180
272
  }
181
273
  };
182
274
 
@@ -446,69 +538,200 @@ var GameAudio = (() => {
446
538
  return { ...base, silent: false, length: Math.max(0, ...layers.map(layerEnd)), layers };
447
539
  }
448
540
 
449
- // src/loops.ts
450
- var LOOP_START_ABOVE = 2e-3;
451
- var LOOP_SMOOTHING = { volume: 0.15, frequency: 0.08 };
452
- function createLoopState(recipe2, open = false) {
453
- const startAbove = recipe2.startAbove ?? LOOP_START_ABOVE;
454
- let created = open;
455
- let volume = recipe2.volume ?? 0;
541
+ // src/sample.ts
542
+ var SAMPLE_ENVIRONMENTS = ["indoor", "outdoor"];
543
+ var DEFAULT_SAMPLE_FALLOFF = { model: "inverse_square", k: 2e-3, minGain: 2e-3 };
544
+ var DEFAULT_CROSSOVER = { from: 30, to: 80 };
545
+ var DEFAULT_SAMPLE_PITCH = 0.04;
546
+ var DEFAULT_TAIL_GAIN = 0.6;
547
+ var DEFAULT_SAMPLE_CUTOFF_K = 0.01;
548
+ var OPEN_CUTOFF = 2e4;
549
+ var LAYER_FLOOR = 1e-3;
550
+ function pickTake(count, last, random) {
551
+ if (count <= 1) return 0;
552
+ if (last === void 0 || last < 0 || last >= count) return Math.min(count - 1, Math.floor(random() * count));
553
+ const drawn = Math.min(count - 2, Math.floor(random() * (count - 1)));
554
+ return drawn >= last ? drawn + 1 : drawn;
555
+ }
556
+ function farMixAt(crossover, distance) {
557
+ if (crossover.to <= crossover.from) return distance >= crossover.to ? 1 : 0;
558
+ const t = (distance - crossover.from) / (crossover.to - crossover.from);
559
+ return t <= 0 ? 0 : t >= 1 ? 1 : t;
560
+ }
561
+ function crossGains(farMix) {
562
+ return { close: Math.cos(farMix * Math.PI / 2), far: Math.sin(farMix * Math.PI / 2) };
563
+ }
564
+ function sampleCutoff(cutoffK, distance, occlusion = 0) {
565
+ const byDistance = cutoffK > 0 ? OPEN_CUTOFF / (1 + distance * cutoffK) : OPEN_CUTOFF;
566
+ const occluded = occlusion > 0 ? byDistance / (1 + occlusion * 9) : byDistance;
567
+ return Math.max(80, Math.min(OPEN_CUTOFF, occluded));
568
+ }
569
+ function sampleUrls(sound) {
570
+ const tiers = (sound.tiers ?? []).flatMap((tier) => [...tier.close, ...tier.far ?? []]);
571
+ const all = [...sound.close, ...sound.far ?? [], ...tiers, ...sound.tail?.indoor ?? [], ...sound.tail?.outdoor ?? []].map((ref) => ref.url);
572
+ return [...new Set(all)];
573
+ }
574
+ function tierFor(sound, intensity) {
575
+ if (intensity === void 0 || !sound.tiers) return void 0;
576
+ let chosen;
577
+ for (const tier of sound.tiers) if (tier.from <= intensity) chosen = tier;
578
+ return chosen ?? sound.tiers[0];
579
+ }
580
+ function planSample(sound, input) {
581
+ if (sound.close.length === 0) throw new Error(`sample sound ${sound.id} has no close take`);
582
+ const listener = input.listener ?? DEFAULT_LISTENER;
583
+ const distance = input.distance ?? (input.position ? listenerDistance(listener, input.position, input.distanceMode) : 0);
584
+ const falloff = sound.falloff ?? DEFAULT_SAMPLE_FALLOFF;
585
+ const occlusion = Math.max(0, Math.min(1, input.occlusion ?? 0));
586
+ const level = falloffGain(falloff, (input.volume ?? 1) * (sound.gain ?? 1), distance) * (1 - 0.45 * occlusion);
587
+ if (isSilent(falloff, level) || level <= 0) return null;
588
+ const pitch = sound.pitch ?? DEFAULT_SAMPLE_PITCH;
589
+ const rate = pitch > 0 ? 1 + (input.random() * 2 - 1) * pitch : 1;
590
+ const environment = input.environment ?? "outdoor";
591
+ const tier = tierFor(sound, input.intensity);
592
+ const close = tier?.close.length ? tier.close : sound.close;
593
+ const far = tier?.far?.length ? tier.far : sound.far ?? [];
594
+ const farMix = far.length > 0 ? farMixAt(sound.crossover ?? DEFAULT_CROSSOVER, distance) : 0;
595
+ const gains = crossGains(farMix);
596
+ const layers = [];
597
+ const take = (role, refs, gain, at) => {
598
+ if (refs.length === 0 || gain < LAYER_FLOOR) return;
599
+ const key = `${sound.id}/${tier && role !== "tail" ? `${tier.name}/` : ""}${role}`;
600
+ const index = pickTake(refs.length, input.memory?.get(key), input.random);
601
+ input.memory?.set(key, index);
602
+ const ref = refs[index];
603
+ layers.push({ role, url: ref.url, take: index, gain: gain * (ref.gain ?? 1) * level, rate, at });
604
+ };
605
+ take("close", close, gains.close, 0);
606
+ take("far", far, gains.far, 0);
607
+ const tails = sound.tail?.[environment] ?? [];
608
+ take("tail", tails, sound.tail?.gain ?? DEFAULT_TAIL_GAIN, sound.tail?.at ?? 0);
609
+ const pan = (sound.pan ?? true) && input.position ? panFor(listener, input.position) : void 0;
456
610
  return {
457
- set(state) {
458
- volume = state.volume ?? volume;
459
- const create = !created;
460
- if (create && !(volume >= startAbove)) return null;
461
- created = true;
462
- const frequency = recipe2.source.kind === "osc" && state.frequency ? state.frequency : void 0;
463
- return { create, volume, ...frequency === void 0 ? {} : { frequency } };
464
- },
465
- get created() {
466
- return created;
467
- },
468
- reset() {
469
- created = false;
470
- }
611
+ kind: "sample",
612
+ id: sound.id,
613
+ bus: sound.bus,
614
+ level,
615
+ distance,
616
+ ...pan === void 0 ? {} : { pan },
617
+ cutoff: sampleCutoff(sound.cutoffK ?? DEFAULT_SAMPLE_CUTOFF_K, distance, occlusion),
618
+ rate,
619
+ environment,
620
+ ...tier ? { tier: tier.name } : {},
621
+ farMix,
622
+ layers,
623
+ priority: sound.priority ?? input.priority ?? busPriority(sound.bus),
624
+ ...sound.group ? { group: sound.group } : {}
471
625
  };
472
626
  }
473
627
 
474
- // src/data/mix-profiles.json
475
- var mix_profiles_default = [
476
- {
477
- id: "game",
478
- master: 0.5,
479
- noise: { seconds: 1.5, create: "lazy" },
480
- sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
481
- },
482
- {
483
- id: "art_scene",
484
- master: 0.85,
485
- compressor: { threshold: -12, ratio: 5 },
486
- reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
487
- noise: { seconds: 2, create: "eager" },
488
- sources: [
489
- "cena:L1308 compressor -12 dB, ratio 5",
490
- "cena:L1309 master 0.85",
491
- "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
492
- "cena:L1311 wet 0.4",
493
- "cena:L1312 noise 2 s made with the chain"
494
- ]
628
+ // src/measure/calibration.ts
629
+ var CALIBRATION_ID = "calibration_shot";
630
+ var CALIBRATION_RATE = 48e3;
631
+ function encodeWav(samples, sampleRate = CALIBRATION_RATE) {
632
+ const bytes = new ArrayBuffer(44 + samples.length * 2);
633
+ const view = new DataView(bytes);
634
+ const text = (at, value) => {
635
+ for (let index = 0; index < value.length; index++) view.setUint8(at + index, value.charCodeAt(index));
636
+ };
637
+ text(0, "RIFF");
638
+ view.setUint32(4, 36 + samples.length * 2, true);
639
+ text(8, "WAVE");
640
+ text(12, "fmt ");
641
+ view.setUint32(16, 16, true);
642
+ view.setUint16(20, 1, true);
643
+ view.setUint16(22, 1, true);
644
+ view.setUint32(24, sampleRate, true);
645
+ view.setUint32(28, sampleRate * 2, true);
646
+ view.setUint16(32, 2, true);
647
+ view.setUint16(34, 16, true);
648
+ text(36, "data");
649
+ view.setUint32(40, samples.length * 2, true);
650
+ for (let index = 0; index < samples.length; index++) {
651
+ const value = Math.max(-1, Math.min(1, samples[index]));
652
+ view.setInt16(44 + index * 2, value < 0 ? value * 32768 : value * 32767, true);
495
653
  }
496
- ];
497
-
498
- // src/mix.ts
499
- var MIX_PROFILES = Object.fromEntries(
500
- mix_profiles_default.map((profile) => [profile.id, profile])
501
- );
502
- var DEFAULT_MIX = "art_scene";
503
- function resolveMix(mix, profiles = MIX_PROFILES) {
504
- if (typeof mix !== "string") return mix;
505
- const profile = profiles[mix];
506
- if (!profile) throw new Error(`game-audio: unknown mix profile "${mix}" (known: ${Object.keys(profiles).join(", ")})`);
507
- return profile;
654
+ return bytes;
508
655
  }
509
- function busGain(setting) {
510
- if (!setting) return 1;
511
- return setting.muted ? 0 : setting.gain ?? 1;
656
+ function shotSamples(shot, sampleRate = CALIBRATION_RATE) {
657
+ const random = createRng(shot.seed);
658
+ const length = Math.round(shot.seconds * sampleRate);
659
+ const samples = new Float32Array(length);
660
+ let low = 0;
661
+ let phase = 0;
662
+ const attack = Math.max(1, Math.round((shot.attack ?? 0) * sampleRate));
663
+ for (let index = 0; index < length; index++) {
664
+ const time = index / sampleRate;
665
+ const envelope2 = Math.exp(-time / shot.decay) * Math.min(1, (index + 1) / attack);
666
+ phase += 2 * Math.PI * shot.thump * (1 + 0.6 * Math.exp(-time * 30)) / sampleRate;
667
+ const noise = random() * 2 - 1;
668
+ low += shot.brightness * (noise - low);
669
+ const fade = Math.min(1, (length - index) / (0.01 * sampleRate));
670
+ samples[index] = shot.gain * envelope2 * fade * (0.55 * Math.sin(phase) + shot.noise * low);
671
+ }
672
+ return samples;
673
+ }
674
+ var TAKES = {
675
+ "close_1.wav": { seconds: 0.4, thump: 150, noise: 1, decay: 0.06, brightness: 0.9, gain: 0.85, seed: 1 },
676
+ "close_2.wav": { seconds: 0.4, thump: 160, noise: 1, decay: 0.065, brightness: 0.85, gain: 0.85, seed: 2 },
677
+ "close_3.wav": { seconds: 0.4, thump: 142, noise: 1, decay: 0.055, brightness: 0.95, gain: 0.85, seed: 3 },
678
+ "close_4.wav": { seconds: 0.4, thump: 170, noise: 1, decay: 0.07, brightness: 0.8, gain: 0.85, seed: 4 },
679
+ "far_1.wav": { seconds: 0.9, thump: 90, noise: 0.8, decay: 0.2, brightness: 0.08, gain: 0.7, seed: 5, attack: 4e-3 },
680
+ "far_2.wav": { seconds: 0.9, thump: 95, noise: 0.8, decay: 0.22, brightness: 0.07, gain: 0.7, seed: 6, attack: 4e-3 },
681
+ "light_1.wav": { seconds: 0.25, thump: 220, noise: 0.6, decay: 0.03, brightness: 0.6, gain: 0.35, seed: 7 },
682
+ "light_2.wav": { seconds: 0.25, thump: 230, noise: 0.6, decay: 0.03, brightness: 0.55, gain: 0.35, seed: 8 },
683
+ "heavy_1.wav": { seconds: 0.6, thump: 110, noise: 1.2, decay: 0.11, brightness: 0.95, gain: 0.95, seed: 9 },
684
+ "heavy_2.wav": { seconds: 0.6, thump: 105, noise: 1.2, decay: 0.12, brightness: 0.9, gain: 0.95, seed: 10 },
685
+ "tail_in.wav": { seconds: 0.5, thump: 0, noise: 1, decay: 0.08, brightness: 0.4, gain: 0.25, seed: 11, attack: 0.01 },
686
+ "tail_out.wav": { seconds: 2, thump: 0, noise: 1, decay: 0.45, brightness: 0.25, gain: 0.2, seed: 12, attack: 0.02 },
687
+ "loop_1.wav": { seconds: 0.6, thump: 0, noise: 1, decay: 100, brightness: 0.5, gain: 0.5, seed: 13, attack: 0.01 },
688
+ "loop_2.wav": { seconds: 0.6, thump: 0, noise: 1, decay: 100, brightness: 0.45, gain: 0.5, seed: 14, attack: 0.01 },
689
+ "loop_3.wav": { seconds: 0.6, thump: 0, noise: 1, decay: 100, brightness: 0.55, gain: 0.5, seed: 15, attack: 0.01 }
690
+ };
691
+ var PREFIX = "calibration/";
692
+ var url = (name) => `${PREFIX}${name}`;
693
+ var CALIBRATION_SOUND = {
694
+ id: CALIBRATION_ID,
695
+ bus: "weapons",
696
+ close: ["close_1.wav", "close_2.wav", "close_3.wav", "close_4.wav"].map((name) => ({ url: url(name) })),
697
+ far: ["far_1.wav", "far_2.wav"].map((name) => ({ url: url(name) })),
698
+ tiers: [
699
+ { name: "light", from: 0, close: ["light_1.wav", "light_2.wav"].map((name) => ({ url: url(name) })) },
700
+ { name: "medium", from: 0.34, close: ["close_1.wav", "close_2.wav", "close_3.wav", "close_4.wav"].map((name) => ({ url: url(name) })) },
701
+ { name: "heavy", from: 0.67, close: ["heavy_1.wav", "heavy_2.wav"].map((name) => ({ url: url(name) })) }
702
+ ],
703
+ tail: { indoor: [{ url: url("tail_in.wav") }], outdoor: [{ url: url("tail_out.wav") }] },
704
+ pitch: 0.04,
705
+ choke: { voices: 2 },
706
+ fallback: "sfx_pistol",
707
+ sources: ["sdk: calibration takes synthesized by the harness (measure/calibration.ts), no file, no licence"]
708
+ };
709
+ var CALIBRATION_LOOP_ID = "calibration_loop";
710
+ var CALIBRATION_LOOP_SOUND = {
711
+ id: CALIBRATION_LOOP_ID,
712
+ bus: "ambience",
713
+ close: ["loop_1.wav", "loop_2.wav", "loop_3.wav"].map((name) => ({ url: url(name) })),
714
+ loop: { crossfade: 0.15 },
715
+ sources: ["sdk: calibration takes synthesized by the harness (measure/calibration.ts), no file, no licence"]
716
+ };
717
+ var isCalibrationUrl = (url2) => url2.startsWith(PREFIX);
718
+ function calibrationLoader(next) {
719
+ const made = /* @__PURE__ */ new Map();
720
+ return async (requested) => {
721
+ if (!requested.startsWith(PREFIX)) {
722
+ if (next) return next(requested);
723
+ throw new Error(`not a calibration file: ${requested}`);
724
+ }
725
+ const name = requested.slice(PREFIX.length);
726
+ const shot = TAKES[name];
727
+ if (!shot) throw new Error(`no calibration take ${name}`);
728
+ let bytes = made.get(name);
729
+ if (!bytes) {
730
+ bytes = encodeWav(shotSamples(shot));
731
+ made.set(name, bytes);
732
+ }
733
+ return bytes.slice(0);
734
+ };
512
735
  }
513
736
 
514
737
  // src/bank.ts
@@ -528,6 +751,7 @@ var GameAudio = (() => {
528
751
  const sounds = /* @__PURE__ */ new Map();
529
752
  const loops = /* @__PURE__ */ new Map();
530
753
  const sets = /* @__PURE__ */ new Map();
754
+ const samples = /* @__PURE__ */ new Map();
531
755
  const owners = /* @__PURE__ */ new Map();
532
756
  const registry = {
533
757
  register(bank) {
@@ -544,293 +768,655 @@ var GameAudio = (() => {
544
768
  sets.set(set.id, set);
545
769
  owners.set(set.id, bank.id);
546
770
  }
771
+ for (const sample of bank.samples ?? []) {
772
+ samples.set(sample.id, sample);
773
+ owners.set(sample.id, bank.id);
774
+ }
547
775
  },
548
776
  patch(id, patch) {
549
777
  const sound = sounds.get(id);
550
778
  if (sound) sounds.set(id, mergeRecords(sound, patch));
551
779
  const loop = loops.get(id);
552
780
  if (loop) loops.set(id, mergeRecords(loop, patch));
781
+ const sample = samples.get(id);
782
+ if (sample) samples.set(id, mergeRecords(sample, patch));
553
783
  },
554
784
  remove(id) {
555
785
  sounds.delete(id);
556
786
  loops.delete(id);
557
787
  sets.delete(id);
788
+ samples.delete(id);
558
789
  owners.delete(id);
559
790
  },
560
791
  sound: (id) => sounds.get(id),
561
792
  loop: (id) => loops.get(id),
562
793
  set: (id) => sets.get(id),
794
+ sample: (id) => samples.get(id),
563
795
  soundIds: () => [...sounds.keys()],
564
796
  setIds: () => [...sets.keys()],
565
797
  loopIds: () => [...loops.keys()],
798
+ sampleIds: () => [...samples.keys()],
566
799
  bankOf: (id) => owners.get(id)
567
800
  };
568
801
  for (const bank of banks) registry.register(bank);
569
802
  return registry;
570
803
  }
571
804
 
572
- // src/data/game-sfx.json
573
- var game_sfx_default = {
574
- sources: ["jogo:L62089-62107"],
575
- note: "The game's sfx table P: [lowpass cutoff Hz, peak gain, seconds]. Played as a noise burst with the game's distance curve (falloff game_sfx) and a 0.05 s tail. Team names in `team`.",
576
- classes: [
577
- { id: "pistol", team: "pistola", bus: "weapons", cutoff: 1800, gain: 0.9, seconds: 0.18 },
578
- { id: "shotgun", team: "escopeta", bus: "weapons", cutoff: 900, gain: 1.2, seconds: 0.35 },
579
- { id: "rifle", team: "fuzil", bus: "weapons", cutoff: 1400, gain: 1.1, seconds: 0.28 },
580
- { id: "long_rifle", team: "rifle", bus: "weapons", cutoff: 1100, gain: 1.3, seconds: 0.45 },
581
- { id: "impact", team: "impacto", bus: "world", cutoff: 2500, gain: 0.25, seconds: 0.05 },
582
- { id: "flesh", team: "carne", bus: "world", cutoff: 600, gain: 0.35, seconds: 0.08 },
583
- { id: "step", team: "passo", bus: "world", cutoff: 400, gain: 0.08, seconds: 0.05 },
584
- { id: "glass", team: "vidro", bus: "world", cutoff: 5e3, gain: 0.5, seconds: 0.3 },
585
- { id: "wood", team: "madeira", bus: "world", cutoff: 700, gain: 0.5, seconds: 0.2 },
586
- { id: "reload", team: "recarga", bus: "weapons", cutoff: 3e3, gain: 0.15, seconds: 0.06 },
587
- { id: "empty", team: "vazio", bus: "weapons", cutoff: 4e3, gain: 0.12, seconds: 0.03 },
588
- { id: "explosion", team: "explosao", bus: "world", cutoff: 200, gain: 1.6, seconds: 1.4 }
589
- ],
590
- fallback: { id: "unknown", team: "(any other name)", bus: "world", cutoff: 1e3, gain: 0.5, seconds: 0.1 }
591
- };
592
-
593
- // src/recipes/noise-class.ts
594
- var GAME_SFX = game_sfx_default;
595
- var GAME_SFX_CLASSES = GAME_SFX.classes;
596
- var GAME_SFX_FALLBACK = GAME_SFX.fallback;
597
- function noiseClassRecipe(row, id = `sfx_${row.id}`) {
598
- return {
599
- id,
600
- bus: row.bus,
601
- order: "burst",
602
- duration: row.seconds,
603
- tail: 0.05,
604
- layers: [
605
- {
606
- source: { kind: "noise", offset: "random" },
607
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: row.cutoff } }] },
608
- envelope: [
609
- [0, row.gain, "set"],
610
- ["1d", 1e-3, "exp", "fixed"]
611
- ]
612
- }
805
+ // src/data/mix-profiles.json
806
+ var mix_profiles_default = [
807
+ {
808
+ id: "game",
809
+ master: 0.5,
810
+ noise: {
811
+ seconds: 1.5,
812
+ create: "lazy"
813
+ },
814
+ sources: [
815
+ "jogo:L62077-62081 master 0.5 to the output",
816
+ "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"
817
+ ]
818
+ },
819
+ {
820
+ id: "art_scene",
821
+ master: 0.85,
822
+ compressor: {
823
+ threshold: -12,
824
+ ratio: 5
825
+ },
826
+ reverb: {
827
+ seconds: 2.2,
828
+ decay: 3.4,
829
+ wet: 0.4
830
+ },
831
+ noise: {
832
+ seconds: 2,
833
+ create: "eager"
834
+ },
835
+ sources: [
836
+ "cena:L1308 compressor -12 dB, ratio 5",
837
+ "cena:L1309 master 0.85",
838
+ "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
839
+ "cena:L1311 wet 0.4",
840
+ "cena:L1312 noise 2 s made with the chain"
841
+ ]
842
+ },
843
+ {
844
+ id: "kit",
845
+ master: 0.85,
846
+ compressor: {
847
+ threshold: -12,
848
+ ratio: 5
849
+ },
850
+ reverb: {
851
+ seconds: 2.2,
852
+ decay: 3.4,
853
+ wet: 0.4
854
+ },
855
+ noise: {
856
+ seconds: 2,
857
+ create: "eager"
858
+ },
859
+ sources: [
860
+ "sdk: the art scene chain (cena L1308-L1312) with a limiter after the compressor: the page has none, and a blast with its debris and a fire catching reached +7 dBFS"
613
861
  ],
614
- falloff: DISTANCE_CURVES.game_sfx,
615
- sources: [...GAME_SFX.sources.map((source) => `${source} ${row.team}`)]
616
- };
862
+ limiter: {
863
+ ceiling: -1,
864
+ knee: -6
865
+ }
866
+ }
867
+ ];
868
+
869
+ // src/mix.ts
870
+ var MIX_PROFILES = Object.fromEntries(
871
+ mix_profiles_default.map((profile) => [profile.id, profile])
872
+ );
873
+ var DEFAULT_MIX = "kit";
874
+ function resolveMix(mix, profiles = MIX_PROFILES) {
875
+ if (typeof mix !== "string") return mix;
876
+ const profile = profiles[mix];
877
+ if (!profile) throw new Error(`game-audio: unknown mix profile "${mix}" (known: ${Object.keys(profiles).join(", ")})`);
878
+ return profile;
879
+ }
880
+ function busGain(setting) {
881
+ if (!setting) return 1;
882
+ return setting.muted ? 0 : setting.gain ?? 1;
617
883
  }
618
- var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
619
884
 
620
- // src/recipes/scene.ts
621
- var ATTACK = 2e-3;
622
- function lengthOf(d, attack) {
623
- if (typeof d === "number") return attack + d;
624
- if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
625
- return { ...d, add: (d.add ?? 0) + attack };
885
+ // src/webaudio/noise.ts
886
+ function fillNoise(random, length) {
887
+ const samples = new Float32Array(length);
888
+ for (let index = 0; index < length; index++) samples[index] = random() * 2 - 1;
889
+ return samples;
626
890
  }
627
- var envelope = (attack) => [
628
- [0, 1e-4, "set", "fixed"],
629
- [attack, 1, "exp"],
630
- ["1d", 1e-4, "exp", "fixed"]
631
- ];
632
- function sceneNoise(o) {
633
- const a = o.a ?? ATTACK;
634
- const over = typeof o.d === "number" ? o.d : 0;
635
- return {
636
- filters: {
637
- mode: "serial",
638
- bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
639
- },
640
- duration: lengthOf(o.d, a),
641
- gain: o.vol,
642
- ...o.delay === void 0 ? {} : { at: o.delay },
643
- ...o.wet === void 0 ? {} : { wet: o.wet },
644
- source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
645
- envelope: envelope(a)
646
- };
891
+ function fillImpulse(random, options) {
892
+ const size = Math.floor(options.length);
893
+ return Array.from({ length: options.channels }, () => {
894
+ const samples = new Float32Array(size);
895
+ for (let index = 0; index < options.length; index++) {
896
+ const value = (random() * 2 - 1) * Math.exp(options.decay * Math.log(1 - index / options.length));
897
+ if (index < size) samples[index] = value;
898
+ }
899
+ return samples;
900
+ });
647
901
  }
648
- function sceneTone(o) {
649
- return {
650
- source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
651
- duration: lengthOf(o.d, ATTACK),
652
- gain: o.vol,
653
- ...o.delay === void 0 ? {} : { at: o.delay },
654
- ...o.wet === void 0 ? {} : { wet: o.wet },
655
- envelope: envelope(ATTACK)
656
- };
902
+ function bufferOf(context, channels, requestedLength) {
903
+ const buffer = context.createBuffer(channels.length, requestedLength, context.sampleRate);
904
+ channels.forEach((samples, channel) => buffer.getChannelData(channel).set(samples));
905
+ return buffer;
657
906
  }
658
- function sceneVoice(o) {
659
- return {
660
- duration: lengthOf(o.d, 0),
661
- gain: o.vol,
662
- wet: 0.45,
663
- source: {
664
- kind: "formant_voice",
665
- wave: o.wave ?? "sawtooth",
666
- frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
667
- vibrato: { rate: [4, 10], depth: 0.08 },
668
- formants: [
669
- { frequency: o.fA, q: 4 },
670
- { frequency: o.fB, q: 6 }
671
- ],
672
- breath: { frequency: o.fB, q: 1.1, gain: o.noise },
673
- mix: 3.2
674
- },
675
- envelope: [
676
- [0, 1e-4, "set", "fixed"],
677
- [0.1, 1, "exp"],
678
- ["0.6d", 1, "set"],
679
- ["1d", 1e-4, "exp", "fixed"]
680
- ]
681
- };
907
+
908
+ // src/webaudio/chain.ts
909
+ var LIMITER_POINTS = 4097;
910
+ function limiterCurve(ceilingDb, kneeDb, range, points = LIMITER_POINTS) {
911
+ const ceiling = 10 ** (ceilingDb / 20);
912
+ const knee = Math.min(10 ** (kneeDb / 20), ceiling);
913
+ const curve = new Float32Array(new ArrayBuffer(points * 4));
914
+ for (let i = 0; i < points; i++) {
915
+ const x = (i / (points - 1) * 2 - 1) * range;
916
+ const a = Math.abs(x);
917
+ const y = a <= knee || ceiling === knee ? Math.min(a, ceiling) : knee + (ceiling - knee) * Math.tanh((a - knee) / (ceiling - knee));
918
+ curve[i] = Math.sign(x) * y;
919
+ }
920
+ return curve;
682
921
  }
683
- var sceneMany = (count, make) => Array.from({ length: count }, make);
684
- function sceneRecipe(id, layers, line, options = {}) {
922
+ function buildChain(context, profile, random) {
923
+ let limiter;
924
+ if (profile.limiter) {
925
+ const range = profile.limiter.range ?? 4;
926
+ const input = context.createGain();
927
+ input.gain.value = 1 / range;
928
+ const shaper = context.createWaveShaper();
929
+ shaper.curve = limiterCurve(profile.limiter.ceiling, profile.limiter.knee, range);
930
+ input.connect(shaper);
931
+ shaper.connect(context.destination);
932
+ limiter = { input, shaper };
933
+ }
934
+ const output = limiter?.input ?? context.destination;
935
+ let compressor;
936
+ if (profile.compressor) {
937
+ compressor = context.createDynamicsCompressor();
938
+ compressor.threshold.value = profile.compressor.threshold;
939
+ compressor.ratio.value = profile.compressor.ratio;
940
+ if (profile.compressor.knee !== void 0) compressor.knee.value = profile.compressor.knee;
941
+ if (profile.compressor.attack !== void 0) compressor.attack.value = profile.compressor.attack;
942
+ if (profile.compressor.release !== void 0) compressor.release.value = profile.compressor.release;
943
+ compressor.connect(output);
944
+ }
945
+ const master = context.createGain();
946
+ master.gain.value = profile.master;
947
+ master.connect(compressor ?? output);
948
+ if (!profile.reverb) {
949
+ return {
950
+ master,
951
+ disconnect: () => {
952
+ master.disconnect();
953
+ compressor?.disconnect();
954
+ limiter?.input.disconnect();
955
+ limiter?.shaper.disconnect();
956
+ }
957
+ };
958
+ }
959
+ const convolver = context.createConvolver();
960
+ const length = context.sampleRate * profile.reverb.seconds;
961
+ convolver.buffer = bufferOf(context, fillImpulse(random, { channels: 2, length, decay: profile.reverb.decay }), length);
962
+ const wet = context.createGain();
963
+ wet.gain.value = profile.reverb.wet;
964
+ wet.connect(convolver);
965
+ convolver.connect(master);
685
966
  return {
686
- id,
687
- bus: options.bus ?? "world",
688
- pan: true,
689
- layers,
690
- falloff: DISTANCE_CURVES.art_scene,
691
- ...options.vars ? { vars: options.vars } : {},
692
- ...options.group ? { group: options.group } : {},
693
- sources: [`cena:${line}`]
967
+ master,
968
+ wet,
969
+ disconnect: () => {
970
+ wet.disconnect();
971
+ convolver.disconnect();
972
+ master.disconnect();
973
+ compressor?.disconnect();
974
+ limiter?.input.disconnect();
975
+ limiter?.shaper.disconnect();
976
+ }
694
977
  };
695
978
  }
696
979
 
697
- // src/library/crowd_rumble.json
698
- var crowd_rumble_default = {
699
- id: "crowd_rumble",
700
- bus: "ambience",
701
- order: "game_loop",
702
- source: { kind: "noise", loop: true },
703
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 160 } }] },
704
- smoothing: { volume: 0.15, frequency: 0.08 },
705
- startAbove: 2e-3,
706
- sources: ["jogo:L62116 horda: noise through a 160 Hz lowpass", "jogo:L70424 level min(0.16, near \xD7 0.0016)", "temporary home until the crowd package (Q12)"]
707
- };
708
-
709
- // src/library/engine_saw.json
710
- var engine_saw_default = {
711
- id: "engine_saw",
712
- bus: "world",
713
- order: "game_loop",
714
- source: { kind: "osc", wave: "sawtooth", loop: true },
715
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 400 } }] },
716
- smoothing: { volume: 0.15, frequency: 0.08 },
717
- startAbove: 2e-3,
718
- sources: [
719
- "jogo:L62115 motor: sawtooth through a 400 Hz lowpass",
720
- "jogo:L70421 level 0.035 + speed \xD7 0.003, frequency 38 + speed \xD7 7 (the host drives `set`)",
721
- "temporary home until the vehicles package (Q12)"
722
- ]
723
- };
724
-
725
- // src/library/horn.json
726
- var horn_default = {
727
- id: "horn",
728
- bus: "world",
729
- order: "chord",
730
- duration: 0.6,
731
- tail: 0.02,
732
- layers: [
733
- { source: { kind: "osc", wave: "square", frequency: { set: 415 } }, envelope: [[0, 0.08, "set"], [0.6, 1e-4, "set", "fixed"]] },
734
- { source: { kind: "osc", wave: "square", frequency: { set: 523 } }, envelope: [[0, 0.08, "set"], [0.6, 1e-4, "set", "fixed"]] }
735
- ],
736
- falloff: { model: "none" },
737
- sources: ["jogo:L70272-70276 buzina: squares at 415 and 523 Hz, 0.08 for 0.6 s", "temporary home until the vehicles package (Q12)"]
738
- };
980
+ // src/webaudio/context.ts
981
+ function globalContext() {
982
+ const scope = globalThis;
983
+ const Constructor = scope.AudioContext ?? scope.webkitAudioContext;
984
+ return Constructor ? new Constructor() : null;
985
+ }
986
+ function unlockContext(context) {
987
+ if (context.state === "suspended" && typeof context.close === "function") void context.resume();
988
+ }
739
989
 
740
- // src/library/radio_static.json
741
- var radio_static_default = {
742
- id: "radio_static",
743
- bus: "world",
744
- order: "static",
745
- duration: 0.6,
746
- layers: [
747
- {
748
- source: { kind: "noise", offset: "random" },
749
- filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 1800 }, q: 0.8 }] },
750
- envelope: [[0, 0.06, "set"], ["1d", 1e-3, "exp", "fixed"]]
990
+ // src/loops.ts
991
+ var LOOP_START_ABOVE = 2e-3;
992
+ var LOOP_SMOOTHING = { volume: 0.15, frequency: 0.08 };
993
+ function createLoopState(recipe2, open = false) {
994
+ const startAbove = recipe2.startAbove ?? LOOP_START_ABOVE;
995
+ let created = open;
996
+ let volume = recipe2.volume ?? 0;
997
+ return {
998
+ set(state) {
999
+ volume = state.volume ?? volume;
1000
+ const create = !created;
1001
+ if (create && !(volume >= startAbove)) return null;
1002
+ created = true;
1003
+ const frequency = recipe2.source.kind === "osc" && state.frequency ? state.frequency : void 0;
1004
+ return { create, volume, ...frequency === void 0 ? {} : { frequency } };
1005
+ },
1006
+ get created() {
1007
+ return created;
1008
+ },
1009
+ reset() {
1010
+ created = false;
751
1011
  }
752
- ],
753
- falloff: { model: "none" },
754
- sources: ["jogo:L69998-70004 chiado: noise through a 1800 Hz bandpass (Q 0.8), 0.06 falling over 0.6 s", "temporary home until the props or radio package (Q12)"]
755
- };
756
-
757
- // src/library/rotor_blades.json
758
- var rotor_blades_default = {
759
- id: "rotor_blades",
760
- bus: "world",
761
- order: "game_loop",
762
- source: { kind: "noise", loop: true },
763
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 300 } }] },
764
- modulators: [{ target: "gain", rate: 11, depth: 0.5, base: 0.5 }],
765
- smoothing: { volume: 0.15, frequency: 0.08 },
766
- startAbove: 2e-3,
767
- sources: [
768
- "jogo:L62116-62120 rotor: noise through a 300 Hz lowpass, blade beat as an 11 Hz LFO of 0.5 on a 0.5 gain stage",
769
- "jogo:L70426 level min(0.5, 30 / (distance + 10))",
770
- "temporary home until the vehicles package (Q12)"
771
- ]
772
- };
1012
+ };
1013
+ }
773
1014
 
774
- // src/library/siren.json
775
- var siren_default = {
776
- id: "siren",
777
- bus: "world",
778
- order: "siren",
779
- duration: 9,
780
- tail: 0.2,
781
- layers: [
782
- {
783
- wet: 1.5,
784
- source: { kind: "osc", wave: "sine", frequency: { set: 850 } },
785
- modulators: [{ target: "frequency", rate: 0.18, depth: 280 }],
786
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 1400 } }] },
787
- envelope: [[0, 1e-4, "set", "fixed"], [2, 0.035, "exp"], [6, 0.035, "set"], [9, 1e-4, "exp", "fixed"]]
1015
+ // src/webaudio/nodes.ts
1016
+ function assign(param, automation, time) {
1017
+ if (automation.value !== void 0) param.value = automation.value;
1018
+ if (automation.from !== void 0) param.setValueAtTime(automation.from, time);
1019
+ }
1020
+ function ramp(param, curve, value, at) {
1021
+ if (curve === "set") param.setValueAtTime(value, at);
1022
+ else if (curve === "exp") param.exponentialRampToValueAtTime(value, at);
1023
+ else param.linearRampToValueAtTime(value, at);
1024
+ }
1025
+ function rampTo(param, automation, time) {
1026
+ if (automation.to !== void 0 && automation.at !== void 0) ramp(param, automation.curve ?? "linear", automation.to, time + automation.at);
1027
+ }
1028
+ function applyEnvelope(param, points, time) {
1029
+ for (const point of points) ramp(param, point.curve, point.value, time + point.time);
1030
+ }
1031
+ function noiseSource(target, loop) {
1032
+ const source = target.context.createBufferSource();
1033
+ source.buffer = target.noise();
1034
+ if (loop) source.loop = true;
1035
+ return source;
1036
+ }
1037
+ function filterNode(context, filter, time) {
1038
+ const node = context.createBiquadFilter();
1039
+ node.type = filter.type;
1040
+ assign(node.frequency, filter.frequency, time);
1041
+ if (filter.q !== void 0) node.Q.value = filter.q;
1042
+ if (filter.gain !== void 0) node.gain.value = filter.gain;
1043
+ return node;
1044
+ }
1045
+ function gainNode(context, value) {
1046
+ const node = context.createGain();
1047
+ node.gain.value = value;
1048
+ return node;
1049
+ }
1050
+ function voiceOf(sources) {
1051
+ return {
1052
+ sources,
1053
+ stop() {
1054
+ for (const source of sources) {
1055
+ try {
1056
+ source.stop();
1057
+ } catch {
1058
+ }
1059
+ }
788
1060
  }
789
- ],
790
- falloff: { model: "none" },
791
- sources: ["cena:L1369 siren: 850 Hz sine swept \xB1280 Hz at 0.18 Hz, 1400 Hz lowpass, 0.035 for 9 s, reverb send 1.5", "temporary home until the vehicles package (Q12)"]
792
- };
793
-
794
- // src/library/index.ts
795
- var LIBRARY_SOUNDS = [horn_default, radio_static_default, siren_default];
796
- var LIBRARY_LOOPS = [engine_saw_default, rotor_blades_default, crowd_rumble_default];
797
- var LIBRARY_BANK = { id: "library", sounds: LIBRARY_SOUNDS, loops: LIBRARY_LOOPS };
1061
+ };
1062
+ }
798
1063
 
799
- // ../game-contracts/dist/index.js
800
- var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
801
- var DEFERRED_QUEUE_ID = {
802
- DESTRUCTION_EVENTS: "destruction.events",
803
- BREACH_PAINT: "floorplan.breach-paint",
804
- AUDIO_VOICES: "audio.voices",
805
- VEHICLE_CREASE: "vehicles.crease"
806
- };
807
- var DEFERRED_PRIORITY = {
808
- [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
809
- [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
810
- [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
811
- [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
812
- };
813
- var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
814
- var GAIT_IDS = ["walk", "run"];
815
- var LANDING_IDS = ["light", "heavy"];
816
- var BODY_SOUND_IDS = [
817
- "jump",
818
- "cloth_rustle",
819
- "body_fall",
820
- "fall_damage",
821
- "player_hurt",
822
- "player_breath_run",
823
- "player_death",
824
- "bite",
825
- "grab",
826
- "punch_swing",
827
- "punch_hit_body",
828
- "punch_hit_face",
829
- "punch_hit_object",
830
- "kick_swing",
831
- "kick_hit",
832
- "kick_hit_object",
833
- "shove_two_hands",
1064
+ // src/webaudio/realize-loop.ts
1065
+ function outputStage(recipe2, target, gain) {
1066
+ if (!recipe2.pan) {
1067
+ gain.connect(target.output);
1068
+ return { out: gain };
1069
+ }
1070
+ const panner = target.context.createStereoPanner();
1071
+ gain.connect(panner);
1072
+ panner.connect(target.output);
1073
+ return { out: panner, panner };
1074
+ }
1075
+ var fixed = (range) => typeof range === "number" ? range : 0;
1076
+ var fixedFrequency = (automation) => fixed("set" in automation ? automation.set : automation.from);
1077
+ function sourceOf(recipe2, target) {
1078
+ if (recipe2.source.kind === "noise") return { source: noiseSource(target, true) };
1079
+ const oscillator = target.context.createOscillator();
1080
+ oscillator.type = recipe2.source.wave;
1081
+ if (recipe2.source.frequency) oscillator.frequency.value = fixedFrequency(recipe2.source.frequency);
1082
+ return { source: oscillator, frequency: oscillator.frequency };
1083
+ }
1084
+ function stopAll(sources) {
1085
+ return () => {
1086
+ for (const source of sources) {
1087
+ try {
1088
+ source.stop();
1089
+ } catch {
1090
+ }
1091
+ }
1092
+ };
1093
+ }
1094
+ function realizeGameLoop(recipe2, target) {
1095
+ const { context } = target;
1096
+ const gain = context.createGain();
1097
+ const filters = (recipe2.filters?.bank ?? []).map(() => context.createBiquadFilter());
1098
+ gain.gain.value = 0;
1099
+ const last = filters.reduce((previous, filter) => previous ? previous.connect(filter) : filter, void 0);
1100
+ last?.connect(gain);
1101
+ const output = outputStage(recipe2, target, gain);
1102
+ const { source, frequency } = sourceOf(recipe2, target);
1103
+ recipe2.filters?.bank.forEach((spec, index) => {
1104
+ const filter = filters[index];
1105
+ filter.type = spec.type;
1106
+ filter.frequency.value = fixedFrequency(spec.frequency);
1107
+ if (spec.q !== void 0) filter.Q.value = fixed(spec.q);
1108
+ });
1109
+ const sources = [source];
1110
+ for (const modulator of recipe2.modulators ?? []) {
1111
+ const lfo = context.createOscillator();
1112
+ const depth = context.createGain();
1113
+ const stage = context.createGain();
1114
+ if (modulator.wave) lfo.type = modulator.wave;
1115
+ lfo.frequency.value = fixed(modulator.rate);
1116
+ depth.gain.value = fixed(modulator.depth);
1117
+ stage.gain.value = modulator.base ?? 0;
1118
+ lfo.connect(depth);
1119
+ depth.connect(stage.gain);
1120
+ last?.disconnect();
1121
+ last?.connect(stage);
1122
+ stage.connect(gain);
1123
+ lfo.start();
1124
+ sources.push(lfo);
1125
+ }
1126
+ source.connect(filters[0] ?? gain);
1127
+ source.start();
1128
+ return { gain, ...output, source, ...frequency ? { frequency } : {}, ...filters[0] ? { cutoff: filters[0].frequency } : {}, stop: stopAll(sources) };
1129
+ }
1130
+ function realizeChainLoop(recipe2, target, volume) {
1131
+ const { context } = target;
1132
+ const { source, frequency } = sourceOf(recipe2, target);
1133
+ const filters = (recipe2.filters?.bank ?? []).map(
1134
+ (spec) => filterNode(context, { type: spec.type, frequency: { value: fixedFrequency(spec.frequency) }, ...spec.q === void 0 ? {} : { q: fixed(spec.q) } }, target.time)
1135
+ );
1136
+ const gain = gainNode(context, volume);
1137
+ const lfos = (recipe2.modulators ?? []).map((modulator) => {
1138
+ const lfo = context.createOscillator();
1139
+ if (modulator.wave) lfo.type = modulator.wave;
1140
+ lfo.frequency.value = fixed(modulator.rate);
1141
+ const depth = gainNode(context, fixed(modulator.depth));
1142
+ lfo.connect(depth);
1143
+ depth.connect(gain.gain);
1144
+ return lfo;
1145
+ });
1146
+ let tail = source;
1147
+ for (const filter of filters) tail = tail.connect(filter);
1148
+ tail.connect(gain);
1149
+ const output = outputStage(recipe2, target, gain);
1150
+ source.start();
1151
+ for (const lfo of lfos) lfo.start();
1152
+ return { gain, ...output, source, ...frequency ? { frequency } : {}, ...filters[0] ? { cutoff: filters[0].frequency } : {}, stop: stopAll([source, ...lfos]) };
1153
+ }
1154
+
1155
+ // src/webaudio/engine-loop.ts
1156
+ var INERT = { set() {
1157
+ }, stop() {
1158
+ } };
1159
+ function createLoopHandle(recipe2, host, volume) {
1160
+ if (!recipe2) return INERT;
1161
+ const loopRecipe = recipe2;
1162
+ const isChain = loopRecipe.order === "chain_loop";
1163
+ const opening = volume ?? loopRecipe.volume ?? 0;
1164
+ const smoothing = loopRecipe.smoothing ?? LOOP_SMOOTHING;
1165
+ let state = createLoopState(loopRecipe);
1166
+ let nodes;
1167
+ const entry = {
1168
+ get created() {
1169
+ return nodes !== void 0;
1170
+ },
1171
+ rewire() {
1172
+ nodes?.out.disconnect();
1173
+ nodes?.out.connect(host.output(loopRecipe.bus));
1174
+ },
1175
+ stop() {
1176
+ nodes?.stop();
1177
+ nodes?.out.disconnect();
1178
+ nodes = void 0;
1179
+ state = createLoopState(loopRecipe);
1180
+ host.unregister(entry);
1181
+ }
1182
+ };
1183
+ const create = (context, startVolume) => {
1184
+ if (loopRecipe.source.kind === "noise") host.prepareNoise();
1185
+ const target = { context, output: host.output(loopRecipe.bus), noise: host.noise, time: context.currentTime };
1186
+ const made = isChain ? realizeChainLoop(loopRecipe, target, startVolume) : realizeGameLoop(loopRecipe, target);
1187
+ host.register(entry);
1188
+ return made;
1189
+ };
1190
+ const open = () => {
1191
+ const context = host.ready();
1192
+ if (!context) return;
1193
+ nodes = create(context, opening);
1194
+ state = createLoopState({ ...loopRecipe, volume: opening }, true);
1195
+ };
1196
+ if (isChain) open();
1197
+ let position;
1198
+ let cutoff;
1199
+ const placed = loopRecipe.falloff !== void 0 || loopRecipe.pan === true;
1200
+ return {
1201
+ set(next) {
1202
+ if (isChain && !nodes) open();
1203
+ const context = host.ready();
1204
+ if (!context) return;
1205
+ position = next.position ?? position;
1206
+ cutoff = next.cutoff ?? cutoff;
1207
+ const action = state.set(next);
1208
+ if (!action) return;
1209
+ nodes ?? (nodes = create(context, 0));
1210
+ const listener = placed && position ? host.listener?.() : void 0;
1211
+ const distance = listener && position ? listenerDistance(listener, position, host.distance) : 0;
1212
+ const level = listener ? falloffGain(loopRecipe.falloff, action.volume, distance) : action.volume;
1213
+ nodes.gain.gain.setTargetAtTime(level, context.currentTime, smoothing.volume);
1214
+ if (action.frequency !== void 0 && nodes.frequency) nodes.frequency.setTargetAtTime(action.frequency, context.currentTime, smoothing.frequency);
1215
+ if (cutoff !== void 0 && nodes.cutoff) nodes.cutoff.setTargetAtTime(cutoff / cutoffDivisor(loopRecipe.falloff, distance), context.currentTime, smoothing.frequency);
1216
+ if (listener && position && nodes.panner) nodes.panner.pan.setTargetAtTime(panFor(listener, position), context.currentTime, smoothing.frequency);
1217
+ },
1218
+ stop: entry.stop
1219
+ };
1220
+ }
1221
+
1222
+ // src/webaudio/sample-loop.ts
1223
+ var DEFAULT_LOOP_CROSSFADE = 0.25;
1224
+ var LOOKAHEAD = 1.5;
1225
+ var VOLUME_SMOOTHING = 0.05;
1226
+ function loopCrossfade(sound) {
1227
+ if (!sound.loop) return void 0;
1228
+ return sound.loop === true ? DEFAULT_LOOP_CROSSFADE : Math.max(0, sound.loop.crossfade ?? DEFAULT_LOOP_CROSSFADE);
1229
+ }
1230
+ var CROSSFADE_STEPS = 8;
1231
+ var equalPowerIn = (t) => Math.sin(Math.max(0, Math.min(1, t)) * Math.PI / 2);
1232
+ var ramp2 = (param, direction, at, seconds) => {
1233
+ const value = (t) => direction === "in" ? equalPowerIn(t) : equalPowerIn(1 - t);
1234
+ param.setValueAtTime(value(0), at);
1235
+ for (let step = 1; step <= CROSSFADE_STEPS; step++) param.linearRampToValueAtTime(value(step / CROSSFADE_STEPS), at + seconds * step / CROSSFADE_STEPS);
1236
+ };
1237
+ function createSampleLoopHandle(sound, host, volume = 0) {
1238
+ const crossfade = loopCrossfade(sound) ?? DEFAULT_LOOP_CROSSFADE;
1239
+ let level = volume;
1240
+ let stopped = false;
1241
+ let fallback;
1242
+ let master;
1243
+ let segments = [];
1244
+ let last;
1245
+ let timer;
1246
+ const entry = {
1247
+ get created() {
1248
+ return master !== void 0;
1249
+ },
1250
+ rewire() {
1251
+ master?.disconnect();
1252
+ master?.connect(host.output(sound.bus));
1253
+ },
1254
+ stop() {
1255
+ stopped = true;
1256
+ if (timer !== void 0) clearTimeout(timer);
1257
+ timer = void 0;
1258
+ for (const segment of segments) {
1259
+ try {
1260
+ segment.source.stop();
1261
+ } catch {
1262
+ }
1263
+ segment.source.disconnect();
1264
+ segment.gain.disconnect();
1265
+ }
1266
+ segments = [];
1267
+ master?.disconnect();
1268
+ master = void 0;
1269
+ fallback?.stop();
1270
+ fallback = void 0;
1271
+ host.unregister(entry);
1272
+ }
1273
+ };
1274
+ const nextBuffer = () => {
1275
+ const takes2 = sound.close;
1276
+ const take = pickTake(takes2.length, last, host.random);
1277
+ last = take;
1278
+ const ref = takes2[take];
1279
+ return ref ? host.buffer(ref.url) : void 0;
1280
+ };
1281
+ const schedule = (context, output, at, first) => {
1282
+ const buffer = nextBuffer();
1283
+ if (!buffer) return void 0;
1284
+ const source = context.createBufferSource();
1285
+ source.buffer = buffer;
1286
+ const gain = context.createGain();
1287
+ source.connect(gain);
1288
+ gain.connect(output);
1289
+ const fade = Math.min(crossfade, buffer.duration / 2);
1290
+ if (buffer.duration < 2 * crossfade || sound.close.length === 1) {
1291
+ source.loop = true;
1292
+ if (!first) ramp2(gain.gain, "in", at, Math.max(1e-3, fade));
1293
+ source.start(at);
1294
+ segments.push({ source, gain, end: Number.POSITIVE_INFINITY });
1295
+ return void 0;
1296
+ }
1297
+ if (first) gain.gain.setValueAtTime(1, at);
1298
+ else ramp2(gain.gain, "in", at, fade);
1299
+ const end3 = at + buffer.duration;
1300
+ ramp2(gain.gain, "out", end3 - fade, fade);
1301
+ source.start(at);
1302
+ source.stop(end3);
1303
+ segments.push({ source, gain, end: end3 });
1304
+ source.onended = () => {
1305
+ segments = segments.filter((segment) => segment.source !== source);
1306
+ source.disconnect();
1307
+ gain.disconnect();
1308
+ };
1309
+ return end3 - fade;
1310
+ };
1311
+ const pump = (context, output, at, first) => {
1312
+ if (stopped) return;
1313
+ let next = schedule(context, output, at, first);
1314
+ while (next !== void 0 && next < context.currentTime + LOOKAHEAD) next = schedule(context, output, Math.max(next, context.currentTime), false);
1315
+ if (next === void 0) return;
1316
+ const wait = Math.max(0, (next - context.currentTime - LOOKAHEAD / 2) * 1e3);
1317
+ timer = setTimeout(() => {
1318
+ timer = void 0;
1319
+ pump(context, output, next, false);
1320
+ }, wait);
1321
+ };
1322
+ const fileLevel = () => level * (sound.gain ?? 1);
1323
+ const startFiles = () => {
1324
+ if (stopped || master || !host.isReady(sound)) return master !== void 0;
1325
+ const context = host.ready();
1326
+ if (!context) return false;
1327
+ master = context.createGain();
1328
+ master.gain.setValueAtTime(fileLevel(), context.currentTime);
1329
+ master.connect(host.output(sound.bus));
1330
+ host.register(entry);
1331
+ pump(context, master, context.currentTime, true);
1332
+ if (fallback) {
1333
+ fallback.set({ volume: 0 });
1334
+ const old = fallback;
1335
+ fallback = void 0;
1336
+ setTimeout(() => old.stop(), VOLUME_SMOOTHING * 6e3);
1337
+ }
1338
+ return true;
1339
+ };
1340
+ const ensure = () => {
1341
+ if (stopped || master) return;
1342
+ if (startFiles()) return;
1343
+ if (!fallback) {
1344
+ fallback = host.fallback(level);
1345
+ void host.load(sound).then(() => {
1346
+ if (level > 0 || master) startFiles();
1347
+ }).catch(() => void 0);
1348
+ }
1349
+ };
1350
+ if (volume > 0) ensure();
1351
+ return {
1352
+ set(next) {
1353
+ if (stopped) return;
1354
+ if (next.volume !== void 0) level = host.muted(sound.bus) ? 0 : Math.max(0, next.volume);
1355
+ if (level > 0) ensure();
1356
+ const context = host.ready();
1357
+ if (master && context) master.gain.setTargetAtTime(fileLevel(), context.currentTime, VOLUME_SMOOTHING);
1358
+ fallback?.set(next.volume === void 0 ? next : { ...next, volume: level });
1359
+ },
1360
+ stop: entry.stop
1361
+ };
1362
+ }
1363
+
1364
+ // src/webaudio/engine-noise.ts
1365
+ function createNoiseStore(random) {
1366
+ const store = {
1367
+ random,
1368
+ reset() {
1369
+ store.samples = void 0;
1370
+ store.buffer = void 0;
1371
+ }
1372
+ };
1373
+ return store;
1374
+ }
1375
+ function prepareNoise(store, context, seconds) {
1376
+ store.samples ?? (store.samples = fillNoise(store.random, Math.floor(context.sampleRate * seconds)));
1377
+ return store.samples;
1378
+ }
1379
+ function noiseBuffer(store, context, seconds) {
1380
+ const samples = prepareNoise(store, context, seconds);
1381
+ store.buffer ?? (store.buffer = bufferOf(context, [samples], context.sampleRate * seconds));
1382
+ return store.buffer;
1383
+ }
1384
+
1385
+ // ../game-contracts/dist/index.js
1386
+ var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
1387
+ var DEFERRED_QUEUE_ID = {
1388
+ DESTRUCTION_EVENTS: "destruction.events",
1389
+ BREACH_PAINT: "floorplan.breach-paint",
1390
+ AUDIO_VOICES: "audio.voices",
1391
+ VEHICLE_CREASE: "vehicles.crease"
1392
+ };
1393
+ var DEFERRED_PRIORITY = {
1394
+ [DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
1395
+ [DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
1396
+ [DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
1397
+ [DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
1398
+ };
1399
+ var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
1400
+ var GAIT_IDS = ["walk", "run"];
1401
+ var LANDING_IDS = ["light", "heavy"];
1402
+ var BODY_SOUND_IDS = [
1403
+ "jump",
1404
+ "cloth_rustle",
1405
+ "body_fall",
1406
+ "fall_damage",
1407
+ "player_hurt",
1408
+ "player_breath_run",
1409
+ "player_death",
1410
+ "bite",
1411
+ "grab",
1412
+ "punch_swing",
1413
+ "punch_hit_body",
1414
+ "punch_hit_face",
1415
+ "punch_hit_object",
1416
+ "kick_swing",
1417
+ "kick_hit",
1418
+ "kick_hit_object",
1419
+ "shove_two_hands",
834
1420
  "knife_swing",
835
1421
  "knife_stab",
836
1422
  "knife_slash",
@@ -848,525 +1434,6 @@ var GameAudio = (() => {
848
1434
  var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
849
1435
  var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
850
1436
 
851
- // src/recipes/body-foley.ts
852
- var BODY_FOLEY_TAKES = 4;
853
- var TAKES = [
854
- { f: 1, t: 1, v: 1 },
855
- { f: 1.08, t: 0.9, v: 0.92 },
856
- { f: 0.93, t: 1.12, v: 1.05 },
857
- { f: 1.03, t: 1.05, v: 0.88 }
858
- ];
859
- var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
860
- var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
861
- var SURFACES = {
862
- concrete: { heel: { f: 95, d: 0.05, vol: 0.55 }, roll: { type: "bandpass", f: 2300, q: 1.1, d: 0.05, vol: 0.38 }, grains: { count: 2, f: 4200, q: 2, spread: 0.04, vol: 0.08 } },
863
- asphalt: { heel: { f: 90, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 1700, q: 0.9, d: 0.06, vol: 0.36 }, grains: { count: 4, f: 3600, q: 2.5, spread: 0.06, vol: 0.1 } },
864
- wood: { heel: { f: 130, d: 0.08, vol: 0.55 }, roll: { type: "lowpass", f: 1300, q: 0.8, d: 0.06, vol: 0.3 }, ring: [{ f: 210, d: 0.1, vol: 0.18 }, { f: 470, d: 0.06, vol: 0.06 }] },
865
- tile: { heel: { f: 110, d: 0.04, vol: 0.45 }, roll: { type: "bandpass", f: 3600, q: 2, d: 0.03, vol: 0.42 }, ring: [{ f: 1900, d: 0.03, vol: 0.05 }] },
866
- dirt: { heel: { f: 75, d: 0.06, vol: 0.45 }, roll: { type: "lowpass", f: 750, q: 0.7, d: 0.09, vol: 0.4 }, grains: { count: 3, f: 1800, q: 1.5, spread: 0.07, vol: 0.06 } },
867
- grass: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "highpass", f: 2600, q: 0.7, d: 0.13, vol: 0.28 }, grains: { count: 4, f: 5200, q: 1.2, spread: 0.1, vol: 0.07 } },
868
- gravel: { heel: { f: 80, d: 0.05, vol: 0.4 }, roll: { type: "lowpass", f: 950, q: 0.8, d: 0.1, vol: 0.3 }, grains: { count: 8, f: 2600, q: 3, spread: 0.13, vol: 0.2 } },
869
- sand: { heel: { f: 65, d: 0.06, vol: 0.3 }, roll: { type: "bandpass", f: 1500, q: 0.6, d: 0.16, vol: 0.3 }, grains: { count: 5, f: 4800, q: 1.5, spread: 0.12, vol: 0.05 } },
870
- metal: { heel: { f: 120, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 2800, q: 1.5, d: 0.03, vol: 0.25 }, ring: [{ f: 420, d: 0.25, vol: 0.16 }, { f: 1130, d: 0.18, vol: 0.09 }, { f: 2470, d: 0.1, vol: 0.04 }] },
871
- water: { heel: { f: 70, d: 0.04, vol: 0.25 }, roll: { type: "bandpass", f: 3e3, q: 1.2, d: 0.1, vol: 0.18 }, splash: { f: 1100, f2: 380, d: 0.22, vol: 0.55 }, grains: { count: 4, f: 2400, q: 6, spread: 0.18, vol: 0.12 } },
872
- glass: { heel: { f: 90, d: 0.04, vol: 0.4 }, roll: { type: "highpass", f: 4500, q: 0.8, d: 0.06, vol: 0.2 }, grains: { count: 9, f: 6200, q: 5, spread: 0.12, vol: 0.22 }, ring: [{ f: 3900, d: 0.04, vol: 0.04 }, { f: 5300, d: 0.03, vol: 0.03 }] },
873
- carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
874
- };
875
- var GAITS = {
876
- walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
877
- run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
878
- };
879
- var grainLayers = (g, take, start, scale) => Array.from(
880
- { length: g.count },
881
- (_, i) => sceneNoise({ type: "bandpass", f: [g.f * take.f * 0.85, g.f * take.f * 1.15], q: g.q, d: [8e-3, 0.02], vol: g.vol * scale * take.v, delay: start + (i + 0.5) / g.count * g.spread * take.t })
882
- );
883
- var ringLayers = (rings, take, scale, at = 0) => rings.map((r) => sceneTone({ f: r.f * take.f, d: r.d * take.t, vol: r.vol * scale * take.v, delay: at }));
884
- function footLayers(surface, take, gait, weight = 1) {
885
- const s = SURFACES[surface];
886
- const toe = gait.toe * take.t;
887
- return [
888
- sceneTone({ f: s.heel.f * take.f * weight ** -0.3, f2: s.heel.f * take.f * 0.6, d: s.heel.d * gait.short * take.t * weight ** 0.5, vol: s.heel.vol * gait.vol * take.v * weight ** 0.5 }),
889
- sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
890
- sceneNoise({ type: s.roll.type, f: [s.roll.f * take.f * gait.bright * 0.92, s.roll.f * take.f * gait.bright * 1.08], q: s.roll.q, d: s.roll.d * gait.short * take.t * weight ** 0.3, vol: s.roll.vol * gait.vol * take.v, delay: toe }),
891
- ...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
892
- ...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
893
- ...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
894
- ];
895
- }
896
- var quieter = (level, layers) => layers.map((layer) => {
897
- const gain = layer.gain ?? 1;
898
- if (typeof gain === "number") return { ...layer, gain: gain * level };
899
- if (Array.isArray(gain)) return { ...layer, gain: [gain[0] * level, gain[1] * level] };
900
- return layer;
901
- });
902
- var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
903
- var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
904
- function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
905
- const sounds = TAKES.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
906
- return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
907
- }
908
- var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
909
- var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
910
- var LANDING_LEVEL = 0.65;
911
- var landing = (surface, landing2) => takes(
912
- `land_${surface}_${landing2}`,
913
- (take) => quieter(LANDING_LEVEL, [
914
- // both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
915
- ...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
916
- ...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer) => ({ ...layer, at: (typeof layer.at === "number" ? layer.at : 0) + 0.018 * take.t })),
917
- sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
918
- sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
919
- ]),
920
- FOOTSTEP_FALLOFF
921
- );
922
- var whoosh = (blow, from, to, d, vol, q = 1.6) => (take) => [
923
- // the air: a band sweeping up as the limb speeds up; it peaks `SWING_LEAD_SECONDS` in, the moment before the contact, then dies away
924
- sceneNoise({ type: "bandpass", f: from * take.f, f2: to * take.f, q, a: SWING_LEAD_SECONDS[blow], d: d * take.t, vol: vol * take.v }),
925
- sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
926
- ];
927
- var fleshHit = (o) => (take) => [
928
- sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
929
- sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
930
- sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
931
- sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
932
- ...o.crack ? [sceneNoise({ type: "highpass", f: 2500, d: 0.015, vol: o.crack * take.v, delay: 6e-3 }), sceneTone({ wave: "square", f: 900 * take.f, f2: 300, d: 0.02, vol: o.crack * 0.15 * take.v, delay: 6e-3 })] : [],
933
- ...o.wet ? [sceneNoise({ type: "bandpass", f: 1400 * take.f, f2: 500, q: 2.5, d: 0.12 * take.t, vol: o.wet * take.v, delay: 0.01 })] : []
934
- ];
935
- var knock = (o) => (take) => [
936
- sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
937
- ...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
938
- ];
939
- var voice = (o) => (take) => [
940
- sceneVoice({ f0: o.f0 * take.f, f1: o.f1 * take.f, d: o.d * take.t, fA: o.fA * take.f, fB: o.fB * take.f, noise: o.noise, vol: o.vol * take.v })
941
- ];
942
- var BODY = {
943
- jump: (take) => [
944
- sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
945
- sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
946
- sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
947
- ],
948
- cloth_rustle: (take) => [
949
- { ...sceneNoise({ type: "bandpass", f: 2400 * take.f, q: 0.8, a: 0.06, d: 0.22 * take.t, vol: 0.12 * take.v }), modulators: [{ target: "gain", wave: "triangle", rate: [9, 14], depth: 0.5 }] }
950
- ],
951
- body_fall: (take) => [
952
- sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
953
- sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
954
- sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
955
- // the limbs a moment later, and the clothes
956
- sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
957
- sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
958
- ],
959
- fall_damage: (take) => [
960
- ...BODY.body_fall(take),
961
- sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
962
- ...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer) => ({ ...layer, at: 0.08 }))
963
- ],
964
- player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
965
- player_breath_run: (take) => [
966
- sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
967
- sceneNoise({ type: "bandpass", f: 900 * take.f, q: 1, a: 0.06, d: 0.26 * take.t, vol: 0.3 * take.v, delay: 0.3 * take.t })
968
- ],
969
- player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
970
- bite: (take) => [
971
- sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
972
- sceneNoise({ type: "bandpass", f: 1600 * take.f, f2: 450, q: 2.2, d: 0.16 * take.t, vol: 0.55 * take.v, delay: 0.01 }),
973
- sceneNoise({ type: "bandpass", f: 3200 * take.f, f2: 1800, q: 1.2, d: 0.22 * take.t, vol: 0.25 * take.v, delay: 0.09 }),
974
- sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
975
- ],
976
- grab: (take) => [
977
- sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
978
- sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
979
- sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
980
- ],
981
- punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
982
- punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
983
- punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
984
- punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
985
- kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
986
- kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
987
- kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
988
- shove_two_hands: (take) => [
989
- sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
990
- sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
991
- sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
992
- ],
993
- knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
994
- knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
995
- knife_slash: (take) => [
996
- sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
997
- sceneNoise({ type: "bandpass", f: 1300 * take.f, f2: 600, q: 2.5, d: 0.12 * take.t, vol: 0.35 * take.v, delay: 0.02 })
998
- ],
999
- knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
1000
- blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
1001
- blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
1002
- blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
1003
- axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
1004
- axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
1005
- axe_hit_object: knock({ clickF: 2200, click: 0.4, tones: [[1500, 0.15, 0.1], [2300, 0.1, 0.06], [140, 0.1, 0.35]] })
1006
- };
1007
- var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
1008
- var built = [
1009
- ...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
1010
- ...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
1011
- ...BODY_SOUND_IDS.map((id) => takes(id, BODY[id], BODY_FALLOFF, id.endsWith("_swing") ? [0.94, 1.06] : [0.96, 1.04], BODY_BUS[id]))
1012
- ];
1013
- var BODY_FOLEY_BANK = {
1014
- id: "body_foley",
1015
- sounds: built.flatMap((entry) => entry.sounds),
1016
- sets: built.map((entry) => entry.set)
1017
- };
1018
- var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
1019
-
1020
- // src/variation.ts
1021
- var tuneAutomation = (automation, rate) => {
1022
- const tuned = { ...automation };
1023
- if (automation.value !== void 0) tuned.value = automation.value * rate;
1024
- if (automation.from !== void 0) tuned.from = automation.from * rate;
1025
- if (automation.to !== void 0) tuned.to = automation.to * rate;
1026
- if (automation.at !== void 0) tuned.at = automation.at / rate;
1027
- return tuned;
1028
- };
1029
- var tuneFilter = (filter, rate) => ({ ...filter, frequency: tuneAutomation(filter.frequency, rate) });
1030
- var tuneSource = (source, rate) => {
1031
- switch (source.kind) {
1032
- case "noise":
1033
- return source;
1034
- case "osc":
1035
- return { ...source, frequency: tuneAutomation(source.frequency, rate) };
1036
- case "formant_voice":
1037
- return {
1038
- ...source,
1039
- frequency: tuneAutomation(source.frequency, rate),
1040
- formants: source.formants.map((formant) => ({ ...formant, frequency: formant.frequency * rate })),
1041
- breath: { ...source.breath, frequency: source.breath.frequency * rate }
1042
- };
1043
- }
1044
- };
1045
- var tuneModulator = (modulator, rate) => ({
1046
- ...modulator,
1047
- rate: modulator.rate * rate,
1048
- ...modulator.target === "frequency" ? { depth: modulator.depth * rate } : {}
1049
- });
1050
- var tuneLayer = (layer, rate) => ({
1051
- ...layer,
1052
- at: layer.at / rate,
1053
- duration: layer.duration / rate,
1054
- tail: layer.tail / rate,
1055
- source: tuneSource(layer.source, rate),
1056
- ...layer.filters ? { filters: { ...layer.filters, bank: layer.filters.bank.map((filter) => tuneFilter(filter, rate)) } } : {},
1057
- modulators: layer.modulators.map((modulator) => tuneModulator(modulator, rate)),
1058
- envelope: layer.envelope.map((point) => ({ ...point, time: point.time / rate }))
1059
- });
1060
- function retunePlan(plan, rate) {
1061
- if (rate === 1 || !(rate > 0)) return plan;
1062
- return { ...plan, length: plan.length / rate, layers: plan.layers.map((layer) => tuneLayer(layer, rate)) };
1063
- }
1064
- function createSetPicker() {
1065
- const last = /* @__PURE__ */ new Map();
1066
- return {
1067
- pick(set, random) {
1068
- const count = set.variants.length;
1069
- const previous = last.get(set.id);
1070
- let index;
1071
- if (count === 1) index = 0;
1072
- else if (set.repeat || previous === void 0) index = Math.min(count - 1, Math.floor(random() * count));
1073
- else {
1074
- index = Math.min(count - 2, Math.floor(random() * (count - 1)));
1075
- if (index >= previous) index += 1;
1076
- }
1077
- last.set(set.id, index);
1078
- const [min, max] = set.rate ?? [1, 1];
1079
- const rate = min === max ? min : min + random() * (max - min);
1080
- return { id: set.variants[index], rate };
1081
- },
1082
- reset: () => last.clear()
1083
- };
1084
- }
1085
-
1086
- // src/webaudio/noise.ts
1087
- function fillNoise(random, length) {
1088
- const samples = new Float32Array(length);
1089
- for (let index = 0; index < length; index++) samples[index] = random() * 2 - 1;
1090
- return samples;
1091
- }
1092
- function fillImpulse(random, options) {
1093
- const size = Math.floor(options.length);
1094
- return Array.from({ length: options.channels }, () => {
1095
- const samples = new Float32Array(size);
1096
- for (let index = 0; index < options.length; index++) {
1097
- const value = (random() * 2 - 1) * Math.exp(options.decay * Math.log(1 - index / options.length));
1098
- if (index < size) samples[index] = value;
1099
- }
1100
- return samples;
1101
- });
1102
- }
1103
- function bufferOf(context, channels, requestedLength) {
1104
- const buffer = context.createBuffer(channels.length, requestedLength, context.sampleRate);
1105
- channels.forEach((samples, channel) => buffer.getChannelData(channel).set(samples));
1106
- return buffer;
1107
- }
1108
-
1109
- // src/webaudio/chain.ts
1110
- function buildChain(context, profile, random) {
1111
- let compressor;
1112
- if (profile.compressor) {
1113
- compressor = context.createDynamicsCompressor();
1114
- compressor.threshold.value = profile.compressor.threshold;
1115
- compressor.ratio.value = profile.compressor.ratio;
1116
- if (profile.compressor.knee !== void 0) compressor.knee.value = profile.compressor.knee;
1117
- if (profile.compressor.attack !== void 0) compressor.attack.value = profile.compressor.attack;
1118
- if (profile.compressor.release !== void 0) compressor.release.value = profile.compressor.release;
1119
- compressor.connect(context.destination);
1120
- }
1121
- const master = context.createGain();
1122
- master.gain.value = profile.master;
1123
- master.connect(compressor ?? context.destination);
1124
- if (!profile.reverb) {
1125
- return {
1126
- master,
1127
- disconnect: () => {
1128
- master.disconnect();
1129
- compressor?.disconnect();
1130
- }
1131
- };
1132
- }
1133
- const convolver = context.createConvolver();
1134
- const length = context.sampleRate * profile.reverb.seconds;
1135
- convolver.buffer = bufferOf(context, fillImpulse(random, { channels: 2, length, decay: profile.reverb.decay }), length);
1136
- const wet = context.createGain();
1137
- wet.gain.value = profile.reverb.wet;
1138
- wet.connect(convolver);
1139
- convolver.connect(master);
1140
- return {
1141
- master,
1142
- wet,
1143
- disconnect: () => {
1144
- wet.disconnect();
1145
- convolver.disconnect();
1146
- master.disconnect();
1147
- compressor?.disconnect();
1148
- }
1149
- };
1150
- }
1151
-
1152
- // src/webaudio/context.ts
1153
- function globalContext() {
1154
- const scope = globalThis;
1155
- const Constructor = scope.AudioContext ?? scope.webkitAudioContext;
1156
- return Constructor ? new Constructor() : null;
1157
- }
1158
- function unlockContext(context) {
1159
- if (context.state === "suspended") void context.resume();
1160
- }
1161
-
1162
- // src/webaudio/nodes.ts
1163
- function assign(param, automation, time) {
1164
- if (automation.value !== void 0) param.value = automation.value;
1165
- if (automation.from !== void 0) param.setValueAtTime(automation.from, time);
1166
- }
1167
- function ramp(param, curve, value, at) {
1168
- if (curve === "set") param.setValueAtTime(value, at);
1169
- else if (curve === "exp") param.exponentialRampToValueAtTime(value, at);
1170
- else param.linearRampToValueAtTime(value, at);
1171
- }
1172
- function rampTo(param, automation, time) {
1173
- if (automation.to !== void 0 && automation.at !== void 0) ramp(param, automation.curve ?? "linear", automation.to, time + automation.at);
1174
- }
1175
- function applyEnvelope(param, points, time) {
1176
- for (const point of points) ramp(param, point.curve, point.value, time + point.time);
1177
- }
1178
- function noiseSource(target, loop) {
1179
- const source = target.context.createBufferSource();
1180
- source.buffer = target.noise();
1181
- if (loop) source.loop = true;
1182
- return source;
1183
- }
1184
- function filterNode(context, filter, time) {
1185
- const node = context.createBiquadFilter();
1186
- node.type = filter.type;
1187
- assign(node.frequency, filter.frequency, time);
1188
- if (filter.q !== void 0) node.Q.value = filter.q;
1189
- if (filter.gain !== void 0) node.gain.value = filter.gain;
1190
- return node;
1191
- }
1192
- function gainNode(context, value) {
1193
- const node = context.createGain();
1194
- node.gain.value = value;
1195
- return node;
1196
- }
1197
- function voiceOf(sources) {
1198
- return {
1199
- sources,
1200
- stop() {
1201
- for (const source of sources) {
1202
- try {
1203
- source.stop();
1204
- } catch {
1205
- }
1206
- }
1207
- }
1208
- };
1209
- }
1210
-
1211
- // src/webaudio/realize-loop.ts
1212
- var fixed = (range) => typeof range === "number" ? range : 0;
1213
- var fixedFrequency = (automation) => fixed("set" in automation ? automation.set : automation.from);
1214
- function sourceOf(recipe2, target) {
1215
- if (recipe2.source.kind === "noise") return { source: noiseSource(target, true) };
1216
- const oscillator = target.context.createOscillator();
1217
- oscillator.type = recipe2.source.wave;
1218
- if (recipe2.source.frequency) oscillator.frequency.value = fixedFrequency(recipe2.source.frequency);
1219
- return { source: oscillator, frequency: oscillator.frequency };
1220
- }
1221
- function stopAll(sources) {
1222
- return () => {
1223
- for (const source of sources) {
1224
- try {
1225
- source.stop();
1226
- } catch {
1227
- }
1228
- }
1229
- };
1230
- }
1231
- function realizeGameLoop(recipe2, target) {
1232
- const { context } = target;
1233
- const gain = context.createGain();
1234
- const filters = (recipe2.filters?.bank ?? []).map(() => context.createBiquadFilter());
1235
- gain.gain.value = 0;
1236
- const last = filters.reduce((previous, filter) => previous ? previous.connect(filter) : filter, void 0);
1237
- last?.connect(gain);
1238
- gain.connect(target.output);
1239
- const { source, frequency } = sourceOf(recipe2, target);
1240
- recipe2.filters?.bank.forEach((spec, index) => {
1241
- const filter = filters[index];
1242
- filter.type = spec.type;
1243
- filter.frequency.value = fixedFrequency(spec.frequency);
1244
- if (spec.q !== void 0) filter.Q.value = fixed(spec.q);
1245
- });
1246
- const sources = [source];
1247
- for (const modulator of recipe2.modulators ?? []) {
1248
- const lfo = context.createOscillator();
1249
- const depth = context.createGain();
1250
- const stage = context.createGain();
1251
- if (modulator.wave) lfo.type = modulator.wave;
1252
- lfo.frequency.value = fixed(modulator.rate);
1253
- depth.gain.value = fixed(modulator.depth);
1254
- stage.gain.value = modulator.base ?? 0;
1255
- lfo.connect(depth);
1256
- depth.connect(stage.gain);
1257
- last?.disconnect();
1258
- last?.connect(stage);
1259
- stage.connect(gain);
1260
- lfo.start();
1261
- sources.push(lfo);
1262
- }
1263
- source.connect(filters[0] ?? gain);
1264
- source.start();
1265
- return { gain, source, ...frequency ? { frequency } : {}, stop: stopAll(sources) };
1266
- }
1267
- function realizeChainLoop(recipe2, target, volume) {
1268
- const { context } = target;
1269
- const { source, frequency } = sourceOf(recipe2, target);
1270
- const filters = (recipe2.filters?.bank ?? []).map(
1271
- (spec) => filterNode(context, { type: spec.type, frequency: { value: fixedFrequency(spec.frequency) }, ...spec.q === void 0 ? {} : { q: fixed(spec.q) } }, target.time)
1272
- );
1273
- const gain = gainNode(context, volume);
1274
- const lfos = (recipe2.modulators ?? []).map((modulator) => {
1275
- const lfo = context.createOscillator();
1276
- if (modulator.wave) lfo.type = modulator.wave;
1277
- lfo.frequency.value = fixed(modulator.rate);
1278
- const depth = gainNode(context, fixed(modulator.depth));
1279
- lfo.connect(depth);
1280
- depth.connect(gain.gain);
1281
- return lfo;
1282
- });
1283
- let tail = source;
1284
- for (const filter of filters) tail = tail.connect(filter);
1285
- tail.connect(gain);
1286
- gain.connect(target.output);
1287
- source.start();
1288
- for (const lfo of lfos) lfo.start();
1289
- return { gain, source, ...frequency ? { frequency } : {}, stop: stopAll([source, ...lfos]) };
1290
- }
1291
-
1292
- // src/webaudio/engine-loop.ts
1293
- var INERT = { set() {
1294
- }, stop() {
1295
- } };
1296
- function createLoopHandle(recipe2, host, volume) {
1297
- if (!recipe2) return INERT;
1298
- const loopRecipe = recipe2;
1299
- const isChain = loopRecipe.order === "chain_loop";
1300
- const opening = volume ?? loopRecipe.volume ?? 0;
1301
- const smoothing = loopRecipe.smoothing ?? LOOP_SMOOTHING;
1302
- let state = createLoopState(loopRecipe);
1303
- let nodes;
1304
- const entry = {
1305
- get created() {
1306
- return nodes !== void 0;
1307
- },
1308
- rewire() {
1309
- nodes?.gain.disconnect();
1310
- nodes?.gain.connect(host.output(loopRecipe.bus));
1311
- },
1312
- stop() {
1313
- nodes?.stop();
1314
- nodes?.gain.disconnect();
1315
- nodes = void 0;
1316
- state = createLoopState(loopRecipe);
1317
- host.unregister(entry);
1318
- }
1319
- };
1320
- const create = (context, startVolume) => {
1321
- if (loopRecipe.source.kind === "noise") host.prepareNoise();
1322
- const target = { context, output: host.output(loopRecipe.bus), noise: host.noise, time: context.currentTime };
1323
- const made = isChain ? realizeChainLoop(loopRecipe, target, startVolume) : realizeGameLoop(loopRecipe, target);
1324
- host.register(entry);
1325
- return made;
1326
- };
1327
- const open = () => {
1328
- const context = host.ready();
1329
- if (!context) return;
1330
- nodes = create(context, opening);
1331
- state = createLoopState({ ...loopRecipe, volume: opening }, true);
1332
- };
1333
- if (isChain) open();
1334
- return {
1335
- set(next) {
1336
- if (isChain && !nodes) open();
1337
- const context = host.ready();
1338
- if (!context) return;
1339
- const action = state.set(next);
1340
- if (!action) return;
1341
- nodes ?? (nodes = create(context, 0));
1342
- nodes.gain.gain.setTargetAtTime(action.volume, context.currentTime, smoothing.volume);
1343
- if (action.frequency !== void 0 && nodes.frequency) nodes.frequency.setTargetAtTime(action.frequency, context.currentTime, smoothing.frequency);
1344
- },
1345
- stop: entry.stop
1346
- };
1347
- }
1348
-
1349
- // src/webaudio/engine-noise.ts
1350
- function createNoiseStore(random) {
1351
- const store = {
1352
- random,
1353
- reset() {
1354
- store.samples = void 0;
1355
- store.buffer = void 0;
1356
- }
1357
- };
1358
- return store;
1359
- }
1360
- function prepareNoise(store, context, seconds) {
1361
- store.samples ?? (store.samples = fillNoise(store.random, Math.floor(context.sampleRate * seconds)));
1362
- return store.samples;
1363
- }
1364
- function noiseBuffer(store, context, seconds) {
1365
- const samples = prepareNoise(store, context, seconds);
1366
- store.buffer ?? (store.buffer = bufferOf(context, [samples], context.sampleRate * seconds));
1367
- return store.buffer;
1368
- }
1369
-
1370
1437
  // src/webaudio/engine-queue.ts
1371
1438
  var nowMs = () => typeof performance === "undefined" ? Date.now() : performance.now();
1372
1439
  function scheduleTask(task) {
@@ -1627,7 +1694,7 @@ var GameAudio = (() => {
1627
1694
 
1628
1695
  // src/webaudio/realize-layered.ts
1629
1696
  var end2 = (layer, start) => start + layer.duration + layer.tail;
1630
- function outputStage(plan, layer, target) {
1697
+ function outputStage2(plan, layer, target) {
1631
1698
  const { context } = target;
1632
1699
  const level = gainNode(context, layer.level);
1633
1700
  let node = level;
@@ -1659,7 +1726,7 @@ var GameAudio = (() => {
1659
1726
  let tail = source;
1660
1727
  for (const filter of filters) tail = tail.connect(filter);
1661
1728
  tail.connect(envelope2);
1662
- envelope2.connect(outputStage(plan, layer, target));
1729
+ envelope2.connect(outputStage2(plan, layer, target));
1663
1730
  source.start(time, layer.source.kind === "noise" ? layer.source.offset : 0);
1664
1731
  source.stop(end2(layer, time));
1665
1732
  return [source];
@@ -1676,7 +1743,7 @@ var GameAudio = (() => {
1676
1743
  const envelope2 = context.createGain();
1677
1744
  applyEnvelope(envelope2.gain, layer.envelope, time);
1678
1745
  oscillator.connect(envelope2);
1679
- envelope2.connect(outputStage(plan, layer, target));
1746
+ envelope2.connect(outputStage2(plan, layer, target));
1680
1747
  oscillator.start(time);
1681
1748
  oscillator.stop(end2(layer, time));
1682
1749
  return [oscillator];
@@ -1708,7 +1775,7 @@ var GameAudio = (() => {
1708
1775
  const envelope2 = context.createGain();
1709
1776
  applyEnvelope(envelope2.gain, layer.envelope, time);
1710
1777
  mix.connect(envelope2);
1711
- envelope2.connect(outputStage(plan, layer, target));
1778
+ envelope2.connect(outputStage2(plan, layer, target));
1712
1779
  const stop = end2(layer, time);
1713
1780
  oscillator.start(time);
1714
1781
  vibrato.start(time);
@@ -1801,307 +1868,2537 @@ var GameAudio = (() => {
1801
1868
  const time = target.time + layer.at;
1802
1869
  return layer.source.kind === "noise" ? noiseLayer(layer, target, time) : toneLayer(layer, target, time);
1803
1870
  });
1804
- return voiceOf(sources);
1805
- }
1806
-
1807
- // src/webaudio/realize.ts
1808
- var BUILDERS = {
1809
- layered: realizeLayered,
1810
- burst: realizeBurst,
1811
- scream: realizeScream,
1812
- groan: realizeGroan,
1813
- boom: realizeBoom,
1814
- static: realizeStatic,
1815
- chord: realizeChord,
1816
- calls: realizeCalls,
1817
- siren: realizeSiren,
1818
- weapon: realizeWeapon
1819
- };
1820
- function realize(plan, target) {
1821
- return BUILDERS[plan.order](plan, target);
1822
- }
1823
-
1824
- // src/webaudio/engine.ts
1825
- var usesNoise = (recipe2) => recipe2.layers.some((layer) => layer.source.kind !== "osc");
1826
- var planUsesNoise = (plan) => plan.layers.some((layer) => layer.source.kind !== "osc");
1827
- function createAudioEngine(options = {}) {
1828
- const seed = options.seed ?? 1;
1829
- const registry = createSoundRegistry(options.banks);
1830
- const voices = createVoiceManager({ ...options.groups ? { groups: options.groups } : {}, ...options.maxVoices === void 0 ? {} : { maxVoices: options.maxVoices } });
1831
- const engineRandom = createRng(seed);
1832
- const shared = createNoiseStore(engineRandom);
1833
- const busSettings = /* @__PURE__ */ new Map();
1834
- const busNodes = /* @__PURE__ */ new Map();
1835
- const meters = /* @__PURE__ */ new Map();
1836
- const active = /* @__PURE__ */ new Map();
1837
- const loops = /* @__PURE__ */ new Set();
1838
- const named = /* @__PURE__ */ new Map();
1839
- let mix = resolveMix(options.mix ?? DEFAULT_MIX, options.profiles);
1840
- let context = null;
1841
- let chain = null;
1842
- let listener = DEFAULT_LISTENER;
1843
- let lastTime = 0;
1844
- const outputFor = (bus) => busNodes.get(bus) ?? chain?.master;
1845
- const watched = /* @__PURE__ */ new Set();
1846
- const meterOf = (bus) => {
1847
- const existing = meters.get(bus);
1871
+ return voiceOf(sources);
1872
+ }
1873
+
1874
+ // src/webaudio/realize.ts
1875
+ var BUILDERS = {
1876
+ layered: realizeLayered,
1877
+ burst: realizeBurst,
1878
+ scream: realizeScream,
1879
+ groan: realizeGroan,
1880
+ boom: realizeBoom,
1881
+ static: realizeStatic,
1882
+ chord: realizeChord,
1883
+ calls: realizeCalls,
1884
+ siren: realizeSiren,
1885
+ weapon: realizeWeapon
1886
+ };
1887
+ function realize(plan, target) {
1888
+ return BUILDERS[plan.order](plan, target);
1889
+ }
1890
+
1891
+ // src/variation.ts
1892
+ var tuneAutomation = (automation, rate) => {
1893
+ const tuned = { ...automation };
1894
+ if (automation.value !== void 0) tuned.value = automation.value * rate;
1895
+ if (automation.from !== void 0) tuned.from = automation.from * rate;
1896
+ if (automation.to !== void 0) tuned.to = automation.to * rate;
1897
+ if (automation.at !== void 0) tuned.at = automation.at / rate;
1898
+ return tuned;
1899
+ };
1900
+ var tuneFilter = (filter, rate) => ({ ...filter, frequency: tuneAutomation(filter.frequency, rate) });
1901
+ var tuneSource = (source, rate) => {
1902
+ switch (source.kind) {
1903
+ case "noise":
1904
+ return source;
1905
+ case "osc":
1906
+ return { ...source, frequency: tuneAutomation(source.frequency, rate) };
1907
+ case "formant_voice":
1908
+ return {
1909
+ ...source,
1910
+ frequency: tuneAutomation(source.frequency, rate),
1911
+ formants: source.formants.map((formant) => ({ ...formant, frequency: formant.frequency * rate })),
1912
+ breath: { ...source.breath, frequency: source.breath.frequency * rate }
1913
+ };
1914
+ }
1915
+ };
1916
+ var tuneModulator = (modulator, rate) => ({
1917
+ ...modulator,
1918
+ rate: modulator.rate * rate,
1919
+ ...modulator.target === "frequency" ? { depth: modulator.depth * rate } : {}
1920
+ });
1921
+ var tuneLayer = (layer, rate) => ({
1922
+ ...layer,
1923
+ at: layer.at / rate,
1924
+ duration: layer.duration / rate,
1925
+ tail: layer.tail / rate,
1926
+ source: tuneSource(layer.source, rate),
1927
+ ...layer.filters ? { filters: { ...layer.filters, bank: layer.filters.bank.map((filter) => tuneFilter(filter, rate)) } } : {},
1928
+ modulators: layer.modulators.map((modulator) => tuneModulator(modulator, rate)),
1929
+ envelope: layer.envelope.map((point) => ({ ...point, time: point.time / rate }))
1930
+ });
1931
+ function retunePlan(plan, rate) {
1932
+ if (rate === 1 || !(rate > 0)) return plan;
1933
+ return { ...plan, length: plan.length / rate, layers: plan.layers.map((layer) => tuneLayer(layer, rate)) };
1934
+ }
1935
+ function createSetPicker() {
1936
+ const last = /* @__PURE__ */ new Map();
1937
+ return {
1938
+ pick(set, random) {
1939
+ const count = set.variants.length;
1940
+ const previous = last.get(set.id);
1941
+ let index;
1942
+ if (count === 1) index = 0;
1943
+ else if (set.repeat || previous === void 0) index = Math.min(count - 1, Math.floor(random() * count));
1944
+ else {
1945
+ index = Math.min(count - 2, Math.floor(random() * (count - 1)));
1946
+ if (index >= previous) index += 1;
1947
+ }
1948
+ last.set(set.id, index);
1949
+ const [min, max] = set.rate ?? [1, 1];
1950
+ const rate = min === max ? min : min + random() * (max - min);
1951
+ return { id: set.variants[index], rate };
1952
+ },
1953
+ reset: () => last.clear()
1954
+ };
1955
+ }
1956
+
1957
+ // src/webaudio/sample-store.ts
1958
+ function fetchLoader(baseUrl) {
1959
+ return async (url2) => {
1960
+ const target = baseUrl ? new URL(url2, baseUrl).toString() : url2;
1961
+ const response = await fetch(target);
1962
+ if (!response.ok) throw new Error(`${response.status} ${response.statusText} for ${target}`);
1963
+ return response.arrayBuffer();
1964
+ };
1965
+ }
1966
+ function inlineLoader(files) {
1967
+ return async (url2) => {
1968
+ const text = files[url2];
1969
+ if (text === void 0) throw new Error(`no inline file for ${url2}`);
1970
+ const base64 = text.startsWith("data:") ? text.slice(text.indexOf(",") + 1) : text;
1971
+ const binary = atob(base64);
1972
+ const bytes = new Uint8Array(binary.length);
1973
+ for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
1974
+ return bytes.buffer;
1975
+ };
1976
+ }
1977
+ var decode = (context, bytes) => new Promise((resolve, reject) => {
1978
+ const result = context.decodeAudioData(bytes, resolve, reject);
1979
+ result?.catch?.(reject);
1980
+ });
1981
+ function createSampleStore(loader = fetchLoader()) {
1982
+ const buffers = /* @__PURE__ */ new Map();
1983
+ const pending = /* @__PURE__ */ new Map();
1984
+ const failed = /* @__PURE__ */ new Map();
1985
+ let generation = 0;
1986
+ const loadOne = (url2, context) => {
1987
+ const existing = buffers.get(url2);
1988
+ if (existing) return Promise.resolve(existing);
1989
+ const inFlight = pending.get(url2);
1990
+ if (inFlight) return inFlight;
1991
+ const started = generation;
1992
+ failed.delete(url2);
1993
+ const promise = loader(url2).then((bytes) => decode(context, bytes)).then((buffer) => {
1994
+ if (started === generation) buffers.set(url2, buffer);
1995
+ return buffer;
1996
+ }).catch((error) => {
1997
+ if (started === generation) failed.set(url2, error instanceof Error ? error.message : String(error));
1998
+ throw error;
1999
+ }).finally(() => {
2000
+ if (pending.get(url2) === promise) pending.delete(url2);
2001
+ });
2002
+ pending.set(url2, promise);
2003
+ return promise;
2004
+ };
2005
+ return {
2006
+ status(url2) {
2007
+ if (buffers.has(url2)) return "ready";
2008
+ if (pending.has(url2)) return "loading";
2009
+ if (failed.has(url2)) return "failed";
2010
+ return "none";
2011
+ },
2012
+ buffer: (url2) => buffers.get(url2),
2013
+ ready: (urls) => urls.every((url2) => buffers.has(url2)),
2014
+ seconds: (url2) => buffers.get(url2)?.duration,
2015
+ async load(urls, context) {
2016
+ const results = await Promise.allSettled(urls.map((url2) => loadOne(url2, context)));
2017
+ const loaded = [];
2018
+ const errors = [];
2019
+ results.forEach((result, index) => {
2020
+ const url2 = urls[index];
2021
+ if (result.status === "fulfilled") loaded.push(url2);
2022
+ else errors.push({ url: url2, error: result.reason instanceof Error ? result.reason.message : String(result.reason) });
2023
+ });
2024
+ return { loaded, failed: errors };
2025
+ },
2026
+ clear() {
2027
+ generation++;
2028
+ buffers.clear();
2029
+ pending.clear();
2030
+ failed.clear();
2031
+ }
2032
+ };
2033
+ }
2034
+
2035
+ // src/webaudio/realize-sample.ts
2036
+ function realizeSample(plan, target) {
2037
+ const context = target.context;
2038
+ let head = target.output;
2039
+ if (plan.pan !== void 0 && typeof context.createStereoPanner === "function") {
2040
+ const panner = context.createStereoPanner();
2041
+ panner.pan.value = plan.pan;
2042
+ panner.connect(head);
2043
+ head = panner;
2044
+ }
2045
+ if (plan.cutoff < OPEN_CUTOFF) {
2046
+ const lowpass = context.createBiquadFilter();
2047
+ lowpass.type = "lowpass";
2048
+ lowpass.frequency.value = plan.cutoff;
2049
+ lowpass.Q.value = 0.7;
2050
+ lowpass.connect(head);
2051
+ head = lowpass;
2052
+ }
2053
+ const voice2 = context.createGain();
2054
+ voice2.connect(head);
2055
+ head = voice2;
2056
+ const sources = [];
2057
+ for (const layer of plan.layers) {
2058
+ const buffer = target.buffer(layer.url);
2059
+ if (!buffer) continue;
2060
+ const source = context.createBufferSource();
2061
+ source.buffer = buffer;
2062
+ source.playbackRate.value = layer.rate;
2063
+ const gain = gainNode(context, layer.gain);
2064
+ source.connect(gain);
2065
+ gain.connect(head);
2066
+ source.start(target.time + layer.at);
2067
+ sources.push(source);
2068
+ }
2069
+ const realized = voiceOf(sources);
2070
+ return {
2071
+ ...realized,
2072
+ fade(at, seconds) {
2073
+ voice2.gain.setValueAtTime(1, at);
2074
+ voice2.gain.linearRampToValueAtTime(0, at + seconds);
2075
+ for (const source of sources) {
2076
+ try {
2077
+ source.stop(at + seconds);
2078
+ } catch {
2079
+ }
2080
+ }
2081
+ }
2082
+ };
2083
+ }
2084
+ function sampleLength(plan, seconds) {
2085
+ let length = 0;
2086
+ for (const layer of plan.layers) length = Math.max(length, layer.at + (seconds(layer.url) ?? 0) / layer.rate);
2087
+ return length;
2088
+ }
2089
+
2090
+ // src/webaudio/engine.ts
2091
+ function limiterCurve2(knee, ceiling, range, points = 2049) {
2092
+ const curve = new Float32Array(points);
2093
+ const room = Math.max(1e-6, ceiling - knee);
2094
+ for (let index = 0; index < points; index++) {
2095
+ const value = (index / (points - 1) * 2 - 1) * range;
2096
+ const size = Math.abs(value);
2097
+ const bent = size <= knee ? size : knee + room * Math.tanh((size - knee) / room);
2098
+ curve[index] = Math.sign(value) * bent;
2099
+ }
2100
+ return curve;
2101
+ }
2102
+ var usesNoise = (recipe2) => recipe2.layers.some((layer) => layer.source.kind !== "osc");
2103
+ var planUsesNoise = (plan) => plan.layers.some((layer) => layer.source.kind !== "osc");
2104
+ function createAudioEngine(options = {}) {
2105
+ const seed = options.seed ?? 1;
2106
+ const registry = createSoundRegistry(options.banks);
2107
+ const voices = createVoiceManager({ ...options.groups ? { groups: options.groups } : {}, ...options.maxVoices === void 0 ? {} : { maxVoices: options.maxVoices } });
2108
+ const engineRandom = createRng(seed);
2109
+ const shared = createNoiseStore(engineRandom);
2110
+ const busSettings = /* @__PURE__ */ new Map();
2111
+ const busNodes = /* @__PURE__ */ new Map();
2112
+ const meters = /* @__PURE__ */ new Map();
2113
+ const active = /* @__PURE__ */ new Map();
2114
+ const loops = /* @__PURE__ */ new Set();
2115
+ const named = /* @__PURE__ */ new Map();
2116
+ let mix = resolveMix(options.mix ?? DEFAULT_MIX, options.profiles);
2117
+ let context = null;
2118
+ let chain = null;
2119
+ let listener = DEFAULT_LISTENER;
2120
+ let lastTime = 0;
2121
+ let environment = options.environment ?? "outdoor";
2122
+ const sampleLimiters = /* @__PURE__ */ new Map();
2123
+ const sampleOutput = (bus) => {
2124
+ const setting = options.samples?.limiter;
2125
+ if (setting === false || !context) return outputFor(bus);
2126
+ const existing = sampleLimiters.get(bus);
2127
+ if (existing) return existing;
2128
+ const range = 4;
2129
+ const input = context.createGain();
2130
+ input.gain.value = 1 / range;
2131
+ const shaper = context.createWaveShaper();
2132
+ shaper.curve = limiterCurve2(setting?.knee ?? 0.5, setting?.ceiling ?? 0.891, range);
2133
+ ensureBus(bus);
2134
+ input.connect(shaper);
2135
+ shaper.connect(outputFor(bus));
2136
+ sampleLimiters.set(bus, input);
2137
+ return input;
2138
+ };
2139
+ const chokes = /* @__PURE__ */ new Map();
2140
+ const choked = (id) => {
2141
+ const list = (chokes.get(id) ?? []).filter((entry) => active.has(entry.id));
2142
+ chokes.set(id, list);
2143
+ return list;
2144
+ };
2145
+ const watchers = /* @__PURE__ */ new Set();
2146
+ const announce = (event) => {
2147
+ options.onVoiceStart?.(event);
2148
+ for (const watcher of watchers) watcher(event);
2149
+ };
2150
+ const sources = [];
2151
+ const baseLoader = options.samples?.loader ?? fetchLoader(options.samples?.baseUrl);
2152
+ const sampleStore = createSampleStore((url2) => (sources.find((source) => source.accepts(url2))?.loader ?? baseLoader)(url2));
2153
+ const memory = /* @__PURE__ */ new Map();
2154
+ const latencyOf = (audio) => {
2155
+ const live = audio;
2156
+ return (live.baseLatency ?? 0) + (live.outputLatency ?? 0);
2157
+ };
2158
+ const outputFor = (bus) => busNodes.get(bus) ?? chain?.master;
2159
+ const watched = /* @__PURE__ */ new Set();
2160
+ const meterOf = (bus) => {
2161
+ const existing = meters.get(bus);
2162
+ if (existing) return existing;
2163
+ const node = busNodes.get(bus);
2164
+ if (!context || !node || typeof context.createAnalyser !== "function") return void 0;
2165
+ const analyser = context.createAnalyser();
2166
+ analyser.fftSize = 1024;
2167
+ node.connect(analyser);
2168
+ const made = { analyser, data: new Float32Array(analyser.fftSize) };
2169
+ meters.set(bus, made);
2170
+ return made;
2171
+ };
2172
+ const ensureBus = (bus) => {
2173
+ if (!context || !chain || busNodes.has(bus)) return;
2174
+ const node = context.createGain();
2175
+ node.connect(chain.master);
2176
+ busNodes.set(bus, node);
2177
+ if (watched.has(bus)) meterOf(bus);
2178
+ };
2179
+ const ready = () => {
2180
+ if (context) return context;
2181
+ const created = (options.createContext ?? globalContext)();
2182
+ if (!created) return null;
2183
+ context = created;
2184
+ chain = buildChain(created, mix, engineRandom);
2185
+ if (mix.noise.create === "eager") noiseBuffer(shared, created, mix.noise.seconds);
2186
+ for (const [bus, setting] of Object.entries(mix.buses ?? {})) {
2187
+ busSettings.set(bus, { ...setting, ...busSettings.get(bus) });
2188
+ }
2189
+ for (const [bus, setting] of busSettings) {
2190
+ ensureBus(bus);
2191
+ busNodes.get(bus).gain.value = busGain(setting);
2192
+ }
2193
+ lastTime = created.currentTime;
2194
+ return created;
2195
+ };
2196
+ const advance = () => {
2197
+ if (!context) return;
2198
+ const elapsed = context.currentTime - lastTime;
2199
+ lastTime = context.currentTime;
2200
+ if (elapsed > 0) for (const id of voices.advance(elapsed)) active.delete(id);
2201
+ };
2202
+ const inputFor = (recipe2, playOptions, random) => ({
2203
+ random,
2204
+ listener,
2205
+ noiseSeconds: mix.noise.seconds,
2206
+ ...options.distance ? { distanceMode: options.distance } : {},
2207
+ ...playOptions.volume === void 0 ? {} : { volume: playOptions.volume },
2208
+ ...playOptions.distance === void 0 ? {} : { distance: playOptions.distance },
2209
+ ...playOptions.position ? { position: playOptions.position } : {},
2210
+ ...playOptions.params ? { params: playOptions.params } : {},
2211
+ ...options.occlusion && playOptions.position && recipe2.falloff?.model !== "none" ? { occlusion: options.occlusion(playOptions.position) } : {}
2212
+ });
2213
+ const startQueue = options.deferredStart ? createStartQueue(options.deferredStart) : null;
2214
+ const build = (plan, store, delay, requestedAt, fallbackOf) => {
2215
+ const audio = context;
2216
+ const time = audio.currentTime + delay;
2217
+ const realized = realize(plan, {
2218
+ context: audio,
2219
+ output: outputFor(plan.bus),
2220
+ ...chain?.wet ? { wet: chain.wet } : {},
2221
+ noise: () => noiseBuffer(store, audio, mix.noise.seconds),
2222
+ time
2223
+ });
2224
+ const recipeBank = registry.bankOf(plan.id);
2225
+ announce({
2226
+ id: plan.id,
2227
+ kind: "recipe",
2228
+ ...fallbackOf ? { fallbackOf } : {},
2229
+ ...recipeBank ? { bank: recipeBank } : {},
2230
+ requestedAt,
2231
+ startAt: time,
2232
+ outputLatency: latencyOf(audio),
2233
+ level: plan.level,
2234
+ distance: plan.distance,
2235
+ ...plan.pan === void 0 ? {} : { pan: plan.pan }
2236
+ });
2237
+ return realized;
2238
+ };
2239
+ const deferVoice = (plan, store, voiceId, delay, fallbackOf) => {
2240
+ const requestedAt = context?.currentTime ?? 0;
2241
+ let built2 = null;
2242
+ let cancelled = false;
2243
+ const pending = {
2244
+ get sources() {
2245
+ return built2?.sources ?? [];
2246
+ },
2247
+ stop() {
2248
+ cancelled = true;
2249
+ built2?.stop();
2250
+ }
2251
+ };
2252
+ startQueue.push({
2253
+ run() {
2254
+ if (!cancelled && context) built2 = build(plan, store, delay, requestedAt, fallbackOf);
2255
+ },
2256
+ drop() {
2257
+ if (voiceId === void 0) return;
2258
+ voices.release(voiceId);
2259
+ active.delete(voiceId);
2260
+ }
2261
+ });
2262
+ return pending;
2263
+ };
2264
+ const start = (plan, store, voiceId, delay = 0, fallbackOf, urgent = false) => {
2265
+ const realized = startQueue && !urgent ? deferVoice(plan, store, voiceId, delay, fallbackOf) : build(plan, store, delay, context?.currentTime ?? 0, fallbackOf);
2266
+ if (voiceId === void 0) return null;
2267
+ voices.setLength(voiceId, plan.length + delay);
2268
+ active.set(voiceId, realized);
2269
+ return {
2270
+ id: voiceId,
2271
+ kind: "recipe",
2272
+ plan,
2273
+ stop() {
2274
+ realized.stop();
2275
+ voices.release(voiceId);
2276
+ active.delete(voiceId);
2277
+ }
2278
+ };
2279
+ };
2280
+ const admit = (recipe2, gain, distance) => {
2281
+ const decision = voices.request({ ...recipe2.group ? { group: recipe2.group } : {}, priority: recipe2.priority ?? busPriority(recipe2.bus), gain, distance, length: 0 });
2282
+ if (!decision.accepted) return null;
2283
+ for (const id of decision.evicted) {
2284
+ active.get(id)?.stop();
2285
+ active.delete(id);
2286
+ }
2287
+ return decision.id;
2288
+ };
2289
+ const picker = createSetPicker();
2290
+ const resolve = (id, random, chooser = picker) => {
2291
+ if (registry.sound(id)) return { id, rate: 1 };
2292
+ const set = registry.set(id);
2293
+ return set ? chooser.pick(set, random) : { id, rate: 1 };
2294
+ };
2295
+ const loadSounds = (sounds) => {
2296
+ const audio = ready();
2297
+ if (!audio) return Promise.resolve({ loaded: [], failed: sounds.flatMap(sampleUrls).map((url2) => ({ url: url2, error: "no audio context" })) });
2298
+ return sampleStore.load([...new Set(sounds.flatMap(sampleUrls))], audio);
2299
+ };
2300
+ const sampleInput = (playOptions, random, withMemory) => ({
2301
+ random,
2302
+ listener,
2303
+ environment: playOptions.environment ?? environment,
2304
+ ...withMemory ? { memory } : {},
2305
+ ...options.distance ? { distanceMode: options.distance } : {},
2306
+ ...playOptions.volume === void 0 ? {} : { volume: playOptions.volume },
2307
+ ...playOptions.intensity === void 0 ? {} : { intensity: playOptions.intensity },
2308
+ ...playOptions.distance === void 0 ? {} : { distance: playOptions.distance },
2309
+ ...playOptions.position ? { position: playOptions.position } : {},
2310
+ ...playOptions.occlusion !== void 0 ? { occlusion: playOptions.occlusion } : options.occlusion && playOptions.position ? { occlusion: options.occlusion(playOptions.position) } : {}
2311
+ });
2312
+ const playSample = (sample, playOptions, random, store) => {
2313
+ if (busSettings.get(sample.bus)?.muted) return null;
2314
+ if (!sampleStore.ready(sampleUrls(sample))) {
2315
+ if (options.samples?.autoLoad !== false && ready()) void loadSounds([sample]).catch(() => void 0);
2316
+ if (sample.fallback && sample.fallback !== sample.id) return playWith(sample.fallback, playOptions, random, store, sample.id);
2317
+ return registry.sound(sample.id) ? void 0 : null;
2318
+ }
2319
+ const plan = planSample(sample, sampleInput(playOptions, playOptions.seed === void 0 ? random : createRng(playOptions.seed), true));
2320
+ if (!plan) return null;
2321
+ const audio = ready();
2322
+ if (!audio) return null;
2323
+ unlockContext(audio);
2324
+ advance();
2325
+ const voiceId = admit(plan, plan.level, plan.distance);
2326
+ if (voiceId === null) return null;
2327
+ const requestedAt = audio.currentTime;
2328
+ const time = requestedAt + (playOptions.delay ?? 0);
2329
+ const chokeList = sample.choke ? choked(sample.id) : void 0;
2330
+ const realized = realizeSample(plan, { context: audio, output: sampleOutput(plan.bus), buffer: (url2) => sampleStore.buffer(url2), time });
2331
+ const sampleBank = registry.bankOf(plan.id);
2332
+ announce({
2333
+ id: plan.id,
2334
+ kind: "sample",
2335
+ ...sampleBank ? { bank: sampleBank } : {},
2336
+ requestedAt,
2337
+ startAt: time,
2338
+ outputLatency: latencyOf(audio),
2339
+ level: plan.level,
2340
+ distance: plan.distance,
2341
+ ...plan.pan === void 0 ? {} : { pan: plan.pan },
2342
+ layers: plan.layers.map((layer) => ({ role: layer.role, url: layer.url, take: layer.take, rate: layer.rate })),
2343
+ ...plan.tier ? { tier: plan.tier } : {}
2344
+ });
2345
+ voices.setLength(voiceId, sampleLength(plan, (url2) => sampleStore.seconds(url2)) + (playOptions.delay ?? 0));
2346
+ active.set(voiceId, realized);
2347
+ if (chokeList && sample.choke) {
2348
+ chokeList.push({ id: voiceId, voice: realized });
2349
+ while (chokeList.length > Math.max(1, sample.choke.voices)) {
2350
+ const oldest = chokeList.shift();
2351
+ if (!oldest) break;
2352
+ oldest.voice.fade(time, sample.choke.fade ?? 0.03);
2353
+ voices.release(oldest.id);
2354
+ active.delete(oldest.id);
2355
+ }
2356
+ }
2357
+ return {
2358
+ id: voiceId,
2359
+ kind: "sample",
2360
+ plan,
2361
+ stop() {
2362
+ realized.stop();
2363
+ voices.release(voiceId);
2364
+ active.delete(voiceId);
2365
+ }
2366
+ };
2367
+ };
2368
+ const playWith = (asked, playOptions, streamRandom, store, fallbackOf) => {
2369
+ const sample = fallbackOf === void 0 ? registry.sample(asked) : void 0;
2370
+ if (sample) {
2371
+ const played = playSample(sample, playOptions, streamRandom, store);
2372
+ if (played !== void 0) return played;
2373
+ }
2374
+ const seeded = playOptions.seed === void 0 ? null : createRng(playOptions.seed);
2375
+ const { id, rate } = resolve(asked, seeded ?? streamRandom);
2376
+ const random = seeded ?? streamRandom;
2377
+ const recipe2 = registry.sound(id);
2378
+ if (!recipe2 || busSettings.get(recipe2.bus)?.muted) return null;
2379
+ const distance = playOptions.distance ?? (playOptions.position ? listenerDistance(listener, playOptions.position, options.distance) : 0);
2380
+ const level = falloffGain(recipe2.falloff, playOptions.volume ?? 1, distance);
2381
+ const silent = isSilent(recipe2.falloff, level);
2382
+ if (silent && !recipe2.keepSilent) return null;
2383
+ const audio = ready();
2384
+ if (!audio) return null;
2385
+ unlockContext(audio);
2386
+ advance();
2387
+ const voiceId = silent ? void 0 : admit(recipe2, level, distance);
2388
+ if (voiceId === null) return null;
2389
+ if (!silent && usesNoise(recipe2)) prepareNoise(store, audio, mix.noise.seconds);
2390
+ const plan = planSound(recipe2, inputFor(recipe2, playOptions, random));
2391
+ return plan ? start(retunePlan(plan, rate), store, voiceId, playOptions.delay, fallbackOf, playOptions.urgent) : null;
2392
+ };
2393
+ const randomFor = (playOptions) => {
2394
+ if (playOptions.channel === void 0) return engineRandom;
2395
+ const existing = named.get(playOptions.channel);
1848
2396
  if (existing) return existing;
1849
- const node = busNodes.get(bus);
1850
- if (!context || !node || typeof context.createAnalyser !== "function") return void 0;
1851
- const analyser = context.createAnalyser();
1852
- analyser.fftSize = 1024;
1853
- node.connect(analyser);
1854
- const made = { analyser, data: new Float32Array(analyser.fftSize) };
1855
- meters.set(bus, made);
1856
- return made;
2397
+ const stream = streamFor(seed, playOptions.channel);
2398
+ named.set(playOptions.channel, stream);
2399
+ return stream;
1857
2400
  };
1858
- const ensureBus = (bus) => {
1859
- if (!context || !chain || busNodes.has(bus)) return;
1860
- const node = context.createGain();
1861
- node.connect(chain.master);
1862
- busNodes.set(bus, node);
1863
- if (watched.has(bus)) meterOf(bus);
2401
+ const planWith = (asked, playOptions, random) => {
2402
+ const { id, rate } = resolve(asked, random, createSetPicker());
2403
+ const recipe2 = registry.sound(id);
2404
+ const plan = recipe2 ? planSound(recipe2, inputFor(recipe2, playOptions, random)) : null;
2405
+ return plan ? retunePlan(plan, rate) : null;
1864
2406
  };
1865
- const ready = () => {
1866
- if (context) return context;
1867
- const created = (options.createContext ?? globalContext)();
1868
- if (!created) return null;
1869
- context = created;
1870
- chain = buildChain(created, mix, engineRandom);
1871
- if (mix.noise.create === "eager") noiseBuffer(shared, created, mix.noise.seconds);
1872
- for (const [bus, setting] of Object.entries(mix.buses ?? {})) {
1873
- busSettings.set(bus, { ...setting, ...busSettings.get(bus) });
2407
+ const loopHost = {
2408
+ ready,
2409
+ output: outputFor,
2410
+ prepareNoise: () => context && prepareNoise(shared, context, mix.noise.seconds),
2411
+ noise: () => noiseBuffer(shared, context, mix.noise.seconds),
2412
+ register: (loop) => void loops.add(loop),
2413
+ unregister: (loop) => void loops.delete(loop),
2414
+ listener: () => listener,
2415
+ ...options.distance ? { distance: options.distance } : {}
2416
+ };
2417
+ return {
2418
+ registry,
2419
+ register: (bank) => registry.register(bank),
2420
+ plan: (id, playOptions = {}) => planWith(id, playOptions, createRng(playOptions.seed ?? seed)),
2421
+ planSample(id, playOptions = {}) {
2422
+ const sample = registry.sample(id);
2423
+ return sample ? planSample(sample, sampleInput(playOptions, createRng(playOptions.seed ?? seed), false)) : null;
2424
+ },
2425
+ loadSamples(ids) {
2426
+ const chosen = (ids ?? registry.sampleIds()).map((id) => registry.sample(id)).filter((sample) => sample !== void 0);
2427
+ return loadSounds(chosen);
2428
+ },
2429
+ sampleStatus(id) {
2430
+ const sample = registry.sample(id);
2431
+ if (!sample) return "none";
2432
+ const statuses = sampleUrls(sample).map((url2) => sampleStore.status(url2));
2433
+ if (statuses.every((status) => status === "ready")) return "ready";
2434
+ if (statuses.includes("failed")) return "failed";
2435
+ if (statuses.includes("loading")) return "loading";
2436
+ return statuses.includes("ready") ? "loading" : "none";
2437
+ },
2438
+ setEnvironment(next) {
2439
+ environment = next;
2440
+ },
2441
+ output: () => chain?.master ?? null,
2442
+ addSampleSource(accepts, loader) {
2443
+ sources.push({ accepts, loader });
2444
+ },
2445
+ watchVoices(listener2) {
2446
+ watchers.add(listener2);
2447
+ return () => void watchers.delete(listener2);
2448
+ },
2449
+ play: (id, playOptions = {}) => playWith(id, playOptions, randomFor(playOptions), shared),
2450
+ playPlan(plan, playOptions = {}) {
2451
+ const audio = ready();
2452
+ if (!audio) return null;
2453
+ advance();
2454
+ const recipe2 = { id: plan.id, bus: plan.bus, layers: [], sources: [], priority: plan.priority, ...plan.group ? { group: plan.group } : {} };
2455
+ const voiceId = plan.silent ? void 0 : admit(recipe2, plan.level, plan.distance);
2456
+ if (voiceId === null) return null;
2457
+ if (planUsesNoise(plan)) prepareNoise(shared, audio, mix.noise.seconds);
2458
+ return start(plan, shared, voiceId, playOptions.delay);
2459
+ },
2460
+ loop(id, loopOptions = {}) {
2461
+ const sample = registry.sample(id);
2462
+ if (!sample?.loop) return createLoopHandle(registry.loop(id), loopHost, loopOptions.volume);
2463
+ return createSampleLoopHandle(
2464
+ sample,
2465
+ {
2466
+ ready,
2467
+ output: sampleOutput,
2468
+ buffer: (url2) => sampleStore.buffer(url2),
2469
+ isReady: (sound) => sampleStore.ready(sampleUrls(sound)),
2470
+ load: (sound) => options.samples?.autoLoad === false ? Promise.resolve() : loadSounds([sound]),
2471
+ fallback: (volume) => createLoopHandle(registry.loop(sample.fallback ?? sample.id) ?? registry.loop(sample.id), loopHost, volume),
2472
+ random: engineRandom,
2473
+ register: loopHost.register,
2474
+ unregister: loopHost.unregister,
2475
+ muted: (bus) => busSettings.get(bus)?.muted === true
2476
+ },
2477
+ loopOptions.volume
2478
+ );
2479
+ },
2480
+ channel(channelOptions) {
2481
+ const random = channelOptions.random ?? (channelOptions.seed === void 0 ? engineRandom : createRng(channelOptions.seed));
2482
+ const store = channelOptions.ownNoise ? createNoiseStore(random) : shared;
2483
+ return {
2484
+ random,
2485
+ play: (id, playOptions = {}) => playWith(id, playOptions, random, store),
2486
+ plan: (id, playOptions = {}) => planWith(id, playOptions, createRng(playOptions.seed ?? seed))
2487
+ };
2488
+ },
2489
+ setListener(next) {
2490
+ listener = next;
2491
+ },
2492
+ setBus(id, setting) {
2493
+ busSettings.set(id, { ...busSettings.get(id), ...setting });
2494
+ if (!context) return;
2495
+ ensureBus(id);
2496
+ busNodes.get(id).gain.value = busGain(busSettings.get(id));
2497
+ },
2498
+ setMix(next) {
2499
+ const previous = mix;
2500
+ mix = resolveMix(next, options.profiles);
2501
+ if (!context || !chain) return;
2502
+ const old = chain;
2503
+ chain = buildChain(context, mix, engineRandom);
2504
+ if (mix.noise.seconds !== previous.noise.seconds) shared.reset();
2505
+ for (const node of busNodes.values()) {
2506
+ node.disconnect();
2507
+ node.connect(chain.master);
2508
+ }
2509
+ for (const loop of loops) loop.rewire();
2510
+ old.disconnect();
2511
+ },
2512
+ setMaster(gain) {
2513
+ mix = { ...mix, master: gain };
2514
+ if (chain) chain.master.gain.value = gain;
2515
+ },
2516
+ unlock() {
2517
+ const audio = ready();
2518
+ if (audio) unlockContext(audio);
2519
+ },
2520
+ warmUp() {
2521
+ const audio = ready();
2522
+ if (!audio) return false;
2523
+ noiseBuffer(shared, audio, mix.noise.seconds);
2524
+ return true;
2525
+ },
2526
+ level(bus) {
2527
+ watched.add(bus);
2528
+ ensureBus(bus);
2529
+ const meter = meterOf(bus);
2530
+ if (!meter) return 0;
2531
+ meter.analyser.getFloatTimeDomainData(meter.data);
2532
+ let sum = 0;
2533
+ for (const sample of meter.data) sum += sample * sample;
2534
+ return Math.sqrt(sum / meter.data.length);
2535
+ },
2536
+ deferredQueue: () => startQueue?.deferredQueue() ?? null,
2537
+ stats() {
2538
+ advance();
2539
+ const voiceStats = voices.stats();
2540
+ return {
2541
+ context: context ? context.state : "none",
2542
+ voices: voiceStats.active,
2543
+ peak: voiceStats.peak,
2544
+ dropped: voiceStats.dropped,
2545
+ maxVoices: voiceStats.maxVoices,
2546
+ loops: [...loops].filter((loop) => loop.created).length
2547
+ };
2548
+ },
2549
+ dispose() {
2550
+ startQueue?.clear();
2551
+ for (const id of active.keys()) voices.release(id);
2552
+ for (const loop of [...loops]) loop.stop();
2553
+ active.clear();
2554
+ void context?.close();
2555
+ context = null;
2556
+ chain = null;
2557
+ busNodes.clear();
2558
+ meters.clear();
2559
+ shared.reset();
2560
+ sampleStore.clear();
2561
+ memory.clear();
2562
+ sampleLimiters.clear();
2563
+ chokes.clear();
2564
+ }
2565
+ };
2566
+ }
2567
+
2568
+ // src/measure/analyze.ts
2569
+ var dbOf = (amplitude) => amplitude > 0 ? 20 * Math.log10(amplitude) : -Infinity;
2570
+ var clampIndex = (channels, index) => Math.max(0, Math.min(channels[0]?.length ?? 0, Math.round(index)));
2571
+ function peakOf(channels, start = 0, end3 = Number.POSITIVE_INFINITY) {
2572
+ let peak = 0;
2573
+ const from = clampIndex(channels, start);
2574
+ for (const channel of channels) {
2575
+ const to = Math.min(channel.length, end3);
2576
+ for (let index = from; index < to; index++) {
2577
+ const value = Math.abs(channel[index]);
2578
+ if (value > peak) peak = value;
1874
2579
  }
1875
- for (const [bus, setting] of busSettings) {
1876
- ensureBus(bus);
1877
- busNodes.get(bus).gain.value = busGain(setting);
2580
+ }
2581
+ return peak;
2582
+ }
2583
+ function peakIndex(channels, start = 0, end3 = Number.POSITIVE_INFINITY) {
2584
+ let peak = -1;
2585
+ let at = -1;
2586
+ const from = clampIndex(channels, start);
2587
+ for (const channel of channels) {
2588
+ const to = Math.min(channel.length, end3);
2589
+ for (let index = from; index < to; index++) {
2590
+ const value = Math.abs(channel[index]);
2591
+ if (value > peak) {
2592
+ peak = value;
2593
+ at = index;
2594
+ }
1878
2595
  }
1879
- lastTime = created.currentTime;
1880
- return created;
2596
+ }
2597
+ return at;
2598
+ }
2599
+ function onsetIndex(channels, start = 0, threshold = 1e-3) {
2600
+ const length = channels[0]?.length ?? 0;
2601
+ for (let index = clampIndex(channels, start); index < length; index++) {
2602
+ for (const channel of channels) if (Math.abs(channel[index]) >= threshold) return index;
2603
+ }
2604
+ return -1;
2605
+ }
2606
+ function rmsDb(channels, start, end3) {
2607
+ let sum = 0;
2608
+ let count = 0;
2609
+ const from = clampIndex(channels, start);
2610
+ for (const channel of channels) {
2611
+ const to = Math.min(channel.length, Math.round(end3));
2612
+ for (let index = from; index < to; index++) {
2613
+ const value = channel[index];
2614
+ sum += value * value;
2615
+ count++;
2616
+ }
2617
+ }
2618
+ return count === 0 ? -Infinity : dbOf(Math.sqrt(sum / count));
2619
+ }
2620
+ function centroidHz(channels, sampleRate, start, size = 2048) {
2621
+ const from = clampIndex(channels, start);
2622
+ const frame = new Float64Array(size);
2623
+ for (const channel of channels) {
2624
+ for (let index = 0; index < size; index++) frame[index] = frame[index] + (channel[from + index] ?? 0);
2625
+ }
2626
+ for (let index = 0; index < size; index++) frame[index] = frame[index] * (0.5 - 0.5 * Math.cos(2 * Math.PI * index / (size - 1)));
2627
+ let weighted = 0;
2628
+ let total = 0;
2629
+ const bins = size / 2;
2630
+ for (let bin = 1; bin < bins; bin++) {
2631
+ let re = 0;
2632
+ let im = 0;
2633
+ const step = 2 * Math.PI * bin / size;
2634
+ for (let index = 0; index < size; index++) {
2635
+ const value = frame[index];
2636
+ re += value * Math.cos(step * index);
2637
+ im -= value * Math.sin(step * index);
2638
+ }
2639
+ const magnitude = Math.hypot(re, im);
2640
+ weighted += magnitude * (bin * sampleRate / size);
2641
+ total += magnitude;
2642
+ }
2643
+ return total > 0 ? weighted / total : 0;
2644
+ }
2645
+ function balanceOf(left, right, start, end3) {
2646
+ let l = 0;
2647
+ let r = 0;
2648
+ const to = Math.min(left.length, right.length, Math.round(end3));
2649
+ for (let index = Math.max(0, Math.round(start)); index < to; index++) {
2650
+ l += left[index] ** 2;
2651
+ r += right[index] ** 2;
2652
+ }
2653
+ return l + r > 0 ? (r - l) / (r + l) : 0;
2654
+ }
2655
+ function ringSeconds(channels, sampleRate, start, floorDb = -60) {
2656
+ const window = Math.max(1, Math.round(sampleRate * 0.01));
2657
+ const length = channels[0]?.length ?? 0;
2658
+ let last = clampIndex(channels, start);
2659
+ for (let index = last; index < length; index += window) {
2660
+ if (dbOf(peakOf(channels, index, index + window)) > floorDb) last = index + window;
2661
+ }
2662
+ return (last - start) / sampleRate;
2663
+ }
2664
+ function countHits(channels, sampleRate, threshold) {
2665
+ const window = Math.max(1, Math.round(sampleRate * 2e-3));
2666
+ const length = channels[0]?.length ?? 0;
2667
+ const envelope2 = [];
2668
+ for (let index = 0; index < length; index += window) envelope2.push(peakOf(channels, index, index + window));
2669
+ const history = 10;
2670
+ const refractory = Math.round(0.03 / 2e-3);
2671
+ let hits = 0;
2672
+ let quietUntil = -1;
2673
+ for (let index = 0; index < envelope2.length; index++) {
2674
+ const level = envelope2[index];
2675
+ if (index < quietUntil || level < threshold) continue;
2676
+ let sum = 0;
2677
+ for (let back = Math.max(0, index - history); back < index; back++) sum += envelope2[back];
2678
+ const before = index === 0 ? 0 : sum / Math.min(history, index);
2679
+ if (level >= 2 * before) {
2680
+ hits++;
2681
+ quietUntil = index + refractory;
2682
+ }
2683
+ }
2684
+ return hits;
2685
+ }
2686
+ function endDropDb(channels, sampleRate, seconds = 5e-3) {
2687
+ const length = channels[0]?.length ?? 0;
2688
+ const whole = peakOf(channels);
2689
+ if (whole === 0) return -Infinity;
2690
+ return dbOf(peakOf(channels, length - Math.round(sampleRate * seconds), length) / whole);
2691
+ }
2692
+
2693
+ // src/measure/criteria.ts
2694
+ var CRITERIA = ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10"];
2695
+ var CRITERIA_TARGETS = {
2696
+ /** C1: event to the first sample, and onset to the peak (one 60 fps frame). */
2697
+ syncMs: 20,
2698
+ peakAfterOnsetMs: 1e3 / 60,
2699
+ /** C3: distances measured, metres. */
2700
+ distances: [1, 10, 50, 200],
2701
+ /** C5: plays, distinct takes and the pitch range (fraction). */
2702
+ plays: 10,
2703
+ minTakes: 4,
2704
+ pitch: [0.03, 0.05],
2705
+ /** C6: the highest peak, dBFS. */
2706
+ peakDb: -1,
2707
+ /** C8: the end of each file against its peak, dB. */
2708
+ endDropDb: -40,
2709
+ /** C9: a burst of rapid fire. */
2710
+ burst: 20,
2711
+ burstSpacing: 0.06
2712
+ };
2713
+ var EVENT_AT = 0.2;
2714
+ var offlineFactory = (options) => options.createOfflineContext ?? ((channels, length, sampleRate) => {
2715
+ const Constructor = globalThis.OfflineAudioContext;
2716
+ if (!Constructor) throw new Error("no OfflineAudioContext here: the harness runs in a browser");
2717
+ return new Constructor(channels, length, sampleRate);
2718
+ });
2719
+ async function render(options, seconds, act, engineExtras = {}) {
2720
+ const sampleRate = options.sampleRate ?? 48e3;
2721
+ const context = offlineFactory(options)(2, Math.ceil(seconds * sampleRate), sampleRate);
2722
+ const events = [];
2723
+ const engine = createAudioEngine({
2724
+ banks: options.banks,
2725
+ createContext: () => context,
2726
+ samples: { loader: options.loader, autoLoad: false },
2727
+ onVoiceStart: (event) => events.push(event),
2728
+ ...options.mix ? { mix: options.mix } : {},
2729
+ ...engineExtras.occlusion ? { occlusion: engineExtras.occlusion } : {},
2730
+ ...engineExtras.environment ? { environment: engineExtras.environment } : {}
2731
+ });
2732
+ const ids = [.../* @__PURE__ */ new Set([options.sound, ...(options.actions ?? []).map((action) => action.play), ...options.loudnessOrder ?? []])].filter((id) => engine.registry.sample(id));
2733
+ const report = await engine.loadSamples(ids);
2734
+ if (report.failed.length > 0) throw new Error(`files did not load: ${report.failed.map((entry) => `${entry.url} (${entry.error})`).join(", ")}`);
2735
+ const handles = act(engine);
2736
+ const buffer = await context.startRendering();
2737
+ const channels = Array.from({ length: buffer.numberOfChannels }, (_, index) => buffer.getChannelData(index));
2738
+ return { channels, sampleRate, events, handles };
2739
+ }
2740
+ var ms = (seconds) => `${(seconds * 1e3).toFixed(1)} ms`;
2741
+ var db = (value) => Number.isFinite(value) ? `${value.toFixed(1)} dB` : "\u2212\u221E";
2742
+ var decreasing = (values) => values.every((value, index) => index === 0 || value < values[index - 1]);
2743
+ async function c1(options) {
2744
+ const result = await render(options, 1.2, (engine) => engine.play(options.sound, { distance: 1, delay: EVENT_AT }) ? 1 : 0);
2745
+ const onset = onsetIndex(result.channels, 0, 1e-3);
2746
+ const event = result.events.find((entry) => entry.id === options.sound);
2747
+ if (onset < 0 || !event) return { id: "C1", pass: false, value: "sem som", target: `\u2264 ${CRITERIA_TARGETS.syncMs} ms` };
2748
+ const delay = onset / result.sampleRate - EVENT_AT + (event.startAt - event.requestedAt - EVENT_AT) + (options.baseLatency ?? 0);
2749
+ const peak = peakIndex(result.channels, onset, onset + Math.round(result.sampleRate * 0.25));
2750
+ const toPeak = (peak - onset) / result.sampleRate;
2751
+ const pass = delay <= CRITERIA_TARGETS.syncMs / 1e3 && toPeak <= CRITERIA_TARGETS.peakAfterOnsetMs / 1e3;
2752
+ return {
2753
+ id: "C1",
2754
+ pass,
2755
+ value: `${ms(delay)}, pico +${ms(toPeak)}`,
2756
+ target: `\u2264 ${CRITERIA_TARGETS.syncMs} ms; pico no quadro do contato`,
2757
+ data: { delay, toPeak, baseLatency: options.baseLatency ?? 0, outputLatency: options.outputLatency ?? 0 }
1881
2758
  };
1882
- const advance = () => {
1883
- if (!context) return;
1884
- const elapsed = context.currentTime - lastTime;
1885
- lastTime = context.currentTime;
1886
- if (elapsed > 0) for (const id of voices.advance(elapsed)) active.delete(id);
2759
+ }
2760
+ async function c2(options) {
2761
+ const actions = options.actions ?? [{ action: options.sound, play: options.sound, expect: options.sound }];
2762
+ const result = await render(options, 0.5, (engine) => {
2763
+ let played = 0;
2764
+ for (const action of actions) if (engine.play(action.play, { distance: 1, ...action.options })) played++;
2765
+ return played;
2766
+ });
2767
+ const wrong = actions.filter((action, index) => {
2768
+ const event = result.events[index];
2769
+ return !event || (event.fallbackOf ?? event.id) !== action.expect || event.kind !== "sample";
2770
+ });
2771
+ return {
2772
+ id: "C2",
2773
+ pass: wrong.length === 0,
2774
+ value: `${actions.length - wrong.length}/${actions.length} certas`,
2775
+ target: "100% das a\xE7\xF5es batem com a tabela (arquivo, n\xE3o o substituto)",
2776
+ data: { wrong: wrong.map((action) => action.action), events: result.events.map((event) => `${event.kind}:${event.id}`) }
1887
2777
  };
1888
- const inputFor = (recipe2, playOptions, random) => ({
1889
- random,
1890
- listener,
1891
- noiseSeconds: mix.noise.seconds,
1892
- ...options.distance ? { distanceMode: options.distance } : {},
1893
- ...playOptions.volume === void 0 ? {} : { volume: playOptions.volume },
1894
- ...playOptions.distance === void 0 ? {} : { distance: playOptions.distance },
1895
- ...playOptions.position ? { position: playOptions.position } : {},
1896
- ...playOptions.params ? { params: playOptions.params } : {},
1897
- ...options.occlusion && playOptions.position && recipe2.falloff?.model !== "none" ? { occlusion: options.occlusion(playOptions.position) } : {}
2778
+ }
2779
+ async function c3(options, sound) {
2780
+ const levels = [];
2781
+ const brightness = [];
2782
+ for (const distance of CRITERIA_TARGETS.distances) {
2783
+ const result = await render(options, 1.2, (engine) => engine.play(options.sound, { position: [0, 0, -distance], seed: 1, delay: EVENT_AT }) ? 1 : 0);
2784
+ const onset = onsetIndex(result.channels, 0, 1e-5);
2785
+ levels.push(onset < 0 ? -Infinity : rmsDb(result.channels, onset, onset + result.sampleRate * 0.3));
2786
+ brightness.push(onset < 0 ? 0 : centroidHz(result.channels, result.sampleRate, onset));
2787
+ }
2788
+ const far = planSample(sound, { random: createRng(1), distance: CRITERIA_TARGETS.distances.at(-1) });
2789
+ const switched = (far?.farMix ?? 0) === 1 && (far?.layers.some((layer) => layer.role === "far") ?? false);
2790
+ const pass = decreasing(levels) && decreasing(brightness) && switched;
2791
+ return {
2792
+ id: "C3",
2793
+ pass,
2794
+ value: `${levels.map((level) => db(level)).join(" \u203A ")}; ${brightness.map((hz) => `${Math.round(hz)} Hz`).join(" \u203A ")}${switched ? "; longe = far" : "; sem camada far"}`,
2795
+ target: "cai e escurece com a dist\xE2ncia; troca para a vers\xE3o de longe depois de ~50 m",
2796
+ data: { distances: CRITERIA_TARGETS.distances, levels, brightness, switched }
2797
+ };
2798
+ }
2799
+ async function c4(options) {
2800
+ const side = async (x) => {
2801
+ const result = await render(options, 0.8, (engine) => engine.play(options.sound, { position: [x, 0, -2], seed: 1, delay: EVENT_AT }) ? 1 : 0);
2802
+ const [left2, right2] = result.channels;
2803
+ return balanceOf(left2, right2, 0, left2.length);
2804
+ };
2805
+ const right = await side(10);
2806
+ const left = await side(-10);
2807
+ const bright = async (occluded) => {
2808
+ const result = await render(options, 0.8, (engine) => engine.play(options.sound, { position: [0, 0, -10], seed: 1, delay: EVENT_AT }) ? 1 : 0, occluded ? { occlusion: () => 1 } : {});
2809
+ const onset = onsetIndex(result.channels, 0, 1e-5);
2810
+ return onset < 0 ? 0 : centroidHz(result.channels, result.sampleRate, onset);
2811
+ };
2812
+ const clear = await bright(false);
2813
+ const wall = await bright(true);
2814
+ const pass = right > 0.2 && left < -0.2 && wall < clear * 0.7;
2815
+ return {
2816
+ id: "C4",
2817
+ pass,
2818
+ value: `dir ${right.toFixed(2)} esq ${left.toFixed(2)}; parede ${Math.round(wall)} Hz de ${Math.round(clear)} Hz`,
2819
+ target: "lado certo; atr\xE1s da parede fica abafado",
2820
+ data: { right, left, clear, wall }
2821
+ };
2822
+ }
2823
+ async function c5(options, sound) {
2824
+ const result = await render(options, 0.3 * CRITERIA_TARGETS.plays + 1, (engine) => {
2825
+ let played = 0;
2826
+ for (let index = 0; index < CRITERIA_TARGETS.plays; index++) if (engine.play(options.sound, { distance: 1, delay: 0.1 + index * 0.3 })) played++;
2827
+ return played;
1898
2828
  });
1899
- const startQueue = options.deferredStart ? createStartQueue(options.deferredStart) : null;
1900
- const build = (plan, store, delay) => {
1901
- const audio = context;
1902
- return realize(plan, {
1903
- context: audio,
1904
- output: outputFor(plan.bus),
1905
- ...chain?.wet ? { wet: chain.wet } : {},
1906
- noise: () => noiseBuffer(store, audio, mix.noise.seconds),
1907
- time: audio.currentTime + delay
1908
- });
2829
+ const close = result.events.map((event) => event.layers?.find((layer) => layer.role === "close"));
2830
+ const takes2 = close.map((layer) => layer?.url ?? "");
2831
+ const repeats = takes2.filter((take, index) => index > 0 && take === takes2[index - 1]).length;
2832
+ const distinct = new Set(takes2).size;
2833
+ const rates = close.map((layer) => layer?.rate ?? 1);
2834
+ const spread = (Math.max(...rates) - Math.min(...rates)) / 2;
2835
+ const pitch = sound.pitch ?? 0.04;
2836
+ const pass = distinct >= CRITERIA_TARGETS.minTakes && repeats === 0 && pitch >= CRITERIA_TARGETS.pitch[0] && pitch <= CRITERIA_TARGETS.pitch[1] && spread > 0.01;
2837
+ return {
2838
+ id: "C5",
2839
+ pass,
2840
+ value: `${distinct} tomadas, ${repeats} repeti\xE7\xF5es seguidas, tom \xB1${(pitch * 100).toFixed(0)}%`,
2841
+ target: `\u2265 ${CRITERIA_TARGETS.minTakes} varia\xE7\xF5es, nunca a mesma duas vezes, tom \xB13\u20135%`,
2842
+ data: { takes: takes2, rates }
1909
2843
  };
1910
- const deferVoice = (plan, store, voiceId, delay) => {
1911
- let built2 = null;
1912
- let cancelled = false;
1913
- const pending = {
1914
- get sources() {
1915
- return built2?.sources ?? [];
1916
- },
1917
- stop() {
1918
- cancelled = true;
1919
- built2?.stop();
1920
- }
1921
- };
1922
- startQueue.push({
1923
- run() {
1924
- if (!cancelled && context) built2 = build(plan, store, delay);
1925
- },
1926
- drop() {
1927
- if (voiceId === void 0) return;
1928
- voices.release(voiceId);
1929
- active.delete(voiceId);
1930
- }
1931
- });
1932
- return pending;
2844
+ }
2845
+ async function c6(options) {
2846
+ const peakAt = async (id) => {
2847
+ const result = await render(options, 1.2, (engine) => engine.play(id, { distance: 1, seed: 1, delay: EVENT_AT }) ? 1 : 0);
2848
+ const onset = onsetIndex(result.channels, 0, 1e-5);
2849
+ return { peak: dbOf(peakOf(result.channels)), rms: onset < 0 ? -Infinity : rmsDb(result.channels, onset, onset + result.sampleRate * 0.3) };
1933
2850
  };
1934
- const start = (plan, store, voiceId, delay = 0) => {
1935
- const realized = startQueue ? deferVoice(plan, store, voiceId, delay) : build(plan, store, delay);
1936
- if (voiceId === void 0) return null;
1937
- voices.setLength(voiceId, plan.length + delay);
1938
- active.set(voiceId, realized);
1939
- return {
1940
- id: voiceId,
1941
- plan,
1942
- stop() {
1943
- realized.stop();
1944
- voices.release(voiceId);
1945
- active.delete(voiceId);
1946
- }
1947
- };
2851
+ const own = await peakAt(options.sound);
2852
+ const order = options.loudnessOrder ?? [];
2853
+ const loudness = [];
2854
+ for (const id of order) loudness.push((await peakAt(id)).rms);
2855
+ const ordered = order.length < 2 || decreasing(loudness);
2856
+ const pass = own.peak <= CRITERIA_TARGETS.peakDb && ordered;
2857
+ return {
2858
+ id: "C6",
2859
+ pass,
2860
+ value: `pico ${db(own.peak)}, RMS ${db(own.rms)}${order.length > 1 ? `; ordem ${ordered ? "ok" : "errada"}` : ""}`,
2861
+ target: "tiro > explos\xE3o > impacto > passo; pico \u2264 \u22121 dBFS",
2862
+ data: { peak: own.peak, rms: own.rms, order, loudness }
1948
2863
  };
1949
- const admit = (recipe2, gain, distance) => {
1950
- const decision = voices.request({ ...recipe2.group ? { group: recipe2.group } : {}, priority: recipe2.priority ?? busPriority(recipe2.bus), gain, distance, length: 0 });
1951
- if (!decision.accepted) return null;
1952
- for (const id of decision.evicted) {
1953
- active.get(id)?.stop();
1954
- active.delete(id);
1955
- }
1956
- return decision.id;
2864
+ }
2865
+ async function c7(options, sound) {
2866
+ if (!sound.tiers || sound.tiers.length < 2) return { id: "C7", pass: false, value: "sem camadas leve/m\xE9dio/forte", target: "muda de camada, n\xE3o s\xF3 de volume" };
2867
+ const at = async (intensity) => {
2868
+ const result = await render(options, 1.2, (engine) => engine.play(options.sound, { distance: 1, seed: 1, intensity, delay: EVENT_AT }) ? 1 : 0);
2869
+ const onset = onsetIndex(result.channels, 0, 1e-5);
2870
+ const event = result.events[0];
2871
+ return { tier: event?.tier ?? "", rms: onset < 0 ? -Infinity : rmsDb(result.channels, onset, onset + result.sampleRate * 0.3), files: event?.layers?.find((layer) => layer.role === "close")?.url ?? "" };
1957
2872
  };
1958
- const picker = createSetPicker();
1959
- const resolve = (id, random, chooser = picker) => {
1960
- if (registry.sound(id)) return { id, rate: 1 };
1961
- const set = registry.set(id);
1962
- return set ? chooser.pick(set, random) : { id, rate: 1 };
2873
+ const light = await at(0.1);
2874
+ const heavy = await at(0.9);
2875
+ const pass = light.tier !== heavy.tier && light.files !== heavy.files && heavy.rms > light.rms;
2876
+ return {
2877
+ id: "C7",
2878
+ pass,
2879
+ value: `${light.tier} ${db(light.rms)} \u2192 ${heavy.tier} ${db(heavy.rms)}`,
2880
+ target: "golpe fraco x forte troca a camada (leve/m\xE9dio/forte), n\xE3o s\xF3 o volume",
2881
+ data: { light, heavy }
1963
2882
  };
1964
- const playWith = (asked, playOptions, streamRandom, store) => {
1965
- const seeded = playOptions.seed === void 0 ? null : createRng(playOptions.seed);
1966
- const { id, rate } = resolve(asked, seeded ?? streamRandom);
1967
- const random = seeded ?? streamRandom;
1968
- const recipe2 = registry.sound(id);
1969
- if (!recipe2 || busSettings.get(recipe2.bus)?.muted) return null;
1970
- const distance = playOptions.distance ?? (playOptions.position ? listenerDistance(listener, playOptions.position, options.distance) : 0);
1971
- const level = falloffGain(recipe2.falloff, playOptions.volume ?? 1, distance);
1972
- const silent = isSilent(recipe2.falloff, level);
1973
- if (silent && !recipe2.keepSilent) return null;
1974
- const audio = ready();
1975
- if (!audio) return null;
1976
- unlockContext(audio);
1977
- advance();
1978
- const voiceId = silent ? void 0 : admit(recipe2, level, distance);
1979
- if (voiceId === null) return null;
1980
- if (!silent && usesNoise(recipe2)) prepareNoise(store, audio, mix.noise.seconds);
1981
- const plan = planSound(recipe2, inputFor(recipe2, playOptions, random));
1982
- return plan ? start(retunePlan(plan, rate), store, voiceId, playOptions.delay) : null;
2883
+ }
2884
+ async function c8(options, sound) {
2885
+ const sampleRate = options.sampleRate ?? 48e3;
2886
+ const decoder = offlineFactory(options)(1, sampleRate, sampleRate);
2887
+ const drops = [];
2888
+ for (const url2 of sampleUrls(sound)) {
2889
+ const buffer = await decoder.decodeAudioData(await options.loader(url2));
2890
+ drops.push({ url: url2, drop: endDropDb([buffer.getChannelData(0)], buffer.sampleRate) });
2891
+ }
2892
+ const worst = drops.reduce((max, entry) => Math.max(max, entry.drop), -Infinity);
2893
+ const ring = async (environment) => {
2894
+ const result = await render(options, 3, (engine) => engine.play(options.sound, { distance: 1, seed: 1, environment, delay: EVENT_AT }) ? 1 : 0);
2895
+ return ringSeconds(result.channels, result.sampleRate, Math.round(EVENT_AT * result.sampleRate));
1983
2896
  };
1984
- const randomFor = (playOptions) => {
1985
- if (playOptions.channel === void 0) return engineRandom;
1986
- const existing = named.get(playOptions.channel);
1987
- if (existing) return existing;
1988
- const stream = streamFor(seed, playOptions.channel);
1989
- named.set(playOptions.channel, stream);
1990
- return stream;
2897
+ const indoor = await ring("indoor");
2898
+ const outdoor = await ring("outdoor");
2899
+ const hasTails = Boolean(sound.tail?.indoor?.length && sound.tail?.outdoor?.length);
2900
+ const pass = worst <= CRITERIA_TARGETS.endDropDb && hasTails && outdoor > indoor;
2901
+ return {
2902
+ id: "C8",
2903
+ pass,
2904
+ value: `fim ${db(worst)}; cauda dentro ${indoor.toFixed(2)} s, fora ${outdoor.toFixed(2)} s${hasTails ? "" : " (sem as duas caudas)"}`,
2905
+ target: "n\xE3o corta seco; cauda de interior x exterior coerente",
2906
+ data: { drops, indoor, outdoor }
1991
2907
  };
1992
- const planWith = (asked, playOptions, random) => {
1993
- const { id, rate } = resolve(asked, random, createSetPicker());
1994
- const recipe2 = registry.sound(id);
1995
- const plan = recipe2 ? planSound(recipe2, inputFor(recipe2, playOptions, random)) : null;
1996
- return plan ? retunePlan(plan, rate) : null;
2908
+ }
2909
+ async function c9(options) {
2910
+ const { burst, burstSpacing } = CRITERIA_TARGETS;
2911
+ const first = 0.1;
2912
+ const result = await render(options, burst * burstSpacing + 1.5, (engine) => {
2913
+ let played = 0;
2914
+ for (let index = 0; index < burst; index++) if (engine.play(options.sound, { distance: 1, delay: first + index * burstSpacing })) played++;
2915
+ return played;
2916
+ });
2917
+ const peak = dbOf(peakOf(result.channels));
2918
+ const slots = [];
2919
+ for (let index = 0; index < burst; index++) {
2920
+ const at = Math.round((first + index * burstSpacing) * result.sampleRate);
2921
+ slots.push(rmsDb(result.channels, at, at + Math.round(burstSpacing * result.sampleRate)));
2922
+ }
2923
+ const sorted = [...slots].sort((a, b) => a - b);
2924
+ const median = sorted[Math.floor(sorted.length / 2)];
2925
+ const vanished = slots.filter((level) => level < median - 6).length;
2926
+ const buildUp = slots.slice(-5).reduce((sum, level) => sum + level, 0) / 5 - slots[0];
2927
+ const pass = result.handles === burst && vanished === 0 && buildUp <= 6 && peak <= CRITERIA_TARGETS.peakDb;
2928
+ return {
2929
+ id: "C9",
2930
+ pass,
2931
+ value: `${result.handles}/${burst} tocaram, ${vanished} sumiram, ac\xFAmulo ${buildUp >= 0 ? "+" : ""}${buildUp.toFixed(1)} dB, pico ${db(peak)}`,
2932
+ target: "rajada sem amontoar (ac\xFAmulo \u2264 6 dB) nem sumir; pico \u2264 \u22121 dBFS",
2933
+ data: { handles: result.handles, slots, vanished, buildUp, peak }
1997
2934
  };
1998
- const loopHost = {
1999
- ready,
2000
- output: outputFor,
2001
- prepareNoise: () => context && prepareNoise(shared, context, mix.noise.seconds),
2002
- noise: () => noiseBuffer(shared, context, mix.noise.seconds),
2003
- register: (loop) => void loops.add(loop),
2004
- unregister: (loop) => void loops.delete(loop)
2935
+ }
2936
+ async function measureCriteria(options, only = CRITERIA) {
2937
+ const probe = createAudioEngine({ banks: options.banks, createContext: () => null });
2938
+ const sound = probe.registry.sample(options.sound);
2939
+ if (!sound) throw new Error(`${options.sound} is not a sample sound of these banks`);
2940
+ const results = [];
2941
+ for (const id of only) {
2942
+ if (id === "C1") results.push(await c1(options));
2943
+ else if (id === "C2") results.push(await c2(options));
2944
+ else if (id === "C3") results.push(await c3(options, sound));
2945
+ else if (id === "C4") results.push(await c4(options));
2946
+ else if (id === "C5") results.push(await c5(options, sound));
2947
+ else if (id === "C6") results.push(await c6(options));
2948
+ else if (id === "C7") results.push(await c7(options, sound));
2949
+ else if (id === "C8") results.push(await c8(options, sound));
2950
+ else if (id === "C9") results.push(await c9(options));
2951
+ else results.push({ id: "C10", pass: null, value: "gravar e ouvir", target: "aprovado pelo ouvido do dono (print + grava\xE7\xE3o na 4280)" });
2952
+ }
2953
+ return results;
2954
+ }
2955
+ async function recordOutput(engine, seconds, act) {
2956
+ engine.unlock();
2957
+ const output = engine.output();
2958
+ const Recorder = globalThis.MediaRecorder;
2959
+ const context = output?.context;
2960
+ if (!output || !Recorder || !context || typeof context.createMediaStreamDestination !== "function") return null;
2961
+ const tap = context.createMediaStreamDestination();
2962
+ output.connect(tap);
2963
+ const recorder = new Recorder(tap.stream);
2964
+ const chunks = [];
2965
+ recorder.ondataavailable = (event) => chunks.push(event.data);
2966
+ const done = new Promise((resolve) => {
2967
+ recorder.onstop = () => resolve();
2968
+ });
2969
+ recorder.start();
2970
+ act();
2971
+ await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
2972
+ recorder.stop();
2973
+ await done;
2974
+ output.disconnect(tap);
2975
+ return new Blob(chunks, { type: recorder.mimeType || "audio/webm" });
2976
+ }
2977
+
2978
+ // src/catalog.ts
2979
+ var SFX_CATEGORIES = ["armas", "impactos", "explosoes-fogo", "personagem", "veiculos", "itens-portas", "ambiente"];
2980
+ var entries = (category, list) => list.map(([id, priority, current]) => ({ id, category, priority, ...current ? { current } : {} }));
2981
+ var SFX_CATALOG = [
2982
+ ...entries("armas", [
2983
+ ["pistol_22lr", 2, "sfx_pistol"],
2984
+ ["pistol_9mm", 1, "sfx_pistol"],
2985
+ ["pistol_45", 1, "sfx_pistol"],
2986
+ ["revolver_38_357", 2, "sfx_pistol"],
2987
+ ["revolver_44_50ae", 3, "sfx_pistol"],
2988
+ ["smg_9mm_auto", 1, "sfx_pistol"],
2989
+ ["shotgun_12", 1, "sfx_shotgun"],
2990
+ ["rifle_5_56", 1, "sfx_rifle"],
2991
+ ["rifle_7_62x39", 1, "sfx_rifle"],
2992
+ ["rifle_auto_burst", 1, "sfx_rifle"],
2993
+ ["rifle_308_3006", 2, "sfx_rifle"],
2994
+ ["sniper_338_300wm", 2, "sfx_rifle"],
2995
+ ["antimateriel_50bmg", 3, "sfx_rifle"],
2996
+ ["suppressed_pistol", 2],
2997
+ ["suppressed_rifle", 2],
2998
+ ["gunshot_far_pistol", 1],
2999
+ ["gunshot_far_rifle", 1],
3000
+ ["gunshot_far_shotgun", 1],
3001
+ ["gunshot_far_heavy", 2],
3002
+ ["gunshot_tail_indoor", 1],
3003
+ ["gunshot_tail_outdoor", 1],
3004
+ ["bullet_crack_by", 2],
3005
+ ["bullet_whiz", 2],
3006
+ ["ricochet_metal", 2, "ping"],
3007
+ ["dry_fire", 1],
3008
+ ["mag_out", 1],
3009
+ ["mag_in", 1],
3010
+ ["slide_pull_release", 1],
3011
+ ["pump_cycle", 1],
3012
+ ["casing_drop_concreto_pistol", 1],
3013
+ ["casing_drop_concreto_rifle", 1],
3014
+ ["casing_drop_concreto_shell", 1],
3015
+ ["casing_drop_madeira_pistol", 1],
3016
+ ["casing_drop_madeira_rifle", 1],
3017
+ ["casing_drop_madeira_shell", 1],
3018
+ ["casing_drop_metal_pistol", 1],
3019
+ ["casing_drop_metal_rifle", 1],
3020
+ ["casing_drop_metal_shell", 1],
3021
+ ["casing_drop_terra_pistol", 1],
3022
+ ["casing_drop_terra_rifle", 1],
3023
+ ["casing_drop_terra_shell", 1],
3024
+ ["rocket_fire", 2],
3025
+ ["smg_45_auto", 2, "sfx_pistol"],
3026
+ ["weapon_draw", 2],
3027
+ ["suppressor_attach", 3],
3028
+ ["suppressor_detach", 3]
3029
+ ]),
3030
+ ...entries("explosoes-fogo", [
3031
+ ["explosion_grenade", 1, "boom"],
3032
+ ["explosion_big", 1, "boom_big"],
3033
+ ["explosion_car", 2, "boom_big"],
3034
+ ["explosion_far", 1],
3035
+ ["debris_rain_stone", 1],
3036
+ ["molotov_break_ignite", 1, "glass_shatter"],
3037
+ ["fire_ignite", 1, "hiss"],
3038
+ ["fire_loop_small", 1],
3039
+ ["fire_loop_large", 1],
3040
+ ["fire_extinguish", 2, "hiss"],
3041
+ ["gas_leak_hiss", 2],
3042
+ ["structure_collapse", 2, "crumble"],
3043
+ ["grenade_bounce", 1],
3044
+ ["c4_plant", 2],
3045
+ ["c4_beep", 2],
3046
+ ["c4_disarm", 2]
3047
+ ]),
3048
+ ...entries("personagem", [
3049
+ ["footstep_concrete_walk", 1, "sfx_step"],
3050
+ ["footstep_wood_walk", 1, "sfx_step"],
3051
+ ["footstep_dirt_walk", 1, "sfx_step"],
3052
+ ["footstep_grass_walk", 1, "sfx_step"],
3053
+ ["footstep_gravel_walk", 1, "sfx_step"],
3054
+ ["land_concrete", 1, "sfx_step"],
3055
+ ["punch_swing", 1],
3056
+ ["punch_hit_body", 1, "sfx_flesh"],
3057
+ ["kick_hit", 1, "sfx_flesh"],
3058
+ ["knife_stab", 1],
3059
+ ["blunt_hit", 1],
3060
+ ["bite", 1, "sfx_flesh"],
3061
+ ["body_fall", 1],
3062
+ ["player_hurt", 2],
3063
+ ["footstep_tile_walk", 1, "sfx_step"],
3064
+ ["footstep_water_walk", 1, "sfx_step"],
3065
+ ["footstep_grate_walk", 2, "sfx_step"],
3066
+ ["footstep_duct_walk", 3, "sfx_step"],
3067
+ ["ladder_climb", 2],
3068
+ ["swim", 2],
3069
+ ["zombie_groan", 1],
3070
+ ["zombie_alert", 1],
3071
+ ["zombie_attack", 1],
3072
+ ["zombie_pain", 1],
3073
+ ["zombie_claw_hit", 1, "sfx_flesh"],
3074
+ ["zombie_claw_miss", 2]
3075
+ ]),
3076
+ ...entries("veiculos", [
3077
+ ["engine_loop_car", 1],
3078
+ ["engine_start", 1],
3079
+ ["crash_light", 1, "crash"],
3080
+ ["crash_heavy", 1, "crash"],
3081
+ ["car_door_open", 1, "latch"],
3082
+ ["car_door_close", 1, "shut_metal"],
3083
+ ["horn_car", 2, "horn"],
3084
+ ["tire_skid_asphalt", 2]
3085
+ ]),
3086
+ ...entries("itens-portas", [
3087
+ ["item_pickup_metal", 1],
3088
+ ["can_open", 1],
3089
+ ["can_crush", 1],
3090
+ ["door_wood_open", 1, "latch"],
3091
+ ["door_wood_close", 1, "shut_wood"],
3092
+ ["door_creak", 1, "creak_wood"],
3093
+ ["door_locked_rattle", 1, "rattle"],
3094
+ ["drawer_open", 2, "slide"],
3095
+ ["fridge_close", 2, "shut_metal"]
3096
+ ]),
3097
+ ...entries("ambiente", [
3098
+ ["rain_loop", 2],
3099
+ ["wind_loop", 2],
3100
+ ["thunder_near", 2],
3101
+ ["thunder_far", 2],
3102
+ ["water_splash", 2],
3103
+ ["helicopter_loop", 3]
3104
+ ])
3105
+ ];
3106
+ var SFX_MATERIALS = [
3107
+ ["steel", "metal"],
3108
+ ["aluminium", "metal"],
3109
+ ["wood", "wood"],
3110
+ ["plywood", "wood"],
3111
+ ["mdf", "wood"],
3112
+ ["glass", "glass"],
3113
+ ["tempered_glass", "glass"],
3114
+ ["ceramic", "glass"],
3115
+ ["concrete", "masonry"],
3116
+ ["brick", "masonry"],
3117
+ ["plaster", "masonry"],
3118
+ ["plastic", "soft"],
3119
+ ["fabric", "soft"],
3120
+ ["paper", "soft"],
3121
+ ["foliage", "soft"],
3122
+ ["soil", "soft"],
3123
+ ["flesh", "soft"]
3124
+ ];
3125
+ var SFX_BLOWS = ["bullet", "blunt", "blade", "break", "drag"];
3126
+ var CURRENT_BY_GROUP = {
3127
+ metal: { bullet: "clang", blunt: "clang", blade: "clang", break: "clang", drag: "drag_metal" },
3128
+ wood: { bullet: "wood_hit", blunt: "wood_hit", blade: "wood_hit", break: "crack", drag: "drag_wood" },
3129
+ glass: { bullet: "ping", blunt: "ping", break: "glass_shatter", drag: "drag_glass" },
3130
+ masonry: { bullet: "chip", blunt: "chip", blade: "chip", break: "crumble", drag: "drag_masonry" },
3131
+ soft: { bullet: "thud", blunt: "thud", blade: "thud", break: "thud", drag: "drag_soft" }
3132
+ };
3133
+ var impactSoundId = (material, blow) => `impact_${material}_${blow}`;
3134
+ var SFX_IMPACTS = SFX_MATERIALS.flatMap(
3135
+ ([material, group]) => SFX_BLOWS.map((blow) => {
3136
+ const current = CURRENT_BY_GROUP[group]?.[blow];
3137
+ return { id: impactSoundId(material, blow), category: "impactos", priority: ["steel", "wood", "glass", "concrete", "brick", "soil", "flesh"].includes(material) && blow === "bullet" ? 1 : 2, ...current ? { current } : {} };
3138
+ })
3139
+ );
3140
+ function sfxState(entry, has) {
3141
+ if (has.sample(entry.id)) return "file";
3142
+ if (entry.current && has.sound(entry.current) || has.sound(entry.id)) return "synth";
3143
+ return "missing";
3144
+ }
3145
+
3146
+ // src/manifest.ts
3147
+ var SDK_LICENSES = ["CC0", "CC-BY"];
3148
+ var SFX_LAYERS = ["close", "far", "tail_in", "tail_out"];
3149
+ var SFX_TIERS = { light: 0, medium: 0.34, heavy: 0.67 };
3150
+ function parseSfxName(name) {
3151
+ const match = /^(.*?)(?:_(close|far|tail_in|tail_out))?(?:_(\d+))?$/.exec(name);
3152
+ const id = match?.[1] || name;
3153
+ const layer = match?.[2] ?? "close";
3154
+ const take = match?.[3] ? Number(match[3]) : 1;
3155
+ return { id, layer, take };
3156
+ }
3157
+ function splitTier(id) {
3158
+ const match = /^(.*)_(light|medium|heavy)$/.exec(id);
3159
+ return match ? { base: match[1], tier: match[2] } : { base: id };
3160
+ }
3161
+ var refsOf = (entries2, baseUrl) => (entries2 ?? []).map((entry) => ({ url: `${baseUrl}${entry.file}` }));
3162
+ function sampleBankFromManifest(manifest, options) {
3163
+ const baseUrl = options.baseUrl ?? "";
3164
+ const samples = [];
3165
+ const tiersOf = /* @__PURE__ */ new Map();
3166
+ for (const sound of manifest.sounds) {
3167
+ const { base, tier } = splitTier(sound.id);
3168
+ if (tier) tiersOf.set(base, [...tiersOf.get(base) ?? [], sound]);
3169
+ }
3170
+ const bases = /* @__PURE__ */ new Map();
3171
+ for (const sound of manifest.sounds) {
3172
+ if (!splitTier(sound.id).tier) bases.set(sound.id, sound);
3173
+ }
3174
+ for (const [base, tiers] of tiersOf) {
3175
+ if (!bases.has(base)) bases.set(base, { id: base, category: tiers[0]?.category ?? "", layers: { close: tiers[0]?.layers.close ?? [] } });
3176
+ }
3177
+ for (const sound of bases.values()) {
3178
+ const numbers = options.sounds?.[sound.id] ?? options.defaults?.(sound);
3179
+ const close = refsOf(sound.layers.close, baseUrl);
3180
+ if (!numbers || close.length === 0) continue;
3181
+ const tiers = (tiersOf.get(sound.id) ?? []).map((tier) => {
3182
+ const name = splitTier(tier.id).tier;
3183
+ const tierFar = refsOf(tier.layers.far, baseUrl);
3184
+ return { name, from: SFX_TIERS[name] ?? 0, close: refsOf(tier.layers.close, baseUrl), ...tierFar.length > 0 ? { far: tierFar } : {} };
3185
+ }).sort((a, b) => a.from - b.from);
3186
+ const far = refsOf(sound.layers.far, baseUrl);
3187
+ const indoor = refsOf(sound.layers.tail_in, baseUrl);
3188
+ const outdoor = refsOf(sound.layers.tail_out, baseUrl);
3189
+ const files = [sound, ...tiersOf.get(sound.id) ?? []].flatMap((entry) => Object.values(entry.layers).flatMap((entries2) => entries2 ?? []));
3190
+ samples.push({
3191
+ id: sound.id,
3192
+ bus: numbers.bus,
3193
+ close,
3194
+ ...far.length > 0 ? { far } : {},
3195
+ ...tiers.length > 0 ? { tiers } : {},
3196
+ ...numbers.crossover ? { crossover: numbers.crossover } : {},
3197
+ ...indoor.length > 0 || outdoor.length > 0 ? { tail: { ...indoor.length > 0 ? { indoor } : {}, ...outdoor.length > 0 ? { outdoor } : {}, ...numbers.tailGain === void 0 ? {} : { gain: numbers.tailGain } } } : {},
3198
+ ...numbers.pitch === void 0 ? {} : { pitch: numbers.pitch },
3199
+ ...numbers.gain === void 0 ? {} : { gain: numbers.gain },
3200
+ ...numbers.falloff ? { falloff: numbers.falloff } : {},
3201
+ ...numbers.cutoffK === void 0 ? {} : { cutoffK: numbers.cutoffK },
3202
+ ...numbers.group ? { group: numbers.group } : {},
3203
+ ...numbers.priority === void 0 ? {} : { priority: numbers.priority },
3204
+ ...numbers.fallback ? { fallback: numbers.fallback } : {},
3205
+ sources: files.map((file) => `${file.file}: ${file.source} ${file.author} ${file.license}`)
3206
+ });
3207
+ }
3208
+ return { id: options.id, samples };
3209
+ }
3210
+
3211
+ // src/kit-samples.ts
3212
+ var KIT_CATEGORY_BUS = {
3213
+ armas: "weapons",
3214
+ impactos: "world",
3215
+ "explosoes-fogo": "world",
3216
+ personagem: "world",
3217
+ veiculos: "world",
3218
+ "itens-portas": "world",
3219
+ ambiente: "ambience"
3220
+ };
3221
+ var VOICE_IDS = /^(player_(hurt|death|effort|breath)|scream|zombie_)/;
3222
+ var KIT_SAMPLE_ALIASES = {
3223
+ // a house door: the latch giving, the hinge, the leaf meeting the frame, a locked one shaken; a drawer on its runners
3224
+ latch: "metal_latch",
3225
+ creak_wood: "door_creak",
3226
+ shut_wood: "door_wood_shut",
3227
+ rattle: "door_locked_rattle",
3228
+ // a drawer stays on its recipe: the recorded runs (1 to 1.9 s) outlast a drawer's pull
3229
+ // an item taken in the hand (game-destruction-three grab_* keys)
3230
+ grab_hard: "item_pickup_generic",
3231
+ grab_soft: "item_pickup_leather"
3232
+ };
3233
+ function kitSampleDefaults(sound) {
3234
+ const bus = VOICE_IDS.test(sound.id) ? "voices" : KIT_CATEGORY_BUS[sound.category];
3235
+ if (!bus) return void 0;
3236
+ return { bus, pitch: sound.category === "ambiente" || /_loop/.test(sound.id) ? 0 : 0.04 };
3237
+ }
3238
+ function withAliases(manifest, aliases) {
3239
+ const byId = new Map(manifest.sounds.map((sound) => [sound.id, sound]));
3240
+ const extra = [];
3241
+ for (const [alias, target] of Object.entries(aliases)) {
3242
+ const sound = byId.get(target);
3243
+ if (sound && !byId.has(alias)) extra.push({ ...sound, id: alias });
3244
+ }
3245
+ return { ...manifest, sounds: [...manifest.sounds, ...extra] };
3246
+ }
3247
+ function kitSampleBank(manifest, options = {}) {
3248
+ return sampleBankFromManifest(withAliases(manifest, options.aliases ?? KIT_SAMPLE_ALIASES), {
3249
+ id: options.id ?? "kit_samples",
3250
+ baseUrl: options.baseUrl ?? "",
3251
+ defaults: kitSampleDefaults
3252
+ });
3253
+ }
3254
+ var isFileUrl = (url2) => {
3255
+ const page = globalThis.location?.href;
3256
+ try {
3257
+ return new URL(url2, page).protocol === "file:";
3258
+ } catch {
3259
+ return false;
3260
+ }
3261
+ };
3262
+ var fetchJson = async (url2) => {
3263
+ if (isFileUrl(url2)) throw new Error(`${url2}: file:// is not fetched`);
3264
+ const response = await fetch(url2);
3265
+ if (!response.ok) throw new Error(`${url2}: ${response.status}`);
3266
+ return await response.json();
3267
+ };
3268
+ async function loadKitSamples(engine, options = {}) {
3269
+ const baseUrl = options.baseUrl ?? "";
3270
+ let manifest;
3271
+ try {
3272
+ manifest = await (options.fetchManifest ?? fetchJson)(`${baseUrl}manifest.json`);
3273
+ } catch {
3274
+ return null;
3275
+ }
3276
+ const bank = kitSampleBank(manifest, options);
3277
+ engine.register(bank);
3278
+ if (options.preload && engine.loadSamples) void engine.loadSamples(options.preload).catch(() => void 0);
3279
+ return bank;
3280
+ }
3281
+
3282
+ // src/user-samples.ts
3283
+ var USER_SAMPLES_BANK = "user_samples";
3284
+ var KIT_SAMPLES_BANK = "kit_samples";
3285
+ var USER_SOUND_PREFIX = "user:";
3286
+ function soundSourceOf(event) {
3287
+ if (event.kind === "recipe") return "synth";
3288
+ return event.bank === USER_SAMPLES_BANK ? "user" : "sdk";
3289
+ }
3290
+ function userSampleBank(manifest, options = {}) {
3291
+ const playing = [];
3292
+ const behindKit = [];
3293
+ const sounds = [];
3294
+ for (const picked of manifest.sounds) {
3295
+ const sound = { ...picked, id: splitTier(picked.id).base };
3296
+ sounds.push({ ...sound, id: `${USER_SOUND_PREFIX}${sound.id}` });
3297
+ if (sound.role === "fallback" && options.kitHas?.(sound.id)) {
3298
+ behindKit.push(sound.id);
3299
+ continue;
3300
+ }
3301
+ sounds.push(sound);
3302
+ playing.push(sound.id);
3303
+ }
3304
+ const bank = sampleBankFromManifest(
3305
+ { sounds },
3306
+ {
3307
+ id: USER_SAMPLES_BANK,
3308
+ baseUrl: options.baseUrl ?? "",
3309
+ defaults: (sound) => kitSampleDefaults({ id: sound.id.replace(USER_SOUND_PREFIX, ""), category: sound.category })
3310
+ }
3311
+ );
3312
+ return { bank, playing, behindKit };
3313
+ }
3314
+ var fetchManifestJson = async (url2) => {
3315
+ const page = globalThis.location?.href;
3316
+ if (new URL(url2, page ?? "http://localhost/").protocol === "file:") throw new Error(`${url2}: file:// is not fetched`);
3317
+ const response = await fetch(url2);
3318
+ if (!response.ok) throw new Error(`${url2}: ${response.status}`);
3319
+ return await response.json();
3320
+ };
3321
+ async function loadUserSamples(engine, options = {}) {
3322
+ const baseUrl = options.baseUrl ?? "./sfx-local/";
3323
+ let manifest;
3324
+ try {
3325
+ manifest = await (options.fetchManifest ?? fetchManifestJson)(`${baseUrl}manifest.json`);
3326
+ } catch {
3327
+ return null;
3328
+ }
3329
+ const kitHas = options.kitHas ?? ((id) => Boolean(engine.registry?.sample(id)));
3330
+ const result = userSampleBank(manifest, { baseUrl, kitHas });
3331
+ engine.register(result.bank);
3332
+ return result;
3333
+ }
3334
+
3335
+ // src/data/game-sfx.json
3336
+ var game_sfx_default = {
3337
+ sources: ["jogo:L62089-62107"],
3338
+ note: "The game's sfx table P: [lowpass cutoff Hz, peak gain, seconds]. Played as a noise burst with the game's distance curve (falloff game_sfx) and a 0.05 s tail. Team names in `team`.",
3339
+ classes: [
3340
+ { id: "pistol", team: "pistola", bus: "weapons", cutoff: 1800, gain: 0.9, seconds: 0.18 },
3341
+ { id: "shotgun", team: "escopeta", bus: "weapons", cutoff: 900, gain: 1.2, seconds: 0.35 },
3342
+ { id: "rifle", team: "fuzil", bus: "weapons", cutoff: 1400, gain: 1.1, seconds: 0.28 },
3343
+ { id: "long_rifle", team: "rifle", bus: "weapons", cutoff: 1100, gain: 1.3, seconds: 0.45 },
3344
+ { id: "impact", team: "impacto", bus: "world", cutoff: 2500, gain: 0.25, seconds: 0.05 },
3345
+ { id: "flesh", team: "carne", bus: "world", cutoff: 600, gain: 0.35, seconds: 0.08 },
3346
+ { id: "step", team: "passo", bus: "world", cutoff: 400, gain: 0.08, seconds: 0.05 },
3347
+ { id: "glass", team: "vidro", bus: "world", cutoff: 5e3, gain: 0.5, seconds: 0.3 },
3348
+ { id: "wood", team: "madeira", bus: "world", cutoff: 700, gain: 0.5, seconds: 0.2 },
3349
+ { id: "reload", team: "recarga", bus: "weapons", cutoff: 3e3, gain: 0.15, seconds: 0.06 },
3350
+ { id: "empty", team: "vazio", bus: "weapons", cutoff: 4e3, gain: 0.12, seconds: 0.03 },
3351
+ { id: "explosion", team: "explosao", bus: "world", cutoff: 200, gain: 1.6, seconds: 1.4 }
3352
+ ],
3353
+ fallback: { id: "unknown", team: "(any other name)", bus: "world", cutoff: 1e3, gain: 0.5, seconds: 0.1 }
3354
+ };
3355
+
3356
+ // src/recipes/noise-class.ts
3357
+ var GAME_SFX = game_sfx_default;
3358
+ var GAME_SFX_CLASSES = GAME_SFX.classes;
3359
+ var GAME_SFX_FALLBACK = GAME_SFX.fallback;
3360
+ function noiseClassRecipe(row, id = `sfx_${row.id}`) {
3361
+ return {
3362
+ id,
3363
+ bus: row.bus,
3364
+ order: "burst",
3365
+ duration: row.seconds,
3366
+ tail: 0.05,
3367
+ layers: [
3368
+ {
3369
+ source: { kind: "noise", offset: "random" },
3370
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: row.cutoff } }] },
3371
+ envelope: [
3372
+ [0, row.gain, "set"],
3373
+ ["1d", 1e-3, "exp", "fixed"]
3374
+ ]
3375
+ }
3376
+ ],
3377
+ falloff: DISTANCE_CURVES.game_sfx,
3378
+ sources: [...GAME_SFX.sources.map((source) => `${source} ${row.team}`)]
2005
3379
  };
3380
+ }
3381
+ var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
3382
+
3383
+ // src/recipes/scene.ts
3384
+ var ATTACK = 2e-3;
3385
+ function lengthOf(d, attack) {
3386
+ if (typeof d === "number") return attack + d;
3387
+ if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
3388
+ return { ...d, add: (d.add ?? 0) + attack };
3389
+ }
3390
+ var envelope = (attack) => [
3391
+ [0, 1e-4, "set", "fixed"],
3392
+ [attack, 1, "exp"],
3393
+ ["1d", 1e-4, "exp", "fixed"]
3394
+ ];
3395
+ function sceneNoise(o) {
3396
+ const a = o.a ?? ATTACK;
3397
+ const over = typeof o.d === "number" ? o.d : 0;
2006
3398
  return {
2007
- registry,
2008
- register: (bank) => registry.register(bank),
2009
- plan: (id, playOptions = {}) => planWith(id, playOptions, createRng(playOptions.seed ?? seed)),
2010
- play: (id, playOptions = {}) => playWith(id, playOptions, randomFor(playOptions), shared),
2011
- playPlan(plan, playOptions = {}) {
2012
- const audio = ready();
2013
- if (!audio) return null;
2014
- advance();
2015
- const recipe2 = { id: plan.id, bus: plan.bus, layers: [], sources: [], priority: plan.priority, ...plan.group ? { group: plan.group } : {} };
2016
- const voiceId = plan.silent ? void 0 : admit(recipe2, plan.level, plan.distance);
2017
- if (voiceId === null) return null;
2018
- if (planUsesNoise(plan)) prepareNoise(shared, audio, mix.noise.seconds);
2019
- return start(plan, shared, voiceId, playOptions.delay);
2020
- },
2021
- loop: (id, loopOptions = {}) => createLoopHandle(registry.loop(id), loopHost, loopOptions.volume),
2022
- channel(channelOptions) {
2023
- const random = channelOptions.random ?? (channelOptions.seed === void 0 ? engineRandom : createRng(channelOptions.seed));
2024
- const store = channelOptions.ownNoise ? createNoiseStore(random) : shared;
2025
- return {
2026
- random,
2027
- play: (id, playOptions = {}) => playWith(id, playOptions, random, store),
2028
- plan: (id, playOptions = {}) => planWith(id, playOptions, createRng(playOptions.seed ?? seed))
2029
- };
2030
- },
2031
- setListener(next) {
2032
- listener = next;
2033
- },
2034
- setBus(id, setting) {
2035
- busSettings.set(id, { ...busSettings.get(id), ...setting });
2036
- if (!context) return;
2037
- ensureBus(id);
2038
- busNodes.get(id).gain.value = busGain(busSettings.get(id));
2039
- },
2040
- setMix(next) {
2041
- const previous = mix;
2042
- mix = resolveMix(next, options.profiles);
2043
- if (!context || !chain) return;
2044
- const old = chain;
2045
- chain = buildChain(context, mix, engineRandom);
2046
- if (mix.noise.seconds !== previous.noise.seconds) shared.reset();
2047
- for (const node of busNodes.values()) {
2048
- node.disconnect();
2049
- node.connect(chain.master);
2050
- }
2051
- for (const loop of loops) loop.rewire();
2052
- old.disconnect();
2053
- },
2054
- setMaster(gain) {
2055
- mix = { ...mix, master: gain };
2056
- if (chain) chain.master.gain.value = gain;
2057
- },
2058
- unlock() {
2059
- const audio = ready();
2060
- if (audio) unlockContext(audio);
2061
- },
2062
- warmUp() {
2063
- const audio = ready();
2064
- if (!audio) return false;
2065
- noiseBuffer(shared, audio, mix.noise.seconds);
2066
- return true;
2067
- },
2068
- level(bus) {
2069
- watched.add(bus);
2070
- ensureBus(bus);
2071
- const meter = meterOf(bus);
2072
- if (!meter) return 0;
2073
- meter.analyser.getFloatTimeDomainData(meter.data);
2074
- let sum = 0;
2075
- for (const sample of meter.data) sum += sample * sample;
2076
- return Math.sqrt(sum / meter.data.length);
3399
+ filters: {
3400
+ mode: "serial",
3401
+ bank: [{ type: o.type ?? "lowpass", frequency: o.f2 === void 0 ? { set: o.f } : { set: o.f, to: o.f2, curve: "exp", over }, q: o.q ?? 0.7 }]
2077
3402
  },
2078
- deferredQueue: () => startQueue?.deferredQueue() ?? null,
2079
- stats() {
2080
- advance();
2081
- const voiceStats = voices.stats();
2082
- return {
2083
- context: context ? context.state : "none",
2084
- voices: voiceStats.active,
2085
- peak: voiceStats.peak,
2086
- dropped: voiceStats.dropped,
2087
- maxVoices: voiceStats.maxVoices,
2088
- loops: [...loops].filter((loop) => loop.created).length
2089
- };
3403
+ duration: lengthOf(o.d, a),
3404
+ gain: o.vol,
3405
+ ...o.delay === void 0 ? {} : { at: o.delay },
3406
+ ...o.wet === void 0 ? {} : { wet: o.wet },
3407
+ source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
3408
+ envelope: envelope(a)
3409
+ };
3410
+ }
3411
+ function sceneTone(o) {
3412
+ return {
3413
+ source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
3414
+ duration: lengthOf(o.d, ATTACK),
3415
+ gain: o.vol,
3416
+ ...o.delay === void 0 ? {} : { at: o.delay },
3417
+ ...o.wet === void 0 ? {} : { wet: o.wet },
3418
+ envelope: envelope(ATTACK)
3419
+ };
3420
+ }
3421
+ function sceneVoice(o) {
3422
+ return {
3423
+ duration: lengthOf(o.d, 0),
3424
+ gain: o.vol,
3425
+ wet: 0.45,
3426
+ source: {
3427
+ kind: "formant_voice",
3428
+ wave: o.wave ?? "sawtooth",
3429
+ frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
3430
+ vibrato: { rate: [4, 10], depth: 0.08 },
3431
+ formants: [
3432
+ { frequency: o.fA, q: 4 },
3433
+ { frequency: o.fB, q: 6 }
3434
+ ],
3435
+ breath: { frequency: o.fB, q: 1.1, gain: o.noise },
3436
+ mix: 3.2
2090
3437
  },
2091
- dispose() {
2092
- startQueue?.clear();
2093
- for (const id of active.keys()) voices.release(id);
2094
- for (const loop of [...loops]) loop.stop();
2095
- active.clear();
2096
- void context?.close();
2097
- context = null;
2098
- chain = null;
2099
- busNodes.clear();
2100
- meters.clear();
2101
- shared.reset();
3438
+ envelope: [
3439
+ [0, 1e-4, "set", "fixed"],
3440
+ [0.1, 1, "exp"],
3441
+ ["0.6d", 1, "set"],
3442
+ ["1d", 1e-4, "exp", "fixed"]
3443
+ ]
3444
+ };
3445
+ }
3446
+ var sceneMany = (count, make) => Array.from({ length: count }, make);
3447
+ function sceneRecipe(id, layers, line, options = {}) {
3448
+ return {
3449
+ id,
3450
+ bus: options.bus ?? "world",
3451
+ pan: true,
3452
+ layers,
3453
+ falloff: DISTANCE_CURVES.art_scene,
3454
+ ...options.vars ? { vars: options.vars } : {},
3455
+ ...options.group ? { group: options.group } : {},
3456
+ sources: [`cena:${line}`]
3457
+ };
3458
+ }
3459
+
3460
+ // src/library/crowd_rumble.json
3461
+ var crowd_rumble_default = {
3462
+ id: "crowd_rumble",
3463
+ bus: "ambience",
3464
+ order: "game_loop",
3465
+ source: { kind: "noise", loop: true },
3466
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 160 } }] },
3467
+ smoothing: { volume: 0.15, frequency: 0.08 },
3468
+ startAbove: 2e-3,
3469
+ sources: ["jogo:L62116 horda: noise through a 160 Hz lowpass", "jogo:L70424 level min(0.16, near \xD7 0.0016)", "temporary home until the crowd package (Q12)"]
3470
+ };
3471
+
3472
+ // src/library/engine_saw.json
3473
+ var engine_saw_default = {
3474
+ id: "engine_saw",
3475
+ bus: "world",
3476
+ order: "game_loop",
3477
+ source: { kind: "osc", wave: "sawtooth", loop: true },
3478
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 400 } }] },
3479
+ smoothing: { volume: 0.15, frequency: 0.08 },
3480
+ startAbove: 2e-3,
3481
+ sources: [
3482
+ "jogo:L62115 motor: sawtooth through a 400 Hz lowpass",
3483
+ "jogo:L70421 level 0.035 + speed \xD7 0.003, frequency 38 + speed \xD7 7 (the host drives `set`)",
3484
+ "temporary home until the vehicles package (Q12)"
3485
+ ]
3486
+ };
3487
+
3488
+ // src/library/horn.json
3489
+ var horn_default = {
3490
+ id: "horn",
3491
+ bus: "world",
3492
+ order: "chord",
3493
+ duration: 0.6,
3494
+ tail: 0.02,
3495
+ layers: [
3496
+ { source: { kind: "osc", wave: "square", frequency: { set: 415 } }, envelope: [[0, 0.08, "set"], [0.6, 1e-4, "set", "fixed"]] },
3497
+ { source: { kind: "osc", wave: "square", frequency: { set: 523 } }, envelope: [[0, 0.08, "set"], [0.6, 1e-4, "set", "fixed"]] }
3498
+ ],
3499
+ falloff: { model: "none" },
3500
+ sources: ["jogo:L70272-70276 buzina: squares at 415 and 523 Hz, 0.08 for 0.6 s", "temporary home until the vehicles package (Q12)"]
3501
+ };
3502
+
3503
+ // src/library/radio_static.json
3504
+ var radio_static_default = {
3505
+ id: "radio_static",
3506
+ bus: "world",
3507
+ order: "static",
3508
+ duration: 0.6,
3509
+ layers: [
3510
+ {
3511
+ source: { kind: "noise", offset: "random" },
3512
+ filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 1800 }, q: 0.8 }] },
3513
+ envelope: [[0, 0.06, "set"], ["1d", 1e-3, "exp", "fixed"]]
3514
+ }
3515
+ ],
3516
+ falloff: { model: "none" },
3517
+ sources: ["jogo:L69998-70004 chiado: noise through a 1800 Hz bandpass (Q 0.8), 0.06 falling over 0.6 s", "temporary home until the props or radio package (Q12)"]
3518
+ };
3519
+
3520
+ // src/library/rotor_blades.json
3521
+ var rotor_blades_default = {
3522
+ id: "rotor_blades",
3523
+ bus: "world",
3524
+ order: "game_loop",
3525
+ source: { kind: "noise", loop: true },
3526
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 300 } }] },
3527
+ modulators: [{ target: "gain", rate: 11, depth: 0.5, base: 0.5 }],
3528
+ smoothing: { volume: 0.15, frequency: 0.08 },
3529
+ startAbove: 2e-3,
3530
+ sources: [
3531
+ "jogo:L62116-62120 rotor: noise through a 300 Hz lowpass, blade beat as an 11 Hz LFO of 0.5 on a 0.5 gain stage",
3532
+ "jogo:L70426 level min(0.5, 30 / (distance + 10))",
3533
+ "temporary home until the vehicles package (Q12)"
3534
+ ]
3535
+ };
3536
+
3537
+ // src/library/siren.json
3538
+ var siren_default = {
3539
+ id: "siren",
3540
+ bus: "world",
3541
+ order: "siren",
3542
+ duration: 9,
3543
+ tail: 0.2,
3544
+ layers: [
3545
+ {
3546
+ wet: 1.5,
3547
+ source: { kind: "osc", wave: "sine", frequency: { set: 850 } },
3548
+ modulators: [{ target: "frequency", rate: 0.18, depth: 280 }],
3549
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 1400 } }] },
3550
+ envelope: [[0, 1e-4, "set", "fixed"], [2, 0.035, "exp"], [6, 0.035, "set"], [9, 1e-4, "exp", "fixed"]]
2102
3551
  }
2103
- };
3552
+ ],
3553
+ falloff: { model: "none" },
3554
+ sources: ["cena:L1369 siren: 850 Hz sine swept \xB1280 Hz at 0.18 Hz, 1400 Hz lowpass, 0.035 for 9 s, reverb send 1.5", "temporary home until the vehicles package (Q12)"]
3555
+ };
3556
+
3557
+ // src/library/index.ts
3558
+ var LIBRARY_SOUNDS = [horn_default, radio_static_default, siren_default];
3559
+ var LIBRARY_LOOPS = [engine_saw_default, rotor_blades_default, crowd_rumble_default];
3560
+ var LIBRARY_BANK = { id: "library", sounds: LIBRARY_SOUNDS, loops: LIBRARY_LOOPS };
3561
+
3562
+ // src/library/cabin.ts
3563
+ var CABIN_SOUNDS = [
3564
+ {
3565
+ id: "blinker_tick",
3566
+ bus: "world",
3567
+ duration: 0.03,
3568
+ layers: [
3569
+ // the relay: a click of bright noise and a short low knock
3570
+ { source: { kind: "noise", offset: "random" }, filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 2600 } }] }, envelope: [[0, 0.22, "set"], [0.025, 1e-4, "exp", "fixed"]] },
3571
+ { source: { kind: "osc", wave: "triangle", frequency: { set: 180 } }, envelope: [[0, 0.12, "set"], [0.03, 1e-4, "exp", "fixed"]] }
3572
+ ],
3573
+ falloff: { model: "none" }
3574
+ },
3575
+ {
3576
+ id: "starter_crank",
3577
+ bus: "world",
3578
+ duration: 0.9,
3579
+ layers: [
3580
+ // the starter motor turning the engine over: low noise chopped at the compression strokes
3581
+ {
3582
+ source: { kind: "noise", offset: "random" },
3583
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 420 } }] },
3584
+ modulators: [{ target: "gain", wave: "square", rate: 11, depth: 0.08, base: 0.1 }],
3585
+ envelope: [[0, 1e-4, "set", "fixed"], [0.05, 0.25, "linear"], [0.8, 0.22, "set"], [0.9, 1e-4, "exp", "fixed"]]
3586
+ },
3587
+ { source: { kind: "osc", wave: "sawtooth", frequency: { set: 95, to: 120, curve: "linear", over: 0.9 } }, filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 300 } }] }, envelope: [[0, 0.04, "set"], [0.9, 1e-4, "exp", "fixed"]] }
3588
+ ],
3589
+ falloff: { model: "none" }
3590
+ },
3591
+ {
3592
+ id: "car_alarm",
3593
+ bus: "world",
3594
+ order: "siren",
3595
+ duration: 3,
3596
+ tail: 0.1,
3597
+ layers: [
3598
+ // the whoop of a car alarm: a square swept up and down three times a second
3599
+ {
3600
+ source: { kind: "osc", wave: "square", frequency: { set: 1100 } },
3601
+ modulators: [{ target: "frequency", wave: "triangle", rate: 3, depth: 420 }],
3602
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 2600 } }] },
3603
+ envelope: [[0, 1e-4, "set", "fixed"], [0.05, 0.05, "linear"], [2.9, 0.05, "set"], [3, 1e-4, "exp", "fixed"]]
3604
+ }
3605
+ ],
3606
+ falloff: { model: "none" }
3607
+ },
3608
+ {
3609
+ id: "alarm_chirp",
3610
+ bus: "world",
3611
+ duration: 0.1,
3612
+ layers: [{ source: { kind: "osc", wave: "square", frequency: { set: 2300 } }, repeat: { count: 2, spacing: 0.16 }, envelope: [[0, 0.05, "set"], [0.09, 1e-4, "exp", "fixed"]] }],
3613
+ falloff: { model: "none" }
3614
+ },
3615
+ {
3616
+ id: "wiper_swish",
3617
+ bus: "world",
3618
+ duration: 0.45,
3619
+ layers: [
3620
+ {
3621
+ source: { kind: "noise", offset: "random" },
3622
+ filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 700, to: 1100, curve: "linear", over: 0.45 }, q: 1.2 }] },
3623
+ envelope: [[0, 1e-4, "set", "fixed"], [0.2, 0.06, "linear"], [0.45, 1e-4, "exp", "fixed"]]
3624
+ }
3625
+ ],
3626
+ falloff: { model: "none" }
3627
+ },
3628
+ {
3629
+ id: "seatbelt_chime",
3630
+ bus: "world",
3631
+ duration: 0.5,
3632
+ layers: [{ source: { kind: "osc", wave: "sine", frequency: { set: 880 } }, repeat: { count: 3, spacing: 0.7 }, envelope: [[0, 0.06, "set"], [0.5, 1e-4, "exp", "fixed"]] }],
3633
+ falloff: { model: "none" }
3634
+ }
3635
+ ];
3636
+ var CABIN_BANK = { id: "vehicle_cabin", sounds: CABIN_SOUNDS, loops: [] };
3637
+
3638
+ // src/recipes/foley.ts
3639
+ var FOLEY_FALLOFF = {
3640
+ model: "inverse_square",
3641
+ k: 0.02,
3642
+ cutoffK: 0.04,
3643
+ minGain: 4e-3
3644
+ };
3645
+ var vary = (f, pct = 0.04) => [Math.round(f * (1 - pct)), Math.round(f * (1 + pct))];
3646
+ var foley = (id, layers, note) => ({
3647
+ id,
3648
+ bus: "world",
3649
+ pan: true,
3650
+ layers,
3651
+ falloff: FOLEY_FALLOFF,
3652
+ sources: [`sdk: ${note}`]
3653
+ });
3654
+ var click = (f, vol, delay = 0) => [
3655
+ sceneNoise({
3656
+ type: "bandpass",
3657
+ f: vary(f, 0.08),
3658
+ q: 4,
3659
+ d: [0.018, 0.03],
3660
+ vol: [vol * 0.85, vol * 1.1],
3661
+ delay
3662
+ }),
3663
+ sceneTone({
3664
+ f: vary(f * 0.8, 0.06),
3665
+ f2: Math.round(f * 0.62),
3666
+ d: [0.015, 0.025],
3667
+ vol: vol * 0.18,
3668
+ delay
3669
+ })
3670
+ ];
3671
+ var meet = (body, panel, vol, latch) => [
3672
+ sceneTone({
3673
+ f: vary(body),
3674
+ f2: Math.round(body * 0.5),
3675
+ d: [0.12, 0.16],
3676
+ vol: [vol * 0.9, vol * 1.1]
3677
+ }),
3678
+ sceneNoise({
3679
+ type: "bandpass",
3680
+ f: vary(panel, 0.06),
3681
+ q: 1.2,
3682
+ d: [0.08, 0.12],
3683
+ vol: [vol * 0.9, vol * 1.1],
3684
+ wet: 0.25
3685
+ }),
3686
+ ...click(latch, vol * 0.45, [0.012, 0.028])
3687
+ ];
3688
+ var LEAF_FOLEY = [
3689
+ // a wooden house door or wardrobe door: latch, a short creak, the leaf on the frame (the hard slam adds the frame rattling)
3690
+ foley("door_wood_open", click(2400, 0.4), "the latch of a wooden door giving"),
3691
+ foley(
3692
+ "door_creak",
3693
+ [
3694
+ sceneTone({ f: [190, 240], f2: 310, d: [0.4, 0.55], vol: [0.04, 0.06] }),
3695
+ sceneNoise({
3696
+ type: "bandpass",
3697
+ f: [480, 560],
3698
+ q: 6,
3699
+ a: 0.05,
3700
+ d: [0.35, 0.5],
3701
+ vol: [0.1, 0.14]
3702
+ })
3703
+ ],
3704
+ "a wooden hinge turning"
3705
+ ),
3706
+ foley("door_wood_close", meet(120, 600, 0.55, 2400), "a wooden leaf meeting its frame"),
3707
+ foley(
3708
+ "door_wood_slam",
3709
+ [
3710
+ ...meet(110, 560, 0.85, 2300),
3711
+ ...sceneMany(
3712
+ 3,
3713
+ () => sceneNoise({
3714
+ type: "bandpass",
3715
+ f: [900, 1500],
3716
+ q: 3,
3717
+ d: 0.03,
3718
+ vol: [0.08, 0.14],
3719
+ delay: [0.05, 0.18]
3720
+ })
3721
+ )
3722
+ ],
3723
+ "a wooden door slammed: the frame and the hinges rattle after the hit"
3724
+ ),
3725
+ // a metal door or gate: a hollow panel that rings a little
3726
+ foley("door_metal_open_close", [...meet(160, 900, 0.55, 2e3), sceneTone({ f: vary(1250), f2: 1150, d: [0.1, 0.16], vol: 0.06, wet: 0.3 })], "a metal door or gate meeting its frame"),
3727
+ foley(
3728
+ "door_creak_metal",
3729
+ [
3730
+ sceneTone({ f: [620, 760], f2: 900, d: [0.3, 0.4], vol: [0.03, 0.045] }),
3731
+ sceneNoise({
3732
+ type: "bandpass",
3733
+ f: [1300, 1500],
3734
+ q: 8,
3735
+ a: 0.04,
3736
+ d: [0.25, 0.35],
3737
+ vol: [0.07, 0.09]
3738
+ })
3739
+ ],
3740
+ "a metal hinge turning"
3741
+ ),
3742
+ // the handle of a locked door shaken: the latch knocking in its keeper, five to seven times in half a second
3743
+ foley(
3744
+ "door_locked_rattle",
3745
+ [
3746
+ ...sceneMany(
3747
+ 6,
3748
+ () => sceneNoise({
3749
+ type: "bandpass",
3750
+ f: [1600, 2600],
3751
+ q: 3,
3752
+ d: [0.025, 0.04],
3753
+ vol: [0.22, 0.34],
3754
+ delay: [0, 0.45]
3755
+ })
3756
+ ),
3757
+ ...sceneMany(
3758
+ 3,
3759
+ () => sceneTone({
3760
+ f: [170, 210],
3761
+ f2: 120,
3762
+ d: 0.05,
3763
+ vol: [0.12, 0.18],
3764
+ delay: [0.02, 0.4]
3765
+ })
3766
+ )
3767
+ ],
3768
+ "the handle of a locked door shaken: latch knocks and the leaf thumping in its frame"
3769
+ ),
3770
+ // a fridge: the rubber seal lets go with a soft suck, the bottles in the door clink; closing it is a soft thump and the seal pulling shut
3771
+ foley(
3772
+ "fridge_open",
3773
+ [
3774
+ sceneNoise({
3775
+ type: "lowpass",
3776
+ f: [320, 380],
3777
+ a: 0.03,
3778
+ d: [0.14, 0.2],
3779
+ vol: [0.32, 0.4]
3780
+ }),
3781
+ sceneNoise({
3782
+ type: "bandpass",
3783
+ f: [800, 950],
3784
+ f2: 1400,
3785
+ q: 2,
3786
+ a: 0.02,
3787
+ d: 0.12,
3788
+ vol: [0.08, 0.12]
3789
+ }),
3790
+ ...sceneMany(
3791
+ 2,
3792
+ () => sceneTone({
3793
+ f: [2600, 4200],
3794
+ d: [0.04, 0.08],
3795
+ vol: [0.015, 0.03],
3796
+ delay: [0.08, 0.22]
3797
+ })
3798
+ )
3799
+ ],
3800
+ "a fridge door: the seal letting go and the bottles in the door"
3801
+ ),
3802
+ foley(
3803
+ "fridge_close",
3804
+ [
3805
+ sceneTone({ f: vary(85), f2: 45, d: [0.1, 0.13], vol: [0.38, 0.46] }),
3806
+ sceneNoise({
3807
+ type: "lowpass",
3808
+ f: [520, 620],
3809
+ d: [0.08, 0.12],
3810
+ vol: [0.3, 0.36]
3811
+ }),
3812
+ sceneNoise({
3813
+ type: "lowpass",
3814
+ f: [260, 320],
3815
+ a: 0.02,
3816
+ d: 0.1,
3817
+ vol: [0.12, 0.18],
3818
+ delay: [0.04, 0.07]
3819
+ }),
3820
+ ...sceneMany(
3821
+ 2,
3822
+ () => sceneTone({
3823
+ f: [2600, 4200],
3824
+ d: [0.04, 0.07],
3825
+ vol: [0.015, 0.03],
3826
+ delay: [0.03, 0.12]
3827
+ })
3828
+ )
3829
+ ],
3830
+ "a fridge door shut: a soft thump, the seal pulling and the bottles"
3831
+ ),
3832
+ // a microwave: the spring catch of the door, plastic on plastic
3833
+ foley(
3834
+ "microwave_open",
3835
+ [
3836
+ ...click(1800, 0.34),
3837
+ sceneTone({
3838
+ f: [3e3, 3400],
3839
+ f2: 2800,
3840
+ d: 0.05,
3841
+ vol: 0.025,
3842
+ delay: [0.01, 0.02]
3843
+ })
3844
+ ],
3845
+ "the spring catch of a microwave door"
3846
+ ),
3847
+ foley(
3848
+ "microwave_close",
3849
+ [
3850
+ sceneNoise({
3851
+ type: "bandpass",
3852
+ f: [1200, 1450],
3853
+ q: 2.5,
3854
+ d: [0.04, 0.06],
3855
+ vol: [0.36, 0.44]
3856
+ }),
3857
+ sceneTone({ f: vary(180), f2: 120, d: 0.05, vol: [0.16, 0.22] }),
3858
+ ...click(2200, 0.2, [8e-3, 0.016])
3859
+ ],
3860
+ "a microwave door clacking shut"
3861
+ ),
3862
+ foley("microwave_beep", [sceneTone({ wave: "square", f: 2e3, d: 0.12, vol: 0.05 })], "the beep of a microwave"),
3863
+ // drawers and sliding leaves: the runners, then the stop
3864
+ foley(
3865
+ "drawer_open",
3866
+ [
3867
+ sceneNoise({
3868
+ type: "bandpass",
3869
+ f: [620, 780],
3870
+ q: 2,
3871
+ a: 0.04,
3872
+ d: [0.28, 0.36],
3873
+ vol: [0.18, 0.24]
3874
+ }),
3875
+ sceneNoise({ type: "bandpass", f: [2e3, 2400], q: 5, d: 0.28, vol: 0.04 }),
3876
+ ...click(1400, 0.16, [0.28, 0.34])
3877
+ ],
3878
+ "a drawer on its runners and its stop"
3879
+ ),
3880
+ foley(
3881
+ "drawer_close",
3882
+ [
3883
+ sceneNoise({
3884
+ type: "bandpass",
3885
+ f: [620, 780],
3886
+ q: 2,
3887
+ a: 0.03,
3888
+ d: [0.2, 0.28],
3889
+ vol: [0.18, 0.24]
3890
+ }),
3891
+ sceneTone({
3892
+ f: vary(150),
3893
+ f2: 90,
3894
+ d: 0.08,
3895
+ vol: [0.22, 0.3],
3896
+ delay: [0.2, 0.26]
3897
+ })
3898
+ ],
3899
+ "a drawer pushed home"
3900
+ )
3901
+ ];
3902
+ var ITEM_FOLEY = [
3903
+ // the hand closing on the item: a touch of skin and cloth, then the item's own small sound
3904
+ foley(
3905
+ "item_pickup_metal",
3906
+ [
3907
+ sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
3908
+ sceneTone({
3909
+ f: [1900, 2600],
3910
+ f2: 1700,
3911
+ d: [0.05, 0.08],
3912
+ vol: [0.06, 0.09],
3913
+ delay: [0.01, 0.03]
3914
+ })
3915
+ ],
3916
+ "picking up a metal thing"
3917
+ ),
3918
+ foley(
3919
+ "item_pickup_plastic",
3920
+ [
3921
+ sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
3922
+ sceneNoise({
3923
+ type: "bandpass",
3924
+ f: [1500, 1900],
3925
+ q: 3,
3926
+ d: 0.03,
3927
+ vol: [0.15, 0.21],
3928
+ delay: [0.01, 0.03]
3929
+ })
3930
+ ],
3931
+ "picking up a plastic thing"
3932
+ ),
3933
+ foley(
3934
+ "item_pickup_glass",
3935
+ [
3936
+ sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.15 }),
3937
+ sceneTone({
3938
+ f: [3200, 4200],
3939
+ d: [0.05, 0.08],
3940
+ vol: [0.038, 0.06],
3941
+ delay: [0.01, 0.03]
3942
+ })
3943
+ ],
3944
+ "picking up a glass thing"
3945
+ ),
3946
+ foley(
3947
+ "item_pickup_paper",
3948
+ [
3949
+ sceneNoise({
3950
+ type: "highpass",
3951
+ f: [2600, 3400],
3952
+ a: 0.01,
3953
+ d: [0.1, 0.14],
3954
+ vol: [0.12, 0.18]
3955
+ }),
3956
+ sceneNoise({ type: "bandpass", f: [800, 1e3], q: 1, d: 0.05, vol: 0.12 })
3957
+ ],
3958
+ "picking up paper or cardboard"
3959
+ ),
3960
+ foley(
3961
+ "item_pickup_fabric",
3962
+ [
3963
+ sceneNoise({
3964
+ type: "bandpass",
3965
+ f: [900, 1300],
3966
+ q: 0.8,
3967
+ a: 0.02,
3968
+ d: [0.12, 0.16],
3969
+ vol: [0.15, 0.21]
3970
+ })
3971
+ ],
3972
+ "picking up cloth"
3973
+ ),
3974
+ foley(
3975
+ "item_pickup_wood",
3976
+ [
3977
+ sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
3978
+ sceneNoise({
3979
+ type: "bandpass",
3980
+ f: [900, 1100],
3981
+ q: 2,
3982
+ d: 0.04,
3983
+ vol: [0.15, 0.21],
3984
+ delay: [0.01, 0.03]
3985
+ })
3986
+ ],
3987
+ "picking up a wooden thing"
3988
+ ),
3989
+ foley(
3990
+ "item_pickup_food",
3991
+ [
3992
+ sceneNoise({ type: "bandpass", f: [700, 900], q: 1, d: 0.06, vol: 0.18 }),
3993
+ sceneNoise({
3994
+ type: "lowpass",
3995
+ f: [500, 650],
3996
+ d: 0.05,
3997
+ vol: 0.12,
3998
+ delay: [0.01, 0.03]
3999
+ })
4000
+ ],
4001
+ "picking up food"
4002
+ ),
4003
+ foley(
4004
+ "item_throw",
4005
+ [
4006
+ sceneNoise({
4007
+ type: "bandpass",
4008
+ f: [450, 650],
4009
+ f2: 1400,
4010
+ q: 1.5,
4011
+ a: 0.05,
4012
+ d: 0.18,
4013
+ vol: [0.1, 0.14]
4014
+ })
4015
+ ],
4016
+ "the arm throwing something: air"
4017
+ ),
4018
+ // a can: the tab cracks, the gas hisses out; drinking is gulps; crushing it crumples the sheet
4019
+ foley(
4020
+ "can_open",
4021
+ [
4022
+ ...click(3200, 0.36),
4023
+ sceneNoise({
4024
+ type: "highpass",
4025
+ f: [3600, 4400],
4026
+ a: 5e-3,
4027
+ d: [0.22, 0.3],
4028
+ vol: [0.16, 0.22],
4029
+ delay: [0.012, 0.02]
4030
+ }),
4031
+ ...sceneMany(5, () => sceneTone({ f: [4e3, 7e3], d: 0.02, vol: 0.01, delay: [0.05, 0.4] }))
4032
+ ],
4033
+ "the tab of a can and the gas hissing out"
4034
+ ),
4035
+ foley(
4036
+ "can_crush",
4037
+ [
4038
+ sceneNoise({
4039
+ type: "bandpass",
4040
+ f: [1600, 2200],
4041
+ q: 1.2,
4042
+ d: [0.18, 0.25],
4043
+ vol: [0.3, 0.38]
4044
+ }),
4045
+ ...sceneMany(
4046
+ 6,
4047
+ () => sceneTone({
4048
+ f: [1200, 3400],
4049
+ f2: 900,
4050
+ d: 0.03,
4051
+ vol: [0.03, 0.06],
4052
+ delay: [0, 0.2]
4053
+ })
4054
+ )
4055
+ ],
4056
+ "a can crushed in the hand"
4057
+ ),
4058
+ foley(
4059
+ "can_puncture_spray",
4060
+ [
4061
+ sceneNoise({
4062
+ type: "highpass",
4063
+ f: [2400, 3e3],
4064
+ a: 5e-3,
4065
+ d: [0.5, 0.7],
4066
+ vol: [0.2, 0.26]
4067
+ }),
4068
+ sceneNoise({ type: "bandpass", f: [600, 800], q: 2, d: 0.4, vol: 0.06 })
4069
+ ],
4070
+ "a can holed: the jet hissing out"
4071
+ ),
4072
+ foley(
4073
+ "bottle_open",
4074
+ [
4075
+ ...sceneMany(
4076
+ 5,
4077
+ () => sceneNoise({
4078
+ type: "bandpass",
4079
+ f: [2200, 3e3],
4080
+ q: 4,
4081
+ d: 8e-3,
4082
+ vol: [0.1, 0.16],
4083
+ delay: [0, 0.18]
4084
+ })
4085
+ ),
4086
+ sceneNoise({
4087
+ type: "highpass",
4088
+ f: 3800,
4089
+ a: 5e-3,
4090
+ d: 0.12,
4091
+ vol: [0.06, 0.1],
4092
+ delay: [0.18, 0.22]
4093
+ })
4094
+ ],
4095
+ "the screw cap of a bottle turning off"
4096
+ ),
4097
+ foley(
4098
+ "drink_gulp",
4099
+ [
4100
+ sceneNoise({
4101
+ type: "lowpass",
4102
+ f: [280, 360],
4103
+ a: 0.02,
4104
+ d: [0.1, 0.14],
4105
+ vol: [0.24, 0.3]
4106
+ }),
4107
+ sceneTone({ f: [140, 170], f2: 85, d: [0.08, 0.11], vol: [0.12, 0.16] })
4108
+ ],
4109
+ "a gulp"
4110
+ ),
4111
+ foley(
4112
+ "eat_crunch",
4113
+ [
4114
+ ...sceneMany(
4115
+ 4,
4116
+ () => sceneNoise({
4117
+ type: "bandpass",
4118
+ f: [1800, 3200],
4119
+ q: 1.5,
4120
+ d: [0.02, 0.04],
4121
+ vol: [0.12, 0.2],
4122
+ delay: [0, 0.12]
4123
+ })
4124
+ )
4125
+ ],
4126
+ "a bite of something crunchy"
4127
+ ),
4128
+ foley(
4129
+ "eat_chew",
4130
+ [
4131
+ sceneNoise({
4132
+ type: "lowpass",
4133
+ f: [450, 600],
4134
+ a: 0.03,
4135
+ d: [0.1, 0.14],
4136
+ vol: [0.12, 0.16]
4137
+ }),
4138
+ sceneNoise({
4139
+ type: "bandpass",
4140
+ f: [1100, 1400],
4141
+ q: 2,
4142
+ d: 0.04,
4143
+ vol: 0.04,
4144
+ delay: [0.03, 0.06]
4145
+ })
4146
+ ],
4147
+ "chewing"
4148
+ ),
4149
+ foley(
4150
+ "bandage_wrap",
4151
+ [
4152
+ sceneNoise({
4153
+ type: "bandpass",
4154
+ f: [1400, 1800],
4155
+ f2: 2400,
4156
+ q: 1,
4157
+ a: 0.04,
4158
+ d: [0.3, 0.4],
4159
+ vol: [0.12, 0.16]
4160
+ })
4161
+ ],
4162
+ "a bandage unrolled round a limb"
4163
+ ),
4164
+ foley(
4165
+ "pills_shake",
4166
+ [
4167
+ ...sceneMany(
4168
+ 8,
4169
+ () => sceneTone({
4170
+ f: [2400, 3600],
4171
+ d: 0.015,
4172
+ vol: [0.03, 0.05],
4173
+ delay: [0, 0.25]
4174
+ })
4175
+ ),
4176
+ sceneNoise({ type: "bandpass", f: [1500, 1900], q: 2, d: 0.25, vol: 0.05 })
4177
+ ],
4178
+ "pills shaken in their bottle"
4179
+ ),
4180
+ foley(
4181
+ "medkit_use",
4182
+ [
4183
+ ...click(1700, 0.24),
4184
+ sceneNoise({
4185
+ type: "bandpass",
4186
+ f: [1e3, 1400],
4187
+ q: 1,
4188
+ a: 0.03,
4189
+ d: [0.25, 0.32],
4190
+ vol: [0.08, 0.12],
4191
+ delay: [0.05, 0.08]
4192
+ })
4193
+ ],
4194
+ "a first aid kit opened and rummaged"
4195
+ ),
4196
+ foley("flashlight_click", click(2600, 0.3), "the switch of a flashlight")
4197
+ ];
4198
+ var FOLEY_BANK = {
4199
+ id: "foley",
4200
+ sounds: [...LEAF_FOLEY, ...ITEM_FOLEY]
4201
+ };
4202
+ var PICKUP_CLASSES = ["metal", "plastic", "glass", "paper", "fabric", "wood", "food"];
4203
+ var LEAF_FOLEY_SETS = {
4204
+ wood_door: {
4205
+ open: "door_wood_open",
4206
+ turn: "door_creak",
4207
+ close: "door_wood_close",
4208
+ slam: "door_wood_slam",
4209
+ locked: "door_locked_rattle"
4210
+ },
4211
+ metal_door: {
4212
+ open: "door_wood_open",
4213
+ turn: "door_creak_metal",
4214
+ close: "door_metal_open_close",
4215
+ locked: "door_locked_rattle"
4216
+ },
4217
+ fridge: {
4218
+ open: "fridge_open",
4219
+ close: "fridge_close",
4220
+ locked: "door_locked_rattle"
4221
+ },
4222
+ microwave: {
4223
+ open: "microwave_open",
4224
+ close: "microwave_close",
4225
+ locked: "door_locked_rattle"
4226
+ },
4227
+ drawer: {
4228
+ open: "drawer_open",
4229
+ close: "drawer_close",
4230
+ locked: "door_locked_rattle"
4231
+ }
4232
+ };
4233
+
4234
+ // src/recipes/body-foley.ts
4235
+ var BODY_FOLEY_TAKES = 4;
4236
+ var TAKES2 = [
4237
+ { f: 1, t: 1, v: 1 },
4238
+ { f: 1.08, t: 0.9, v: 0.92 },
4239
+ { f: 0.93, t: 1.12, v: 1.05 },
4240
+ { f: 1.03, t: 1.05, v: 0.88 }
4241
+ ];
4242
+ var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
4243
+ var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
4244
+ var SURFACES = {
4245
+ concrete: { heel: { f: 95, d: 0.05, vol: 0.55 }, roll: { type: "bandpass", f: 2300, q: 1.1, d: 0.05, vol: 0.38 }, grains: { count: 2, f: 4200, q: 2, spread: 0.04, vol: 0.08 } },
4246
+ asphalt: { heel: { f: 90, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 1700, q: 0.9, d: 0.06, vol: 0.36 }, grains: { count: 4, f: 3600, q: 2.5, spread: 0.06, vol: 0.1 } },
4247
+ wood: { heel: { f: 130, d: 0.08, vol: 0.55 }, roll: { type: "lowpass", f: 1300, q: 0.8, d: 0.06, vol: 0.3 }, ring: [{ f: 210, d: 0.1, vol: 0.18 }, { f: 470, d: 0.06, vol: 0.06 }] },
4248
+ tile: { heel: { f: 110, d: 0.04, vol: 0.45 }, roll: { type: "bandpass", f: 3600, q: 2, d: 0.03, vol: 0.42 }, ring: [{ f: 1900, d: 0.03, vol: 0.05 }] },
4249
+ dirt: { heel: { f: 75, d: 0.06, vol: 0.45 }, roll: { type: "lowpass", f: 750, q: 0.7, d: 0.09, vol: 0.4 }, grains: { count: 3, f: 1800, q: 1.5, spread: 0.07, vol: 0.06 } },
4250
+ grass: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "highpass", f: 2600, q: 0.7, d: 0.13, vol: 0.28 }, grains: { count: 4, f: 5200, q: 1.2, spread: 0.1, vol: 0.07 } },
4251
+ gravel: { heel: { f: 80, d: 0.05, vol: 0.4 }, roll: { type: "lowpass", f: 950, q: 0.8, d: 0.1, vol: 0.3 }, grains: { count: 8, f: 2600, q: 3, spread: 0.13, vol: 0.2 } },
4252
+ sand: { heel: { f: 65, d: 0.06, vol: 0.3 }, roll: { type: "bandpass", f: 1500, q: 0.6, d: 0.16, vol: 0.3 }, grains: { count: 5, f: 4800, q: 1.5, spread: 0.12, vol: 0.05 } },
4253
+ metal: { heel: { f: 120, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 2800, q: 1.5, d: 0.03, vol: 0.25 }, ring: [{ f: 420, d: 0.25, vol: 0.16 }, { f: 1130, d: 0.18, vol: 0.09 }, { f: 2470, d: 0.1, vol: 0.04 }] },
4254
+ water: { heel: { f: 70, d: 0.04, vol: 0.25 }, roll: { type: "bandpass", f: 3e3, q: 1.2, d: 0.1, vol: 0.18 }, splash: { f: 1100, f2: 380, d: 0.22, vol: 0.55 }, grains: { count: 4, f: 2400, q: 6, spread: 0.18, vol: 0.12 } },
4255
+ glass: { heel: { f: 90, d: 0.04, vol: 0.4 }, roll: { type: "highpass", f: 4500, q: 0.8, d: 0.06, vol: 0.2 }, grains: { count: 9, f: 6200, q: 5, spread: 0.12, vol: 0.22 }, ring: [{ f: 3900, d: 0.04, vol: 0.04 }, { f: 5300, d: 0.03, vol: 0.03 }] },
4256
+ carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
4257
+ };
4258
+ var GAITS = {
4259
+ walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
4260
+ run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
4261
+ };
4262
+ var grainLayers = (g, take, start, scale) => Array.from(
4263
+ { length: g.count },
4264
+ (_, i) => sceneNoise({ type: "bandpass", f: [g.f * take.f * 0.85, g.f * take.f * 1.15], q: g.q, d: [8e-3, 0.02], vol: g.vol * scale * take.v, delay: start + (i + 0.5) / g.count * g.spread * take.t })
4265
+ );
4266
+ var ringLayers = (rings, take, scale, at = 0) => rings.map((r) => sceneTone({ f: r.f * take.f, d: r.d * take.t, vol: r.vol * scale * take.v, delay: at }));
4267
+ function footLayers(surface, take, gait, weight = 1) {
4268
+ const s = SURFACES[surface];
4269
+ const toe = gait.toe * take.t;
4270
+ return [
4271
+ sceneTone({ f: s.heel.f * take.f * weight ** -0.3, f2: s.heel.f * take.f * 0.6, d: s.heel.d * gait.short * take.t * weight ** 0.5, vol: s.heel.vol * gait.vol * take.v * weight ** 0.5 }),
4272
+ sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
4273
+ sceneNoise({ type: s.roll.type, f: [s.roll.f * take.f * gait.bright * 0.92, s.roll.f * take.f * gait.bright * 1.08], q: s.roll.q, d: s.roll.d * gait.short * take.t * weight ** 0.3, vol: s.roll.vol * gait.vol * take.v, delay: toe }),
4274
+ ...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
4275
+ ...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
4276
+ ...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
4277
+ ];
4278
+ }
4279
+ var quieter = (level, layers) => layers.map((layer) => {
4280
+ const gain = layer.gain ?? 1;
4281
+ if (typeof gain === "number") return { ...layer, gain: gain * level };
4282
+ if (Array.isArray(gain)) return { ...layer, gain: [gain[0] * level, gain[1] * level] };
4283
+ return layer;
4284
+ });
4285
+ var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
4286
+ var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
4287
+ function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
4288
+ const sounds = TAKES2.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
4289
+ return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
2104
4290
  }
4291
+ var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
4292
+ var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
4293
+ var LANDING_LEVEL = 0.65;
4294
+ var landing = (surface, landing2) => takes(
4295
+ `land_${surface}_${landing2}`,
4296
+ (take) => quieter(LANDING_LEVEL, [
4297
+ // both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
4298
+ ...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
4299
+ ...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer) => ({ ...layer, at: (typeof layer.at === "number" ? layer.at : 0) + 0.018 * take.t })),
4300
+ sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
4301
+ sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
4302
+ ]),
4303
+ FOOTSTEP_FALLOFF
4304
+ );
4305
+ var whoosh = (blow, from, to, d, vol, q = 1.6) => (take) => [
4306
+ // the air: a band sweeping up as the limb speeds up; it peaks `SWING_LEAD_SECONDS` in, the moment before the contact, then dies away
4307
+ sceneNoise({ type: "bandpass", f: from * take.f, f2: to * take.f, q, a: SWING_LEAD_SECONDS[blow], d: d * take.t, vol: vol * take.v }),
4308
+ sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
4309
+ ];
4310
+ var fleshHit = (o) => (take) => [
4311
+ sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
4312
+ sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
4313
+ sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
4314
+ sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
4315
+ ...o.crack ? [sceneNoise({ type: "highpass", f: 2500, d: 0.015, vol: o.crack * take.v, delay: 6e-3 }), sceneTone({ wave: "square", f: 900 * take.f, f2: 300, d: 0.02, vol: o.crack * 0.15 * take.v, delay: 6e-3 })] : [],
4316
+ ...o.wet ? [sceneNoise({ type: "bandpass", f: 1400 * take.f, f2: 500, q: 2.5, d: 0.12 * take.t, vol: o.wet * take.v, delay: 0.01 })] : []
4317
+ ];
4318
+ var knock = (o) => (take) => [
4319
+ sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
4320
+ ...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
4321
+ ];
4322
+ var voice = (o) => (take) => [
4323
+ sceneVoice({ f0: o.f0 * take.f, f1: o.f1 * take.f, d: o.d * take.t, fA: o.fA * take.f, fB: o.fB * take.f, noise: o.noise, vol: o.vol * take.v })
4324
+ ];
4325
+ var BODY = {
4326
+ jump: (take) => [
4327
+ sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
4328
+ sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
4329
+ sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
4330
+ ],
4331
+ cloth_rustle: (take) => [
4332
+ { ...sceneNoise({ type: "bandpass", f: 2400 * take.f, q: 0.8, a: 0.06, d: 0.22 * take.t, vol: 0.12 * take.v }), modulators: [{ target: "gain", wave: "triangle", rate: [9, 14], depth: 0.5 }] }
4333
+ ],
4334
+ body_fall: (take) => [
4335
+ sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
4336
+ sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
4337
+ sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
4338
+ // the limbs a moment later, and the clothes
4339
+ sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
4340
+ sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
4341
+ ],
4342
+ fall_damage: (take) => [
4343
+ ...BODY.body_fall(take),
4344
+ sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
4345
+ ...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer) => ({ ...layer, at: 0.08 }))
4346
+ ],
4347
+ player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
4348
+ player_breath_run: (take) => [
4349
+ sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
4350
+ sceneNoise({ type: "bandpass", f: 900 * take.f, q: 1, a: 0.06, d: 0.26 * take.t, vol: 0.3 * take.v, delay: 0.3 * take.t })
4351
+ ],
4352
+ player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
4353
+ bite: (take) => [
4354
+ sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
4355
+ sceneNoise({ type: "bandpass", f: 1600 * take.f, f2: 450, q: 2.2, d: 0.16 * take.t, vol: 0.55 * take.v, delay: 0.01 }),
4356
+ sceneNoise({ type: "bandpass", f: 3200 * take.f, f2: 1800, q: 1.2, d: 0.22 * take.t, vol: 0.25 * take.v, delay: 0.09 }),
4357
+ sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
4358
+ ],
4359
+ grab: (take) => [
4360
+ sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
4361
+ sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
4362
+ sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
4363
+ ],
4364
+ punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
4365
+ punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
4366
+ punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
4367
+ punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
4368
+ kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
4369
+ kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
4370
+ kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
4371
+ shove_two_hands: (take) => [
4372
+ sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
4373
+ sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
4374
+ sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
4375
+ ],
4376
+ knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
4377
+ knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
4378
+ knife_slash: (take) => [
4379
+ sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
4380
+ sceneNoise({ type: "bandpass", f: 1300 * take.f, f2: 600, q: 2.5, d: 0.12 * take.t, vol: 0.35 * take.v, delay: 0.02 })
4381
+ ],
4382
+ knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
4383
+ blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
4384
+ blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
4385
+ blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
4386
+ axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
4387
+ axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
4388
+ axe_hit_object: knock({ clickF: 2200, click: 0.4, tones: [[1500, 0.15, 0.1], [2300, 0.1, 0.06], [140, 0.1, 0.35]] })
4389
+ };
4390
+ var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
4391
+ var built = [
4392
+ ...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
4393
+ ...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
4394
+ ...BODY_SOUND_IDS.map((id) => takes(id, BODY[id], BODY_FALLOFF, id.endsWith("_swing") ? [0.94, 1.06] : [0.96, 1.04], BODY_BUS[id]))
4395
+ ];
4396
+ var BODY_FOLEY_BANK = {
4397
+ id: "body_foley",
4398
+ sounds: built.flatMap((entry) => entry.sounds),
4399
+ sets: built.map((entry) => entry.set)
4400
+ };
4401
+ var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
2105
4402
 
2106
4403
  // src/webaudio/sink.ts
2107
4404
  function createAudioSink(engine) {
@@ -2114,7 +4411,8 @@ var GameAudio = (() => {
2114
4411
  ...request.volume === void 0 ? {} : { volume: request.volume },
2115
4412
  ...request.params ? { params: request.params } : {},
2116
4413
  ...request.seed === void 0 ? {} : { seed: request.seed },
2117
- ...request.delay === void 0 ? {} : { delay: request.delay }
4414
+ ...request.delay === void 0 ? {} : { delay: request.delay },
4415
+ ...request.urgent ? { urgent: true } : {}
2118
4416
  });
2119
4417
  if (!handle) return null;
2120
4418
  playing.set(handle.id, handle);
@@ -2124,9 +4422,22 @@ var GameAudio = (() => {
2124
4422
  playing.get(handle.id)?.stop();
2125
4423
  playing.delete(handle.id);
2126
4424
  },
2127
- loop(sound) {
4425
+ loop(sound, options) {
2128
4426
  const loop = engine.loop(sound);
2129
- return { set: (state) => loop.set({ ...state.volume === void 0 ? {} : { volume: state.volume }, ...state.frequency === void 0 ? {} : { frequency: state.frequency } }), stop: () => loop.stop() };
4427
+ let position = options?.position;
4428
+ return {
4429
+ set: (state) => {
4430
+ position = state.position ?? position;
4431
+ const cutoff = state.params?.["cutoff"];
4432
+ loop.set({
4433
+ ...state.volume === void 0 ? {} : { volume: state.volume },
4434
+ ...state.frequency === void 0 ? {} : { frequency: state.frequency },
4435
+ ...position ? { position } : {},
4436
+ ...cutoff === void 0 ? {} : { cutoff }
4437
+ });
4438
+ },
4439
+ stop: () => loop.stop()
4440
+ };
2130
4441
  },
2131
4442
  setListener(listener) {
2132
4443
  engine.setListener(listener);
@@ -2189,7 +4500,7 @@ var GameAudio = (() => {
2189
4500
  determinism: "The same seed gives the same sound plan, another seed another one",
2190
4501
  limiter: "The voice limiter holds a storm of voices to its group rule and its global cap"
2191
4502
  },
2192
- mixes: { game: "Game", art_scene: "Art scene" },
4503
+ mixes: { game: "Game", art_scene: "Art scene", kit: "Kit (art scene + limiter)" },
2193
4504
  sheet: { title: "Sound plan", level: "level", distance: "distance", pan: "pan", length: "seconds", silent: "too far to be heard", layer: "layer", none: "no sound chosen" }
2194
4505
  };
2195
4506
  var PT_BR = {
@@ -2243,7 +4554,7 @@ var GameAudio = (() => {
2243
4554
  determinism: "A mesma semente d\xE1 o mesmo plano de som, outra semente d\xE1 outro",
2244
4555
  limiter: "O limite de vozes segura uma tempestade de vozes na regra do grupo e no teto global"
2245
4556
  },
2246
- mixes: { game: "Jogo", art_scene: "Cena de Arte" },
4557
+ mixes: { game: "Jogo", art_scene: "Cena de Arte", kit: "Kit (Cena de Arte + limitador)" },
2247
4558
  sheet: { title: "Plano do som", level: "n\xEDvel", distance: "dist\xE2ncia", pan: "pan", length: "segundos", silent: "longe demais para ouvir", layer: "camada", none: "nenhum som escolhido" }
2248
4559
  };
2249
4560
  var audioBenchTextOf = (locale) => locale === "en" ? EN : PT_BR;
@@ -2317,7 +4628,7 @@ var GameAudio = (() => {
2317
4628
  distance: 10,
2318
4629
  bearing: 0,
2319
4630
  volume: 1,
2320
- mix: "art_scene",
4631
+ mix: DEFAULT_MIX,
2321
4632
  seed: options.seed ?? 1,
2322
4633
  source: "bench",
2323
4634
  loop: NONE,