<!DOCTYPE html>
<html>
<head>
<title>OpenAI TTS</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
textarea { width: 90%; height: 200px; margin-bottom: 10px; padding: 10px; }
button { padding: 10px 20px; background-color: #007BFF; color: white; border: none; border-radius: 5px; cursor: pointer; }
button:disabled { background-color: #cccccc; cursor: not-allowed; }
#audioPlayer { margin-top: 10px; width: 90%; }
#message { color: red; margin-top: 10px; }
.config-panel {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 20px;
border-radius: 5px;
}
.config-panel label {
display: block;
margin-bottom: 5px;
}
.config-panel input, .config-panel select {
width: 90%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>OpenAI TTS</h1>
<div class="config-panel">
<h2>配置</h2>
<label for="baseUrl">API基础URL:</label>
<input type="text" id="baseUrl" value="https://api.siliconflow.cn">
<label for="apiKey">API密钥:</label>
<input type="text" id="apiKey">
<label for="model">模型:</label>
<select id="model">
<option value="FunAudioLLM/CosyVoice2-0.5B">FunAudioLLM/CosyVoice2-0.5B</option>
<option value="RVC-Boss/GPT-SoVITS">RVC-Boss/GPT-SoVITS</option>
<option value="LoRA/RVC-Boss/GPT-SoVITS">LoRA/RVC-Boss/GPT-SoVITS</option>
</select>
<label for="voice">语音:</label>
<select id="voice">
<option value="speech:onyx:cm151jhbw0007srjw2455dz6y:pxpgizwsmaxttynlcqye">Onyx</option>
</select>
</div>
<textarea id="textInput" placeholder="在此输入文本..."></textarea><br>
<button id="generateButton">生成语音</button>
<audio id="audioPlayer" controls></audio>
<div id="message"></div>
<script>
const textInput = document.getElementById('textInput');
const generateButton = document.getElementById('generateButton');
const audioPlayer = document.getElementById('audioPlayer');
const messageDiv = document.getElementById('message');
let currentAudioUrl = null;
const baseUrlInput = document.getElementById('baseUrl');
const apiKeyInput = document.getElementById('apiKey');
const modelSelect = document.getElementById('model');
const voiceSelect = document.getElementById('voice');
generateButton.addEventListener('click', () => {
const text = textInput.value.trim();
if (!text) {
messageDiv.textContent = '请输入一些文本。';
return;
}
messageDiv.textContent = '';
generateButton.disabled = true;
generateButton.textContent = '生成中...';
const baseUrl = baseUrlInput.value;
const apiKey = apiKeyInput.value;
const voice = voiceSelect.value;
const model = modelSelect.value;
if (!apiKey || !baseUrl) {
messageDiv.textContent = 'API密钥或基础URL未配置。';
generateButton.disabled = false;
generateButton.textContent = '生成语音';
return;
}
callTTSApi(text, baseUrl, apiKey, voice, model, (error, audioUrl) => {
generateButton.disabled = false;
generateButton.textContent = '生成语音';
if (error) {
messageDiv.textContent = error;
} else {
if (currentAudioUrl) {
URL.revokeObjectURL(currentAudioUrl);
}
audioPlayer.src = audioUrl;
currentAudioUrl = audioUrl;
audioPlayer.play();
}
});
});
function callTTSApi(text, baseUrl, apiKey, voice, model, callback) {
const url = `${baseUrl}/v1/audio/speech`;
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: model,
input: text,
voice: voice
})
})
.then(response => {
if (!response.ok) {
return response.text().then(text => {
throw new Error(`HTTP 错误!状态: ${response.status}, 响应: ${text}`);
});
}
return response.arrayBuffer();
})
.then(buffer => {
const audioBlob = new Blob([buffer], { type: 'audio/mpeg' });
const audioUrl = URL.createObjectURL(audioBlob);
callback(null, audioUrl);
})
.catch(error => {
console.error('Fetch 错误:', error);
callback(`TTS API请求失败:${error.message}`);
});
}
window.addEventListener('beforeunload', () => {
if (currentAudioUrl) {
URL.revokeObjectURL(currentAudioUrl);
}
});
</script>
</body>
</html>
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.