Spaces:
Running
Running
| 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); | |
| // hack to wait for the audio to finish playing | |
| 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, | |
| }; | |
| // Remove null values from request body | |
| 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 audio blob | |
| return await response.blob(); | |
| } catch (error) { | |
| throw new Error(`Failed to create speech: ${error.message}`); | |
| } | |
| } | |
| } | |