diff --git a/README.md b/README.md index 99a08c6..4058a68 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ - πŸ”’ **Offline operation** for privacy and reliability (with VOSK) - πŸ€– **Optional Whisper AI** support for enhanced accuracy - 🎨 **System tray integration** with visual status indicators -- πŸ”Š **Audio feedback** for recording status +- πŸ”Š **Pleasant audio feedback** - smooth gliding tones, headphone-friendly - βš™οΈ **Graphical settings** dialog for easy configuration ## πŸš€ Quick Install @@ -220,6 +220,8 @@ vocalinux/ β”‚ β”œβ”€β”€ ui/ # GTK UI components β”‚ └── utils/ # Utility functions β”œβ”€β”€ tests/ # Test suite +β”œβ”€β”€ scripts/ # Development utilities +β”‚ └── generate_sounds.py # Sound generation script β”œβ”€β”€ resources/ # Icons and sounds β”œβ”€β”€ docs/ # Documentation └── web/ # Website source @@ -232,6 +234,26 @@ vocalinux/ - [User Guide](docs/USER_GUIDE.md) - Complete user documentation - [Contributing](CONTRIBUTING.md) - Development setup and contribution guidelines +## πŸ”Š Sound Customization + +Vocalinux uses smooth, pleasant gliding tones for audio feedback: + +- **Start**: Ascending F4β†’A4 (0.6s) - positive, uplifting +- **Stop**: Descending A4β†’F4 (0.6s) - resolves completion +- **Error**: Lower descending E4β†’C4 (0.7s) - gentle but noticeable + +All sounds use pure sine waves with smoothstep interpolation for buttery smooth pitch transitions - perfect for headphone use! + +### Regenerate Sounds + +To modify or regenerate the notification sounds: + +```bash +python scripts/generate_sounds.py +``` + +This script generates all three sounds using the same smooth glide algorithm. You can edit the frequencies, durations, and amplitudes in the script to customize the sounds to your preference. + ## πŸ—ΊοΈ Roadmap - [x] ~~Custom icon design~~ βœ… diff --git a/resources/sounds/error.wav b/resources/sounds/error.wav index dfb16a4..0a1186f 100644 Binary files a/resources/sounds/error.wav and b/resources/sounds/error.wav differ diff --git a/resources/sounds/start_recording.wav b/resources/sounds/start_recording.wav index 1e8fc1d..c592bf7 100644 Binary files a/resources/sounds/start_recording.wav and b/resources/sounds/start_recording.wav differ diff --git a/resources/sounds/stop_recording.wav b/resources/sounds/stop_recording.wav index ef356e2..ce834f9 100644 Binary files a/resources/sounds/stop_recording.wav and b/resources/sounds/stop_recording.wav differ diff --git a/scripts/generate_sounds.py b/scripts/generate_sounds.py new file mode 100644 index 0000000..596925a --- /dev/null +++ b/scripts/generate_sounds.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +""" +Sound generation utility for Vocalinux. + +This script generates the notification sounds used by Vocalinux for: +- Start recording (ascending pitch glide) +- Stop recording (descending pitch glide) +- Error notification (lower descending glide) + +The sounds use smooth pitch glides with pure sine waves and soft envelopes +to create pleasant, headphone-friendly audio feedback. + +Usage: + python scripts/generate_sounds.py + +The generated sounds will be placed in resources/sounds/ + +Design Philosophy: +- Pure sine waves (no harsh harmonics) +- Smoothstep interpolation for pitch transitions +- Soft attack/decay envelopes +- Lower frequencies (F4-A4 range) for warmth +- Consistent glide aesthetic across all sounds +""" + +import math +import os +import struct +import wave + + +def generate_glide_tone( + filename: str, + freq_start: float, + freq_end: float, + duration: float = 0.6, + amplitude: float = 0.16, + sample_rate: int = 44100, +) -> None: + """ + Generate a smooth pitch glide tone. + + Args: + filename: Output WAV file path + freq_start: Starting frequency in Hz + freq_end: Ending frequency in Hz + duration: Duration in seconds + amplitude: Volume (0.0 to 1.0) + sample_rate: Audio sample rate + """ + num_samples = int(sample_rate * duration) + samples = [] + + for i in range(num_samples): + t = i / sample_rate + progress = i / num_samples + + # Ultra-soft envelope: sine-squared with exponential decay + envelope = (math.sin(math.pi * progress) ** 2) * math.exp(-0.5 * progress) + + # Smooth pitch glide using smoothstep interpolation + # This creates buttery smooth transitions with no abrupt changes + glide = progress ** 2 * (3 - 2 * progress) + freq_current = freq_start + (freq_end - freq_start) * glide + + # Generate pure sine wave + phase = 2 * math.pi * freq_current * t + sample = amplitude * envelope * math.sin(phase) + samples.append(sample) + + # Convert to 16-bit PCM + wav_samples = [int(sample * 32767) for sample in samples] + + # Write WAV file + with wave.open(filename, "w") as wav_file: + wav_file.setnchannels(1) # Mono + wav_file.setsampwidth(2) # 16-bit + wav_file.setframerate(sample_rate) + wav_file.writeframes(struct.pack("<" + "h" * len(wav_samples), *wav_samples)) + + print(f"Generated: {filename}") + + +def main(): + """Generate all Vocalinux notification sounds.""" + # Determine output directory + script_dir = os.path.dirname(os.path.abspath(__file__)) + repo_root = os.path.dirname(script_dir) + sounds_dir = os.path.join(repo_root, "resources", "sounds") + + os.makedirs(sounds_dir, exist_ok=True) + print(f"Generating sounds in: {sounds_dir}\n") + + # START: Ascending F4β†’A4 (positive, uplifting) + print("Generating start_recording.wav (ascending F4β†’A4)...") + generate_glide_tone( + os.path.join(sounds_dir, "start_recording.wav"), + freq_start=349.23, # F4 + freq_end=440.00, # A4 + duration=0.6, + amplitude=0.16, + ) + + # STOP: Descending A4β†’F4 (resolution, completion) + print("Generating stop_recording.wav (descending A4β†’F4)...") + generate_glide_tone( + os.path.join(sounds_dir, "stop_recording.wav"), + freq_start=440.00, # A4 + freq_end=349.23, # F4 + duration=0.6, + amplitude=0.16, + ) + + # ERROR: Lower descending E4β†’C4 (sad, concerning but gentle) + print("Generating error.wav (descending E4β†’C4)...") + generate_glide_tone( + os.path.join(sounds_dir, "error.wav"), + freq_start=329.63, # E4 + freq_end=261.63, # C4 + duration=0.7, + amplitude=0.14, # Slightly quieter + ) + + print("\nβœ“ All sounds generated successfully!") + print("\nSound characteristics:") + print("- Pure sine waves (headphone-friendly)") + print("- Smoothstep pitch interpolation (no jumps)") + print("- Soft envelopes (sine-squared with decay)") + print("- Lower frequencies (F4-A4 range, warm and pleasant)") + print("- Consistent glide aesthetic") + + +if __name__ == "__main__": + main()