Compare commits
No commits in common. "ff7ce42a7f71e96c031b5c74c9f12da5c9fcf8f5" and "56e8216a1d9a921ab196d80236dfb302c2b2fe42" have entirely different histories.
ff7ce42a7f
...
56e8216a1d
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,11 +0,0 @@
|
|||||||
venv/
|
|
||||||
logs/
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
*.pid
|
|
||||||
*.log
|
|
||||||
ahserver/
|
|
||||||
appPublic/
|
|
||||||
longtasks/
|
|
||||||
sqlor/
|
|
||||||
wwwroot/
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
def extract_embedding(audio_path):
|
|
||||||
signal = MODEL.load_audio(audio_path)
|
|
||||||
if isinstance(signal, tuple):
|
|
||||||
signal = signal[0]
|
|
||||||
signal = signal.unsqueeze(0).to(DEVICE)
|
|
||||||
with torch.no_grad():
|
|
||||||
emb = MODEL.encode_batch(signal)
|
|
||||||
return emb.squeeze().cpu().numpy().tolist()
|
|
||||||
|
|
||||||
def verify_speakers(audio_path, ref_path):
|
|
||||||
score, pred = MODEL.verify_files(audio_path, ref_path)
|
|
||||||
return float(score), bool(pred)
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"device": "cuda:1",
|
|
||||||
"redis_url": "redis://127.0.0.1:6379",
|
|
||||||
"worker_cnt": 2,
|
|
||||||
"logger": {
|
|
||||||
"name": "voiceprint",
|
|
||||||
"levelname": "info",
|
|
||||||
"logfile": "$[workdir]$/logs/voiceprint.log"
|
|
||||||
},
|
|
||||||
"website": {
|
|
||||||
"paths": [
|
|
||||||
["$[workdir]$/wwwroot", ""]
|
|
||||||
],
|
|
||||||
"client_max_size": 10000,
|
|
||||||
"host": "0.0.0.0",
|
|
||||||
"port": 9087,
|
|
||||||
"coding": "utf-8",
|
|
||||||
"indexes": ["index.html"],
|
|
||||||
"startswiths": [
|
|
||||||
{"leading": "/api/voiceprint-submit", "registerfunction": "voiceprint_submit"},
|
|
||||||
{"leading": "/api/voiceprint-status", "registerfunction": "voiceprint_status"},
|
|
||||||
{"leading": "/api/voiceprint/extract", "registerfunction": "voiceprint_extract"},
|
|
||||||
{"leading": "/api/voiceprint/verify", "registerfunction": "voiceprint_verify"}
|
|
||||||
],
|
|
||||||
"processors": [
|
|
||||||
[".tmpl", "tmpl"],
|
|
||||||
[".ui", "bui"],
|
|
||||||
[".dspy", "dspy"]
|
|
||||||
],
|
|
||||||
"session_max_time": 3000,
|
|
||||||
"session_issue_time": 2500
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Download ECAPA-TDNN model via ModelScope to /share/models/ecapa-tdnn/"""
|
|
||||||
import os, sys, time
|
|
||||||
|
|
||||||
MODEL_ID = 'iic/speech_ecapa-tdnn_sv_en_voxceleb_16k'
|
|
||||||
TARGET = '/share/models/ecapa-tdnn'
|
|
||||||
|
|
||||||
os.makedirs(TARGET, exist_ok=True)
|
|
||||||
|
|
||||||
# Try ModelScope first
|
|
||||||
for attempt in range(3):
|
|
||||||
try:
|
|
||||||
from modelscope import snapshot_download
|
|
||||||
print(f"Attempt {attempt+1}: ModelScope download...")
|
|
||||||
snapshot_download(MODEL_ID, local_dir=TARGET)
|
|
||||||
print("SUCCESS via ModelScope")
|
|
||||||
sys.exit(0)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"ModelScope failed: {e}")
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
# Fallback: try HF mirror
|
|
||||||
print("Trying HF mirror...")
|
|
||||||
os.system(f'export HF_ENDPOINT=https://hf-mirror.com && python3 -c "from huggingface_hub import snapshot_download; snapshot_download(\'speechbrain/spkrec-ecapa-voxceleb\', local_dir=\'{TARGET}\')"')
|
|
||||||
8
start.sh
8
start.sh
@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
mkdir -p logs
|
|
||||||
setsid python3 app/voiceprint_app.py -p 9087 >> logs/voiceprint.log 2>&1 &
|
|
||||||
echo $! > voiceprint.pid
|
|
||||||
echo "Voiceprint PID=$(cat voiceprint.pid)"
|
|
||||||
sleep 3
|
|
||||||
curl -s http://localhost:9087/api/health && echo "" || echo "(checking...)"
|
|
||||||
9
stop.sh
9
stop.sh
@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
if [ -f voiceprint.pid ]; then
|
|
||||||
kill $(cat voiceprint.pid) 2>/dev/null
|
|
||||||
rm -f voiceprint.pid
|
|
||||||
echo "Voiceprint stopped"
|
|
||||||
else
|
|
||||||
pkill -f voiceprint_app.py 2>/dev/null && echo "Voiceprint stopped (via pkill)"
|
|
||||||
fi
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from ahserver.serverenv import ServerEnv
|
|
||||||
from vp.engine import VoiceprintEngine
|
|
||||||
|
|
||||||
|
|
||||||
def load_voiceprint():
|
|
||||||
env = ServerEnv()
|
|
||||||
env.voiceprint = VoiceprintEngine()
|
|
||||||
63
vp/engine.py
63
vp/engine.py
@ -1,63 +0,0 @@
|
|||||||
"""ECAPA-TDNN Voiceprint Engine — loads model, processes extract/verify tasks."""
|
|
||||||
import torch
|
|
||||||
from longtasks.longtasks import LongTasks
|
|
||||||
from appPublic.worker import awaitify
|
|
||||||
from appPublic.jsonConfig import getConfig
|
|
||||||
from appPublic.log import debug
|
|
||||||
from speechbrain.inference.speaker import SpeakerRecognition
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
class VoiceprintEngine(LongTasks):
|
|
||||||
def __init__(self):
|
|
||||||
self.config = getConfig()
|
|
||||||
super().__init__(self.config.redis_url, 'voiceprint', worker_cnt=self.config.worker_cnt)
|
|
||||||
self.load_model()
|
|
||||||
|
|
||||||
def load_model(self):
|
|
||||||
device = self.config.device
|
|
||||||
debug(f'loading ECAPA-TDNN on {device}...')
|
|
||||||
self.verifier = SpeakerRecognition.from_hparams(
|
|
||||||
source="speechbrain/spkrec-ecapa-voxceleb",
|
|
||||||
savedir="/share/models/ecapa-tdnn",
|
|
||||||
run_opts={"device": device}
|
|
||||||
)
|
|
||||||
debug('ECAPA-TDNN loaded')
|
|
||||||
|
|
||||||
async def process_task(self, payload, workerid=None):
|
|
||||||
task_type = payload.get('task_type', 'extract')
|
|
||||||
audio_file = payload.get('audio_file', '')
|
|
||||||
if not audio_file:
|
|
||||||
return {'status': 'FAILED', 'result': 'missing audio_file'}
|
|
||||||
|
|
||||||
if task_type == 'extract':
|
|
||||||
f = awaitify(self._extract)
|
|
||||||
return await f(audio_file)
|
|
||||||
elif task_type == 'verify':
|
|
||||||
ref_file = payload.get('reference_file', '')
|
|
||||||
if not ref_file:
|
|
||||||
return {'status': 'FAILED', 'result': 'missing reference_file'}
|
|
||||||
f = awaitify(self._verify)
|
|
||||||
return await f(audio_file, ref_file)
|
|
||||||
return {'status': 'FAILED', 'result': f'unknown task_type: {task_type}'}
|
|
||||||
|
|
||||||
def _extract(self, audio_path):
|
|
||||||
signal = self.verifier.load_audio(audio_path, 16000)
|
|
||||||
t = torch.tensor(signal).unsqueeze(0).to(self.config.device)
|
|
||||||
emb = self.verifier.encode_batch(t)
|
|
||||||
vec = emb.squeeze().cpu().numpy().tolist()
|
|
||||||
return {
|
|
||||||
'status': 'SUCCEEDED',
|
|
||||||
'embedding': vec,
|
|
||||||
'embedding_dim': len(vec),
|
|
||||||
'usage': {'audio_duration': round(len(signal) / 16000, 2)},
|
|
||||||
}
|
|
||||||
|
|
||||||
def _verify(self, audio_path, ref_path):
|
|
||||||
score, pred = self.verifier.verify_files(audio_path, ref_path)
|
|
||||||
return {
|
|
||||||
'status': 'SUCCEEDED',
|
|
||||||
'similarity': round(float(score), 4),
|
|
||||||
'is_same_speaker': bool(pred),
|
|
||||||
'usage': {},
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user