SiamCafe · Blog
Voice Cloning Automation Script — วิธีสร้างระบบ
บทความ

Voice Cloning Automation Script — วิธีสร้างระบบ

เผยแพร่ 28 พฤษภาคม 2569

Voice Cloning คืออะไร

Voice Cloning เป็นเทคโนโลยี AI ที่ใช้ Deep Learning สร้างเสียงเลียนแบบเสียงของคนจริง โดยวิเคราะห์ลักษณะเฉพาะของเสียง (Speaker Embedding) เช่น โทนเสียง จังหวะการพูด น้ำเสียง ความถี่ แล้วสร้างเสียงใหม่จากข้อความที่กำหนด (Text-to-Speech) ให้ฟังเหมือนคนต้นแบบ

ปัจจุบัน Voice Cloning ใช้กันแพร่หลายใน Audiobook Production, Video Dubbing, Virtual Assistant, Content Creation, Accessibility สำหรับผู้พิการทางเสียง และ Personalized TTS เทคโนโลยีพัฒนาเร็วมากจนสามารถ Clone เสียงได้จากตัวอย่างเพียง 3-10 วินาที

ติดตั้งและเตรียมสภาพแวดล้อม

# === ติดตั้ง Voice Cloning Environment ===

# สร้าง Virtual Environment
python -m venv voice-clone-env
source voice-clone-env/bin/activate  # Linux/Mac
# voice-clone-env\Scripts\activate   # Windows

# ติดตั้ง Coqui TTS (XTTS v2)
pip install TTS torch torchaudio

# ติดตั้ง Dependencies เพิ่มเติม
pip install soundfile librosa scipy numpy pydub
pip install fastapi uvicorn python-multipart  # สำหรับ API

# ตรวจสอบ GPU
python -c "import torch; print(f'CUDA: {torch.cuda.is_available()}')"
python -c "import torch; print(f'GPU: {torch.cuda.get_device_name(0)}')" 

# ดาวน์โหลด Model
python -c "from TTS.api import TTS; tts = TTS('tts_models/multilingual/multi-dataset/xtts_v2')"
# Model จะถูกดาวน์โหลดอัตโนมัติ (~1.8GB)

# โครงสร้าง Project
voice-cloner/
├── main.py              # Main Script
├── api.py               # FastAPI Server
├── preprocess.py        # Audio Preprocessing
├── clone.py             # Voice Cloning Engine
├── batch_process.py     # Batch Processing
├── samples/             # Voice Samples
│   └── speaker.wav
├── output/              # Generated Audio
├── config.yaml          # Configuration
└── requirements.txt

Voice Cloning Script

# clone.py — Voice Cloning Engine
import os
import torch
import numpy as np
import soundfile as sf
from TTS.api import TTS
from pydub import AudioSegment
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class VoiceCloner:
    """Voice Cloning Engine ด้วย XTTS v2"""

    def __init__(self, model_name="tts_models/multilingual/multi-dataset/xtts_v2",
                 device=None):
        self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
        logger.info(f"Loading model on {self.device}...")
        self.tts = TTS(model_name).to(self.device)
        logger.info("Model loaded successfully")

    def preprocess_audio(self, audio_path, output_path=None,
                         target_sr=22050, max_duration=30):
        """เตรียมไฟล์เสียงสำหรับ Voice Cloning"""
        audio = AudioSegment.from_file(audio_path)

        # Convert to mono
        if audio.channels > 1:
            audio = audio.set_channels(1)

        # Set sample rate
        audio = audio.set_frame_rate(target_sr)

        # Normalize volume
        target_dbfs = -20
        change_in_dbfs = target_dbfs - audio.dBFS
        audio = audio.apply_gain(change_in_dbfs)

        # Trim silence
        from pydub.silence import detect_leading_silence
        start_trim = detect_leading_silence(audio, silence_threshold=-40)
        end_trim = detect_leading_silence(audio.reverse(), silence_threshold=-40)
        audio = audio[start_trim:len(audio) - end_trim]

        # Limit duration
        if len(audio) > max_duration * 1000:
            audio = audio[:max_duration * 1000]

        out_path = output_path or audio_path.replace(".wav", "_processed.wav")
        audio.export(out_path, format="wav")
        logger.info(f"Preprocessed: {out_path} ({len(audio)/1000:.1f}s)")
        return out_path

    def clone_voice(self, text, speaker_wav, output_path,
                    language="th", speed=1.0):
        """Clone เสียงและสร้าง Audio จากข้อความ"""
        logger.info(f"Generating speech: '{text[:50]}...'")

        self.tts.tts_to_file(
            text=text,
            speaker_wav=speaker_wav,
            language=language,
            file_path=output_path,
            speed=speed,
        )

        # ตรวจสอบไฟล์ Output
        if os.path.exists(output_path):
            audio = AudioSegment.from_file(output_path)
            duration = len(audio) / 1000
            logger.info(f"Generated: {output_path} ({duration:.1f}s)")
            return {"path": output_path, "duration": duration}
        else:
            raise FileNotFoundError(f"Failed to generate: {output_path}")

    def batch_clone(self, texts, speaker_wav, output_dir,
                    language="th", prefix="output"):
        """Clone เสียงหลายข้อความพร้อมกัน"""
        os.makedirs(output_dir, exist_ok=True)
        results = []

        for i, text in enumerate(texts):
            output_path = os.path.join(output_dir, f"{prefix}_{i:04d}.wav")
            try:
                result = self.clone_voice(text, speaker_wav, output_path,
                                          language=language)
                results.append(result)
            except Exception as e:
                logger.error(f"Error on text {i}: {e}")
                results.append({"path": None, "error": str(e)})

        success = sum(1 for r in results if r.get("path"))
        logger.info(f"Batch complete: {success}/{len(texts)} succeeded")
        return results

    def concatenate_audio(self, audio_files, output_path, gap_ms=500):
        """รวมไฟล์เสียงหลายไฟล์เข้าด้วยกัน"""
        combined = AudioSegment.empty()
        gap = AudioSegment.silent(duration=gap_ms)

        for f in audio_files:
            if f and os.path.exists(f):
                audio = AudioSegment.from_file(f)
                combined += audio + gap

        combined.export(output_path, format="wav")
        logger.info(f"Concatenated {len(audio_files)} files -> {output_path}")
        return output_path

# ตัวอย่างการใช้งาน
if __name__ == "__main__":
    cloner = VoiceCloner()

    # Preprocess Speaker Sample
    processed = cloner.preprocess_audio("samples/speaker.wav")

    # Clone เสียงเดียว
    result = cloner.clone_voice(
        text="สวัสดีครับ นี่คือเสียงที่สร้างจาก AI Voice Cloning",
        speaker_wav=processed,
        output_path="output/test_output.wav",
        language="th",
    )
    print(f"Generated: {result}")

    # Batch Clone
    texts = [
        "ยินดีต้อนรับสู่ระบบ Voice Cloning อัตโนมัติ",
        "ระบบนี้ใช้เทคโนโลยี XTTS v2 ในการสร้างเสียง",
        "สามารถ Clone เสียงได้จากตัวอย่างเพียงไม่กี่วินาที",
    ]
    results = cloner.batch_clone(texts, processed, "output/batch/")

API Server สำหรับ Voice Cloning

# api.py — FastAPI Server สำหรับ Voice Cloning
from fastapi import FastAPI, UploadFile, File, Form
from fastapi.responses import FileResponse
import tempfile
import os
import uuid
from clone import VoiceCloner

app = FastAPI(title="Voice Cloning API")
cloner = VoiceCloner()

@app.post("/clone")
async def clone_voice(
    text: str = Form(...),
    language: str = Form(default="th"),
    speaker: UploadFile = File(...),
):
    """Clone เสียงจากไฟล์ตัวอย่างและข้อความ"""
    # Save uploaded file
    tmp_speaker = os.path.join(tempfile.gettempdir(), f"speaker_{uuid.uuid4()}.wav")
    with open(tmp_speaker, "wb") as f:
        content = await speaker.read()
        f.write(content)

    # Preprocess
    processed = cloner.preprocess_audio(tmp_speaker)

    # Generate
    output_path = os.path.join(tempfile.gettempdir(), f"output_{uuid.uuid4()}.wav")
    result = cloner.clone_voice(text, processed, output_path, language=language)

    # Cleanup
    os.remove(tmp_speaker)
    if os.path.exists(processed):
        os.remove(processed)

    return FileResponse(
        output_path,
        media_type="audio/wav",
        filename="cloned_voice.wav",
    )

@app.post("/batch")
async def batch_clone(
    texts: str = Form(...),  # JSON array of texts
    language: str = Form(default="th"),
    speaker: UploadFile = File(...),
):
    """Batch Clone หลายข้อความ"""
    import json

    text_list = json.loads(texts)

    tmp_speaker = os.path.join(tempfile.gettempdir(), f"speaker_{uuid.uuid4()}.wav")
    with open(tmp_speaker, "wb") as f:
        f.write(await speaker.read())

    processed = cloner.preprocess_audio(tmp_speaker)
    output_dir = os.path.join(tempfile.gettempdir(), f"batch_{uuid.uuid4()}")
    results = cloner.batch_clone(text_list, processed, output_dir, language)

    # Concatenate all outputs
    audio_files = [r["path"] for r in results if r.get("path")]
    final_output = os.path.join(tempfile.gettempdir(), f"final_{uuid.uuid4()}.wav")
    cloner.concatenate_audio(audio_files, final_output)

    return FileResponse(final_output, media_type="audio/wav")

@app.get("/health")
async def health():
    return {"status": "ok", "device": cloner.device}

# รัน: uvicorn api:app --host 0.0.0.0 --port 8000
# ทดสอบ:
# curl -X POST http://localhost:8000/clone \
#   -F "text=สวัสดีครับ" \
#   -F "language=th" \
#   -F "speaker=@samples/speaker.wav" \
#   -o output.wav

Automation Pipeline

# batch_process.py — Automation Pipeline สำหรับ Voice Cloning
import os
import csv
import yaml
import time
import logging
from clone import VoiceCloner

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)

def process_csv(csv_path, speaker_wav, output_dir, language="th"):
    """
    อ่านข้อความจาก CSV แล้ว Clone เสียงอัตโนมัติ
    CSV Format: id, text, filename
    """
    cloner = VoiceCloner()
    os.makedirs(output_dir, exist_ok=True)

    # Preprocess speaker
    processed = cloner.preprocess_audio(speaker_wav)

    results = []
    with open(csv_path, "r", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        rows = list(reader)

    logger.info(f"Processing {len(rows)} items...")
    start_time = time.time()

    for i, row in enumerate(rows):
        text = row["text"]
        filename = row.get("filename", f"output_{i:04d}.wav")
        output_path = os.path.join(output_dir, filename)

        try:
            result = cloner.clone_voice(text, processed, output_path,
                                        language=language)
            results.append({"id": row.get("id", i), "status": "ok",
                            **result})
            logger.info(f"[{i+1}/{len(rows)}] OK: {filename}")
        except Exception as e:
            results.append({"id": row.get("id", i), "status": "error",
                            "error": str(e)})
            logger.error(f"[{i+1}/{len(rows)}] FAIL: {filename} - {e}")

    elapsed = time.time() - start_time
    success = sum(1 for r in results if r["status"] == "ok")
    logger.info(f"Done: {success}/{len(rows)} in {elapsed:.1f}s")

    # Save report
    report_path = os.path.join(output_dir, "report.csv")
    with open(report_path, "w", encoding="utf-8", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["id", "status", "path",
                                                "duration", "error"])
        writer.writeheader()
        writer.writerows(results)

    return results

if __name__ == "__main__":
    process_csv(
        csv_path="texts.csv",
        speaker_wav="samples/speaker.wav",
        output_dir="output/batch/",
        language="th",
    )

Best Practices และข้อควรระวัง

  • คุณภาพเสียงตัวอย่าง: ใช้เสียงที่สะอาด ไม่มี Background Noise, เสียงดนตรี หรือเสียงรบกวน บันทึกในห้องเงียบ ใช้ไมค์คุณภาพดี
  • ความยาวเสียง: สำหรับ XTTS v2 ใช้ตัวอย่าง 6-30 วินาที ยาวเกินไปอาจไม่ดีกว่า คัดเลือกส่วนที่ชัดเจนที่สุด
  • จริยธรรม: ใช้เฉพาะเสียงตัวเองหรือได้รับอนุญาต ห้ามใช้สร้าง Deepfake หลอกลวง ระบุว่าเป็นเสียง AI เสมอ
  • GPU Memory: XTTS v2 ใช้ VRAM ~4GB สำหรับ Inference ถ้าไม่มี GPU ใช้ CPU ได้แต่ช้ากว่า 5-10 เท่า
  • Batch Processing: สำหรับงานจำนวนมาก ใช้ Batch Processing กับ Queue System เพื่อจัดการ Resources
  • Watermarking: เพิ่ม Audio Watermark ในเสียงที่สร้าง เพื่อระบุว่าเป็นเสียง AI-generated

Voice Cloning คืออะไร

Voice Cloning ใช้ AI สร้างเสียงเลียนแบบเสียงคนจริง วิเคราะห์ลักษณะเฉพาะของเสียงแล้วสร้างเสียงใหม่จากข้อความ ใช้ใน Text-to-Speech, Audiobook, Dubbing, Virtual Assistant เทคโนโลยีใหม่ Clone ได้จากตัวอย่างเพียง 3-10 วินาที