reachy_mini_simon / generate_sounds.py
apirrone
update
0962c31
"""Generate sound files for Simon game."""
import numpy as np
from scipy.io import wavfile
SAMPLE_RATE = 44100
ASSETS_DIR = "reachy_mini_simon/assets"
def generate_tone(frequency, duration, fade_ms=50):
"""Generate a pure tone with fade in/out."""
samples = int(SAMPLE_RATE * duration)
t = np.linspace(0, duration, samples)
# Generate sine wave
tone = np.sin(2 * np.pi * frequency * t)
# Apply fade in/out to avoid clicks
fade_samples = int(SAMPLE_RATE * fade_ms / 1000)
fade_in = np.linspace(0, 1, fade_samples)
fade_out = np.linspace(1, 0, fade_samples)
tone[:fade_samples] *= fade_in
tone[-fade_samples:] *= fade_out
# Convert to 16-bit PCM
tone = (tone * 32767).astype(np.int16)
return tone
def generate_chord(frequencies, duration, fade_ms=50):
"""Generate multiple tones combined."""
samples = int(SAMPLE_RATE * duration)
t = np.linspace(0, duration, samples)
# Combine multiple frequencies
chord = np.zeros(samples)
for freq in frequencies:
chord += np.sin(2 * np.pi * freq * t)
# Normalize
chord = chord / len(frequencies)
# Apply fade
fade_samples = int(SAMPLE_RATE * fade_ms / 1000)
fade_in = np.linspace(0, 1, fade_samples)
fade_out = np.linspace(1, 0, fade_samples)
chord[:fade_samples] *= fade_in
chord[-fade_samples:] *= fade_out
# Convert to 16-bit PCM
chord = (chord * 32767).astype(np.int16)
return chord
def generate_ascending_chime():
"""Generate success sound - ascending notes."""
notes = [523, 659, 784] # C5, E5, G5
duration_per_note = 0.15
result = []
for freq in notes:
tone = generate_tone(freq, duration_per_note, fade_ms=20)
result.append(tone)
return np.concatenate(result)
def generate_descending_tone():
"""Generate game over sound - descending tone."""
duration = 0.6
samples = int(SAMPLE_RATE * duration)
t = np.linspace(0, duration, samples)
# Frequency sweep from 600 Hz to 200 Hz
freq = 600 - 400 * t / duration
tone = np.sin(2 * np.pi * freq * t)
# Fade out
fade_out = np.linspace(1, 0, samples)
tone *= fade_out
# Convert to 16-bit PCM
tone = (tone * 32767).astype(np.int16)
return tone
def generate_cheerful_beep():
"""Generate start game sound - two quick beeps."""
beep1 = generate_tone(784, 0.1, fade_ms=10) # G5
silence = np.zeros(int(SAMPLE_RATE * 0.05), dtype=np.int16)
beep2 = generate_tone(988, 0.1, fade_ms=10) # B5
return np.concatenate([beep1, silence, beep2])
def main():
"""Generate all sound files."""
print("Generating sound files...")
# Head direction tones (simple pure tones)
print(" up.wav (E5 - 659 Hz)")
tone = generate_tone(659, 0.3)
wavfile.write(f"{ASSETS_DIR}/up.wav", SAMPLE_RATE, tone)
print(" down.wav (C5 - 523 Hz)")
tone = generate_tone(523, 0.3)
wavfile.write(f"{ASSETS_DIR}/down.wav", SAMPLE_RATE, tone)
print(" left.wav (A4 - 440 Hz)")
tone = generate_tone(440, 0.3)
wavfile.write(f"{ASSETS_DIR}/left.wav", SAMPLE_RATE, tone)
print(" right.wav (G5 - 784 Hz)")
tone = generate_tone(784, 0.3)
wavfile.write(f"{ASSETS_DIR}/right.wav", SAMPLE_RATE, tone)
# Body yaw tones (Difficulty 2)
print(" body_left.wav (F4 - 349 Hz)")
tone = generate_tone(349, 0.3)
wavfile.write(f"{ASSETS_DIR}/body_left.wav", SAMPLE_RATE, tone)
print(" body_right.wav (B4 - 494 Hz)")
tone = generate_tone(494, 0.3)
wavfile.write(f"{ASSETS_DIR}/body_right.wav", SAMPLE_RATE, tone)
# Antenna tones (Difficulty 3)
print(" left_antenna_left.wav (D4 - 294 Hz)")
tone = generate_tone(294, 0.3)
wavfile.write(f"{ASSETS_DIR}/left_antenna_left.wav", SAMPLE_RATE, tone)
print(" left_antenna_right.wav (E4 - 330 Hz)")
tone = generate_tone(330, 0.3)
wavfile.write(f"{ASSETS_DIR}/left_antenna_right.wav", SAMPLE_RATE, tone)
print(" right_antenna_left.wav (F#4 - 370 Hz)")
tone = generate_tone(370, 0.3)
wavfile.write(f"{ASSETS_DIR}/right_antenna_left.wav", SAMPLE_RATE, tone)
print(" right_antenna_right.wav (G#4 - 415 Hz)")
tone = generate_tone(415, 0.3)
wavfile.write(f"{ASSETS_DIR}/right_antenna_right.wav", SAMPLE_RATE, tone)
# Difficulty selection sounds
print(" difficulty1.wav (single beep - C major chord)")
tone = generate_chord([523, 659, 784], 0.3)
wavfile.write(f"{ASSETS_DIR}/difficulty1.wav", SAMPLE_RATE, tone)
print(" difficulty2.wav (double beep)")
beep1 = generate_tone(659, 0.15)
silence = np.zeros(int(SAMPLE_RATE * 0.1), dtype=np.int16)
beep2 = generate_tone(784, 0.15)
tone = np.concatenate([beep1, silence, beep2])
wavfile.write(f"{ASSETS_DIR}/difficulty2.wav", SAMPLE_RATE, tone)
print(" difficulty3.wav (triple beep)")
beep1 = generate_tone(523, 0.12)
silence = np.zeros(int(SAMPLE_RATE * 0.08), dtype=np.int16)
beep2 = generate_tone(659, 0.12)
beep3 = generate_tone(784, 0.12)
tone = np.concatenate([beep1, silence, beep2, silence, beep3])
wavfile.write(f"{ASSETS_DIR}/difficulty3.wav", SAMPLE_RATE, tone)
# Feedback sounds
print(" success.wav (ascending chime)")
tone = generate_ascending_chime()
wavfile.write(f"{ASSETS_DIR}/success.wav", SAMPLE_RATE, tone)
print(" game_over.wav (descending tone)")
tone = generate_descending_tone()
wavfile.write(f"{ASSETS_DIR}/game_over.wav", SAMPLE_RATE, tone)
print(" start_game.wav (cheerful beep)")
tone = generate_cheerful_beep()
wavfile.write(f"{ASSETS_DIR}/start_game.wav", SAMPLE_RATE, tone)
print("\nAll sound files generated successfully!")
if __name__ == "__main__":
main()