@adatechnology/game-audio 0.3.6 → 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-7TLFY7VO.js +4170 -0
  1044. package/dist/chunk-7TLFY7VO.js.map +1 -0
  1045. package/dist/index.d.ts +228 -603
  1046. package/dist/index.global.js +2958 -463
  1047. package/dist/index.global.js.map +1 -1
  1048. package/dist/index.js +1088 -2044
  1049. package/dist/index.js.map +1 -1
  1050. package/dist/locale-BugSe8dA.d.ts +1071 -0
  1051. package/dist/testing.d.ts +82 -0
  1052. package/dist/testing.js +19 -0
  1053. package/dist/testing.js.map +1 -0
  1054. package/package.json +13 -6
@@ -22,58 +22,136 @@ var GameAudio = (() => {
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
24
  BENCH_BANK: () => BENCH_BANK,
25
+ BODY_FALLOFF: () => BODY_FALLOFF,
26
+ BODY_FOLEY_BANK: () => BODY_FOLEY_BANK,
27
+ BODY_FOLEY_SET_IDS: () => BODY_FOLEY_SET_IDS,
28
+ BODY_FOLEY_TAKES: () => BODY_FOLEY_TAKES,
25
29
  BUILD_ORDERS: () => BUILD_ORDERS,
26
30
  BUS_IDS: () => BUS_IDS,
27
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,
28
40
  DEFAULT_LISTENER: () => DEFAULT_LISTENER,
41
+ DEFAULT_LOOP_CROSSFADE: () => DEFAULT_LOOP_CROSSFADE,
29
42
  DEFAULT_MAX_VOICES: () => DEFAULT_MAX_VOICES,
30
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,
31
48
  DISTANCE_CURVES: () => DISTANCE_CURVES,
49
+ FOLEY_BANK: () => FOLEY_BANK,
50
+ FOLEY_FALLOFF: () => FOLEY_FALLOFF,
51
+ FOOTSTEP_FALLOFF: () => FOOTSTEP_FALLOFF,
32
52
  GAME_SFX_BANK: () => GAME_SFX_BANK,
33
53
  GAME_SFX_CLASSES: () => GAME_SFX_CLASSES,
34
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,
35
61
  LIBRARY_BANK: () => LIBRARY_BANK,
36
62
  LIBRARY_LOOPS: () => LIBRARY_LOOPS,
37
63
  LIBRARY_SOUNDS: () => LIBRARY_SOUNDS,
38
64
  LOOP_SMOOTHING: () => LOOP_SMOOTHING,
39
65
  LOOP_START_ABOVE: () => LOOP_START_ABOVE,
40
66
  MIX_PROFILES: () => MIX_PROFILES,
67
+ OPEN_CUTOFF: () => OPEN_CUTOFF,
41
68
  PAN_LIMIT: () => PAN_LIMIT,
42
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,
43
80
  VOICE_GROUPS: () => VOICE_GROUPS,
44
81
  audioBenchTextOf: () => audioBenchTextOf,
82
+ balanceOf: () => balanceOf,
45
83
  bufferOf: () => bufferOf,
46
84
  buildChain: () => buildChain,
47
85
  busGain: () => busGain,
48
86
  busPriority: () => busPriority,
87
+ calibrationLoader: () => calibrationLoader,
88
+ centroidHz: () => centroidHz,
89
+ countHits: () => countHits,
49
90
  createAudioEngine: () => createAudioEngine,
50
91
  createAudioSink: () => createAudioSink,
51
92
  createAudioTestModule: () => createAudioTestModule,
52
93
  createLoopState: () => createLoopState,
53
94
  createRng: () => createRng,
95
+ createSampleStore: () => createSampleStore,
96
+ createSetPicker: () => createSetPicker,
54
97
  createSoundRegistry: () => createSoundRegistry,
55
98
  createVoiceManager: () => createVoiceManager,
99
+ crossGains: () => crossGains,
56
100
  cutoffDivisor: () => cutoffDivisor,
101
+ dbOf: () => dbOf,
57
102
  drawBetween: () => drawBetween,
103
+ encodeWav: () => encodeWav,
104
+ endDropDb: () => endDropDb,
105
+ equalPowerIn: () => equalPowerIn,
58
106
  falloffGain: () => falloffGain,
107
+ farMixAt: () => farMixAt,
108
+ fetchLoader: () => fetchLoader,
59
109
  fillImpulse: () => fillImpulse,
60
110
  fillNoise: () => fillNoise,
61
111
  globalContext: () => globalContext,
112
+ impactSoundId: () => impactSoundId,
113
+ inlineLoader: () => inlineLoader,
114
+ isCalibrationUrl: () => isCalibrationUrl,
62
115
  isSilent: () => isSilent,
116
+ kitSampleBank: () => kitSampleBank,
117
+ kitSampleDefaults: () => kitSampleDefaults,
118
+ limiterCurve: () => limiterCurve2,
63
119
  listenerDistance: () => listenerDistance,
120
+ loadKitSamples: () => loadKitSamples,
121
+ loopCrossfade: () => loopCrossfade,
122
+ measureCriteria: () => measureCriteria,
64
123
  noiseClassRecipe: () => noiseClassRecipe,
124
+ onsetIndex: () => onsetIndex,
65
125
  panFor: () => panFor,
126
+ parseSfxName: () => parseSfxName,
127
+ peakIndex: () => peakIndex,
128
+ peakOf: () => peakOf,
129
+ pickTake: () => pickTake,
130
+ planSample: () => planSample,
66
131
  planSound: () => planSound,
67
132
  realize: () => realize,
133
+ realizeSample: () => realizeSample,
134
+ recordOutput: () => recordOutput,
68
135
  resolveMix: () => resolveMix,
136
+ retunePlan: () => retunePlan,
137
+ ringSeconds: () => ringSeconds,
138
+ rmsDb: () => rmsDb,
139
+ sampleBankFromManifest: () => sampleBankFromManifest,
140
+ sampleCutoff: () => sampleCutoff,
141
+ sampleLength: () => sampleLength,
142
+ sampleUrls: () => sampleUrls,
69
143
  sceneMany: () => sceneMany,
70
144
  sceneNoise: () => sceneNoise,
71
145
  sceneRecipe: () => sceneRecipe,
72
146
  sceneTone: () => sceneTone,
73
147
  sceneVoice: () => sceneVoice,
148
+ sfxState: () => sfxState,
149
+ splitTier: () => splitTier,
74
150
  streamFor: () => streamFor,
75
151
  streamSeed: () => streamSeed,
76
- unlockContext: () => unlockContext
152
+ tierFor: () => tierFor,
153
+ unlockContext: () => unlockContext,
154
+ withAliases: () => withAliases
77
155
  });
78
156
 
79
157
  // src/rng.ts
@@ -170,6 +248,19 @@ var GameAudio = (() => {
170
248
  "jogo:L66986 one groan every 0.25 s (`groanBudget`), only nearer than 40 m",
171
249
  "jogo:L70472 the budget counts down by the frame time"
172
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
+ ]
173
264
  }
174
265
  };
175
266
 
@@ -197,7 +288,7 @@ var GameAudio = (() => {
197
288
  if (!group) return void 0;
198
289
  if (group.maxDistance !== void 0 && !((request.distance ?? 0) < group.maxDistance)) return "distance";
199
290
  if (group.cooldown !== void 0 && (budgets.get(request.group) ?? 0) > 0) return "cooldown";
200
- const members = voices.filter((voice) => voice.group === request.group);
291
+ const members = voices.filter((voice2) => voice2.group === request.group);
201
292
  if (group.maxConcurrent === void 0 || members.length < group.maxConcurrent) return void 0;
202
293
  if (!group.steal) return "group_full";
203
294
  evicted.push(members[0].id);
@@ -209,9 +300,9 @@ var GameAudio = (() => {
209
300
  const refusal = groupCheck(request, evicted);
210
301
  if (refusal) return refuse(refusal);
211
302
  const score = request.priority * request.gain;
212
- const remaining = voices.filter((voice) => !evicted.includes(voice.id));
303
+ const remaining = voices.filter((voice2) => !evicted.includes(voice2.id));
213
304
  if (remaining.length >= maxVoices) {
214
- const lowest = remaining.reduce((low, voice) => voice.score < low.score ? voice : low);
305
+ const lowest = remaining.reduce((low, voice2) => voice2.score < low.score ? voice2 : low);
215
306
  if (score <= lowest.score) return refuse("global_full");
216
307
  evicted.push(lowest.id);
217
308
  dropped += 1;
@@ -219,25 +310,25 @@ var GameAudio = (() => {
219
310
  const group = request.group === void 0 ? void 0 : groups.get(request.group);
220
311
  if (group?.cooldown !== void 0) budgets.set(request.group, group.cooldown);
221
312
  const id = nextId++;
222
- voices = [...voices.filter((voice) => !evicted.includes(voice.id)), { id, ...request.group === void 0 ? {} : { group: request.group }, score, remaining: request.length }];
313
+ voices = [...voices.filter((voice2) => !evicted.includes(voice2.id)), { id, ...request.group === void 0 ? {} : { group: request.group }, score, remaining: request.length }];
223
314
  peak = Math.max(peak, voices.length);
224
315
  return { accepted: true, id, evicted };
225
316
  },
226
317
  release(id) {
227
- voices = voices.filter((voice) => voice.id !== id);
318
+ voices = voices.filter((voice2) => voice2.id !== id);
228
319
  },
229
320
  setLength(id, seconds) {
230
- const voice = voices.find((entry) => entry.id === id);
231
- if (voice) voice.remaining = seconds;
321
+ const voice2 = voices.find((entry) => entry.id === id);
322
+ if (voice2) voice2.remaining = seconds;
232
323
  },
233
324
  advance(dt) {
234
325
  for (const [group, budget] of budgets) budgets.set(group, budget - dt);
235
326
  const ended = [];
236
- for (const voice of voices) {
237
- voice.remaining -= dt;
238
- if (voice.remaining <= 0) ended.push(voice.id);
327
+ for (const voice2 of voices) {
328
+ voice2.remaining -= dt;
329
+ if (voice2.remaining <= 0) ended.push(voice2.id);
239
330
  }
240
- if (ended.length > 0) voices = voices.filter((voice) => voice.remaining > 0);
331
+ if (ended.length > 0) voices = voices.filter((voice2) => voice2.remaining > 0);
241
332
  return ended;
242
333
  },
243
334
  setGroup(id, group) {
@@ -345,14 +436,14 @@ var GameAudio = (() => {
345
436
  const source = layer.source;
346
437
  if (source.kind === "noise") return { kind: "noise", offset, loop: source.loop === true };
347
438
  if (source.kind === "osc") return { kind: "osc", wave: source.wave, frequency: finishAutomation(pending.automation ?? {}, duration) };
348
- const voice = pending.voice;
439
+ const voice2 = pending.voice;
349
440
  return {
350
441
  kind: "formant_voice",
351
442
  wave: source.wave,
352
- frequency: finishAutomation(voice.frequency, duration),
353
- vibrato: { rate: voice.vibratoRate, depth: startFrequency(voice.frequency) * source.vibrato.depth },
354
- formants: voice.formants,
355
- breath: { ...voice.breath, offset },
443
+ frequency: finishAutomation(voice2.frequency, duration),
444
+ vibrato: { rate: voice2.vibratoRate, depth: startFrequency(voice2.frequency) * source.vibrato.depth },
445
+ formants: voice2.formants,
446
+ breath: { ...voice2.breath, offset },
356
447
  mix: source.mix
357
448
  };
358
449
  }
@@ -408,100 +499,231 @@ var GameAudio = (() => {
408
499
  return { ...layer, filters: { ...layer.filters, bank } };
409
500
  }
410
501
  var layerEnd = (layer) => layer.at + layer.duration + layer.tail;
411
- function planSound(recipe, input = {}) {
502
+ function planSound(recipe2, input = {}) {
412
503
  const listener = input.listener ?? DEFAULT_LISTENER;
413
504
  const distance = input.distance ?? (input.position ? listenerDistance(listener, input.position, input.distanceMode) : 0);
414
505
  const occlusion = Math.min(1, Math.max(0, input.occlusion ?? 0));
415
- const order = recipe.order ?? "layered";
416
- const falloffLevel = falloffGain(recipe.falloff, input.volume ?? 1, distance);
506
+ const order = recipe2.order ?? "layered";
507
+ const falloffLevel = falloffGain(recipe2.falloff, input.volume ?? 1, distance);
417
508
  const level = occlusion > 0 ? falloffLevel * (1 - OCCLUSION_GAIN * occlusion) : falloffLevel;
418
- const silent = isSilent(recipe.falloff, falloffLevel);
509
+ const silent = isSilent(recipe2.falloff, falloffLevel);
419
510
  const base = {
420
- id: recipe.id,
421
- bus: recipe.bus,
511
+ id: recipe2.id,
512
+ bus: recipe2.bus,
422
513
  order,
423
- ...recipe.group === void 0 ? {} : { group: recipe.group },
424
- priority: recipe.priority ?? busPriority(recipe.bus),
514
+ ...recipe2.group === void 0 ? {} : { group: recipe2.group },
515
+ priority: recipe2.priority ?? busPriority(recipe2.bus),
425
516
  level,
426
517
  distance,
427
- ...recipe.pan ? { pan: input.position ? panFor(listener, input.position) : 0 } : {}
518
+ ...recipe2.pan ? { pan: input.position ? panFor(listener, input.position) : 0 } : {}
428
519
  };
429
- if (silent) return recipe.keepSilent ? { ...base, silent: true, length: 0, layers: [] } : null;
520
+ if (silent) return recipe2.keepSilent ? { ...base, silent: true, length: 0, layers: [] } : null;
430
521
  const scope = { random: input.random ?? createRng(1), params: { ...input.params } };
431
- for (const [name, range] of Object.entries(recipe.vars ?? {})) if (scope.params[name] === void 0) scope.params[name] = draw(range, scope);
522
+ for (const [name, range] of Object.entries(recipe2.vars ?? {})) if (scope.params[name] === void 0) scope.params[name] = draw(range, scope);
432
523
  const defaults = {
433
- ...recipe.duration === void 0 ? {} : { duration: draw(recipe.duration, scope) },
434
- ...recipe.tail === void 0 ? {} : { tail: recipe.tail },
524
+ ...recipe2.duration === void 0 ? {} : { duration: draw(recipe2.duration, scope) },
525
+ ...recipe2.tail === void 0 ? {} : { tail: recipe2.tail },
435
526
  noiseSeconds: input.noiseSeconds ?? DEFAULT_NOISE_SECONDS
436
527
  };
437
- const divisor = cutoffDivisor(recipe.falloff, distance) / (1 - OCCLUSION_CUTOFF * occlusion);
438
- const layers = recipe.layers.flatMap((layer) => planLayer(layer, scope, defaults, order !== "layered", level)).map((layer) => muffle(layer, divisor));
528
+ const divisor = cutoffDivisor(recipe2.falloff, distance) / (1 - OCCLUSION_CUTOFF * occlusion);
529
+ const layers = recipe2.layers.flatMap((layer) => planLayer(layer, scope, defaults, order !== "layered", level)).map((layer) => muffle(layer, divisor));
439
530
  return { ...base, silent: false, length: Math.max(0, ...layers.map(layerEnd)), layers };
440
531
  }
441
532
 
442
- // src/loops.ts
443
- var LOOP_START_ABOVE = 2e-3;
444
- var LOOP_SMOOTHING = { volume: 0.15, frequency: 0.08 };
445
- function createLoopState(recipe, open = false) {
446
- const startAbove = recipe.startAbove ?? LOOP_START_ABOVE;
447
- let created = open;
448
- let volume = recipe.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;
449
602
  return {
450
- set(state) {
451
- volume = state.volume ?? volume;
452
- const create = !created;
453
- if (create && !(volume >= startAbove)) return null;
454
- created = true;
455
- const frequency = recipe.source.kind === "osc" && state.frequency ? state.frequency : void 0;
456
- return { create, volume, ...frequency === void 0 ? {} : { frequency } };
457
- },
458
- get created() {
459
- return created;
460
- },
461
- reset() {
462
- created = false;
463
- }
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 } : {}
464
617
  };
465
618
  }
466
619
 
467
- // src/data/mix-profiles.json
468
- var mix_profiles_default = [
469
- {
470
- id: "game",
471
- master: 0.5,
472
- noise: { seconds: 1.5, create: "lazy" },
473
- 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"]
474
- },
475
- {
476
- id: "art_scene",
477
- master: 0.85,
478
- compressor: { threshold: -12, ratio: 5 },
479
- reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
480
- noise: { seconds: 2, create: "eager" },
481
- sources: [
482
- "cena:L1308 compressor -12 dB, ratio 5",
483
- "cena:L1309 master 0.85",
484
- "cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
485
- "cena:L1311 wet 0.4",
486
- "cena:L1312 noise 2 s made with the chain"
487
- ]
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);
488
645
  }
489
- ];
490
-
491
- // src/mix.ts
492
- var MIX_PROFILES = Object.fromEntries(
493
- mix_profiles_default.map((profile) => [profile.id, profile])
494
- );
495
- var DEFAULT_MIX = "art_scene";
496
- function resolveMix(mix, profiles = MIX_PROFILES) {
497
- if (typeof mix !== "string") return mix;
498
- const profile = profiles[mix];
499
- if (!profile) throw new Error(`game-audio: unknown mix profile "${mix}" (known: ${Object.keys(profiles).join(", ")})`);
500
- return profile;
646
+ return bytes;
501
647
  }
502
- function busGain(setting) {
503
- if (!setting) return 1;
504
- 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
+ };
505
727
  }
506
728
 
507
729
  // src/bank.ts
@@ -520,6 +742,8 @@ var GameAudio = (() => {
520
742
  function createSoundRegistry(banks = []) {
521
743
  const sounds = /* @__PURE__ */ new Map();
522
744
  const loops = /* @__PURE__ */ new Map();
745
+ const sets = /* @__PURE__ */ new Map();
746
+ const samples = /* @__PURE__ */ new Map();
523
747
  const owners = /* @__PURE__ */ new Map();
524
748
  const registry = {
525
749
  register(bank) {
@@ -531,280 +755,175 @@ var GameAudio = (() => {
531
755
  loops.set(loop.id, loop);
532
756
  owners.set(loop.id, bank.id);
533
757
  }
758
+ for (const set of bank.sets ?? []) {
759
+ if (set.variants.length === 0) throw new Error(`sound set ${set.id} has no variant`);
760
+ sets.set(set.id, set);
761
+ owners.set(set.id, bank.id);
762
+ }
763
+ for (const sample of bank.samples ?? []) {
764
+ samples.set(sample.id, sample);
765
+ owners.set(sample.id, bank.id);
766
+ }
534
767
  },
535
768
  patch(id, patch) {
536
769
  const sound = sounds.get(id);
537
770
  if (sound) sounds.set(id, mergeRecords(sound, patch));
538
771
  const loop = loops.get(id);
539
772
  if (loop) loops.set(id, mergeRecords(loop, patch));
773
+ const sample = samples.get(id);
774
+ if (sample) samples.set(id, mergeRecords(sample, patch));
540
775
  },
541
776
  remove(id) {
542
777
  sounds.delete(id);
543
778
  loops.delete(id);
779
+ sets.delete(id);
780
+ samples.delete(id);
544
781
  owners.delete(id);
545
782
  },
546
783
  sound: (id) => sounds.get(id),
547
784
  loop: (id) => loops.get(id),
785
+ set: (id) => sets.get(id),
786
+ sample: (id) => samples.get(id),
548
787
  soundIds: () => [...sounds.keys()],
788
+ setIds: () => [...sets.keys()],
549
789
  loopIds: () => [...loops.keys()],
790
+ sampleIds: () => [...samples.keys()],
550
791
  bankOf: (id) => owners.get(id)
551
792
  };
552
793
  for (const bank of banks) registry.register(bank);
553
794
  return registry;
554
795
  }
555
796
 
556
- // src/data/game-sfx.json
557
- var game_sfx_default = {
558
- sources: ["jogo:L62089-62107"],
559
- 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`.",
560
- classes: [
561
- { id: "pistol", team: "pistola", bus: "weapons", cutoff: 1800, gain: 0.9, seconds: 0.18 },
562
- { id: "shotgun", team: "escopeta", bus: "weapons", cutoff: 900, gain: 1.2, seconds: 0.35 },
563
- { id: "rifle", team: "fuzil", bus: "weapons", cutoff: 1400, gain: 1.1, seconds: 0.28 },
564
- { id: "long_rifle", team: "rifle", bus: "weapons", cutoff: 1100, gain: 1.3, seconds: 0.45 },
565
- { id: "impact", team: "impacto", bus: "world", cutoff: 2500, gain: 0.25, seconds: 0.05 },
566
- { id: "flesh", team: "carne", bus: "world", cutoff: 600, gain: 0.35, seconds: 0.08 },
567
- { id: "step", team: "passo", bus: "world", cutoff: 400, gain: 0.08, seconds: 0.05 },
568
- { id: "glass", team: "vidro", bus: "world", cutoff: 5e3, gain: 0.5, seconds: 0.3 },
569
- { id: "wood", team: "madeira", bus: "world", cutoff: 700, gain: 0.5, seconds: 0.2 },
570
- { id: "reload", team: "recarga", bus: "weapons", cutoff: 3e3, gain: 0.15, seconds: 0.06 },
571
- { id: "empty", team: "vazio", bus: "weapons", cutoff: 4e3, gain: 0.12, seconds: 0.03 },
572
- { id: "explosion", team: "explosao", bus: "world", cutoff: 200, gain: 1.6, seconds: 1.4 }
573
- ],
574
- fallback: { id: "unknown", team: "(any other name)", bus: "world", cutoff: 1e3, gain: 0.5, seconds: 0.1 }
575
- };
576
-
577
- // src/recipes/noise-class.ts
578
- var GAME_SFX = game_sfx_default;
579
- var GAME_SFX_CLASSES = GAME_SFX.classes;
580
- var GAME_SFX_FALLBACK = GAME_SFX.fallback;
581
- function noiseClassRecipe(row, id = `sfx_${row.id}`) {
582
- return {
583
- id,
584
- bus: row.bus,
585
- order: "burst",
586
- duration: row.seconds,
587
- tail: 0.05,
588
- layers: [
589
- {
590
- source: { kind: "noise", offset: "random" },
591
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: row.cutoff } }] },
592
- envelope: [
593
- [0, row.gain, "set"],
594
- ["1d", 1e-3, "exp", "fixed"]
595
- ]
596
- }
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"
597
853
  ],
598
- falloff: DISTANCE_CURVES.game_sfx,
599
- sources: [...GAME_SFX.sources.map((source) => `${source} ${row.team}`)]
600
- };
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;
601
875
  }
602
- var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
603
876
 
604
- // src/recipes/scene.ts
605
- var ATTACK = 2e-3;
606
- function lengthOf(d, attack) {
607
- if (typeof d === "number") return attack + d;
608
- if (Array.isArray(d)) return [attack + d[0], attack + d[1]];
609
- 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;
610
882
  }
611
- var envelope = (attack) => [
612
- [0, 1e-4, "set", "fixed"],
613
- [attack, 1, "exp"],
614
- ["1d", 1e-4, "exp", "fixed"]
615
- ];
616
- function sceneNoise(o) {
617
- const a = o.a ?? ATTACK;
618
- const over = typeof o.d === "number" ? o.d : 0;
619
- return {
620
- filters: {
621
- mode: "serial",
622
- 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 }]
623
- },
624
- duration: lengthOf(o.d, a),
625
- gain: o.vol,
626
- ...o.delay === void 0 ? {} : { at: o.delay },
627
- ...o.wet === void 0 ? {} : { wet: o.wet },
628
- source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
629
- envelope: envelope(a)
630
- };
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
+ });
631
893
  }
632
- function sceneTone(o) {
633
- return {
634
- source: { kind: "osc", wave: o.wave ?? "sine", frequency: { from: o.f, to: o.f2 ?? "from", curve: "exp", over: "duration" } },
635
- duration: lengthOf(o.d, ATTACK),
636
- gain: o.vol,
637
- ...o.delay === void 0 ? {} : { at: o.delay },
638
- ...o.wet === void 0 ? {} : { wet: o.wet },
639
- envelope: envelope(ATTACK)
640
- };
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;
641
898
  }
642
- function sceneVoice(o) {
643
- return {
644
- duration: lengthOf(o.d, 0),
645
- gain: o.vol,
646
- wet: 0.45,
647
- source: {
648
- kind: "formant_voice",
649
- wave: o.wave ?? "sawtooth",
650
- frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
651
- vibrato: { rate: [4, 10], depth: 0.08 },
652
- formants: [
653
- { frequency: o.fA, q: 4 },
654
- { frequency: o.fB, q: 6 }
655
- ],
656
- breath: { frequency: o.fB, q: 1.1, gain: o.noise },
657
- mix: 3.2
658
- },
659
- envelope: [
660
- [0, 1e-4, "set", "fixed"],
661
- [0.1, 1, "exp"],
662
- ["0.6d", 1, "set"],
663
- ["1d", 1e-4, "exp", "fixed"]
664
- ]
665
- };
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;
666
913
  }
667
- var sceneMany = (count, make) => Array.from({ length: count }, make);
668
- function sceneRecipe(id, layers, line, options = {}) {
669
- return {
670
- id,
671
- bus: options.bus ?? "world",
672
- pan: true,
673
- layers,
674
- falloff: DISTANCE_CURVES.art_scene,
675
- ...options.vars ? { vars: options.vars } : {},
676
- ...options.group ? { group: options.group } : {},
677
- sources: [`cena:${line}`]
678
- };
679
- }
680
-
681
- // src/library/crowd_rumble.json
682
- var crowd_rumble_default = {
683
- id: "crowd_rumble",
684
- bus: "ambience",
685
- order: "game_loop",
686
- source: { kind: "noise", loop: true },
687
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 160 } }] },
688
- smoothing: { volume: 0.15, frequency: 0.08 },
689
- startAbove: 2e-3,
690
- 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)"]
691
- };
692
-
693
- // src/library/engine_saw.json
694
- var engine_saw_default = {
695
- id: "engine_saw",
696
- bus: "world",
697
- order: "game_loop",
698
- source: { kind: "osc", wave: "sawtooth", loop: true },
699
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 400 } }] },
700
- smoothing: { volume: 0.15, frequency: 0.08 },
701
- startAbove: 2e-3,
702
- sources: [
703
- "jogo:L62115 motor: sawtooth through a 400 Hz lowpass",
704
- "jogo:L70421 level 0.035 + speed \xD7 0.003, frequency 38 + speed \xD7 7 (the host drives `set`)",
705
- "temporary home until the vehicles package (Q12)"
706
- ]
707
- };
708
-
709
- // src/library/horn.json
710
- var horn_default = {
711
- id: "horn",
712
- bus: "world",
713
- order: "chord",
714
- duration: 0.6,
715
- tail: 0.02,
716
- layers: [
717
- { source: { kind: "osc", wave: "square", frequency: { set: 415 } }, envelope: [[0, 0.08, "set"], [0.6, 1e-4, "set", "fixed"]] },
718
- { source: { kind: "osc", wave: "square", frequency: { set: 523 } }, envelope: [[0, 0.08, "set"], [0.6, 1e-4, "set", "fixed"]] }
719
- ],
720
- falloff: { model: "none" },
721
- sources: ["jogo:L70272-70276 buzina: squares at 415 and 523 Hz, 0.08 for 0.6 s", "temporary home until the vehicles package (Q12)"]
722
- };
723
-
724
- // src/library/radio_static.json
725
- var radio_static_default = {
726
- id: "radio_static",
727
- bus: "world",
728
- order: "static",
729
- duration: 0.6,
730
- layers: [
731
- {
732
- source: { kind: "noise", offset: "random" },
733
- filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 1800 }, q: 0.8 }] },
734
- envelope: [[0, 0.06, "set"], ["1d", 1e-3, "exp", "fixed"]]
735
- }
736
- ],
737
- falloff: { model: "none" },
738
- 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)"]
739
- };
740
-
741
- // src/library/rotor_blades.json
742
- var rotor_blades_default = {
743
- id: "rotor_blades",
744
- bus: "world",
745
- order: "game_loop",
746
- source: { kind: "noise", loop: true },
747
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 300 } }] },
748
- modulators: [{ target: "gain", rate: 11, depth: 0.5, base: 0.5 }],
749
- smoothing: { volume: 0.15, frequency: 0.08 },
750
- startAbove: 2e-3,
751
- sources: [
752
- "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",
753
- "jogo:L70426 level min(0.5, 30 / (distance + 10))",
754
- "temporary home until the vehicles package (Q12)"
755
- ]
756
- };
757
-
758
- // src/library/siren.json
759
- var siren_default = {
760
- id: "siren",
761
- bus: "world",
762
- order: "siren",
763
- duration: 9,
764
- tail: 0.2,
765
- layers: [
766
- {
767
- wet: 1.5,
768
- source: { kind: "osc", wave: "sine", frequency: { set: 850 } },
769
- modulators: [{ target: "frequency", rate: 0.18, depth: 280 }],
770
- filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 1400 } }] },
771
- envelope: [[0, 1e-4, "set", "fixed"], [2, 0.035, "exp"], [6, 0.035, "set"], [9, 1e-4, "exp", "fixed"]]
772
- }
773
- ],
774
- falloff: { model: "none" },
775
- 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)"]
776
- };
777
-
778
- // src/library/index.ts
779
- var LIBRARY_SOUNDS = [horn_default, radio_static_default, siren_default];
780
- var LIBRARY_LOOPS = [engine_saw_default, rotor_blades_default, crowd_rumble_default];
781
- var LIBRARY_BANK = { id: "library", sounds: LIBRARY_SOUNDS, loops: LIBRARY_LOOPS };
782
-
783
- // src/webaudio/noise.ts
784
- function fillNoise(random, length) {
785
- const samples = new Float32Array(length);
786
- for (let index = 0; index < length; index++) samples[index] = random() * 2 - 1;
787
- return samples;
788
- }
789
- function fillImpulse(random, options) {
790
- const size = Math.floor(options.length);
791
- return Array.from({ length: options.channels }, () => {
792
- const samples = new Float32Array(size);
793
- for (let index = 0; index < options.length; index++) {
794
- const value = (random() * 2 - 1) * Math.exp(options.decay * Math.log(1 - index / options.length));
795
- if (index < size) samples[index] = value;
796
- }
797
- return samples;
798
- });
799
- }
800
- function bufferOf(context, channels, requestedLength) {
801
- const buffer = context.createBuffer(channels.length, requestedLength, context.sampleRate);
802
- channels.forEach((samples, channel) => buffer.getChannelData(channel).set(samples));
803
- return buffer;
804
- }
805
-
806
- // src/webaudio/chain.ts
807
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;
808
927
  let compressor;
809
928
  if (profile.compressor) {
810
929
  compressor = context.createDynamicsCompressor();
@@ -813,17 +932,19 @@ var GameAudio = (() => {
813
932
  if (profile.compressor.knee !== void 0) compressor.knee.value = profile.compressor.knee;
814
933
  if (profile.compressor.attack !== void 0) compressor.attack.value = profile.compressor.attack;
815
934
  if (profile.compressor.release !== void 0) compressor.release.value = profile.compressor.release;
816
- compressor.connect(context.destination);
935
+ compressor.connect(output);
817
936
  }
818
937
  const master = context.createGain();
819
938
  master.gain.value = profile.master;
820
- master.connect(compressor ?? context.destination);
939
+ master.connect(compressor ?? output);
821
940
  if (!profile.reverb) {
822
941
  return {
823
942
  master,
824
943
  disconnect: () => {
825
944
  master.disconnect();
826
945
  compressor?.disconnect();
946
+ limiter?.input.disconnect();
947
+ limiter?.shaper.disconnect();
827
948
  }
828
949
  };
829
950
  }
@@ -842,6 +963,8 @@ var GameAudio = (() => {
842
963
  convolver.disconnect();
843
964
  master.disconnect();
844
965
  compressor?.disconnect();
966
+ limiter?.input.disconnect();
967
+ limiter?.shaper.disconnect();
845
968
  }
846
969
  };
847
970
  }
@@ -853,7 +976,32 @@ var GameAudio = (() => {
853
976
  return Constructor ? new Constructor() : null;
854
977
  }
855
978
  function unlockContext(context) {
856
- if (context.state === "suspended") void context.resume();
979
+ if (context.state === "suspended" && typeof context.close === "function") void context.resume();
980
+ }
981
+
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;
1003
+ }
1004
+ };
857
1005
  }
858
1006
 
859
1007
  // src/webaudio/nodes.ts
@@ -906,13 +1054,23 @@ var GameAudio = (() => {
906
1054
  }
907
1055
 
908
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
+ }
909
1067
  var fixed = (range) => typeof range === "number" ? range : 0;
910
1068
  var fixedFrequency = (automation) => fixed("set" in automation ? automation.set : automation.from);
911
- function sourceOf(recipe, target) {
912
- if (recipe.source.kind === "noise") return { source: noiseSource(target, true) };
1069
+ function sourceOf(recipe2, target) {
1070
+ if (recipe2.source.kind === "noise") return { source: noiseSource(target, true) };
913
1071
  const oscillator = target.context.createOscillator();
914
- oscillator.type = recipe.source.wave;
915
- if (recipe.source.frequency) oscillator.frequency.value = fixedFrequency(recipe.source.frequency);
1072
+ oscillator.type = recipe2.source.wave;
1073
+ if (recipe2.source.frequency) oscillator.frequency.value = fixedFrequency(recipe2.source.frequency);
916
1074
  return { source: oscillator, frequency: oscillator.frequency };
917
1075
  }
918
1076
  function stopAll(sources) {
@@ -925,23 +1083,23 @@ var GameAudio = (() => {
925
1083
  }
926
1084
  };
927
1085
  }
928
- function realizeGameLoop(recipe, target) {
1086
+ function realizeGameLoop(recipe2, target) {
929
1087
  const { context } = target;
930
1088
  const gain = context.createGain();
931
- const filters = (recipe.filters?.bank ?? []).map(() => context.createBiquadFilter());
1089
+ const filters = (recipe2.filters?.bank ?? []).map(() => context.createBiquadFilter());
932
1090
  gain.gain.value = 0;
933
1091
  const last = filters.reduce((previous, filter) => previous ? previous.connect(filter) : filter, void 0);
934
1092
  last?.connect(gain);
935
- gain.connect(target.output);
936
- const { source, frequency } = sourceOf(recipe, target);
937
- recipe.filters?.bank.forEach((spec, index) => {
1093
+ const output = outputStage(recipe2, target, gain);
1094
+ const { source, frequency } = sourceOf(recipe2, target);
1095
+ recipe2.filters?.bank.forEach((spec, index) => {
938
1096
  const filter = filters[index];
939
1097
  filter.type = spec.type;
940
1098
  filter.frequency.value = fixedFrequency(spec.frequency);
941
1099
  if (spec.q !== void 0) filter.Q.value = fixed(spec.q);
942
1100
  });
943
1101
  const sources = [source];
944
- for (const modulator of recipe.modulators ?? []) {
1102
+ for (const modulator of recipe2.modulators ?? []) {
945
1103
  const lfo = context.createOscillator();
946
1104
  const depth = context.createGain();
947
1105
  const stage = context.createGain();
@@ -959,16 +1117,16 @@ var GameAudio = (() => {
959
1117
  }
960
1118
  source.connect(filters[0] ?? gain);
961
1119
  source.start();
962
- return { gain, source, ...frequency ? { frequency } : {}, stop: stopAll(sources) };
1120
+ return { gain, ...output, source, ...frequency ? { frequency } : {}, ...filters[0] ? { cutoff: filters[0].frequency } : {}, stop: stopAll(sources) };
963
1121
  }
964
- function realizeChainLoop(recipe, target, volume) {
1122
+ function realizeChainLoop(recipe2, target, volume) {
965
1123
  const { context } = target;
966
- const { source, frequency } = sourceOf(recipe, target);
967
- const filters = (recipe.filters?.bank ?? []).map(
1124
+ const { source, frequency } = sourceOf(recipe2, target);
1125
+ const filters = (recipe2.filters?.bank ?? []).map(
968
1126
  (spec) => filterNode(context, { type: spec.type, frequency: { value: fixedFrequency(spec.frequency) }, ...spec.q === void 0 ? {} : { q: fixed(spec.q) } }, target.time)
969
1127
  );
970
1128
  const gain = gainNode(context, volume);
971
- const lfos = (recipe.modulators ?? []).map((modulator) => {
1129
+ const lfos = (recipe2.modulators ?? []).map((modulator) => {
972
1130
  const lfo = context.createOscillator();
973
1131
  if (modulator.wave) lfo.type = modulator.wave;
974
1132
  lfo.frequency.value = fixed(modulator.rate);
@@ -980,19 +1138,19 @@ var GameAudio = (() => {
980
1138
  let tail = source;
981
1139
  for (const filter of filters) tail = tail.connect(filter);
982
1140
  tail.connect(gain);
983
- gain.connect(target.output);
1141
+ const output = outputStage(recipe2, target, gain);
984
1142
  source.start();
985
1143
  for (const lfo of lfos) lfo.start();
986
- return { gain, source, ...frequency ? { frequency } : {}, stop: stopAll([source, ...lfos]) };
1144
+ return { gain, ...output, source, ...frequency ? { frequency } : {}, ...filters[0] ? { cutoff: filters[0].frequency } : {}, stop: stopAll([source, ...lfos]) };
987
1145
  }
988
1146
 
989
1147
  // src/webaudio/engine-loop.ts
990
1148
  var INERT = { set() {
991
1149
  }, stop() {
992
1150
  } };
993
- function createLoopHandle(recipe, host, volume) {
994
- if (!recipe) return INERT;
995
- const loopRecipe = recipe;
1151
+ function createLoopHandle(recipe2, host, volume) {
1152
+ if (!recipe2) return INERT;
1153
+ const loopRecipe = recipe2;
996
1154
  const isChain = loopRecipe.order === "chain_loop";
997
1155
  const opening = volume ?? loopRecipe.volume ?? 0;
998
1156
  const smoothing = loopRecipe.smoothing ?? LOOP_SMOOTHING;
@@ -1003,12 +1161,12 @@ var GameAudio = (() => {
1003
1161
  return nodes !== void 0;
1004
1162
  },
1005
1163
  rewire() {
1006
- nodes?.gain.disconnect();
1007
- nodes?.gain.connect(host.output(loopRecipe.bus));
1164
+ nodes?.out.disconnect();
1165
+ nodes?.out.connect(host.output(loopRecipe.bus));
1008
1166
  },
1009
1167
  stop() {
1010
1168
  nodes?.stop();
1011
- nodes?.gain.disconnect();
1169
+ nodes?.out.disconnect();
1012
1170
  nodes = void 0;
1013
1171
  state = createLoopState(loopRecipe);
1014
1172
  host.unregister(entry);
@@ -1028,16 +1186,168 @@ var GameAudio = (() => {
1028
1186
  state = createLoopState({ ...loopRecipe, volume: opening }, true);
1029
1187
  };
1030
1188
  if (isChain) open();
1189
+ let position;
1190
+ let cutoff;
1191
+ const placed = loopRecipe.falloff !== void 0 || loopRecipe.pan === true;
1031
1192
  return {
1032
1193
  set(next) {
1033
1194
  if (isChain && !nodes) open();
1034
1195
  const context = host.ready();
1035
1196
  if (!context) return;
1197
+ position = next.position ?? position;
1198
+ cutoff = next.cutoff ?? cutoff;
1036
1199
  const action = state.set(next);
1037
1200
  if (!action) return;
1038
1201
  nodes ?? (nodes = create(context, 0));
1039
- nodes.gain.gain.setTargetAtTime(action.volume, context.currentTime, smoothing.volume);
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);
1040
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 });
1041
1351
  },
1042
1352
  stop: entry.stop
1043
1353
  };
@@ -1064,6 +1374,58 @@ var GameAudio = (() => {
1064
1374
  return store.buffer;
1065
1375
  }
1066
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",
1412
+ "knife_swing",
1413
+ "knife_stab",
1414
+ "knife_slash",
1415
+ "knife_hit_object",
1416
+ "blunt_swing",
1417
+ "blunt_hit",
1418
+ "blunt_hit_object",
1419
+ "axe_swing",
1420
+ "axe_chop",
1421
+ "axe_hit_object"
1422
+ ];
1423
+ var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
1424
+ var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
1425
+ var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing2) => `land_${surface}_${landing2}`));
1426
+ var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
1427
+ var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
1428
+
1067
1429
  // src/webaudio/engine-queue.ts
1068
1430
  var nowMs = () => typeof performance === "undefined" ? Date.now() : performance.now();
1069
1431
  function scheduleTask(task) {
@@ -1083,23 +1445,28 @@ var GameAudio = (() => {
1083
1445
  const maxLatencyMs = options.maxLatencyMs ?? 250;
1084
1446
  let jobs = [];
1085
1447
  let scheduled = false;
1448
+ let attached = false;
1449
+ const runNext = () => {
1450
+ const job = jobs.shift();
1451
+ if (nowMs() - job.queuedAt > maxLatencyMs) job.drop();
1452
+ else job.run();
1453
+ };
1086
1454
  const drain = () => {
1087
1455
  scheduled = false;
1456
+ if (attached) return;
1088
1457
  const began = nowMs();
1089
1458
  while (jobs.length > 0) {
1090
- const job = jobs.shift();
1091
- if (nowMs() - job.queuedAt > maxLatencyMs) job.drop();
1092
- else job.run();
1459
+ runNext();
1093
1460
  if (nowMs() - began >= budgetMs) break;
1094
1461
  }
1095
1462
  if (jobs.length > 0) schedule();
1096
1463
  };
1097
1464
  const schedule = () => {
1098
- if (scheduled) return;
1465
+ if (scheduled || attached) return;
1099
1466
  scheduled = true;
1100
1467
  scheduleTask(drain);
1101
1468
  };
1102
- return {
1469
+ const queue = {
1103
1470
  push(job) {
1104
1471
  jobs.push({ ...job, queuedAt: nowMs() });
1105
1472
  schedule();
@@ -1110,8 +1477,33 @@ var GameAudio = (() => {
1110
1477
  },
1111
1478
  get size() {
1112
1479
  return jobs.length;
1480
+ },
1481
+ deferredQueue: () => deferredQueue
1482
+ };
1483
+ const deferredQueue = {
1484
+ id: DEFERRED_QUEUE_ID.AUDIO_VOICES,
1485
+ pending: () => jobs.length,
1486
+ run(deadline, clock) {
1487
+ let ran = 0;
1488
+ while (jobs.length > 0) {
1489
+ runNext();
1490
+ ran++;
1491
+ if (clock() >= deadline) break;
1492
+ }
1493
+ return ran;
1494
+ },
1495
+ flush() {
1496
+ while (jobs.length > 0) runNext();
1497
+ },
1498
+ attach() {
1499
+ attached = true;
1500
+ },
1501
+ detach() {
1502
+ attached = false;
1503
+ if (jobs.length > 0) schedule();
1113
1504
  }
1114
1505
  };
1506
+ return queue;
1115
1507
  }
1116
1508
 
1117
1509
  // src/webaudio/realize-game.ts
@@ -1294,7 +1686,7 @@ var GameAudio = (() => {
1294
1686
 
1295
1687
  // src/webaudio/realize-layered.ts
1296
1688
  var end2 = (layer, start) => start + layer.duration + layer.tail;
1297
- function outputStage(plan, layer, target) {
1689
+ function outputStage2(plan, layer, target) {
1298
1690
  const { context } = target;
1299
1691
  const level = gainNode(context, layer.level);
1300
1692
  let node = level;
@@ -1326,7 +1718,7 @@ var GameAudio = (() => {
1326
1718
  let tail = source;
1327
1719
  for (const filter of filters) tail = tail.connect(filter);
1328
1720
  tail.connect(envelope2);
1329
- envelope2.connect(outputStage(plan, layer, target));
1721
+ envelope2.connect(outputStage2(plan, layer, target));
1330
1722
  source.start(time, layer.source.kind === "noise" ? layer.source.offset : 0);
1331
1723
  source.stop(end2(layer, time));
1332
1724
  return [source];
@@ -1343,7 +1735,7 @@ var GameAudio = (() => {
1343
1735
  const envelope2 = context.createGain();
1344
1736
  applyEnvelope(envelope2.gain, layer.envelope, time);
1345
1737
  oscillator.connect(envelope2);
1346
- envelope2.connect(outputStage(plan, layer, target));
1738
+ envelope2.connect(outputStage2(plan, layer, target));
1347
1739
  oscillator.start(time);
1348
1740
  oscillator.stop(end2(layer, time));
1349
1741
  return [oscillator];
@@ -1375,7 +1767,7 @@ var GameAudio = (() => {
1375
1767
  const envelope2 = context.createGain();
1376
1768
  applyEnvelope(envelope2.gain, layer.envelope, time);
1377
1769
  mix.connect(envelope2);
1378
- envelope2.connect(outputStage(plan, layer, target));
1770
+ envelope2.connect(outputStage2(plan, layer, target));
1379
1771
  const stop = end2(layer, time);
1380
1772
  oscillator.start(time);
1381
1773
  vibrato.start(time);
@@ -1488,45 +1880,292 @@ var GameAudio = (() => {
1488
1880
  return BUILDERS[plan.order](plan, target);
1489
1881
  }
1490
1882
 
1491
- // src/webaudio/engine.ts
1492
- var usesNoise = (recipe) => recipe.layers.some((layer) => layer.source.kind !== "osc");
1493
- var planUsesNoise = (plan) => plan.layers.some((layer) => layer.source.kind !== "osc");
1494
- function createAudioEngine(options = {}) {
1495
- const seed = options.seed ?? 1;
1496
- const registry = createSoundRegistry(options.banks);
1497
- const voices = createVoiceManager({ ...options.groups ? { groups: options.groups } : {}, ...options.maxVoices === void 0 ? {} : { maxVoices: options.maxVoices } });
1498
- const engineRandom = createRng(seed);
1499
- const shared = createNoiseStore(engineRandom);
1500
- const busSettings = /* @__PURE__ */ new Map();
1501
- const busNodes = /* @__PURE__ */ new Map();
1502
- const meters = /* @__PURE__ */ new Map();
1503
- const active = /* @__PURE__ */ new Map();
1504
- const loops = /* @__PURE__ */ new Set();
1505
- const named = /* @__PURE__ */ new Map();
1506
- let mix = resolveMix(options.mix ?? DEFAULT_MIX, options.profiles);
1507
- let context = null;
1508
- let chain = null;
1509
- let listener = DEFAULT_LISTENER;
1510
- let lastTime = 0;
1511
- const outputFor = (bus) => busNodes.get(bus) ?? chain?.master;
1512
- const watched = /* @__PURE__ */ new Set();
1513
- const meterOf = (bus) => {
1514
- const existing = meters.get(bus);
1515
- if (existing) return existing;
1516
- const node = busNodes.get(bus);
1517
- if (!context || !node || typeof context.createAnalyser !== "function") return void 0;
1518
- const analyser = context.createAnalyser();
1519
- analyser.fftSize = 1024;
1520
- node.connect(analyser);
1521
- const made = { analyser, data: new Float32Array(analyser.fftSize) };
1522
- meters.set(bus, made);
1523
- return made;
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()
1524
1946
  };
1525
- const ensureBus = (bus) => {
1526
- if (!context || !chain || busNodes.has(bus)) return;
1527
- const node = context.createGain();
1528
- node.connect(chain.master);
1529
- busNodes.set(bus, node);
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
+
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
+ }
2094
+ var usesNoise = (recipe2) => recipe2.layers.some((layer) => layer.source.kind !== "osc");
2095
+ var planUsesNoise = (plan) => plan.layers.some((layer) => layer.source.kind !== "osc");
2096
+ function createAudioEngine(options = {}) {
2097
+ const seed = options.seed ?? 1;
2098
+ const registry = createSoundRegistry(options.banks);
2099
+ const voices = createVoiceManager({ ...options.groups ? { groups: options.groups } : {}, ...options.maxVoices === void 0 ? {} : { maxVoices: options.maxVoices } });
2100
+ const engineRandom = createRng(seed);
2101
+ const shared = createNoiseStore(engineRandom);
2102
+ const busSettings = /* @__PURE__ */ new Map();
2103
+ const busNodes = /* @__PURE__ */ new Map();
2104
+ const meters = /* @__PURE__ */ new Map();
2105
+ const active = /* @__PURE__ */ new Map();
2106
+ const loops = /* @__PURE__ */ new Set();
2107
+ const named = /* @__PURE__ */ new Map();
2108
+ let mix = resolveMix(options.mix ?? DEFAULT_MIX, options.profiles);
2109
+ let context = null;
2110
+ let chain = null;
2111
+ let listener = DEFAULT_LISTENER;
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
+ };
2150
+ const outputFor = (bus) => busNodes.get(bus) ?? chain?.master;
2151
+ const watched = /* @__PURE__ */ new Set();
2152
+ const meterOf = (bus) => {
2153
+ const existing = meters.get(bus);
2154
+ if (existing) return existing;
2155
+ const node = busNodes.get(bus);
2156
+ if (!context || !node || typeof context.createAnalyser !== "function") return void 0;
2157
+ const analyser = context.createAnalyser();
2158
+ analyser.fftSize = 1024;
2159
+ node.connect(analyser);
2160
+ const made = { analyser, data: new Float32Array(analyser.fftSize) };
2161
+ meters.set(bus, made);
2162
+ return made;
2163
+ };
2164
+ const ensureBus = (bus) => {
2165
+ if (!context || !chain || busNodes.has(bus)) return;
2166
+ const node = context.createGain();
2167
+ node.connect(chain.master);
2168
+ busNodes.set(bus, node);
1530
2169
  if (watched.has(bus)) meterOf(bus);
1531
2170
  };
1532
2171
  const ready = () => {
@@ -1552,7 +2191,7 @@ var GameAudio = (() => {
1552
2191
  lastTime = context.currentTime;
1553
2192
  if (elapsed > 0) for (const id of voices.advance(elapsed)) active.delete(id);
1554
2193
  };
1555
- const inputFor = (recipe, playOptions, random) => ({
2194
+ const inputFor = (recipe2, playOptions, random) => ({
1556
2195
  random,
1557
2196
  listener,
1558
2197
  noiseSeconds: mix.noise.seconds,
@@ -1561,34 +2200,48 @@ var GameAudio = (() => {
1561
2200
  ...playOptions.distance === void 0 ? {} : { distance: playOptions.distance },
1562
2201
  ...playOptions.position ? { position: playOptions.position } : {},
1563
2202
  ...playOptions.params ? { params: playOptions.params } : {},
1564
- ...options.occlusion && playOptions.position && recipe.falloff?.model !== "none" ? { occlusion: options.occlusion(playOptions.position) } : {}
2203
+ ...options.occlusion && playOptions.position && recipe2.falloff?.model !== "none" ? { occlusion: options.occlusion(playOptions.position) } : {}
1565
2204
  });
1566
2205
  const startQueue = options.deferredStart ? createStartQueue(options.deferredStart) : null;
1567
- const build = (plan, store, delay) => {
2206
+ const build = (plan, store, delay, requestedAt, fallbackOf) => {
1568
2207
  const audio = context;
1569
- return realize(plan, {
2208
+ const time = audio.currentTime + delay;
2209
+ const realized = realize(plan, {
1570
2210
  context: audio,
1571
2211
  output: outputFor(plan.bus),
1572
2212
  ...chain?.wet ? { wet: chain.wet } : {},
1573
2213
  noise: () => noiseBuffer(store, audio, mix.noise.seconds),
1574
- time: audio.currentTime + delay
2214
+ time
1575
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 }
2226
+ });
2227
+ return realized;
1576
2228
  };
1577
- const deferVoice = (plan, store, voiceId, delay) => {
1578
- let built = null;
2229
+ const deferVoice = (plan, store, voiceId, delay, fallbackOf) => {
2230
+ const requestedAt = context?.currentTime ?? 0;
2231
+ let built2 = null;
1579
2232
  let cancelled = false;
1580
2233
  const pending = {
1581
2234
  get sources() {
1582
- return built?.sources ?? [];
2235
+ return built2?.sources ?? [];
1583
2236
  },
1584
2237
  stop() {
1585
2238
  cancelled = true;
1586
- built?.stop();
2239
+ built2?.stop();
1587
2240
  }
1588
2241
  };
1589
2242
  startQueue.push({
1590
2243
  run() {
1591
- if (!cancelled && context) built = build(plan, store, delay);
2244
+ if (!cancelled && context) built2 = build(plan, store, delay, requestedAt, fallbackOf);
1592
2245
  },
1593
2246
  drop() {
1594
2247
  if (voiceId === void 0) return;
@@ -1598,13 +2251,14 @@ var GameAudio = (() => {
1598
2251
  });
1599
2252
  return pending;
1600
2253
  };
1601
- const start = (plan, store, voiceId, delay = 0) => {
1602
- 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);
1603
2256
  if (voiceId === void 0) return null;
1604
2257
  voices.setLength(voiceId, plan.length + delay);
1605
2258
  active.set(voiceId, realized);
1606
2259
  return {
1607
2260
  id: voiceId,
2261
+ kind: "recipe",
1608
2262
  plan,
1609
2263
  stop() {
1610
2264
  realized.stop();
@@ -1613,8 +2267,8 @@ var GameAudio = (() => {
1613
2267
  }
1614
2268
  };
1615
2269
  };
1616
- const admit = (recipe, gain, distance) => {
1617
- const decision = voices.request({ ...recipe.group ? { group: recipe.group } : {}, priority: recipe.priority ?? busPriority(recipe.bus), gain, distance, length: 0 });
2270
+ const admit = (recipe2, gain, distance) => {
2271
+ const decision = voices.request({ ...recipe2.group ? { group: recipe2.group } : {}, priority: recipe2.priority ?? busPriority(recipe2.bus), gain, distance, length: 0 });
1618
2272
  if (!decision.accepted) return null;
1619
2273
  for (const id of decision.evicted) {
1620
2274
  active.get(id)?.stop();
@@ -1622,22 +2276,107 @@ var GameAudio = (() => {
1622
2276
  }
1623
2277
  return decision.id;
1624
2278
  };
1625
- const playWith = (id, playOptions, random, store) => {
1626
- const recipe = registry.sound(id);
1627
- if (!recipe || busSettings.get(recipe.bus)?.muted) return null;
2279
+ const picker = createSetPicker();
2280
+ const resolve = (id, random, chooser = picker) => {
2281
+ if (registry.sound(id)) return { id, rate: 1 };
2282
+ const set = registry.set(id);
2283
+ return set ? chooser.pick(set, random) : { id, rate: 1 };
2284
+ };
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
+ }
2362
+ const seeded = playOptions.seed === void 0 ? null : createRng(playOptions.seed);
2363
+ const { id, rate } = resolve(asked, seeded ?? streamRandom);
2364
+ const random = seeded ?? streamRandom;
2365
+ const recipe2 = registry.sound(id);
2366
+ if (!recipe2 || busSettings.get(recipe2.bus)?.muted) return null;
1628
2367
  const distance = playOptions.distance ?? (playOptions.position ? listenerDistance(listener, playOptions.position, options.distance) : 0);
1629
- const level = falloffGain(recipe.falloff, playOptions.volume ?? 1, distance);
1630
- const silent = isSilent(recipe.falloff, level);
1631
- if (silent && !recipe.keepSilent) return null;
2368
+ const level = falloffGain(recipe2.falloff, playOptions.volume ?? 1, distance);
2369
+ const silent = isSilent(recipe2.falloff, level);
2370
+ if (silent && !recipe2.keepSilent) return null;
1632
2371
  const audio = ready();
1633
2372
  if (!audio) return null;
1634
2373
  unlockContext(audio);
1635
2374
  advance();
1636
- const voiceId = silent ? void 0 : admit(recipe, level, distance);
2375
+ const voiceId = silent ? void 0 : admit(recipe2, level, distance);
1637
2376
  if (voiceId === null) return null;
1638
- if (!silent && usesNoise(recipe)) prepareNoise(store, audio, mix.noise.seconds);
1639
- const plan = planSound(recipe, inputFor(recipe, playOptions, playOptions.seed === void 0 ? random : createRng(playOptions.seed)));
1640
- return plan ? start(plan, store, voiceId, playOptions.delay) : null;
2377
+ if (!silent && usesNoise(recipe2)) prepareNoise(store, audio, mix.noise.seconds);
2378
+ const plan = planSound(recipe2, inputFor(recipe2, playOptions, random));
2379
+ return plan ? start(retunePlan(plan, rate), store, voiceId, playOptions.delay, fallbackOf, playOptions.urgent) : null;
1641
2380
  };
1642
2381
  const randomFor = (playOptions) => {
1643
2382
  if (playOptions.channel === void 0) return engineRandom;
@@ -1647,9 +2386,11 @@ var GameAudio = (() => {
1647
2386
  named.set(playOptions.channel, stream);
1648
2387
  return stream;
1649
2388
  };
1650
- const planWith = (id, playOptions, random) => {
1651
- const recipe = registry.sound(id);
1652
- return recipe ? planSound(recipe, inputFor(recipe, playOptions, random)) : 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;
1653
2394
  };
1654
2395
  const loopHost = {
1655
2396
  ready,
@@ -1657,24 +2398,73 @@ var GameAudio = (() => {
1657
2398
  prepareNoise: () => context && prepareNoise(shared, context, mix.noise.seconds),
1658
2399
  noise: () => noiseBuffer(shared, context, mix.noise.seconds),
1659
2400
  register: (loop) => void loops.add(loop),
1660
- unregister: (loop) => void loops.delete(loop)
2401
+ unregister: (loop) => void loops.delete(loop),
2402
+ listener: () => listener,
2403
+ ...options.distance ? { distance: options.distance } : {}
1661
2404
  };
1662
2405
  return {
1663
2406
  registry,
1664
2407
  register: (bank) => registry.register(bank),
1665
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
+ },
1666
2437
  play: (id, playOptions = {}) => playWith(id, playOptions, randomFor(playOptions), shared),
1667
2438
  playPlan(plan, playOptions = {}) {
1668
2439
  const audio = ready();
1669
2440
  if (!audio) return null;
1670
2441
  advance();
1671
- const recipe = { id: plan.id, bus: plan.bus, layers: [], sources: [], priority: plan.priority, ...plan.group ? { group: plan.group } : {} };
1672
- const voiceId = plan.silent ? void 0 : admit(recipe, plan.level, plan.distance);
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);
1673
2444
  if (voiceId === null) return null;
1674
2445
  if (planUsesNoise(plan)) prepareNoise(shared, audio, mix.noise.seconds);
1675
2446
  return start(plan, shared, voiceId, playOptions.delay);
1676
2447
  },
1677
- loop: (id, loopOptions = {}) => createLoopHandle(registry.loop(id), loopHost, loopOptions.volume),
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
+ },
1678
2468
  channel(channelOptions) {
1679
2469
  const random = channelOptions.random ?? (channelOptions.seed === void 0 ? engineRandom : createRng(channelOptions.seed));
1680
2470
  const store = channelOptions.ownNoise ? createNoiseStore(random) : shared;
@@ -1731,6 +2521,7 @@ var GameAudio = (() => {
1731
2521
  for (const sample of meter.data) sum += sample * sample;
1732
2522
  return Math.sqrt(sum / meter.data.length);
1733
2523
  },
2524
+ deferredQueue: () => startQueue?.deferredQueue() ?? null,
1734
2525
  stats() {
1735
2526
  advance();
1736
2527
  const voiceStats = voices.stats();
@@ -1754,33 +2545,1737 @@ var GameAudio = (() => {
1754
2545
  busNodes.clear();
1755
2546
  meters.clear();
1756
2547
  shared.reset();
2548
+ sampleStore.clear();
2549
+ memory.clear();
2550
+ sampleLimiters.clear();
2551
+ chokes.clear();
1757
2552
  }
1758
2553
  };
1759
2554
  }
1760
2555
 
1761
- // src/webaudio/sink.ts
1762
- function createAudioSink(engine) {
1763
- const playing = /* @__PURE__ */ new Map();
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;
1764
2740
  return {
1765
- engine,
1766
- play(request) {
1767
- const handle = engine.play(request.sound, {
1768
- ...request.position ? { position: request.position } : {},
1769
- ...request.volume === void 0 ? {} : { volume: request.volume },
1770
- ...request.params ? { params: request.params } : {},
1771
- ...request.seed === void 0 ? {} : { seed: request.seed }
1772
- });
1773
- if (!handle) return null;
1774
- playing.set(handle.id, handle);
1775
- return { id: handle.id };
1776
- },
1777
- stop(handle) {
1778
- playing.get(handle.id)?.stop();
1779
- playing.delete(handle.id);
1780
- },
1781
- loop(sound) {
1782
- const loop = engine.loop(sound);
1783
- return { set: (state) => loop.set({ ...state.volume === void 0 ? {} : { volume: state.volume }, ...state.frequency === void 0 ? {} : { frequency: state.frequency } }), stop: () => loop.stop() };
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)
3323
+ };
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)
3333
+ };
3334
+ }
3335
+ function sceneVoice(o) {
3336
+ return {
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
3351
+ },
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"]]
3465
+ }
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 } };
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];
4240
+
4241
+ // src/webaudio/sink.ts
4242
+ function createAudioSink(engine) {
4243
+ const playing = /* @__PURE__ */ new Map();
4244
+ return {
4245
+ engine,
4246
+ play(request) {
4247
+ const handle = engine.play(request.sound, {
4248
+ ...request.position ? { position: request.position } : {},
4249
+ ...request.volume === void 0 ? {} : { volume: request.volume },
4250
+ ...request.params ? { params: request.params } : {},
4251
+ ...request.seed === void 0 ? {} : { seed: request.seed },
4252
+ ...request.delay === void 0 ? {} : { delay: request.delay },
4253
+ ...request.urgent ? { urgent: true } : {}
4254
+ });
4255
+ if (!handle) return null;
4256
+ playing.set(handle.id, handle);
4257
+ return { id: handle.id };
4258
+ },
4259
+ stop(handle) {
4260
+ playing.get(handle.id)?.stop();
4261
+ playing.delete(handle.id);
4262
+ },
4263
+ loop(sound, options) {
4264
+ const loop = engine.loop(sound);
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
+ };
1784
4279
  },
1785
4280
  setListener(listener) {
1786
4281
  engine.setListener(listener);
@@ -1843,7 +4338,7 @@ var GameAudio = (() => {
1843
4338
  determinism: "The same seed gives the same sound plan, another seed another one",
1844
4339
  limiter: "The voice limiter holds a storm of voices to its group rule and its global cap"
1845
4340
  },
1846
- mixes: { game: "Game", art_scene: "Art scene" },
4341
+ mixes: { game: "Game", art_scene: "Art scene", kit: "Kit (art scene + limiter)" },
1847
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" }
1848
4343
  };
1849
4344
  var PT_BR = {
@@ -1897,7 +4392,7 @@ var GameAudio = (() => {
1897
4392
  determinism: "A mesma semente d\xE1 o mesmo plano de som, outra semente d\xE1 outro",
1898
4393
  limiter: "O limite de vozes segura uma tempestade de vozes na regra do grupo e no teto global"
1899
4394
  },
1900
- mixes: { game: "Jogo", art_scene: "Cena de Arte" },
4395
+ mixes: { game: "Jogo", art_scene: "Cena de Arte", kit: "Kit (Cena de Arte + limitador)" },
1901
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" }
1902
4397
  };
1903
4398
  var audioBenchTextOf = (locale) => locale === "en" ? EN : PT_BR;
@@ -1971,7 +4466,7 @@ var GameAudio = (() => {
1971
4466
  distance: 10,
1972
4467
  bearing: 0,
1973
4468
  volume: 1,
1974
- mix: "art_scene",
4469
+ mix: DEFAULT_MIX,
1975
4470
  seed: options.seed ?? 1,
1976
4471
  source: "bench",
1977
4472
  loop: NONE,