File size: 2,714 Bytes
c2d5ee4
 
c1f680b
 
c2d5ee4
 
 
41964f0
c2d5ee4
 
41964f0
 
c2d5ee4
41964f0
 
 
c1f680b
 
c2d5ee4
41964f0
 
c2d5ee4
c1f680b
c2d5ee4
41964f0
c2d5ee4
 
41964f0
c1f680b
c2d5ee4
41964f0
c2d5ee4
 
 
c1f680b
 
 
 
 
c2d5ee4
c1f680b
c2d5ee4
 
 
 
 
 
 
 
 
 
 
 
 
 
41964f0
c2d5ee4
 
 
 
 
 
 
41964f0
 
 
 
c2d5ee4
41964f0
 
c2d5ee4
41964f0
 
 
c2d5ee4
 
41964f0
 
 
 
 
 
 
 
c2d5ee4
41964f0
 
 
c2d5ee4
 
41964f0
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
"""
ESP32 TTS Server — Hugging Face Space
WAV output: 22050Hz, Mono, 8-bit unsigned
ESP32 DAC ke liye perfect — direct mapping, no conversion
"""

from flask import Flask, request, Response
import os
import io
import hashlib
from gtts import gTTS
from pydub import AudioSegment
from pydub.effects import normalize

app = Flask(__name__)

BASE_DIR  = os.path.dirname(os.path.abspath(__file__))
CACHE_DIR = os.path.join(BASE_DIR, "tts_cache")
os.makedirs(CACHE_DIR, exist_ok=True)


def text_to_wav_bytes(text, lang):
    # Step 1: gTTS → MP3 in memory
    mp3_buf = io.BytesIO()
    tts = gTTS(text, lang=lang)
    tts.write_to_fp(mp3_buf)
    mp3_buf.seek(0)

    # Step 2: Load audio
    audio = AudioSegment.from_file(mp3_buf, format="mp3")

    # Step 3: Normalize — consistent volume
    audio = normalize(audio)

    # Step 4: Convert — 22050Hz, Mono, 8-bit unsigned
    # ESP32 DAC = 8-bit (0-255), 22050Hz enough for clear voice
    audio = audio.set_frame_rate(22050)
    audio = audio.set_channels(1)
    audio = audio.set_sample_width(1)   # 8-bit unsigned (0-255)

    # Step 5: WAV bytes
    wav_buf = io.BytesIO()
    audio.export(wav_buf, format="wav")
    wav_buf.seek(0)
    return wav_buf.read()


def get_wav(text, lang):
    key        = hashlib.md5((text + lang).encode()).hexdigest()
    cache_path = os.path.join(CACHE_DIR, key + ".wav")

    if os.path.exists(cache_path):
        print(f'[Cache HIT] "{text}"')
        with open(cache_path, "rb") as f:
            return f.read()

    print(f'[Generating] "{text}" | {lang}')
    wav_data = text_to_wav_bytes(text, lang)

    with open(cache_path, "wb") as f:
        f.write(wav_data)

    return wav_data


@app.route("/tts", methods=["GET"])
def tts_get():
    text = request.args.get("text", "Hello").replace("+", " ")
    lang = request.args.get("lang", "en")
    try:
        wav_data = get_wav(text, lang)
    except Exception as e:
        print(f"[ERROR] {e}")
        return f"Error: {e}", 500
    return Response(wav_data, mimetype="audio/wav",
                    headers={"Content-Length": str(len(wav_data))})


@app.route("/tts", methods=["POST"])
def tts_post():
    data = request.get_json(silent=True) or {}
    text = data.get("text", "Hello")
    lang = data.get("lang", "en")
    try:
        wav_data = get_wav(text, lang)
    except Exception as e:
        print(f"[ERROR] {e}")
        return f"Error: {e}", 500
    return Response(wav_data, mimetype="audio/wav",
                    headers={"Content-Length": str(len(wav_data))})


@app.route("/ping")
def ping():
    return {"status": "ok", "server": "ESP32 TTS Server"}


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860, debug=False)