Change default sample rate to 48kHz and add validation
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 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
"chunk_size": 1024,
|
"chunk_size": 1024,
|
||||||
"format": "paInt16",
|
"format": "paInt16",
|
||||||
"channels": 1,
|
"channels": 1,
|
||||||
"sample_rate": 44100,
|
"sample_rate": 48000,
|
||||||
"max_record_seconds": 300
|
"max_record_seconds": 300
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|||||||
@@ -354,10 +354,9 @@ class RotaryPhone:
|
|||||||
|
|
||||||
def generate_default_dialtone(self):
|
def generate_default_dialtone(self):
|
||||||
"""Generate a classic dial tone (350Hz + 440Hz) and save as default"""
|
"""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
|
duration = 3
|
||||||
sample_rate = 44100
|
t = np.linspace(0, duration, int(RATE * duration), False)
|
||||||
t = np.linspace(0, duration, int(sample_rate * duration), False)
|
|
||||||
|
|
||||||
# Generate two frequencies and combine them
|
# Generate two frequencies and combine them
|
||||||
tone1 = np.sin(2 * np.pi * 350 * t)
|
tone1 = np.sin(2 * np.pi * 350 * t)
|
||||||
@@ -371,18 +370,17 @@ class RotaryPhone:
|
|||||||
wf = wave.open(DIALTONE_FILE, 'wb')
|
wf = wave.open(DIALTONE_FILE, 'wb')
|
||||||
wf.setnchannels(1)
|
wf.setnchannels(1)
|
||||||
wf.setsampwidth(2) # 16-bit
|
wf.setsampwidth(2) # 16-bit
|
||||||
wf.setframerate(sample_rate)
|
wf.setframerate(RATE)
|
||||||
wf.writeframes(tone.tobytes())
|
wf.writeframes(tone.tobytes())
|
||||||
wf.close()
|
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):
|
def generate_default_beep(self):
|
||||||
"""Generate a simple beep tone (1000Hz) to indicate recording start"""
|
"""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")
|
beep_file = os.path.join(SOUNDS_DIR, "beep.wav")
|
||||||
duration = 0.5 # Half second beep
|
duration = 0.5 # Half second beep
|
||||||
sample_rate = 44100
|
t = np.linspace(0, duration, int(RATE * duration), False)
|
||||||
t = np.linspace(0, duration, int(sample_rate * duration), False)
|
|
||||||
|
|
||||||
# Generate 1000Hz tone
|
# Generate 1000Hz tone
|
||||||
frequency = 1000
|
frequency = 1000
|
||||||
@@ -395,10 +393,10 @@ class RotaryPhone:
|
|||||||
wf = wave.open(beep_file, 'wb')
|
wf = wave.open(beep_file, 'wb')
|
||||||
wf.setnchannels(1)
|
wf.setnchannels(1)
|
||||||
wf.setsampwidth(2) # 16-bit
|
wf.setsampwidth(2) # 16-bit
|
||||||
wf.setframerate(sample_rate)
|
wf.setframerate(RATE)
|
||||||
wf.writeframes(tone.tobytes())
|
wf.writeframes(tone.tobytes())
|
||||||
wf.close()
|
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):
|
def play_sound_file(self, filepath, check_hook_status=True):
|
||||||
"""Play a WAV file with volume control
|
"""Play a WAV file with volume control
|
||||||
@@ -417,11 +415,19 @@ class RotaryPhone:
|
|||||||
try:
|
try:
|
||||||
wf = wave.open(filepath, 'rb')
|
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
|
# Use configured audio device
|
||||||
stream = self.audio.open(
|
stream = self.audio.open(
|
||||||
format=self.audio.get_format_from_width(wf.getsampwidth()),
|
format=self.audio.get_format_from_width(wf.getsampwidth()),
|
||||||
channels=wf.getnchannels(),
|
channels=wf.getnchannels(),
|
||||||
rate=wf.getframerate(),
|
rate=file_rate,
|
||||||
output=True,
|
output=True,
|
||||||
output_device_index=AUDIO_DEVICE_INDEX,
|
output_device_index=AUDIO_DEVICE_INDEX,
|
||||||
frames_per_buffer=CHUNK
|
frames_per_buffer=CHUNK
|
||||||
|
|||||||
Reference in New Issue
Block a user