From c144f6962533ab9e25fff32d19eee94020262bda Mon Sep 17 00:00:00 2001 From: Ritesh Prajapati Date: Sat, 4 Oct 2025 22:16:37 +0530 Subject: [PATCH 1/5] Added text-to-speech play --- src/plays/text-to-speech/Readme.md | 27 ++++ src/plays/text-to-speech/TextToSpeech.jsx | 98 ++++++++++++++ src/plays/text-to-speech/cover.png | Bin 0 -> 6356 bytes src/plays/text-to-speech/styles.css | 151 ++++++++++++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 src/plays/text-to-speech/Readme.md create mode 100644 src/plays/text-to-speech/TextToSpeech.jsx create mode 100644 src/plays/text-to-speech/cover.png create mode 100644 src/plays/text-to-speech/styles.css diff --git a/src/plays/text-to-speech/Readme.md b/src/plays/text-to-speech/Readme.md new file mode 100644 index 000000000..53ef63785 --- /dev/null +++ b/src/plays/text-to-speech/Readme.md @@ -0,0 +1,27 @@ +# Text To Speech + +text to speech + +## Play Demographic + +- Language: js +- Level: Intermediate + +## Creator Information + +- User: Ritesh381 +- Gihub Link: https://github.com/Ritesh381 +- Blog: +- Video: + +## Implementation Details + +Update your implementation idea and details here + +## Consideration + +Update all considerations(if any) + +## Resources + +Update external resources(if any) diff --git a/src/plays/text-to-speech/TextToSpeech.jsx b/src/plays/text-to-speech/TextToSpeech.jsx new file mode 100644 index 000000000..8941dff6e --- /dev/null +++ b/src/plays/text-to-speech/TextToSpeech.jsx @@ -0,0 +1,98 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { FaVolumeUp, FaStop } from 'react-icons/fa'; +import PlayHeader from 'common/playlists/PlayHeader'; +import './styles.css'; + +// WARNING: Do not change the entry component name +function TextToSpeech(props) { + const [inputText, setInputText] = useState(''); + const [convertedText, setConvertedText] = useState(''); + const [isSpeaking, setIsSpeaking] = useState(false); + const utteranceRef = useRef(null); + + const stopSpeech = () => { + if (window.speechSynthesis.speaking || window.speechSynthesis.paused) { + window.speechSynthesis.cancel(); + setIsSpeaking(false); + } + }; + + const handleSpeak = () => { + if (isSpeaking) { + stopSpeech(); + + return; + } + + if (!convertedText.trim()) return; + + const utterance = new SpeechSynthesisUtterance(convertedText); + utterance.lang = 'en-US'; + utterance.rate = 1; + utterance.pitch = 1; + + utterance.onend = () => setIsSpeaking(false); + + utteranceRef.current = utterance; + window.speechSynthesis.speak(utterance); + setIsSpeaking(true); + }; + + const handleConvert = () => { + stopSpeech(); + setConvertedText(inputText.trim()); + }; + + // stop on unmount / tab close + useEffect(() => { + const handleBeforeUnload = () => stopSpeech(); + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + stopSpeech(); + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + return ( + <> +
+ +
+ {/* Your Code Starts Here */} +
+ {/* Left side: textarea + button */} +
+