#!/usr/bin/env python3 """ Audio Resampling Utility for Wedding Phone Resamples all WAV files in sounds directory to match configured sample rate """ import wave import numpy as np import os import json from scipy import signal def load_config(): """Load sample rate from config.json""" config_path = 'config.json' if not os.path.exists(config_path): print("config.json not found, using 48000Hz as default") return 48000 with open(config_path, 'r') as f: config = json.load(f) return config['audio']['sample_rate'] def resample_wav(input_file, output_file, target_rate): """Resample a WAV file to target sample rate""" try: # Open source file with wave.open(input_file, 'rb') as wf: n_channels = wf.getnchannels() sampwidth = wf.getsampwidth() source_rate = wf.getframerate() n_frames = wf.getnframes() # Read audio data audio_data = wf.readframes(n_frames) # Convert to numpy array if sampwidth == 1: dtype = np.uint8 elif sampwidth == 2: dtype = np.int16 elif sampwidth == 4: dtype = np.int32 else: print(f"Unsupported sample width: {sampwidth}") return False audio_array = np.frombuffer(audio_data, dtype=dtype) # Handle stereo if n_channels == 2: audio_array = audio_array.reshape(-1, 2) # Resample if source_rate != target_rate: print(f" Resampling from {source_rate}Hz to {target_rate}Hz...") num_samples = int(len(audio_array) * target_rate / source_rate) if n_channels == 1: resampled = signal.resample(audio_array, num_samples) else: # Resample each channel separately left = signal.resample(audio_array[:, 0], num_samples) right = signal.resample(audio_array[:, 1], num_samples) resampled = np.column_stack((left, right)) # Convert back to original dtype resampled = np.clip(resampled, np.iinfo(dtype).min, np.iinfo(dtype).max) audio_array = resampled.astype(dtype) # Write output file with wave.open(output_file, 'wb') as wf_out: wf_out.setnchannels(n_channels) wf_out.setsampwidth(sampwidth) wf_out.setframerate(target_rate) wf_out.writeframes(audio_array.tobytes()) return True except Exception as e: print(f" Error: {e}") return False def main(): """Main resampling function""" print("Wedding Phone Audio Resampler") print("=" * 50) # Load target sample rate target_rate = load_config() print(f"\nTarget sample rate: {target_rate}Hz") # Check sounds directory sounds_dir = './rotary_phone_data/sounds' if not os.path.exists(sounds_dir): print(f"\nSounds directory not found: {sounds_dir}") return # Get all WAV files wav_files = [f for f in os.listdir(sounds_dir) if f.endswith('.wav')] if not wav_files: print("\nNo WAV files found in sounds directory") return print(f"\nFound {len(wav_files)} WAV file(s)") print("-" * 50) # Process each file for filename in wav_files: input_path = os.path.join(sounds_dir, filename) print(f"\nProcessing: {filename}") # Check current sample rate try: with wave.open(input_path, 'rb') as wf: current_rate = wf.getframerate() print(f" Current rate: {current_rate}Hz") if current_rate == target_rate: print(f" ✓ Already at {target_rate}Hz, skipping") continue except Exception as e: print(f" Error reading file: {e}") continue # Create backup backup_path = input_path + '.backup' if not os.path.exists(backup_path): os.rename(input_path, backup_path) print(f" Backup created: {filename}.backup") else: input_path = backup_path print(f" Using existing backup") # Resample output_path = os.path.join(sounds_dir, filename) if resample_wav(input_path, output_path, target_rate): print(f" ✓ Successfully resampled to {target_rate}Hz") else: print(f" ✗ Failed to resample, restoring backup") if os.path.exists(backup_path): os.rename(backup_path, output_path) print("\n" + "=" * 50) print("Resampling complete!") print("\nBackup files (.backup) have been created.") print("If everything works, you can delete them.") if __name__ == "__main__": main()