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