นิยายโต้ตอบและวิชวลโนเวลภาษาไทย
ทำเสียงให้นิยายแบบแตกกิ่งภาษาไทย ทั้งนิยายแชท วิชวลโนเวล และเกมที่ผู้อ่านเลือกทางเดินเอง จะเรนเดอร์ทั้งต้นไม้ไว้ล่วงหน้า หรือพูดสดตามเส้นทางที่ผู้อ่านเลือกก็ได้
ไอเดียให้ลองเล่น
ทุกการ์ดเล่นคลิปจริงที่สร้างจากสคริปต์นั้น ๆ ด้วยเสียงนมเย็น เปิดใน Playground เพื่อให้พูดข้อความของคุณเองได้
บทแบบแตกกิ่งกลายเป็นเสียงได้อย่างไร
นิยายแบบแตกกิ่งเป็นงานเขียนประเภทเดียวที่ยังไม่รู้ว่าบรรทัดถัดไปคืออะไรจนกว่าผู้อ่านจะลงมือเลือก จอยลดาเผยแพร่นิยายไทยในรูปแบบแชทที่อ่านทีละข้อความ ส่วนวิชวลโนเวลที่สร้างบนเอนจินอย่าง Ren'Py เขียนบทพูดบรรทัดละหนึ่งผู้พูดและมีจุดให้เลือกคั่นระหว่างฉาก ทั้งสองรูปแบบทิ้งคำถามเดียวกันไว้ให้สตูดิโอ ถ้าเรนเดอร์ทั้งต้นไม้ไว้ล่วงหน้า ทุกกิ่งจะกลายเป็นไฟล์ที่พร้อมอยู่ก่อนใครจะเปิดแอป และจ่ายไปแล้วไม่ว่าจะมีผู้อ่านเดินไปทางนั้นหรือไม่ ถ้าพูดสดตอนเล่น ผู้อ่านจะจ่ายตามเส้นทางที่เดินจริงเท่านั้น แลกกับการเปิดการเชื่อมต่อค้างไว้ตลอดที่อ่านอยู่ แบบไหนถูกกว่าขึ้นอยู่กับว่าผู้อ่านทั่วไปเห็นต้นไม้นั้นมากแค่ไหน และตารางข้างล่างใส่ตัวเลขจริงลงไปให้แล้ว
ที่มา:Joylada(opens in a new tab)Ren'Py(opens in a new tab)
- แผ่ต้นไม้ออกเป็นช่วงคำพูดทุกโหนดกลายเป็นช่วงคำพูดที่มีผู้พูดและรหัสเสียงกำกับ ช่วงที่ยาวไม่เกิน 5,000 ตัวอักษรใช้คำขอเดียว ที่ยาวกว่านั้นแบ่งตรงรอยต่อประโยค
- เลือกเรนเดอร์ล่วงหน้าหรือพูดสด เป็นรายฉากบทเปิดที่ผู้อ่านทุกคนต้องผ่านคุ้มที่จะเรนเดอร์ไว้ครั้งเดียว ส่วนกิ่งที่แทบไม่มีใครเดินไปคุ้มกว่าที่จะพูดสด ตัดสินใจแยกรายฉากได้ และเปลี่ยนใจหลังเปิดตัวได้โดยไม่ต้องแก้สคริปต์
- เก็บสิ่งที่ผู้อ่านเคยได้ยินไว้บรรทัดที่พูดสดออกมาก็เป็นไฟล์ MP3 ธรรมดา การเก็บไว้ทำให้เส้นทางที่เล่นซ้ำกลายเป็นการอ่านจากแคช และการย้อนอ่านเส้นทางเดิมไม่มีค่าใช้จ่ายในรอบที่สอง
เรนเดอร์ทั้งต้นไม้ หรือพูดเฉพาะเส้นทาง
| วิธีส่งเสียง | ตัวอักษร | เครดิต | ค่าใช้จ่าย |
|---|---|---|---|
| เส้นทางของผู้อ่านหนึ่งคน 120 บรรทัด | 10,800 | 162 | $0.16 |
| ผู้อ่าน 10,000 คน พูดสดตอนเล่น | 108,000,000 | 1,620,000 | $1,620 |
| ทั้งต้นไม้ ทำเสียงครั้งเดียว ใช้ได้กับผู้อ่านทุกคนที่จะมีต่อจากนี้ | ประมาณ $2.16 | ||
คำนวณที่ 15 เครดิตต่อ 1,000 ตัวอักษร จากบรรทัดยาว 90 ตัวอักษร การเรนเดอร์ล่วงหน้าจ่ายครั้งเดียวสำหรับผู้อ่านทั้งหมด ส่วนการพูดสดจ่ายต่อผู้อ่านหนึ่งคน จุดคุ้มทุนจึงอยู่ราว ๆ ผู้อ่าน 13 คน
หนึ่งบท สี่ตอนจบ
scene_07a ณิชา nomyen 96 chars scene_07a ธาวิน massaman 82 chars choice_1 -> scene_07b choice_2 -> scene_07c scene_07b ณิชา nomyen 88 chars [pre-rendered] scene_07c ณิชา nomyen 94 chars [pre-rendered] scene_07c คุณยาย tako 90 chars [pre-rendered] # 4 กิ่ง x ~400 บรรทัด = ต้นไม้ทั้งต้นด้านบน # ผู้อ่านหนึ่งคนได้ยินราว 120 บรรทัด
ต้นไม้คือหน่วยที่สตูดิโอตั้งงบ ส่วนเส้นทางคือหน่วยที่ผู้อ่านได้ยิน ทั้งสองแบบใช้รหัสเสียงหนึ่งตัวต่อหนึ่งผู้พูดเหมือนกัน
จากวันเปิดตัวจนเข้าที่
การเชื่อมต่อสดที่เปิดอยู่หนึ่งเส้นถือหนึ่งช่องคำขอพร้อมกันของแพ็กเกจ การติดตั้งแบบพูดสดจึงวัดขนาดจากจำนวนผู้อ่านพร้อมกัน แทนที่จะวัดจากจำนวนบรรทัดต่อวัน
สำหรับนักพัฒนา
ตัวอย่าง TypeScript เปิดซ็อกเก็ตสดแบบที่เซสชันพูดสดใช้ ส่วนตัวอย่าง Python ส่งหนึ่งช่วงแบบเดียวกับที่ชุดงานเรนเดอร์ล่วงหน้าทำ และตัวอย่างออปชันแสดงฟิลด์เสียง โมเดล และรูปแบบไฟล์ที่แต่ละฉากตั้งไว้
// Runs on Bun as-is; on Node, `import WebSocket from "ws"` accepts
// the same constructor options. Browsers cannot send this header, and
// API keys never ship to browsers anyway.
const socket = new WebSocket("wss://api.paxalabs.com/v1/tts/live", {
headers: { Authorization: `Bearer ${process.env.PAXA_API_KEY}` },
});
socket.binaryType = "arraybuffer";
socket.onopen = () => {
socket.send(JSON.stringify({
type: "start",
model: "paxa-tts-flash-v1",
voice: "khanomkrok",
timestamps: "word",
}));
// Send text whenever it becomes available, token by token if you like.
socket.send(JSON.stringify({ type: "text", text: "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs" }));
socket.send(JSON.stringify({ type: "flush" }));
socket.send(JSON.stringify({ type: "end" }));
};
socket.onmessage = (event) => {
if (typeof event.data === "string") {
const message = JSON.parse(event.data);
if (message.type === "done") socket.close();
return;
}
player.feed(new Uint8Array(event.data)); // binary frames are audio
};import os
import requests
response = requests.post(
"https://api.paxalabs.com/v1/tts",
headers={"Authorization": f"Bearer {os.environ['PAXA_API_KEY']}"},
json={"text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs", "voice": "khanomkrok", "model": "paxa-tts-flash-v1"},
)
response.raise_for_status()
with open("speech.mp3", "wb") as f:
f.write(response.content)curl -X POST https://api.paxalabs.com/v1/tts \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
"voice": "khanomkrok",
"model": "paxa-tts-flash-v1",
"format": "mp3",
"stream": false
}' \
--output speech.mp3ฟังเสียงจริง
ลูกชุบหญิง0:04
เสียงเล็กแหลมน่ารัก เหมาะกับตัวการ์ตูนและงานสนุก ๆ
สวัสดีค่า วันนี้หนูมีเรื่องน่ารัก ๆ มาเล่าให้ฟังด้วยนะคะ
ขนมครกชาย0:03
เสียงผู้ใหญ่ใจเย็น ไม่เร่งรีบ เสียงชายหลักของทั้งชุด เหมาะกับสารคดี เรื่องเล่าย้อนวัน และงานบรรยาย
สมัยผมยังหนุ่ม แถวนี้ยังเป็นทุ่งนา เดินไปตลาดทีหนึ่งก็ค่อนวันแล้วครับ
นมเย็นหญิง0:06
เสียงสดใสมีพลัง เสียงหญิงหลักของทั้งชุด เหมาะกับโปรโมต คลิปโซเชียล และเสียงพูดในผลิตภัณฑ์ทั่วไป
ยินดีต้อนรับสู่ Paxa Labs ค่ะ เสียงภาษาไทยที่สลับเป็น English ได้กลางประโยค อย่างเป็นธรรมชาติ
ตะโก้หญิง0:05
เสียงสาวเป็นกันเอง เหมาะกับหนังสือเสียง สรุปข่าว และคลิปอธิบาย
ยามเช้าในกรุงเทพฯ แสงแดดอ่อน ๆ ทาบลงบนถนนที่เริ่มคึกคัก
เสียงบางส่วนจากทั้งหมด 26 เสียง สร้างจาก API ด้วยรหัสเสียงที่เปิดให้ใช้งานจริง ฟังครบทุกเสียงได้ที่หน้า Text-to-Speech
บรรทัดสั้นเป็นโจทย์ของมันเอง
นิยายแบบแตกกิ่งเขียนเป็นชิ้นสั้น ๆ ทั้งข้อความแชทหนึ่งบรรทัด คำโต้กลับประโยคเดียว หรือข้อความบนปุ่มให้เลือก ภาษาไทยเขียนติดกันโดยไม่มีช่องว่างระหว่างคำ ชิ้นที่มีแค่สี่คำจึงให้บริบทน้อยมากสำหรับการตัดคำ Paxa TTS Flash อ่านวรรณยุกต์จากตัวสะกดแทนการเปิดพจนานุกรม นี่คือสิ่งที่ทำให้บรรทัดสั้นที่อยู่โดด ๆ ยังฟังรู้เรื่องแทนที่จะเป็นการเดา
คำถามที่พบบ่อย
- ควรเรนเดอร์ทั้งต้นไม้ หรือพูดสดดี
- การเรนเดอร์ล่วงหน้ามีค่าใช้จ่ายราว $2.16 ครั้งเดียวสำหรับผู้อ่านทั้งหมด ส่วนการพูดสดราว $0.16 ต่อผู้อ่านหนึ่งคน ถ้ามีผู้อ่านน้อยกว่าราว 13 คน การพูดสดถูกกว่า มากกว่านั้นการเรนเดอร์ล่วงหน้าถูกกว่า
- ให้ตัวละครแต่ละตัวมีเสียงต่างกันได้ไหม
- ได้ ถึง 26 เสียงตามแคตตาล็อก รหัสเสียงหนึ่งตัวต่อหนึ่งผู้พูดถูกเก็บไว้ในสคริปต์ และรหัสเดิมให้เสียงเดิมทุกครั้งที่รัน
- ถ้าผู้อ่านกดข้ามบรรทัดจะเกิดอะไรขึ้น
- ส่งเฟรม cancel บนซ็อกเก็ตสด บรรทัดนั้นจะหยุด ส่วนที่ค้างในคิวถูกทิ้งโดยไม่คิดเงิน และการเชื่อมต่อเดิมรับบรรทัดถัดไปต่อได้เลย
- ผู้อ่านหนึ่งคนใช้กำลังของแพ็กเกจไปเท่าไร
- การเชื่อมต่อสดที่เปิดอยู่หนึ่งเส้นถือหนึ่งช่องคำขอพร้อมกันตลอดเวลาที่เปิด การติดตั้งแบบพูดสดจึงวัดขนาดจากจำนวนผู้อ่านพร้อมกัน แทนที่จะวัดจากจำนวนบรรทัดต่อวัน
- เอาไฟล์เสียงใส่ในเกมที่ขายได้ไหม
- ผลลัพธ์ที่สร้างด้วยเครดิตฟรีใช้เพื่อการส่วนตัวและการทดลองใช้เท่านั้น เมื่อซื้อแพ็กเกจหรือเครดิตแล้ว ผลลัพธ์ที่สร้างขึ้นเป็นของคุณและใช้ได้ทุกวัตถุประสงค์ รวมถึงการนำไปใส่ในเกมเชิงพาณิชย์ และครอบคลุมถึงผลลัพธ์ที่สร้างไว้ก่อนการซื้อด้วย
เริ่มต้นด้วยเครดิตฟรี
เข้าสู่ระบบด้วย Google, GitHub หรือ Hugging Face แล้วใช้เครดิตฟรี 100 เครดิตกับข้อความของคุณเอง