speed slider

modified:   index.html
	modified:   uncloseai.js
This commit is contained in:
root 2024-11-13 12:35:03 +00:00
parent b5a6f75627
commit 292c94cc67
2 changed files with 32 additions and 103 deletions

View file

@ -204,6 +204,7 @@ client = openai.OpenAI(
with client.audio.speech.with_streaming_response.create(
model="tts-1",
voice="alloy",
speed=0.8,
input="I think so therefore, Today is a wonderful day to build something people love!"
) as response:
response.stream_to_file("speech.mp3")
@ -222,6 +223,7 @@ async function getSpeech() {
const response = await client.audio.speech.with_streaming_response.create({
model: "tts-1",
voice: "alloy",
speed: 0.8,
input: "I think so therefore, Today is a wonderful day to build something people love!"
});

View file

@ -77,106 +77,8 @@ function extractWebpageContent() {
return content.trim();
}
// Function to compute SHA-256 hash
async function sha256(message) {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex;
}
// Function to open IndexedDB
function openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('TTSCache', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore('audio', { keyPath: 'hash' });
};
request.onsuccess = (event) => {
resolve(event.target.result);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// Function to get audio from IndexedDB
async function getAudioFromCache(hash) {
const db = await openDatabase();
return new Promise((resolve, reject) => {
const transaction = db.transaction('audio', 'readonly');
const store = transaction.objectStore('audio');
const request = store.get(hash);
request.onsuccess = (event) => {
resolve(event.target.result ? event.target.result.audioData : null);
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// Function to save audio to IndexedDB
async function saveAudioToCache(hash, audioData) {
const db = await openDatabase();
return new Promise((resolve, reject) => {
const transaction = db.transaction('audio', 'readwrite');
const store = transaction.objectStore('audio');
const request = store.put({ hash, audioData });
request.onsuccess = () => {
resolve();
};
request.onerror = (event) => {
reject(event.target.error);
};
});
}
// Function to convert Blob to Base64
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
// Function to convert Base64 to Blob
function base64ToBlob(base64) {
const byteString = atob(base64.split(',')[1]);
const mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeString });
}
// Function to read text using TTS
async function speakText(text, voice = 'alloy') {
const textHash = await sha256(text + voice); // Use text and voice for hashing
const cachedAudioData = await getAudioFromCache(textHash);
if (cachedAudioData) {
const audioBlob = base64ToBlob(cachedAudioData);
const audioUrl = URL.createObjectURL(audioBlob);
return { audio: new Audio(audioUrl), blob: audioBlob };
}
async function speakText(text, voice = 'alloy', rate = 0.8) {
try {
const response = await fetch(TTS_API_URL, {
method: 'POST',
@ -196,10 +98,10 @@ async function speakText(text, voice = 'alloy') {
}
const audioBlob = await response.blob();
const audioData = await blobToBase64(audioBlob);
await saveAudioToCache(textHash, audioData); // Cache the audio data
const audioUrl = URL.createObjectURL(audioBlob);
return { audio: new Audio(audioUrl), blob: audioBlob };
const audio = new Audio(audioUrl);
audio.playbackRate = rate;
return { audio, blob: audioBlob };
} catch (error) {
console.error('Error in TTS:', error);
throw error;
@ -562,16 +464,41 @@ async function openTTSModal() {
});
article.appendChild(voiceSelection);
// Speed selection
const speedLabel = document.createElement('label');
const speedValue = document.createElement('span');
speedValue.textContent = '0.8'; // initial value
speedLabel.textContent = `Speed: ${speedValue.textContent}`;
const speedSlider = document.createElement('input');
speedSlider.type = 'range';
speedSlider.min = '0.25';
speedSlider.max = '4.0';
speedSlider.step = '0.05';
speedSlider.value = '0.8';
speedSlider.oninput = () => {
speedValue.textContent = speedSlider.value;
speedLabel.textContent = `Speed: ${speedSlider.value}`;
};
article.appendChild(speedLabel);
article.appendChild(speedSlider);
const playButton = document.createElement('button');
playButton.textContent = 'Play Text';
playButton.onclick = async () => {
const currentText = textArea.value.trim();
const selectedVoice = document.querySelector('input[name="voice"]:checked').value;
const selectedSpeed = parseFloat(speedSlider.value);
if (currentText !== lastTTSInput || selectedVoice !== lastTTSResult?.voice) {
if (lastTTSResult?.audio) {
lastTTSResult.audio.pause(); // Stop previous audio
}
playButton.textContent = 'Processing...';
playButton.disabled = true; // Disable button while processing
lastTTSInput = currentText;
lastTTSResult = await speakText(currentText, selectedVoice);
lastTTSResult = await speakText(currentText, selectedVoice, selectedSpeed);
lastTTSResult.voice = selectedVoice;
playButton.textContent = 'Pause Text';
playButton.disabled = false; // Re-enable button after processing