feat(audio): replace sound alerts with more pleasant tones (#145)
Replace the current jarring sound alerts with smoother, more pleasant bell-like tones inspired by voice assistants like Siri and Gemini. Changes: - start_recording.wav: Ascending perfect fifth (A4→E5, 0.30s) - stop_recording.wav: Descending perfect fifth (E5→A4, 0.25s) - error.wav: Low gentle chime (E4, 0.35s) Technical improvements: - Smooth frequency sweeps (sigmoid curve transitions) - Subtle vibrato (8Hz) for warmth and richness - Harmonic overtones for fullness - Elegant exponential decay envelopes - Reduced file sizes (926KB → 80KB total) The new sounds are shorter (0.25-0.35s vs 0.5-1s), quieter (20-25% amplitude vs full volume), and more pleasant for frequent use.
This commit is contained in:
@@ -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~~ ✅
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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()
|
||||
Reference in New Issue
Block a user