Fix Porcupine initialization and add fix script

This commit is contained in:
Claw - AI Now Inc 2026-03-01 20:42:13 -08:00
parent 08b4aece2b
commit 8d1a8ea12b
2 changed files with 128 additions and 46 deletions

66
fix_porcupine.sh Normal file
View File

@ -0,0 +1,66 @@
#!/bin/bash
#
# Fix Porcupine Installation
# AI Now Inc - Del Mar Demo Unit
#
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "🔧 Fixing Porcupine Installation..."
echo ""
# Load API key from .env if it exists
if [ -f ".env" ]; then
source .env
if [ -n "$PICOVOICE_API_KEY" ]; then
echo "✓ Found API key in .env"
export PICOVOICE_API_KEY="$PICOVOICE_API_KEY"
else
echo "⚠️ No API key found in .env"
echo "Please run ./setup_porcupine.sh first"
exit 1
fi
else
echo "⚠️ .env file not found"
echo "Please run ./setup_porcupine.sh first"
exit 1
fi
# Check if venv exists and is valid
if [ ! -f "venv/bin/activate" ]; then
echo "📦 Virtual environment not found or broken. Recreating..."
rm -rf venv
python3 -m venv venv
fi
# Activate venv
source venv/bin/activate
# Upgrade pip
echo "📦 Upgrading pip..."
pip install --upgrade pip
# Install Porcupine
echo "🎤 Installing Porcupine..."
pip install pvporcupine
# Test installation
echo ""
echo "🧪 Testing Porcupine installation..."
if python3 -c "import pvporcupine; print('✓ Porcupine version:', pvporcupine.__version__)"; then
echo ""
echo "✅ Porcupine installed successfully!"
echo ""
echo "Now you can run:"
echo " ./start_tui.sh"
echo ""
echo "Say 'Hey Osiris' to test hotword detection."
else
echo ""
echo "❌ Porcupine installation failed!"
echo "Please check your API key and internet connection."
exit 1
fi

View File

@ -2,7 +2,6 @@
"""
Hotword Detector
Detects wake words: "Hey Osiris" / "你好 Osiris"
Supports:
- Porcupine (PicoVoice) for accurate hotword detection
- Custom keyword spotting
@ -17,19 +16,22 @@ import wave
from typing import Optional, Callable, List
from pathlib import Path
# Try to import Porcupine
HAS_PORCUPINE = False
porcupine_instance = None
try:
import pvporcupine
import pyaudio
HAS_PORCUPINE = True
logging.info(f"Porcupine module found (version: {pvporcupine.__version__})")
except ImportError:
HAS_PORCUPINE = False
logging.warning("Porcupine not installed. Install with: pip install pvporcupine")
# Try to import WebRTC VAD
HAS_VAD = False
try:
import webrtcvad
HAS_VAD = True
except ImportError:
HAS_VAD = False
logging.warning("WebRTC VAD not installed")
logger = logging.getLogger(__name__)
@ -46,7 +48,6 @@ class HotwordDetector:
"sample_rate": 16000,
"frame_length": 512
})
self.hotwords = self.config.get("hotwords", [])
self.is_running = False
self.callback = None
@ -91,15 +92,35 @@ class HotwordDetector:
return
try:
# Create Porcupine instance with custom keywords
# Get API key from environment or .env file
api_key = os.getenv('PICOVOICE_API_KEY')
if not api_key:
# Try to load from .env file
env_file = Path(__file__).parent / '.env'
if env_file.exists():
with open(env_file) as f:
for line in f:
if line.startswith('PICOVOICE_API_KEY='):
api_key = line.split('=')[1].strip()
break
if not api_key:
logger.warning("Porcupine API key not found. Set PICOVOICE_API_KEY environment variable.")
self.porcupine = None
return
# Initialize Porcupine with the built-in "hey osiris" keyword
self.porcupine = pvporcupine.create(
access_key=api_key,
keywords=["hey osiris"],
sensitivities=[0.5]
)
self.keyword_index = 0
logger.info("Porcupine initialized with 'Hey Osiris'")
logger.info("✓ Porcupine initialized with 'Hey Osiris'")
except Exception as e:
logger.warning(f"Porcupine initialization failed: {e}")
logger.warning("Falling back to simple detection")
self.porcupine = None
def set_callback(self, callback: Callable[[], None]):
@ -157,7 +178,12 @@ class HotwordDetector:
break
# Read audio frame
pcm = stream.read(self.porcupine.frame_length, exception_on_overflow=False)
pcm = stream.read(
self.porcupine.frame_length,
exception_on_overflow=False
)
# Convert to signed 16-bit integers
pcm = struct.unpack_from(
f"h{self.porcupine.frame_length}",
pcm
@ -167,7 +193,7 @@ class HotwordDetector:
keyword_index = self.porcupine.process(pcm)
if keyword_index >= 0:
logger.info("Hotword detected!")
logger.info("🎯 Hotword detected!")
if self.callback:
self.callback()
return "hey osiris"
@ -189,77 +215,67 @@ class HotwordDetector:
Detects any speech as hotword.
"""
logger.warning("Using simple voice detection (not recommended)")
# This is a placeholder - in production you'd use:
# - Snowboy
# - Custom trained model
# - Or just use Porcupine
return None
def stop(self):
"""Stop detection."""
self.is_running = False
logger.info("Hotword detection stopped")
if self.porcupine:
self.porcupine.delete()
def create_custom_hotword(self, keyword: str, output_path: str):
"""
Create custom hotword model (requires Porcupine training).
Create custom hotword model (Porcupine only).
This is a placeholder - actual implementation requires:
1. Recording multiple samples of the keyword
2. Training with Porcupine Console
3. Exporting the model
Args:
keyword: Keyword phrase
output_path: Path to save the model
"""
logger.info(f"Custom hotword creation not implemented: {keyword}")
logger.info("Use Porcupine Console to train custom keywords")
if not HAS_PORCUPINE:
raise RuntimeError("Porcupine not available")
# This would require Porcupine training API
logger.warning("Custom hotword training not yet implemented")
class SimpleHotwordDetector:
"""
Simple hotword detection using audio level threshold.
Fallback when Porcupine is not available.
"""
"""Simple energy-based hotword detection."""
def __init__(self, keyword: str = "hey osiris"):
self.keyword = keyword
self.threshold = 0.5
self.is_running = False
def detect(self, timeout: int = None) -> Optional[str]:
"""Simple energy-based detection."""
"""Simple detection - not reliable."""
logger.warning("Simple detection is not reliable. Install Porcupine for best results.")
return None
def main():
"""Test hotword detection."""
print("\n" + "="*60)
print(" 🔍 Hotword Detector Test")
print(" Say 'Hey Osiris' or '你好 Osiris'")
print("="*60)
detector = HotwordDetector()
import time
def on_hotword():
print("\n🎉 HOTWORD DETECTED!")
print("✨ Hotword detected!")
detector = HotwordDetector()
detector.set_callback(on_hotword)
print("Listening for hotword... (Ctrl+C to stop)")
try:
result = detector.detect(timeout=30)
if result:
print(f"Detected: {result}")
else:
print("No hotword detected")
while True:
result = detector.detect(timeout=30)
if result:
print(f"Detected: {result}")
time.sleep(1)
except KeyboardInterrupt:
print("\nTest stopped")
detector.stop()
print("\nStopped")
detector.stop()
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
if __name__ == '__main__':
main()