Files
Papay-Shooter/generate_sounds.py
T

491 lines
15 KiB
Python

import math
import random
import struct
import wave
import os
os.makedirs('assets/sounds', exist_ok=True)
def write_wav(filename, samples, sample_rate=44100):
with wave.open(filename, 'w') as wav_file:
wav_file.setnchannels(1)
wav_file.setsampwidth(2)
wav_file.setframerate(sample_rate)
for s in samples:
# Clamp and convert to 16-bit
val = int(max(-1.0, min(1.0, s)) * 32767)
data = struct.pack('<h', val)
wav_file.writeframesraw(data)
# 1. Shotgun Fire Sound (Bang + Boom + Decay)
sample_rate = 44100
duration = 0.8
num_samples = int(sample_rate * duration)
fire_samples = []
# Low-pass filter states
prev_val1 = 0.0
prev_val2 = 0.0
for i in range(num_samples):
t = i / sample_rate
# Base broadband noise
noise = random.uniform(-1.0, 1.0)
# 1. The Crack (Initial explosion, very sharp envelope)
crack_env = math.exp(-t * 50.0)
crack = noise * crack_env * 1.5
# 2. The Body (Deep explosive boom, slower decay)
body_env = math.exp(-t * 6.0)
# Dynamic low-pass filter (closes rapidly to muffle the tail into a boom)
alpha = max(0.01, 0.2 * math.exp(-t * 15.0))
filtered1 = prev_val1 + alpha * (noise - prev_val1)
prev_val1 = filtered1
# Second pass for deeper muffle
filtered2 = prev_val2 + alpha * (filtered1 - prev_val2)
prev_val2 = filtered2
body = filtered2 * body_env * 10.0 # Boost the bass significantly
# Mix crack and body
raw_sample = crack + body
# Soft clipping (tanh) to simulate a microphone peaking from a loud blast
sample = math.tanh(raw_sample * 2.5)
fire_samples.append(sample)
write_wav('assets/sounds/shotgun_fire.wav', fire_samples)
# 2. Shotgun Reload Sound (Break, Insert, Snap)
duration = 1.0
num_samples = int(sample_rate * duration)
reload_samples = []
for i in range(num_samples):
t = i / sample_rate
sample = 0.0
noise = random.uniform(-1.0, 1.0)
# Phase 1: Break open (t=0.0)
if t < 0.2:
env_click = math.exp(-t * 60.0)
click = noise * env_click * 0.5
clunk_freq = 300.0 * math.exp(-t * 20.0) + 100.0
clunk = math.sin(t * clunk_freq * 2 * math.pi) * math.exp(-t * 15.0) * 0.4
sample += click + clunk
# Phase 2: Insert shells (t=0.3 to 0.6)
if 0.3 <= t < 0.6:
t2 = t - 0.3
slide_env = math.sin(t2 * math.pi / 0.3) * 0.15
slide = noise * slide_env
if t2 > 0.15:
seat_env = math.exp(-(t2 - 0.15) * 40.0)
seat_click = noise * seat_env * 0.4
sample += seat_click
sample += slide
# Phase 3: Snap shut (t=0.7)
if t >= 0.7:
t3 = t - 0.7
snap_env = math.exp(-t3 * 40.0)
snap = noise * snap_env * 0.8
ring_env = math.exp(-t3 * 10.0)
ring1 = math.sin(t3 * 3500.0 * 2 * math.pi)
ring2 = math.sin(t3 * 4200.0 * 2 * math.pi)
ring = (ring1 + ring2) * ring_env * 0.1
thud = math.sin(t3 * 80.0 * 2 * math.pi) * math.exp(-t3 * 15.0) * 0.5
sample += snap + ring + thud
reload_samples.append(sample)
# Global low-pass to smooth out the harsh digital noise
prev = 0.0
for i in range(len(reload_samples)):
prev = prev + 0.5 * (reload_samples[i] - prev)
reload_samples[i] = max(-1.0, min(1.0, prev * 1.5))
write_wav('assets/sounds/shotgun_reload.wav', reload_samples)
# 3. Footstep Sound (Quiet Thud + Scrape)
footstep_duration = 0.15
footstep_samples = []
for i in range(int(sample_rate * footstep_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Dull Thud (lower frequency, less metallic)
thud_env = math.exp(-t * 25.0)
thud = math.sin(t * 50.0 * 2 * math.pi) * thud_env * 0.15
# Scrape (very quiet, low-passed)
scrape_env = math.exp(-t * 50.0)
scrape = noise * scrape_env * 0.03
footstep_samples.append(thud + scrape)
# Filter footstep to ensure no tin-can high frequencies
prev = 0.0
for i in range(len(footstep_samples)):
prev = prev + 0.3 * (footstep_samples[i] - prev)
footstep_samples[i] = prev
write_wav('assets/sounds/footstep.wav', footstep_samples)
# 4. Dash Sound (Whoosh)
dash_duration = 0.5
dash_samples = []
prev_dash = 0.0
for i in range(int(sample_rate * dash_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
env = math.sin(t * math.pi / dash_duration) ** 2 # Smooth bell curve
alpha = 0.1
filtered = prev_dash + alpha * (noise - prev_dash)
prev_dash = filtered
dash_samples.append(filtered * env * 0.8)
write_wav('assets/sounds/dash.wav', dash_samples)
# 5. Hit Confirm (Tink/Chirp)
hit_duration = 0.2
hit_samples = []
for i in range(int(sample_rate * hit_duration)):
t = i / sample_rate
env = math.exp(-t * 30.0)
# High pitch chirp sweeping slightly
freq = 2000.0 + math.exp(-t * 20.0) * 1000.0
tink = math.sin(t * freq * 2 * math.pi) * env * 0.3
hit_samples.append(tink)
write_wav('assets/sounds/hit_confirm.wav', hit_samples)
# 6. Wallrun (Continuous scrape)
# We want this to seamlessly loop
wallrun_duration = 1.0
wallrun_samples = []
for i in range(int(sample_rate * wallrun_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Give it a slight 4/4 rhythm
rhythm = 0.8 + 0.2 * math.sin(t * 8.0 * math.pi)
# Low pass to keep it as a dull scrape
alpha = 0.05
sample = noise * alpha * rhythm * 0.4
wallrun_samples.append(sample)
write_wav('assets/sounds/wallrun.wav', wallrun_samples)
# 7. Slide (Continuous deep friction)
slide_duration = 1.0
slide_samples = []
for i in range(int(sample_rate * slide_duration)):
noise = random.uniform(-1.0, 1.0)
# Deeper, steadier scrape than wallrun
alpha = 0.02
sample = noise * alpha * 0.5
slide_samples.append(sample)
write_wav('assets/sounds/slide.wav', slide_samples)
# 8. Wind (Subtle rushing air)
wind_duration = 2.0 # Generate 2 seconds
sample_count = int(sample_rate * wind_duration)
raw_wind = []
prev_wind = 0.0
# Generate extra long to stabilize filter
for i in range(sample_count + int(sample_rate * 0.5)):
noise = random.uniform(-1.0, 1.0)
alpha = 0.015
filtered = prev_wind + alpha * (noise - prev_wind)
prev_wind = filtered
if i >= int(sample_rate * 0.5):
raw_wind.append(filtered * 0.5)
# Crossfade the last 0.1s into the first 0.1s to make it perfectly loop
crossfade_samples = int(sample_rate * 0.1)
loop_wind = raw_wind[:-crossfade_samples] # remove the tail
for i in range(crossfade_samples):
# Blend the tail into the start
tail_sample = raw_wind[-(crossfade_samples - i)]
start_sample = loop_wind[i]
t = i / float(crossfade_samples)
loop_wind[i] = start_sample * t + tail_sample * (1.0 - t)
write_wav('assets/sounds/wind.wav', loop_wind)
# 9. Jump Sound (Quick exertion / push)
jump_duration = 0.2
jump_samples = []
for i in range(int(sample_rate * jump_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Lower frequency push (less tin can)
push_env = math.exp(-t * 25.0)
push = math.sin(t * 60.0 * 2 * math.pi) * push_env * 0.3
# High frequency clothing ruffle (quieter)
ruffle_env = math.exp(-t * 30.0)
ruffle = noise * ruffle_env * 0.05
jump_samples.append(push + ruffle)
# Filter jump to ensure no tin-can high frequencies
prev = 0.0
for i in range(len(jump_samples)):
prev = prev + 0.4 * (jump_samples[i] - prev)
jump_samples[i] = prev
write_wav('assets/sounds/jump.wav', jump_samples)
# 10. Double Jump Sound (Airy, cloudy whoosh)
dj_duration = 0.4
dj_samples = []
prev_dj = 0.0
for i in range(int(sample_rate * dj_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Airy filter
alpha = 0.05
filtered = prev_dj + alpha * (noise - prev_dj)
prev_dj = filtered
# Envelope: soft attack, long decay
env = (math.exp(-t * 8.0) - math.exp(-t * 40.0)) * 1.5
# Slight pitch movement to give it a "lifting" feel
lift = math.sin(t * 150.0 * 2 * math.pi * (1.0 + t)) * env * 0.1
dj_samples.append((filtered * 0.4 + lift) * env)
write_wav('assets/sounds/double_jump.wav', dj_samples)
# ==========================================
# NEW WEAPONS (AK47, M4, MP7, DMR, Plasma, Nail)
# ==========================================
# 11. AK47 (Heavy punch, low thud, sharp crack)
ak_duration = 0.2
ak_samples = []
for i in range(int(sample_rate * ak_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Sharp metallic crack
crack_env = math.exp(-t * 100.0)
crack = noise * crack_env * 0.4
# Deep heavy punch (70Hz sweep down to 40Hz)
punch_freq = max(40.0, 70.0 - t * 300.0)
punch_env = math.exp(-t * 20.0)
punch = math.sin(t * punch_freq * 2 * math.pi) * punch_env * 0.6
# Mechanism ring
ring_env = math.exp(-t * 15.0)
ring = math.sin(t * 800.0 * 2 * math.pi) * ring_env * 0.05
ak_samples.append(crack + punch + ring)
write_wav('assets/sounds/ak47_fire.wav', ak_samples)
# 12. M4 (Moderate punch, sharper crack, tighter burst)
m4_duration = 0.15
m4_samples = []
for i in range(int(sample_rate * m4_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
crack_env = math.exp(-t * 120.0)
crack = noise * crack_env * 0.5
punch_freq = 90.0
punch_env = math.exp(-t * 25.0)
punch = math.sin(t * punch_freq * 2 * math.pi) * punch_env * 0.4
m4_samples.append(crack + punch)
write_wav('assets/sounds/m4_fire.wav', m4_samples)
# 13. MP7 (Very fast, light pop, high pitched)
mp7_duration = 0.1
mp7_samples = []
for i in range(int(sample_rate * mp7_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
pop_env = math.exp(-t * 80.0)
pop = noise * pop_env * 0.3
punch_freq = 150.0
punch_env = math.exp(-t * 35.0)
punch = math.sin(t * punch_freq * 2 * math.pi) * punch_env * 0.2
mp7_samples.append(pop + punch)
write_wav('assets/sounds/mp7_fire.wav', mp7_samples)
# 14. DMR (Extremely loud, echoing boom, very sharp)
dmr_duration = 0.4
dmr_samples = []
for i in range(int(sample_rate * dmr_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
crack_env = math.exp(-t * 50.0)
crack = noise * crack_env * 0.6
boom_freq = max(30.0, 60.0 - t * 100.0)
boom_env = math.exp(-t * 8.0)
boom = math.sin(t * boom_freq * 2 * math.pi) * boom_env * 0.7
tail_env = math.exp(-t * 5.0)
tail = noise * tail_env * 0.1 # outdoor echo reflection
dmr_samples.append(crack + boom + tail)
write_wav('assets/sounds/dmr_fire.wav', dmr_samples)
# 15. Plasma Gun (Sci-Fi pew, tonal sweep)
plasma_duration = 0.2
plasma_samples = []
for i in range(int(sample_rate * plasma_duration)):
t = i / sample_rate
# Sweep from 1200 Hz down to 300 Hz rapidly
freq = max(300.0, 1200.0 * math.exp(-t * 30.0))
pew_env = math.exp(-t * 15.0)
pew = math.sin(t * freq * 2 * math.pi) * pew_env * 0.6
plasma_samples.append(pew)
write_wav('assets/sounds/plasma_fire.wav', plasma_samples)
# 16. Nail Gun (Clack clack clack, metallic hit)
nail_duration = 0.1
nail_samples = []
for i in range(int(sample_rate * nail_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Sharp metallic impact
hit_env = math.exp(-t * 150.0)
hit = noise * hit_env * 0.4
# Ringing metal (two tones)
ring_env = math.exp(-t * 40.0)
ring = (math.sin(t * 3500.0 * 2 * math.pi) + math.sin(t * 4100.0 * 2 * math.pi)) * ring_env * 0.15
# Mechanism chunk
chunk_env = math.exp(-t * 30.0)
chunk = math.sin(t * 200.0 * 2 * math.pi) * chunk_env * 0.2
nail_samples.append(hit + ring + chunk)
write_wav('assets/sounds/nailgun_fire.wav', nail_samples)
# 17. AWP (Thunderous crack, massive bass, slow echo)
awp_duration = 0.6
awp_samples = []
for i in range(int(sample_rate * awp_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
crack = noise * math.exp(-t * 80.0) * 0.8
boom = math.sin(t * max(30.0, 80.0 - t * 150.0) * 2 * math.pi) * math.exp(-t * 10.0) * 0.9
awp_samples.append(crack + boom)
write_wav('assets/sounds/awp_fire.wav', awp_samples)
# 18. Rocket Launcher (Deep thud, whoosh)
rocket_duration = 0.4
rocket_samples = []
prev_r = 0.0
for i in range(int(sample_rate * rocket_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
thud = math.sin(t * 50.0 * 2 * math.pi) * math.exp(-t * 20.0) * 0.7
alpha = 0.05
filtered = prev_r + alpha * (noise - prev_r)
prev_r = filtered
whoosh = filtered * math.exp(-t * 15.0) * 0.5
rocket_samples.append(thud + whoosh)
write_wav('assets/sounds/rocket_fire.wav', rocket_samples)
# 19. Swarm Launcher (High-pitched whistling zip)
swarm_duration = 0.2
swarm_samples = []
for i in range(int(sample_rate * swarm_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
zip_freq = 600.0 + t * 2000.0
pew = math.sin(t * zip_freq * 2 * math.pi) * math.exp(-t * 25.0) * 0.4
swarm_samples.append(pew + noise * math.exp(-t * 50.0) * 0.2)
write_wav('assets/sounds/swarm_fire.wav', swarm_samples)
# 20. Mortar (Heavy hollow thunk)
mortar_duration = 0.3
mortar_samples = []
for i in range(int(sample_rate * mortar_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
thunk_freq = 60.0
thunk = math.sin(t * thunk_freq * 2 * math.pi) * math.exp(-t * 15.0) * 0.7
ring = math.sin(t * 200.0 * 2 * math.pi) * math.exp(-t * 30.0) * 0.2
mortar_samples.append(thunk + ring)
write_wav('assets/sounds/mortar_fire.wav', mortar_samples)
# 21. Rocket Flight (Deep continuous looping whoosh/rumble)
flight_duration = 1.0 # 1 second loop
flight_samples = []
prev_f = 0.0
for i in range(int(sample_rate * flight_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
alpha = 0.15 # Higher cutoff for more whoosh
filtered = prev_f + alpha * (noise - prev_f)
prev_f = filtered
rumble = math.sin(t * 70.0 * 2 * math.pi) * 1.5
raw = (filtered * 2.5 + rumble)
# Heavy soft clipping to make it sound intensely loud
flight_samples.append(math.tanh(raw * 2.0))
write_wav('assets/sounds/rocket_flight.wav', flight_samples)
# 22. Swarm Flight (Higher pitched continuous hiss)
swarm_flight_samples = []
prev_sf = 0.0
for i in range(int(sample_rate * flight_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
alpha = 0.4 # Higher cutoff for hiss
filtered = prev_sf + alpha * (noise - prev_sf)
prev_sf = filtered
# Aggressive screaming tone
whine = math.sin(t * 800.0 * 2 * math.pi) * 1.5
raw = (filtered * 2.0 + whine)
# Heavy soft clipping
swarm_flight_samples.append(math.tanh(raw * 2.0))
write_wav('assets/sounds/swarm_flight.wav', swarm_flight_samples)
# 23. Vault Sound (Quick airy whoosh + clothing ruffle)
vault_duration = 0.3
vault_samples = []
prev_v = 0.0
for i in range(int(sample_rate * vault_duration)):
t = i / sample_rate
noise = random.uniform(-1.0, 1.0)
# Airy whoosh
alpha = 0.08
filtered = prev_v + alpha * (noise - prev_v)
prev_v = filtered
whoosh_env = math.sin(t * math.pi / vault_duration)
whoosh = filtered * whoosh_env * 0.6
# Ruffle/grab sound
ruffle_env = math.exp(-t * 30.0)
ruffle = noise * ruffle_env * 0.2
vault_samples.append(whoosh + ruffle)
write_wav('assets/sounds/vault.wav', vault_samples)
print("Generated all sounds!")