diff options
| author | polwex <polwex@sortug.com> | 2025-11-23 01:12:53 +0700 |
|---|---|---|
| committer | polwex <polwex@sortug.com> | 2025-11-23 01:12:53 +0700 |
| commit | cb1b56f5a0eddbf77446f415f2beda57c8305f85 (patch) | |
| tree | d333ca5c143063af8ee1b2f9e2d1d25f8ef2007c /packages/ai/src/tts | |
wut
Diffstat (limited to 'packages/ai/src/tts')
| -rw-r--r-- | packages/ai/src/tts/eleven.ts | 20 | ||||
| -rw-r--r-- | packages/ai/src/tts/minimax.ts | 107 | ||||
| -rw-r--r-- | packages/ai/src/tts/output.mp3 | bin | 0 -> 1020960 bytes |
3 files changed, 127 insertions, 0 deletions
diff --git a/packages/ai/src/tts/eleven.ts b/packages/ai/src/tts/eleven.ts new file mode 100644 index 0000000..c870b11 --- /dev/null +++ b/packages/ai/src/tts/eleven.ts @@ -0,0 +1,20 @@ +import { ElevenLabsClient, play } from "@elevenlabs/elevenlabs-js"; + +const elevenlabs = new ElevenLabsClient({ + apiKey: Bun.env.ELEVEN_KEY!, // Defaults to process.env.ELEVENLABS_API_KEY +}); + +const models = await elevenlabs.models.list(); +for (const model of models) { + const langs = model.languages || []; + for (const lang of langs) { + if (lang.name === "Thai") console.log(model.modelId); + } +} +// ONLY eleven_v3 has Thai! +// const audio = await elevenlabs.textToSpeech.convert("Xb7hH8MSUJpSbSDYk0k2", { +// text: "Hello! 你好! Hola! नमस्ते! Bonjour! こんにちは! مرحبا! 안녕하세요! Ciao! Cześć! Привіт! வணக்கம்!", +// modelId: "eleven_multilingual_v2", +// }); + +// await play(audio); diff --git a/packages/ai/src/tts/minimax.ts b/packages/ai/src/tts/minimax.ts new file mode 100644 index 0000000..4421f94 --- /dev/null +++ b/packages/ai/src/tts/minimax.ts @@ -0,0 +1,107 @@ +// https://platform.minimax.io/docs/api-reference/speech-t2a-async-create/ +// +// +// + +const text = `สำนักข่าวต่างประเทศรายงานเมื่อ 18 พ.ย. 2568 ว่า เจ้าหน้าที่กู้ภัยของประเทศชิลี กำลังดำเนินการค้นหากลุ่มนักท่องเที่ยวที่สูญหายไปในพายุหิมะรุนแรงซึ่งเกิดขึ้นที่ อุทยานแห่งชาติ “ตอร์เรส เดล ไพเน” ในภูมิภาคปาตาโกเนีย ทางตอนใต้ของชิลี หลังพายุทำให้มีผู้เสียชีวิตแล้วอย่างน้อย 5 ศพ`; +// const text = `So I start using it for my project and after about 20 mins - oh, no. Out of credits. +// I didn't even get to try a single Gemini 3 prompt. I was out of credits before my first had completed. I guess I've burned through the free tier in some other app but the error message gave me no clues. As far as I can tell there's no link to give Google my money in the app. Maybe they think they have enough. + +// After switching to gpt-oss:120b it did some things quite well, and the annotation feature in the plan doc is really nice. It has potential but I suspect it's suffering from Google's typical problem that it's only really been tested on Googlers.`; +const model = "speech-2.6-hd"; +const voice1 = "Thai_male_1_sample8"; +const voice2 = "Thai_male_2_sample2"; +const voice3 = "Thai_female_1_sample1"; +const voice4 = "Thai_female_2_sample2"; +const params = { + model, + language_boost: "auto", + voice_setting: { voice_id: voice1, speed: 1, vol: 1, pitch: 1 }, + pronunciation_dct: { tone: ["lol, lmao"] }, + audio_setting: { + audio_sample_rate: 32000, + bitrate: 128_000, + format: "mp3", + channel: 2, + }, + voice_modify: { + pitch: 0, + intensity: 0, + timbre: 0, + sound_Effects: "spacious_echo", + }, +}; + +async function getVoices() { + const endpoint = "/get_voice"; + const body = { voice_type: "all" }; + return await post(endpoint, body); +} +async function tts() { + const endpoint = "/t2a_v2"; + const body = { text, stream: false, ...params }; + return await post(endpoint, body); +} +async function ws() { + const url = "wss://api.minimax.io/ws/v1/t2a_v2"; + const event = "task_start"; + + const headers = { + Authorization: `Bearer ${Bun.env.MINIMAX_API_KEY!}`, + }; + const socket = new WebSocket(url, { headers }); + const body = { event, ...params }; + const body2 = { event: "task_continue", text }; + socket.send(JSON.stringify(body)); + // const event = "task_continue"; + // const event = "task_finish"; +} +async function tts_async() { + const body = { + text, + ...params, + }; + return await post("/t2a_async_v2", body); +} +async function post(path: string, body: any) { + const url = "https://api.minimax.io/v1" + path; + const options = { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${Bun.env.MINIMAX_API_KEY2!}`, + }, + body: JSON.stringify(body), + }; + + try { + const response = await fetch(url, options); + const data = await response.json(); + return data; + } catch (error) { + console.error(error); + } +} +async function get(path: string) { + const url = "https://api.minimax.io/v1" + path; + const options = { + headers: { + Authorization: `Bearer ${Bun.env.MINIMAX_API_KEY!}`, + }, + }; + + try { + const response = await fetch(url, options); + const data = await response.json(); + console.log(data); + } catch (error) { + console.error(error); + } +} + +import fs from "node:fs"; +const res = await tts(); +const audio = res.data.audio; +const audioBuffer = Buffer.from(audio, "hex"); +const filename = "output.mp3"; +fs.writeFileSync(filename, audioBuffer); diff --git a/packages/ai/src/tts/output.mp3 b/packages/ai/src/tts/output.mp3 Binary files differnew file mode 100644 index 0000000..9f22e3a --- /dev/null +++ b/packages/ai/src/tts/output.mp3 |
