Fix Porcupine initialization and add fix script
This commit is contained in:
parent
08b4aece2b
commit
8d1a8ea12b
66
fix_porcupine.sh
Normal file
66
fix_porcupine.sh
Normal 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
|
||||
@ -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,11 +193,11 @@ 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"
|
||||
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Detection interrupted")
|
||||
except Exception as e:
|
||||
@ -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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user