Add industrial metal soundtrack with seamless loop

- assets/audio/music/zombie_industrial_loop.ogg: 57.60 s Ogg Vorbis loop
  generated locally with YuE2-3B on the Tesla V100 (score written for this
  track; vocals removed with HTDemucs). The loop is spliced at the track's
  structural repeat with a 0.6 s crossfade: measured seam dip <= 1.6 dB.
- scripts/main.gd: "Music" AudioStreamPlayer at -11 dB, started at boot on
  desktop and on the start click on Web (browser autoplay policy). It forces
  stream.loop at runtime because the Web staging step re-imports the OGG with
  loop=false.
- tools/test_music.gd: soundtrack test (loop, length, volume, playback,
  still playing after player death).
- docs/MUSIC.md: source, loop construction, verification, licence.
- docs/WEB-DEPLOY.md: release ab6054111f5919a8 notes.

Initial commit of the project.
This commit is contained in:
enne2
2026-10-10 23:11:13 +02:00
commit 956a15a525
199 changed files with 4563 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
extends SceneTree
## Verifica la colonna sonora in loop: nodo presente, stream OGG con loop attivo,
## volume da sottofondo, durata attesa e riproduzione avviata.
const EXPECTED_LENGTH := 57.6
const EXPECTED_VOLUME_DB := -11.0
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
var world = load("res://scenes/main.tscn").instantiate()
root.add_child(world)
await process_frame
var players := get_nodes_in_group("music")
assert(players.size() == 1)
var music: AudioStreamPlayer = players[0]
assert(music.name == "Music")
assert(music.stream is AudioStreamOggVorbis)
assert(music.stream.loop)
assert(absf(music.volume_db - EXPECTED_VOLUME_DB) < 0.001)
var length: float = music.stream.get_length()
assert(absf(length - EXPECTED_LENGTH) < 0.3)
assert(music.playing)
var first := music.get_playback_position()
await create_timer(0.5).timeout
assert(music.playing)
var second := music.get_playback_position()
print("PASS: Music loop=%s durata=%.2fs volume=%.1fdB posizione %.2f -> %.2f | driver audio: %s"
% [music.stream.loop, length, music.volume_db, first, second, AudioServer.get_driver_name()])
# La musica deve restare in riproduzione anche a giocatore morto.
world.player.take_damage(9999)
await create_timer(0.3).timeout
assert(music.playing)
print("PASS: musica attiva anche dopo la morte del giocatore")
world.queue_free()
await process_frame
quit()