Real thunder (Gitea #4): six CC0 recordings, close cracks and distant rolls, in place of the synthesised rumble; a storm's flash is followed by a roll seconds later or a crack sooner, quieter indoors, and a strike on a tree cracks from the tree
This commit is contained in:
@@ -699,6 +699,44 @@ def _decode(src, sr, af=None):
|
||||
return [x * 0.06 / rms for x in left], [x * 0.06 / rms for x in right]
|
||||
|
||||
|
||||
def make_thunder():
|
||||
"""Thunder (0.6.2, Gitea #4): CC0 recordings (tools/audio/sources/
|
||||
thunder_near_* and thunder_far_*, credited in assets/audio/CREDITS.md),
|
||||
each cut round its loudest strike, faded out and evened out in level, into
|
||||
assets/audio/sfx/thunder/thunder_near_N.ogg (a crack close by) and
|
||||
thunder_far_N.ogg (a roll in the distance). Replaces the synthesised rumble."""
|
||||
d = os.path.join(OUT, "sfx", "thunder")
|
||||
os.makedirs(d, exist_ok=True)
|
||||
print("thunder ->", os.path.relpath(d, ROOT))
|
||||
sr = 44100
|
||||
for kind, lead, most in (("near", 0.15, 7.0), ("far", 0.6, 10.0)):
|
||||
for n, src in enumerate(sources("thunder_" + kind)):
|
||||
raw = subprocess.run(["ffmpeg", "-v", "error", "-i", src, "-ac", "1", "-ar", str(sr), "-f", "s16le", "-"],
|
||||
capture_output=True, check=True).stdout
|
||||
x = [struct.unpack_from("<h", raw, i * 2)[0] / 32768.0 for i in range(len(raw) // 2)]
|
||||
win = sr // 20
|
||||
env = [math.sqrt(sum(v * v for v in x[i:i + win]) / win) + 1e-9 for i in range(0, len(x) - win, win)]
|
||||
ip = max(range(len(env)), key=lambda i: env[i])
|
||||
top = env[ip]
|
||||
# Back from the loudest moment to where this strike starts (20 dB down).
|
||||
j = ip
|
||||
while j > 0 and env[j] > top * 0.1:
|
||||
j -= 1
|
||||
start = max(0, int((j * 0.05 - lead) * sr))
|
||||
# On until it's died away (40 dB down), or `most` seconds.
|
||||
k = ip
|
||||
while k < len(env) - 1 and env[k] > top * 0.01 and (k - j) * 0.05 < most:
|
||||
k += 1
|
||||
end = min(len(x), int(k * 0.05 * sr) + win)
|
||||
seg = x[start:end]
|
||||
fade_in, fade_out = int(0.02 * sr), min(int(1.5 * sr), len(seg) // 3)
|
||||
for i in range(fade_in):
|
||||
seg[i] *= i / fade_in
|
||||
for i in range(fade_out):
|
||||
seg[-1 - i] *= i / fade_out
|
||||
_encode(os.path.join(d, "thunder_%s_%d.ogg" % (kind, n)), [seg], sr, 5)
|
||||
|
||||
|
||||
def make_recorded(name, srcs=None):
|
||||
"""A field-recorded ambience loop (assets/audio/ambience/<name>.ogg) from
|
||||
CC0 recordings (tools/audio/sources/<name>_*, or the files given): each
|
||||
@@ -808,6 +846,9 @@ if __name__ == "__main__":
|
||||
if what in RECORDED: # one recorded loop: birds | crickets
|
||||
make_recorded(what)
|
||||
sys.exit(0)
|
||||
if what == "thunder": # the recorded thunder (not part of "all": it's from sources/)
|
||||
make_thunder()
|
||||
sys.exit(0)
|
||||
if what in ("all", "sfx"):
|
||||
make_sfx()
|
||||
if what in ("all", "sfx", "voices"):
|
||||
|
||||
Reference in New Issue
Block a user