├── .npmignore ├── .gitignore ├── .babelrc ├── webpack.production.config.js ├── demo ├── dist │ └── index.html ├── build │ ├── index.html │ └── demo.bundle.js └── demo.js ├── webpack.config.js ├── package.json ├── src ├── utils.js └── speak-tts.js ├── lib ├── utils.js ├── speak-tts.js └── ios.js └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | npm-debug.log -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } -------------------------------------------------------------------------------- /webpack.production.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | var path = require('path'); 3 | 4 | var config = { 5 | entry: { 6 | demo: [ path.resolve(__dirname, 'demo/demo.js') ], 7 | }, 8 | output: { 9 | path: path.resolve(__dirname, 'demo/build'), 10 | filename: '[name].bundle.js' 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.(js)$/, 16 | exclude: /node_modules/, 17 | use: ['babel-loader'] 18 | } 19 | ] 20 | }, 21 | resolve: { 22 | extensions: ['*', '.js'] 23 | } 24 | }; 25 | 26 | module.exports = config; -------------------------------------------------------------------------------- /demo/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speak-tts 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Speech synthesis

13 | 14 |

15 | 16 |
17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | var path = require('path'); 3 | 4 | var config = { 5 | entry: { 6 | demo: [ path.resolve(__dirname, 'demo/demo.js') ], 7 | }, 8 | output: { 9 | path: path.resolve(__dirname, 'demo/build'), 10 | filename: '[name].bundle.js' 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.(js)$/, 16 | exclude: /node_modules/, 17 | use: ['babel-loader'] 18 | } 19 | ] 20 | }, 21 | resolve: { 22 | extensions: ['*', '.js'] 23 | }, 24 | devServer: { 25 | contentBase: path.resolve(__dirname, 'demo/build') 26 | } 27 | }; 28 | 29 | module.exports = config; -------------------------------------------------------------------------------- /demo/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Speak-tts 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Speech synthesis

13 | 14 |

15 | 16 |
17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speak-tts", 3 | "version": "2.0.8", 4 | "description": "Browser TTS (using Web speech API) made easy", 5 | "main": "lib/speak-tts.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --devtool eval --progress --colors --content-base demo/build --hot --inline --mode development", 8 | "deploy": "NODE_ENV=production webpack -p --config webpack.production.config.js --mode production", 9 | "compile": "npx babel -d lib/ src/", 10 | "prepublish": "npm run compile" 11 | }, 12 | "author": "tom-s", 13 | "homepage": "https://github.com/tom-s/speak-tts#readme", 14 | "keywords": [ 15 | "web speech api", 16 | "speech synthesis", 17 | "speak", 18 | "speechSynthesis", 19 | "SpeechSynthesisUtterance", 20 | "tts", 21 | "text to speech", 22 | "browser speech", 23 | "browser tts" 24 | ], 25 | "license": "MIT", 26 | "devDependencies": { 27 | "@babel/cli": "^7.1.2", 28 | "@babel/core": "^7.1.2", 29 | "@babel/preset-env": "^7.1.0", 30 | "babel-loader": "^8.0.4", 31 | "json-loader": "^0.5.4", 32 | "webpack": "^4.20.2", 33 | "webpack-cli": "^3.1.2", 34 | "webpack-dev-server": "^3.1.9" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export const splitSentences = (text = '') => text.replace(/\.+/g,'.|') 2 | .replace(/\?/g,'?|') 3 | .replace(/\!/g,'!|') 4 | .split("|") 5 | .map(sentence => trim(sentence)) 6 | .filter(Boolean) 7 | 8 | const bcp47LocalePattern = /^(?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))$|^((?:[a-z]{2,3}(?:(?:-[a-z]{3}){1,3})?)|[a-z]{4}|[a-z]{5,8})(?:-([a-z]{4}))?(?:-([a-z]{2}|\d{3}))?((?:-(?:[\da-z]{5,8}|\d[\da-z]{3}))*)?((?:-[\da-wy-z](?:-[\da-z]{2,8})+)*)?(-x(?:-[\da-z]{1,8})+)?$|^(x(?:-[\da-z]{1,8})+)$/i; // eslint-disable-line max-len 9 | 10 | /** 11 | * Validate a locale string to test if it is bcp47 compliant 12 | * @param {String} locale The tag locale to parse 13 | * @return {Boolean} True if tag is bcp47 compliant false otherwise 14 | */ 15 | export const validateLocale = locale => (typeof locale !== 'string') 16 | ? false 17 | : bcp47LocalePattern.test(locale) 18 | 19 | export const isString = value => 20 | typeof value === 'string' || value instanceof String 21 | 22 | export const size = value => value && Array.isArray(value) && value.length 23 | ? value.length 24 | : 0 25 | 26 | export const isNan = value => 27 | typeof value === "number" && isNaN(value) 28 | 29 | export const isNil = value => 30 | value === null || value === undefined 31 | 32 | export const isObject = value => 33 | Object.prototype.toString.call(value) === '[object Object]' 34 | 35 | export const trim = value => isString(value) 36 | ? value.trim() 37 | : '' 38 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.trim = exports.isObject = exports.isNil = exports.isNan = exports.size = exports.isString = exports.validateLocale = exports.splitSentences = void 0; 7 | 8 | var splitSentences = function splitSentences() { 9 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 10 | return text.replace(/\.+/g, '.|').replace(/\?/g, '?|').replace(/\!/g, '!|').split("|").map(function (sentence) { 11 | return trim(sentence); 12 | }).filter(Boolean); 13 | }; 14 | 15 | exports.splitSentences = splitSentences; 16 | var bcp47LocalePattern = /^(?:(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))$|^((?:[a-z]{2,3}(?:(?:-[a-z]{3}){1,3})?)|[a-z]{4}|[a-z]{5,8})(?:-([a-z]{4}))?(?:-([a-z]{2}|\d{3}))?((?:-(?:[\da-z]{5,8}|\d[\da-z]{3}))*)?((?:-[\da-wy-z](?:-[\da-z]{2,8})+)*)?(-x(?:-[\da-z]{1,8})+)?$|^(x(?:-[\da-z]{1,8})+)$/i; // eslint-disable-line max-len 17 | 18 | /** 19 | * Validate a locale string to test if it is bcp47 compliant 20 | * @param {String} locale The tag locale to parse 21 | * @return {Boolean} True if tag is bcp47 compliant false otherwise 22 | */ 23 | 24 | var validateLocale = function validateLocale(locale) { 25 | return typeof locale !== 'string' ? false : bcp47LocalePattern.test(locale); 26 | }; 27 | 28 | exports.validateLocale = validateLocale; 29 | 30 | var isString = function isString(value) { 31 | return typeof value === 'string' || value instanceof String; 32 | }; 33 | 34 | exports.isString = isString; 35 | 36 | var size = function size(value) { 37 | return value && Array.isArray(value) && value.length ? value.length : 0; 38 | }; 39 | 40 | exports.size = size; 41 | 42 | var isNan = function isNan(value) { 43 | return typeof value === "number" && isNaN(value); 44 | }; 45 | 46 | exports.isNan = isNan; 47 | 48 | var isNil = function isNil(value) { 49 | return value === null || value === undefined; 50 | }; 51 | 52 | exports.isNil = isNil; 53 | 54 | var isObject = function isObject(value) { 55 | return Object.prototype.toString.call(value) === '[object Object]'; 56 | }; 57 | 58 | exports.isObject = isObject; 59 | 60 | var trim = function trim(value) { 61 | return isString(value) ? value.trim() : ''; 62 | }; 63 | 64 | exports.trim = trim; -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | import Speech from '../src/speak-tts.js' 2 | 3 | const _addVoicesList = (voices) => { 4 | const list = window.document.createElement('div') 5 | let html = '

Available Voices

';t.forEach(function(t){n+='")}),e.innerHTML=n,window.document.body.appendChild(e)};!function(){var t=new S;t.init({volume:.5,lang:"en-GB",rate:1,pitch:1,listeners:{onvoiceschanged:function(t){console.log("Voices changed",t)}}}).then(function(e){console.log("Speech is ready",e),O(e.voices),function(t){var e=document.getElementById("play"),n=document.getElementById("pause"),r=document.getElementById("resume"),o=document.getElementById("text"),u=document.getElementById("languages");e.addEventListener("click",function(){var e=u.value,n=u.options[u.selectedIndex].dataset.name;e&&t.setLanguage(u.value),n&&t.setVoice(n),t.speak({text:o.value,queue:!1,listeners:{onstart:function(){console.log("Start utterance")},onend:function(){console.log("End utterance")},onresume:function(){console.log("Resume utterance")},onboundary:function(t){console.log(t.name+" boundary reached after "+t.elapsedTime+" milliseconds.")}}}).then(function(t){console.log("Success !",t)}).catch(function(t){console.error("An error occurred :",t)})}),n.addEventListener("click",function(){t.pause()}),r.addEventListener("click",function(){t.resume()})}(t)}).catch(function(t){console.error("An error occured while initializing : ",t)});var e=t.hasBrowserSupport()?"Hurray, your browser supports speech synthesis":"Your browser does NOT support speech synthesis. Try using Chrome of Safari instead !";document.getElementById("support").innerHTML=e}()}]); --------------------------------------------------------------------------------