Skip to content

Commit

Permalink
chore: tts
Browse files Browse the repository at this point in the history
Signed-off-by: Neko Ayaka <[email protected]>
  • Loading branch information
nekomeowww committed Dec 2, 2024
1 parent f2fe7a8 commit 912aa25
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 32 deletions.
40 changes: 29 additions & 11 deletions .nuxt/dev/index.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import process from 'node:process';globalThis._importMeta_={url:import.meta.url,env:process.env};import { getRequestHeader, splitCookiesString, setResponseStatus, setResponseHeader, send, getRequestHeaders, defineEventHandler, handleCacheHeaders, createEvent, fetchWithEvent, isEvent, eventHandler, setHeaders, sendRedirect, proxyRequest, createApp, createRouter as createRouter$1, toNodeListener, lazyEventHandler, getResponseStatus, setResponseHeaders, createError, getRouterParam, getQuery as getQuery$1, readBody, getResponseStatusText } from 'file:///Users/neko/Git/nekomeowww/airi-vtuber/node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs';
import process from 'node:process';globalThis._importMeta_={url:import.meta.url,env:process.env};import { getRequestHeader, splitCookiesString, setResponseStatus, setResponseHeader, send, getRequestHeaders, defineEventHandler, handleCacheHeaders, createEvent, fetchWithEvent, isEvent, eventHandler, setHeaders, sendRedirect, proxyRequest, createApp, createRouter as createRouter$1, toNodeListener, lazyEventHandler, getResponseStatus, setResponseHeaders, createError, getRouterParam, getQuery as getQuery$1, readBody, sendStream, getResponseStatusText } from 'file:///Users/neko/Git/nekomeowww/airi-vtuber/node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs';
import { Server } from 'node:http';
import { mkdirSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { parentPort, threadId } from 'node:worker_threads';
import { ElevenLabsClient } from 'file:///Users/neko/Git/nekomeowww/airi-vtuber/node_modules/.pnpm/[email protected]/node_modules/elevenlabs/index.js';
import { getRequestDependencies, getPreloadLinks, getPrefetchLinks, createRenderer } from 'file:///Users/neko/Git/nekomeowww/airi-vtuber/node_modules/.pnpm/[email protected]/node_modules/vue-bundle-renderer/dist/runtime.mjs';
import { stringify, uneval } from 'file:///Users/neko/Git/nekomeowww/airi-vtuber/node_modules/.pnpm/[email protected]/node_modules/devalue/index.js';
import destr from 'file:///Users/neko/Git/nekomeowww/airi-vtuber/node_modules/.pnpm/[email protected]/node_modules/destr/dist/index.mjs';
Expand Down Expand Up @@ -280,11 +281,11 @@ _FqXo62o6um,
_XwmoL2xytk
];

const _lazy_RvIs71 = () => Promise.resolve().then(function () { return pageview$1; });
const _lazy_Gb4bMB = () => Promise.resolve().then(function () { return tts$1; });
const _lazy_gokVod = () => Promise.resolve().then(function () { return renderer$1; });

const handlers = [
{ route: '/api/pageview', handler: _lazy_RvIs71, lazy: true, middleware: false, method: undefined },
{ route: '/api/v1/llm/tts', handler: _lazy_Gb4bMB, lazy: true, middleware: false, method: undefined },
{ route: '/__nuxt_error', handler: _lazy_gokVod, lazy: true, middleware: false, method: undefined },
{ route: '/**', handler: _lazy_gokVod, lazy: true, middleware: false, method: undefined }
];
Expand Down Expand Up @@ -1103,16 +1104,33 @@ const errorDev = /*#__PURE__*/Object.freeze({
template: template$1
});

const startAt = Date.now();
let count = 0;
const pageview = defineEventHandler(() => ({
pageview: count++,
startAt
}));
const tts = defineEventHandler(async (event) => {
const body = await readBody(event);
const client = new ElevenLabsClient({
apiKey: ""
});
const res = await client.generate({
// voice: 'ShanShan',
// Quite good for English
voice: "Myriam",
// Beatrice is not 'childish' like the others
// voice: 'Beatrice',
text: body.text,
stream: true,
model_id: "eleven_multilingual_v2",
voice_settings: {
stability: 0.4,
similarity_boost: 0.5
}
});
event.node.res.setHeader("Content-Type", "audio/mpeg");
event.node.res.setHeader("Transfer-Encoding", "chunked");
return sendStream(event, res);
});

const pageview$1 = /*#__PURE__*/Object.freeze({
const tts$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
default: pageview
default: tts
});

const Vue3 = version[0] === "3";
Expand Down
2 changes: 1 addition & 1 deletion .nuxt/dev/index.mjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .nuxt/manifest/latest.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"id":"dev","timestamp":1733141803112}
{"id":"dev","timestamp":1733143340735}
2 changes: 1 addition & 1 deletion .nuxt/manifest/meta/dev.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"id":"dev","timestamp":1733141803112,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
{"id":"dev","timestamp":1733143340735,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
6 changes: 3 additions & 3 deletions .nuxt/nitro.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"date": "2024-12-02T12:16:47.771Z",
"date": "2024-12-02T12:42:25.973Z",
"preset": "nitro-dev",
"framework": {
"name": "nuxt",
Expand All @@ -9,9 +9,9 @@
"nitro": "2.10.4"
},
"dev": {
"pid": 53008,
"pid": 78949,
"workerAddress": {
"socketPath": "/var/folders/m0/k_38ftb53yg0mqbcrrjypr3m0000gn/T/nitro/worker-53008-2.sock"
"socketPath": "/var/folders/m0/k_38ftb53yg0mqbcrrjypr3m0000gn/T/nitro/worker-78949-2.sock"
}
}
}
2 changes: 1 addition & 1 deletion .nuxt/nuxt.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
/// <reference types="@pinia/nuxt" />
/// <reference types="@nuxt/eslint" />
/// <reference types="@vite-pwa/nuxt" />
/// <reference types="@nuxtjs/color-mode" />
/// <reference types="@nuxt/telemetry" />
/// <reference types="@nuxt/devtools" />
/// <reference types="@nuxtjs/color-mode" />
/// <reference types="nuxt" />
/// <reference path="types/app-defaults.d.ts" />
/// <reference path="types/plugins.d.ts" />
Expand Down
6 changes: 3 additions & 3 deletions .nuxt/types/nitro-routes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import type { Serialize, Simplify } from "nitropack/types";
declare module "nitropack/types" {
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
interface InternalApi {
'/api/pageview': {
'default': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/pageview').default>>>>
'/api/v1/llm/tts': {
'default': Simplify<Serialize<Awaited<ReturnType<typeof import('../../server/api/v1/llm/voice/text-to-speech').default>>>>
}
'/__nuxt_error': {
'default': Simplify<Serialize<Awaited<ReturnType<typeof import('../../node_modules/.pnpm/[email protected]_@[email protected]_@[email protected][email protected][email protected]__ioredis@5._dnsf4ozg37sim4qflnckqfichq/node_modules/nuxt/dist/core/runtime/nitro/renderer').default>>>>
}
}
}
export {}
export {}
14 changes: 11 additions & 3 deletions app/components/MainStage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const nowSpeakingAvatarBorderOpacityMax = 100
const openAiApiKey = useLocalStorage('openai-api-key', '')
const openAiApiBaseURL = useLocalStorage('openai-api-base-url', '')
const openAIModel = useLocalStorage<{ id: string, name?: string }>('openai-model', { id: 'openai/gpt-3.5-turbo', name: 'OpenAI GPT3.5 Turbo' })
const elevenLabsApiKey = useLocalStorage('elevenlabs-api-key', '')
const { setupOpenAI, streamSpeech, stream, models } = useLLM()
const { audioContext, calculateVolume } = useAudioContext()
Expand Down Expand Up @@ -102,7 +103,7 @@ const ttsQueue = useQueue<string>({
handlers: [
async (ctx) => {
const now = Date.now()
const res = await streamSpeech(ctx.data)
const res = await streamSpeech(ctx.data, elevenLabsApiKey.value)
const elapsed = Date.now() - now
// eslint-disable-next-line no-console
Expand Down Expand Up @@ -253,6 +254,13 @@ onUnmounted(() => {
<template>
<div max-h="[100vh]" h-full p="2" flex="~ col">
<div space-x="2" flex="~ row" w-full>
<div flex="~ row" w-full>
<input
v-model="openAiApiBaseURL"
placeholder="Input your API base URL"
p="2" bg="zinc-100 dark:zinc-700" w-full rounded-lg outline-none
>
</div>
<div flex="~ row" w-full>
<input
v-model="openAiApiKey"
Expand All @@ -262,8 +270,8 @@ onUnmounted(() => {
</div>
<div flex="~ row" w-full>
<input
v-model="openAiApiBaseURL"
placeholder="Input your API base URL"
v-model="elevenLabsApiKey"
placeholder="Input your ElevenLabs API key"
p="2" bg="zinc-100 dark:zinc-700" w-full rounded-lg outline-none
>
</div>
Expand Down
3 changes: 2 additions & 1 deletion app/stores/llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ export const useLLM = defineStore('llm', () => {
return await openAI.value.models.list()
}

async function streamSpeech(text: string) {
async function streamSpeech(text: string, apiKey: string) {
if (!text || !text.trim())
throw new Error('Text is required')

return await ofetch('/api/v1/llm/voice/text-to-speech', {
body: {
text,
apiKey,
},
method: 'POST',
cache: 'no-cache',
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,8 @@
"vue-tsc": "^2.1.10",
"yauzl": "^3.2.0",
"zod": "^3.23.8"
},
"dependencies": {

Check failure on line 66 in package.json

View workflow job for this annotation

GitHub Actions / lint

Expected object keys to be in specified order. 'dependencies' should be before 'devDependencies'
"elevenlabs": "^0.18.1"
}
}
Loading

0 comments on commit 912aa25

Please sign in to comment.