JuggleBoard game v0.1: Godot 4.7 arcade, combo decay, config menu, circus BGM
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
extends RefCounted
|
||||
## Genera proceduralmente un loop musicale da circo (valzer calliope) in WAV.
|
||||
## Nessun file audio esterno: tutto sintetizzato a runtime, con loop seamless.
|
||||
|
||||
const SR := 22050
|
||||
|
||||
|
||||
static func generate() -> AudioStreamWAV:
|
||||
var bpm := 132.0
|
||||
var beat := 60.0 / bpm
|
||||
var bars := 8
|
||||
var total_beats := bars * 3
|
||||
var total_seconds := total_beats * beat
|
||||
var n := int(SR * total_seconds)
|
||||
var mix := PackedFloat32Array()
|
||||
mix.resize(n)
|
||||
|
||||
# Melodia (calliope) — 8 battute in 3/4
|
||||
var melody := [
|
||||
[72, 1.0], [76, 1.0], [79, 1.0],
|
||||
[72, 1.0], [76, 1.0], [79, 1.0],
|
||||
[69, 1.0], [72, 1.0], [76, 1.0],
|
||||
[69, 1.0], [72, 1.0], [76, 1.0],
|
||||
[65, 1.0], [69, 1.0], [72, 1.0],
|
||||
[65, 1.0], [69, 1.0], [72, 1.0],
|
||||
[67, 1.0], [71, 1.0], [74, 1.0],
|
||||
[72, 2.0], [0, 1.0],
|
||||
]
|
||||
# Accompagnamento oom-pah-pah: [basso, [accordo]]
|
||||
var chords := [
|
||||
[48, [60, 64, 67]],
|
||||
[48, [60, 64, 67]],
|
||||
[45, [57, 60, 64]],
|
||||
[45, [57, 60, 64]],
|
||||
[41, [53, 57, 60]],
|
||||
[41, [53, 57, 60]],
|
||||
[43, [55, 59, 62]],
|
||||
[48, [60, 64, 67]],
|
||||
]
|
||||
|
||||
# Melodia
|
||||
var t := 0.0
|
||||
for note in melody:
|
||||
var midi: int = note[0]
|
||||
var beats: float = note[1]
|
||||
var dur := beats * beat
|
||||
if midi > 0:
|
||||
_add_note(mix, t, dur, _freq(midi), 0.5, "calliope")
|
||||
t += dur
|
||||
|
||||
# Accompagnamento
|
||||
t = 0.0
|
||||
for bar in bars:
|
||||
var bass: int = chords[bar][0]
|
||||
var chord: Array = chords[bar][1]
|
||||
_add_note(mix, t, beat * 0.9, _freq(bass), 0.42, "bass")
|
||||
for b in [1, 2]:
|
||||
var ct: float = t + b * beat
|
||||
for m in chord:
|
||||
_add_note(mix, ct, beat * 0.8, _freq(int(m)), 0.16, "chord")
|
||||
t += 3.0 * beat
|
||||
|
||||
# Fade in/out (30ms) per loop seamless senza click
|
||||
var fade := int(SR * 0.03)
|
||||
for i in fade:
|
||||
var g := float(i) / fade
|
||||
mix[i] *= g
|
||||
mix[n - 1 - i] *= g
|
||||
|
||||
# Normalizzazione
|
||||
var peak := 0.0
|
||||
for i in n:
|
||||
var a := absf(mix[i])
|
||||
if a > peak:
|
||||
peak = a
|
||||
var norm := 1.0
|
||||
if peak > 0.0:
|
||||
norm = 0.9 / peak
|
||||
|
||||
var data := PackedByteArray()
|
||||
data.resize(n)
|
||||
for i in n:
|
||||
var v := int(clampf(mix[i] * norm * 127.0, -128.0, 127.0))
|
||||
data[i] = v & 0xFF
|
||||
|
||||
var stream := AudioStreamWAV.new()
|
||||
stream.format = AudioStreamWAV.FORMAT_8_BITS
|
||||
stream.mix_rate = SR
|
||||
stream.data = data
|
||||
stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
|
||||
stream.loop_begin = 0
|
||||
stream.loop_end = n
|
||||
return stream
|
||||
|
||||
|
||||
static func _freq(midi: int) -> float:
|
||||
return 440.0 * pow(2.0, (midi - 69) / 12.0)
|
||||
|
||||
|
||||
static func _square(t: float, f: float) -> float:
|
||||
return 1.0 if sin(t * f * TAU) > 0.0 else -1.0
|
||||
|
||||
|
||||
static func _saw(t: float, f: float) -> float:
|
||||
return fmod(t * f, 1.0) * 2.0 - 1.0
|
||||
|
||||
|
||||
static func _envelope(t: float, dur: float) -> float:
|
||||
var a := 0.02
|
||||
var r := 0.06
|
||||
var e := 1.0
|
||||
if t < a:
|
||||
e = t / a
|
||||
var rem := dur - t
|
||||
if rem < r:
|
||||
e = minf(e, rem / r)
|
||||
return e
|
||||
|
||||
|
||||
static func _add_note(mix: PackedFloat32Array, start: float, dur: float, freq: float, amp: float, kind: String) -> void:
|
||||
var start_i := int(start * SR)
|
||||
var cnt := int(dur * SR)
|
||||
for i in cnt:
|
||||
var idx := start_i + i
|
||||
if idx < 0 or idx >= mix.size():
|
||||
break
|
||||
var t := float(i) / SR
|
||||
var env := _envelope(t, dur)
|
||||
var s := 0.0
|
||||
if kind == "calliope":
|
||||
var vib := 1.0 + 0.006 * sin(t * 2.0 * PI * 5.0)
|
||||
var f := freq * vib
|
||||
s = 0.5 * _square(t, f) + 0.3 * _saw(t, f * 1.004) + 0.2 * _square(t, f * 0.996)
|
||||
elif kind == "bass":
|
||||
s = 0.6 * _saw(t, freq) + 0.4 * _square(t, freq)
|
||||
elif kind == "chord":
|
||||
s = 0.5 * _square(t, freq) + 0.5 * _saw(t, freq)
|
||||
mix[idx] += s * env * amp
|
||||
Reference in New Issue
Block a user