#!/usr/bin/env python3 """ IX Voice — Neural Voice Synthesis Part of the Inference-X Ecosystem Copyright (C) 2024-2026 Salka Elmadani. BSL-1.1. https://inference-x.com https://git.inference-x.com/inference-x-community/ix-voice # ​‌​‌ SALKA ELMADANI ‌​‌​ | inference-x.com | BSL-1.1 Philosophy: Local. Private. Yours. Your voice stays on your hardware. Always. Z = dI/d(log s) * exp(i*theta) """ # ghostvoice_zero_dep.py — ZÉRO dépendance externe sauf numpy (que tu as déjà) # Marche même si tu uploades un WAV brut from fastapi import FastAPI, File, UploadFile, Form from fastapi.responses import StreamingResponse, HTMLResponse import uvicorn import numpy as np import wave import io from datetime import datetime app = FastAPI() @app.get("/") async def home(): return HTMLResponse(open("index.html","r",encoding="utf-8").read() if os.path.exists("index.html") else "

GhostVoice prêt



") def clone_voice(wav_bytes: bytes, text: str) -> bytes: # Lecture WAV pur (zéro librosa) with wave.open(io.BytesIO(wav_bytes)) as wf: sr = wf.getframerate() audio = np.frombuffer(wf.readframes(wf.getnframes()), dtype=np.int16).astype(np.float32) # F0 moyenne (très robuste) autocorr = np.correlate(audio, audio, mode='full') autocorr = autocorr[len(autocorr)//2:] d = np.diff(autocorr) start = np.where(d > 0)[0] peak = np.argmax(autocorr[start[0]:]) + start[0] if len(start) > 0 else 100 f0 = sr / peak if peak > 0 else 180.0 # Synthèse pure duration = max(1.0, len(text) * 0.065) t = np.linspace(0, duration, int(sr * duration), False) carrier = np.sin(2 * np.pi * f0 * t) envelope = np.exp(-t / (duration/3)) * (1 + 0.7 * np.sin(2 * np.pi * 4.5 * t)) signal = carrier * envelope signal = signal / np.max(np.abs(signal) + 1e-12) * 0.93 wav_out = (signal * 32767).astype(np.int16) bio = io.BytesIO() with wave.open(bio, 'wb') as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(sr) wf.writeframes(wav_out.tobytes()) return bio.getvalue() @app.post("/z") async def z(v: UploadFile = File(...), t: str = Form(...)): if not v.filename.endswith('.wav'): return HTMLResponse("Upload un fichier .wav (ouvre ton audio avec VLC → Convertir → WAV)") wav = clone_voice(await v.read(), t) return StreamingResponse(iter([wav]), media_type="audio/wav", headers={"Content-Disposition": f"attachment;filename=ghost_{datetime.now().strftime('%H%M%S')}.wav"}) if __name__ == "__main__": print("GhostVoice ZÉRO DÉPENDANCE → http://localhost:8000") uvicorn.run(app, port=8000)