3D-AI-Avatar / app.py
SS-2005's picture
Update app.py
b89494f verified
Raw
History Blame Contribute Delete
2.29 kB
from flask import Flask, request, jsonify, send_from_directory
from piper import PiperVoice
import os, uuid, wave
app = Flask(__name__, static_folder="static")
BASE = os.path.dirname(__file__)
MODELS = os.path.join(BASE, "models")
OUTPUT = os.path.join(BASE, "output")
os.makedirs(OUTPUT, exist_ok=True)
print("Loading Piper…")
voice = PiperVoice.load(
os.path.join(MODELS, "en_US-lessac-medium.onnx"),
os.path.join(MODELS, "en_US-lessac-medium.onnx.json")
)
print("Piper ready")
@app.route("/")
def index():
return send_from_directory("static", "index.html")
@app.route("/speak", methods=["POST"])
def speak():
data = request.json
text = data.get("text", "").strip()
if not text:
return jsonify({"error": "Empty text"}), 400
uid = str(uuid.uuid4())
wav_path = os.path.join(OUTPUT, f"{uid}.wav")
with wave.open(wav_path, "wb") as wf:
wf.setnchannels(1)
wf.setsampwidth(2) # PCM16
wf.setframerate(22050)
audio_stream = voice.synthesize(text)
for chunk in audio_stream:
# CASE 1: chunk IS bytes
if isinstance(chunk, (bytes, bytearray)):
wf.writeframes(chunk)
# CASE 2: chunk is numpy array
elif hasattr(chunk, "dtype"):
wf.writeframes(chunk.astype("int16").tobytes())
# CASE 3: chunk is object → extract safely
elif hasattr(chunk, "__dict__"):
for v in chunk.__dict__.values():
if isinstance(v, (bytes, bytearray)):
wf.writeframes(v)
break
if hasattr(v, "dtype"):
wf.writeframes(v.astype("int16").tobytes())
break
else:
raise RuntimeError(f"Unsupported AudioChunk: {chunk}")
else:
raise RuntimeError(f"Unknown audio chunk type: {type(chunk)}")
return jsonify({
"audio": f"/audio/{uid}.wav"
})
@app.route("/audio/<name>")
def audio(name):
return send_from_directory(OUTPUT, name)
@app.route("/<path:path>")
def static_files(path):
return send_from_directory("static", path)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)