Files
wedding-phone/test_complete.py
grabowski ef0373e60b Add external configuration file system
Breaking Changes:
- Configuration now via config.json instead of editing Python code
- Remove all hardcoded paths (no more /home/berwn)
- Separate system config (config.json) from runtime config (user_config.json)

Features:
- config.example.json with all configurable options
- GPIO pin and state configuration
- Audio device index configuration
- Customizable paths (relative or absolute)
- Web port and upload size settings
- No code editing required for deployment

Configuration Structure:
- gpio: hook_pin, hook_pressed_state
- audio: device_index, chunk_size, channels, sample_rate, max_record_seconds
- paths: base_dir, recordings_dir, sounds_dir
- web: port, max_upload_size_mb
- system: active_greeting, default_volume

Script automatically:
- Checks for config.json on startup
- Provides helpful error if missing
- Uses relative paths by default
- Loads test_complete.py config from same file

Updated Documentation:
- Complete configuration guide in README
- Setup instructions without hardcoded paths
- Troubleshooting for config errors
- Device index discovery command

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 14:48:43 +07:00

302 lines
8.7 KiB
Python

#!/usr/bin/env python3
"""
Complete HiFiBerry Test - Verify speaker and microphone work
"""
import pyaudio
import wave
import numpy as np
import time
import os
import json
import sys
# Load configuration
def load_config():
"""Load audio device configuration"""
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json')
if not os.path.exists(config_path):
print("Config file not found. Using default device index 1")
return {"audio": {"device_index": 1, "sample_rate": 44100}}
try:
with open(config_path, 'r') as f:
return json.load(f)
except:
print("Error reading config. Using defaults.")
return {"audio": {"device_index": 1, "sample_rate": 44100}}
CONFIG = load_config()
HIFIBERRY_INDEX = CONFIG['audio']['device_index']
SAMPLE_RATE = CONFIG['audio']['sample_rate']
def test_playback():
"""Test speaker output"""
print("\n" + "="*60)
print("🔊 TESTING SPEAKER PLAYBACK")
print("="*60)
audio = pyaudio.PyAudio()
try:
# Show device info
info = audio.get_device_info_by_index(HIFIBERRY_INDEX)
print(f"\nUsing device: {info['name']}")
print(f"Max output channels: {info['maxOutputChannels']}")
# Generate test tone (440Hz A note)
print("\nGenerating 440Hz test tone...")
duration = 3
t = np.linspace(0, duration, int(SAMPLE_RATE * duration), False)
tone = np.sin(2 * np.pi * 440 * t)
tone = (tone * 0.3 * 32767).astype(np.int16) # 30% volume
# Open stream
stream = audio.open(
format=pyaudio.paInt16,
channels=1,
rate=SAMPLE_RATE,
output=True,
output_device_index=HIFIBERRY_INDEX,
frames_per_buffer=1024
)
print("🎵 Playing 3-second tone - LISTEN NOW!")
print(" You should hear a clear beep from your speaker...")
# Play
chunk_size = 2048
for i in range(0, len(tone.tobytes()), chunk_size):
stream.write(tone.tobytes()[i:i + chunk_size])
stream.stop_stream()
stream.close()
print("✓ Playback completed")
return True
except Exception as e:
print(f"❌ Playback error: {e}")
return False
finally:
audio.terminate()
def test_recording():
"""Test microphone input"""
print("\n" + "="*60)
print("🎙️ TESTING MICROPHONE RECORDING")
print("="*60)
audio = pyaudio.PyAudio()
try:
# Show device info
info = audio.get_device_info_by_index(HIFIBERRY_INDEX)
print(f"\nUsing device: {info['name']}")
print(f"Max input channels: {info['maxInputChannels']}")
# Record
print("\n🔴 Recording for 5 seconds...")
print(" SPEAK NOW or make noise near the microphone...")
stream = audio.open(
format=pyaudio.paInt16,
channels=1,
rate=SAMPLE_RATE,
input=True,
input_device_index=HIFIBERRY_INDEX,
frames_per_buffer=1024
)
frames = []
for i in range(0, int(SAMPLE_RATE / 1024 * 5)):
data = stream.read(1024, exception_on_overflow=False)
frames.append(data)
stream.stop_stream()
stream.close()
print("✓ Recording completed")
# Save to file
filename = "/tmp/test_recording.wav"
wf = wave.open(filename, 'wb')
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
wf.setframerate(SAMPLE_RATE)
wf.writeframes(b''.join(frames))
wf.close()
print(f"✓ Saved to {filename}")
# Calculate volume level
audio_data = np.frombuffer(b''.join(frames), dtype=np.int16)
volume = np.abs(audio_data).mean()
max_volume = np.abs(audio_data).max()
print(f"\nRecording analysis:")
print(f" Average level: {volume:.0f}")
print(f" Peak level: {max_volume}")
if max_volume > 1000:
print(" ✓ Good signal detected!")
else:
print(" ⚠️ Very low signal - microphone might not be working")
audio.terminate()
# Play back
print("\n🔊 Playing back your recording...")
audio = pyaudio.PyAudio()
wf = wave.open(filename, 'rb')
stream = audio.open(
format=audio.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
output_device_index=HIFIBERRY_INDEX
)
data = wf.readframes(1024)
while data:
stream.write(data)
data = wf.readframes(1024)
stream.stop_stream()
stream.close()
wf.close()
print("✓ Playback of recording completed")
print(f"\nYou can listen again with: aplay {filename}")
return True
except Exception as e:
print(f"❌ Recording error: {e}")
return False
finally:
audio.terminate()
def test_dial_tone():
"""Test classic dial tone (350Hz + 440Hz)"""
print("\n" + "="*60)
print("📞 TESTING DIAL TONE")
print("="*60)
audio = pyaudio.PyAudio()
try:
print("\nGenerating classic dial tone (350Hz + 440Hz)...")
duration = 3
t = np.linspace(0, duration, int(SAMPLE_RATE * duration), False)
# Generate two frequencies
tone1 = np.sin(2 * np.pi * 350 * t)
tone2 = np.sin(2 * np.pi * 440 * t)
tone = (tone1 + tone2) / 2
tone = (tone * 0.3 * 32767).astype(np.int16)
stream = audio.open(
format=pyaudio.paInt16,
channels=1,
rate=SAMPLE_RATE,
output=True,
output_device_index=HIFIBERRY_INDEX,
frames_per_buffer=1024
)
print("🎵 Playing dial tone - This is what you'll hear when picking up the phone!")
chunk_size = 2048
for i in range(0, len(tone.tobytes()), chunk_size):
stream.write(tone.tobytes()[i:i + chunk_size])
stream.stop_stream()
stream.close()
print("✓ Dial tone playback completed")
return True
except Exception as e:
print(f"❌ Dial tone error: {e}")
return False
finally:
audio.terminate()
def show_device_info():
"""Show all audio devices"""
print("\n" + "="*60)
print("📋 AUDIO DEVICES ON YOUR SYSTEM")
print("="*60)
audio = pyaudio.PyAudio()
print("\nAll devices:")
for i in range(audio.get_device_count()):
info = audio.get_device_info_by_index(i)
device_type = []
if info['maxOutputChannels'] > 0:
device_type.append("OUTPUT")
if info['maxInputChannels'] > 0:
device_type.append("INPUT")
marker = " ← USING THIS" if i == HIFIBERRY_INDEX else ""
print(f" [{i}] {info['name']}{marker}")
print(f" Type: {', '.join(device_type)}")
print(f" Sample rate: {info['defaultSampleRate']}")
print()
audio.terminate()
def main():
"""Main test routine"""
print("\n" + "="*60)
print("🎛️ HIFIBERRY COMPLETE SYSTEM TEST")
print("="*60)
print(f"\nTesting HiFiBerry at device index: {HIFIBERRY_INDEX}")
# Show devices
show_device_info()
input("\nPress Enter to start tests...")
# Test 1: Playback
test1 = test_playback()
time.sleep(1)
# Test 2: Dial tone
test2 = test_dial_tone()
time.sleep(1)
# Test 3: Recording
test3 = test_recording()
# Summary
print("\n" + "="*60)
print("📊 TEST SUMMARY")
print("="*60)
print(f"\n✓ Speaker playback: {'PASS ✓' if test1 else 'FAIL ❌'}")
print(f"✓ Dial tone: {'PASS ✓' if test2 else 'FAIL ❌'}")
print(f"✓ Microphone recording: {'PASS ✓' if test3 else 'FAIL ❌'}")
if test1 and test2 and test3:
print("\n🎉 ALL TESTS PASSED!")
print("\nYour rotary phone system is ready to use!")
print("Run: python3 rotary_phone_web.py")
else:
print("\n⚠️ Some tests failed. Check the output above for details.")
print("\n" + "="*60)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\nTest interrupted by user")
except Exception as e:
print(f"\n❌ Error: {e}")
import traceback
traceback.print_exc()