|
import { SHYGUY_LABEL, SISTER_LABEL, GIRL_LABEL, BAR_LABEL, DJ_LABEL, WINGMAN_LABEL } from "./constants.js";
|
|
import { ELEVENLABS_API_KEY } from "../api.js";
|
|
|
|
export class ElevenLabsClient {
|
|
constructor() {
|
|
this.apiKey = ELEVENLABS_API_KEY;
|
|
this.baseUrl = "https://api.elevenlabs.io/v1";
|
|
}
|
|
|
|
static characterToVoiceIdMapping = {
|
|
[SHYGUY_LABEL]: "bGNROVfU5WbK6F0AyHII",
|
|
[SISTER_LABEL]: "XA4HJz0cEOIlrQq0BGwu",
|
|
[GIRL_LABEL]: "XA4HJz0cEOIlrQq0BGwu",
|
|
[BAR_LABEL]: "XA2bIQ92TabjGbpO2xRr",
|
|
[DJ_LABEL]: "T0pkYhIZ7UMOc26gqqeX",
|
|
[WINGMAN_LABEL]: "XA2bIQ92TabjGbpO2xRr",
|
|
};
|
|
|
|
async playAudioForCharacter(character, text) {
|
|
const voiceId = ElevenLabsClient.characterToVoiceIdMapping[character];
|
|
if (!voiceId) {
|
|
throw new Error(`No voice mapping found for character: ${character}`);
|
|
}
|
|
const audioBlob = await this.createSpeech({
|
|
text: text,
|
|
voiceId: voiceId,
|
|
});
|
|
const audioUrl = URL.createObjectURL(audioBlob);
|
|
const audio = new Audio(audioUrl);
|
|
|
|
|
|
return new Promise((res) => {
|
|
audio.play();
|
|
audio.onended = res;
|
|
});
|
|
}
|
|
|
|
async createSpeech({
|
|
text,
|
|
voiceId,
|
|
modelId = "eleven_flash_v2",
|
|
outputFormat = "mp3_44100_128",
|
|
voiceSettings = null,
|
|
pronunciationDictionaryLocators = null,
|
|
seed = null,
|
|
previousText = null,
|
|
nextText = null,
|
|
previousRequestIds = null,
|
|
nextRequestIds = null,
|
|
usePvcAsIvc = false,
|
|
applyTextNormalization = "auto",
|
|
}) {
|
|
const url = `${this.baseUrl}/text-to-speech/${voiceId}?output_format=${outputFormat}`;
|
|
|
|
const requestBody = {
|
|
text,
|
|
model_id: modelId,
|
|
voice_settings: voiceSettings,
|
|
pronunciation_dictionary_locators: pronunciationDictionaryLocators,
|
|
seed,
|
|
previous_text: previousText,
|
|
next_text: nextText,
|
|
previous_request_ids: previousRequestIds,
|
|
next_request_ids: nextRequestIds,
|
|
use_pvc_as_ivc: usePvcAsIvc,
|
|
apply_text_normalization: applyTextNormalization,
|
|
};
|
|
|
|
|
|
Object.keys(requestBody).forEach((key) => requestBody[key] === null && delete requestBody[key]);
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
"xi-api-key": this.apiKey,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(requestBody),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`ElevenLabs API error: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
|
|
return await response.blob();
|
|
} catch (error) {
|
|
throw new Error(`Failed to create speech: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|