From 52b8348a03c1ea6366d4ea6efd0ff693a6713039 Mon Sep 17 00:00:00 2001 From: grabowski Date: Mon, 27 Oct 2025 12:55:13 +0700 Subject: [PATCH] Change default sample rate to 48kHz and add validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Update default sample rate from 44.1kHz to 48kHz (more professional standard) - Add sample rate validation in play_sound_file() - Display clear warning when uploaded file doesn't match configured rate - Update default sound generation (dial tone & beep) to use configured RATE - Prevent playback of incompatible sample rates (avoids ALSA errors) Error Handling: - Check file sample rate before attempting playback - Print warning with actual vs expected sample rate - Return False and skip playback if mismatch detected - User-friendly error message with resampling instructions This fixes the "Invalid sample rate" error when playing files with non-matching sample rates. Users must now resample their audio files to match the configured rate (default 48kHz). To resample audio files: ffmpeg -i input.wav -ar 48000 output.wav 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- config.example.json | 2 +- rotary_phone_web.py | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/config.example.json b/config.example.json index a9a4741..cda5fac 100644 --- a/config.example.json +++ b/config.example.json @@ -11,7 +11,7 @@ "chunk_size": 1024, "format": "paInt16", "channels": 1, - "sample_rate": 44100, + "sample_rate": 48000, "max_record_seconds": 300 }, "paths": { diff --git a/rotary_phone_web.py b/rotary_phone_web.py index a6c0faf..e591ae0 100644 --- a/rotary_phone_web.py +++ b/rotary_phone_web.py @@ -354,35 +354,33 @@ class RotaryPhone: def generate_default_dialtone(self): """Generate a classic dial tone (350Hz + 440Hz) and save as default""" - print("Generating default dial tone...") + print(f"Generating default dial tone at {RATE}Hz...") duration = 3 - sample_rate = 44100 - t = np.linspace(0, duration, int(sample_rate * duration), False) - + t = np.linspace(0, duration, int(RATE * duration), False) + # Generate two frequencies and combine them tone1 = np.sin(2 * np.pi * 350 * t) tone2 = np.sin(2 * np.pi * 440 * t) tone = (tone1 + tone2) / 2 - + # Convert to 16-bit PCM tone = (tone * 32767).astype(np.int16) - + # Save as WAV file wf = wave.open(DIALTONE_FILE, 'wb') wf.setnchannels(1) wf.setsampwidth(2) # 16-bit - wf.setframerate(sample_rate) + wf.setframerate(RATE) wf.writeframes(tone.tobytes()) wf.close() - print(f"Default dial tone saved to {DIALTONE_FILE}") + print(f"Default dial tone saved to {DIALTONE_FILE} ({RATE}Hz, 16-bit)") def generate_default_beep(self): """Generate a simple beep tone (1000Hz) to indicate recording start""" - print("Generating default beep sound...") + print(f"Generating default beep sound at {RATE}Hz...") beep_file = os.path.join(SOUNDS_DIR, "beep.wav") duration = 0.5 # Half second beep - sample_rate = 44100 - t = np.linspace(0, duration, int(sample_rate * duration), False) + t = np.linspace(0, duration, int(RATE * duration), False) # Generate 1000Hz tone frequency = 1000 @@ -395,10 +393,10 @@ class RotaryPhone: wf = wave.open(beep_file, 'wb') wf.setnchannels(1) wf.setsampwidth(2) # 16-bit - wf.setframerate(sample_rate) + wf.setframerate(RATE) wf.writeframes(tone.tobytes()) wf.close() - print(f"Default beep sound saved to {beep_file}") + print(f"Default beep sound saved to {beep_file} ({RATE}Hz, 16-bit)") def play_sound_file(self, filepath, check_hook_status=True): """Play a WAV file with volume control @@ -417,11 +415,19 @@ class RotaryPhone: try: wf = wave.open(filepath, 'rb') + # Check sample rate compatibility + file_rate = wf.getframerate() + if file_rate != RATE: + print(f"WARNING: File sample rate ({file_rate}Hz) doesn't match configured rate ({RATE}Hz)") + print(f"Audio may not play correctly. Please resample the file to {RATE}Hz") + wf.close() + return False + # Use configured audio device stream = self.audio.open( format=self.audio.get_format_from_width(wf.getsampwidth()), channels=wf.getnchannels(), - rate=wf.getframerate(), + rate=file_rate, output=True, output_device_index=AUDIO_DEVICE_INDEX, frames_per_buffer=CHUNK