├── .eslintrc.json ├── .gitignore ├── README.md ├── jsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── demo.png ├── favicon.png ├── og-image-thumbnail.png ├── og-image.png └── og-image.svg ├── src ├── pages │ ├── _app.js │ ├── _document.js │ ├── api │ │ └── hello.js │ └── index.js └── styles │ └── globals.css └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Open-Audio TTS OG Image](public/og-image.png) 2 | 3 | Check out the live website: [OpenAudio.ai](https://openaudio.ai) 4 | 5 | 6 | # Open-Audio TTS 7 | 8 | Open-Audio TTS is a web application that allows users to convert text into natural-sounding speech. Powered by OpenAI's text-to-speech models, this tool offers an intuitive user interface built with Chakra UI, providing a seamless experience for generating and downloading speech audio files. 9 | 10 | ## Features 11 | 12 | - **Text-to-Speech**: Convert any text into speech with high-quality voices powered by OpenAI's TTS models. 13 | - **Customizable Voices**: Choose from a variety of voices to find the one that best suits your needs. 14 | - **Adjustable Speed**: Control the speed of the speech to match your preferred listening pace. 15 | - **BYO**: Bring your Own (BYO) API keys, no data is stored on server side. 16 | - **Downloadable Audio**: Easily download the generated speech as an MP3 file directly from the browser. 17 | - **User-Friendly Interface**: Built with responsiveness in mind, offering a comfortable experience across different devices. 18 | 19 | 20 | ## Deploy on Vercel 21 | 22 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 23 | 24 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/Justmalhar/open-audio) 25 | 26 | ## Installation 27 | 28 | To set up the project locally, follow these steps: 29 | 30 | 1. Clone the repository to your local machine. 31 | 2. Navigate to the project directory. 32 | 3. Install dependencies with `npm install`. 33 | 4. Start the development server with `npm run dev`. 34 | 5. Open `http://localhost:3000` to view it in the browser. 35 | 36 | ## How to use Environment Variable (Disabled and reverted to keep things purely on Client Side) 37 | 1. Copy the `.env.example` file to: 38 | - .env.local file: For Local Development (This should not be committed to Git) - `cp .env.example .env.local` 39 | - .env: For Production Deployment (This can be via Vercel Environment Variables under your Project's settings page. `cp .env.example .env` 40 | 2. Add your OPENAI_API_KEY by creating or copying your OpenAI API key under the [API keys](https://platform.openai.com/api-keys) section on OpenAI's website. 41 | 3. Save, restart your app and run `npm run dev`. Your API key will now be set from the Environment Variables instead of the UI and UI element will be disabled. 42 | 43 | 44 | ## Usage 45 | 46 | To use Open-Audio TTS, simply: 47 | 48 | 1. Enter your OpenAI API key in the provided field. 49 | 2. Type or paste the text you wish to convert into the 'Input Text' field. 50 | 3. Select the voice and adjust the speed as desired. 51 | 4. Click on 'Create Speech' to generate the audio. 52 | 5. Once the audio is generated, use the controls to play it or click 'Download MP3' to save it. 53 | 54 | # Demo 55 | 56 | ![Open-Audio TTS Demo](public/demo.png) 57 | 58 | ## Contributing 59 | 60 | Contributions are welcome! If you have a suggestion or an issue, please use the [issues](#) page to let me know. 61 | 62 | ## License 63 | 64 | This project is licensed under the MIT License - see the [LICENSE](LICENSE.md) file for details. 65 | 66 | ## Acknowledgments 67 | 68 | - Thanks to OpenAI for providing the text-to-speech API. 69 | - Chakra UI for the beautiful component library. 70 | - Speech To Text icon by Icons8 71 | 72 | ## Deploy on Vercel 73 | 74 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 75 | 76 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 77 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-audio", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/icons": "^2.1.1", 13 | "@chakra-ui/react": "^2.8.1", 14 | "@emotion/react": "^11.11.1", 15 | "@emotion/styled": "^11.11.0", 16 | "@vercel/analytics": "^1.1.1", 17 | "file-saver": "^2.0.5", 18 | "framer-motion": "^10.16.4", 19 | "next": "14.0.1", 20 | "openai": "^4.16.1", 21 | "react": "^18", 22 | "react-dom": "^18" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^10.0.1", 26 | "eslint": "^8", 27 | "eslint-config-next": "14.0.1", 28 | "postcss": "^8", 29 | "tailwindcss": "^3.3.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justmalhar/open-audio/6d7b3ac9a0fefb57949c50cf461c352a0860c5cd/public/demo.png -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justmalhar/open-audio/6d7b3ac9a0fefb57949c50cf461c352a0860c5cd/public/favicon.png -------------------------------------------------------------------------------- /public/og-image-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justmalhar/open-audio/6d7b3ac9a0fefb57949c50cf461c352a0860c5cd/public/og-image-thumbnail.png -------------------------------------------------------------------------------- /public/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justmalhar/open-audio/6d7b3ac9a0fefb57949c50cf461c352a0860c5cd/public/og-image.png -------------------------------------------------------------------------------- /public/og-image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css' 2 | import { ChakraProvider, extendTheme } from '@chakra-ui/react'; 3 | 4 | const theme = extendTheme({}); 5 | 6 | 7 | export default function App({ Component, pageProps }) { 8 | return ( 9 | {/* If using a custom theme */} 10 | 11 | 12 | ); 13 | } -------------------------------------------------------------------------------- /src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | import { Analytics } from '@vercel/analytics/react'; 3 | 4 | export default function Document() { 5 | return ( 6 | 7 | 8 | {/* Page Title */} 9 | Open-Audio TTS 10 | 11 | {/* Favicon */} 12 | 13 | 14 | {/* Meta Tags for SEO */} 15 | 16 | 17 | {/* Open Graph / Facebook */} 18 | 19 | 20 | 21 | 22 | 23 | 24 | {/* Twitter */} 25 | 26 | 27 | 28 | 29 | 30 | 31 | {/* Add additional meta tags as needed */} 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /src/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | // pages/index.js 2 | import { 3 | Container, 4 | Flex, 5 | Heading, 6 | FormControl, 7 | FormLabel, 8 | Input, 9 | Textarea, 10 | Select, 11 | Button, 12 | VStack, 13 | HStack, 14 | Slider, 15 | SliderTrack, 16 | SliderFilledTrack, 17 | SliderThumb, 18 | SliderMark, 19 | Text, 20 | useToast, 21 | Spinner, 22 | Grid, 23 | Box, 24 | Tooltip, 25 | Switch, 26 | FormHelperText, 27 | } from "@chakra-ui/react"; 28 | import OpenAI from "openai"; 29 | import { useState, useRef, useEffect } from "react"; 30 | import { saveAs } from "file-saver"; // You will need to install file-saver: npm install file-saver 31 | 32 | export default function Home() { 33 | const [apiKeyInput, setApiKey] = useState(""); 34 | 35 | const [model, setModel] = useState("tts-1"); 36 | const [inputText, setInputText] = useState(""); 37 | const [voice, setVoice] = useState("alloy"); 38 | const [speed, setSpeed] = useState(1); 39 | const [isSubmitting, setIsSubmitting] = useState(false); 40 | const [sliderValue, setSliderValue] = useState(1); 41 | const [showTooltip, setShowTooltip] = useState(false); 42 | const sliderRef = useRef(null); 43 | const [audioUrl, setAudioUrl] = useState(null); 44 | 45 | useEffect(() => { 46 | // Clean up the URL object when the component is unmounted or audioUrl changes 47 | return () => { 48 | if (audioUrl) { 49 | URL.revokeObjectURL(audioUrl); 50 | } 51 | }; 52 | }, [audioUrl]); 53 | 54 | const toast = useToast(); 55 | 56 | const handleModelToggle = () => { 57 | setModel(model === "tts-1" ? "tts-1-hd" : "tts-1"); 58 | }; 59 | 60 | const handleDownload = () => { 61 | saveAs(audioUrl, "speech.mp3"); // This will save the file as "speech.mp3" 62 | }; 63 | 64 | // Assuming `openai.audio.speech.create` returns a stream or binary data 65 | const handleSubmit = async (e) => { 66 | e.preventDefault(); 67 | setIsSubmitting(true); 68 | setAudioUrl(null); 69 | try { 70 | // Define the request headers 71 | const headers = new Headers(); 72 | const apiKey = apiKeyInput; 73 | headers.append("Authorization", `Bearer ${apiKey}`); 74 | headers.append("Content-Type", "application/json"); 75 | 76 | // Define the request body 77 | const body = JSON.stringify({ 78 | model: model, 79 | input: inputText, 80 | voice: voice, 81 | speed: speed.toFixed(1), 82 | }); 83 | 84 | // Make the fetch request to the OpenAI API 85 | const response = await fetch("https://api.openai.com/v1/audio/speech", { 86 | method: "POST", 87 | headers: headers, 88 | body: body, 89 | }); 90 | 91 | console.log(response); 92 | 93 | if (!response.ok) { 94 | throw new Error(`HTTP error! status: ${response.status}`); 95 | } 96 | 97 | // Get the response body as Blob 98 | const blob = await response.blob(); 99 | 100 | // Create a URL for the Blob 101 | const audioUrl = URL.createObjectURL(blob); 102 | 103 | // Update your component's state or context 104 | setAudioUrl(audioUrl); 105 | } catch (error) { 106 | console.error("Error:", error); 107 | toast({ 108 | title: "An error occurred", 109 | description: error.message, 110 | status: "error", 111 | duration: 5000, 112 | isClosable: true, 113 | }); 114 | } finally { 115 | setIsSubmitting(false); 116 | } 117 | }; 118 | 119 | const handleInputChange = (e) => { 120 | if (e.target.value.length <= 4096) { 121 | setInputText(e.target.value); 122 | } 123 | }; 124 | 125 | return ( 126 | 127 | 128 | 135 | 143 | 150 | 157 | 158 | Open-Audio TTS 159 | 160 | 161 | Powered by OpenAI TTS{" "} 162 | 163 | 170 | 176 | View on GitHub 177 | 178 | 179 | 180 | 185 | 186 | API Key 187 | setApiKey(e.target.value)} 193 | variant="outline" 194 | borderColor="black" 195 | /> 196 | 197 | 198 | 199 | 200 | Quality 201 | 202 | 209 | 210 | {model === "tts-1" ? "High" : "HD"} 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | Input Text 219 |