├── .gitignore ├── BingTTSGen.py ├── README.md ├── bingtts └── __init__.py ├── freeswitch └── tts_commandline.conf.xml ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/settings.json 2 | *.pyc -------------------------------------------------------------------------------- /BingTTSGen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import print_function 4 | import hashlib 5 | import os 6 | import sys 7 | import argparse 8 | import shutil 9 | 10 | # Define a few maps for reference 11 | namemap = { 12 | "ar-EG" : ["Hoda"], 13 | "ar-SA" : ["Naayf"], 14 | "bg-BG" : ["Ivan"], 15 | "ca-ES" : ["HerenaRUS"], 16 | "cs-CZ" : ["Jakub"], 17 | "da-DK" : ["HelleRUS"], 18 | "de-AT" : ["Michael"], 19 | "de-CH" : ["Karsten"], 20 | "de-DE" : ["Hedda", "HeddaRUS", "Stefan, Apollo"], 21 | "el-GR" : ["Stefanos"], 22 | "en-AU" : ["Catherine", "HayleyRUS"], 23 | "en-CA" : ["Linda", "HeatherRUS"], 24 | "en-GB" : ["Susan, Apollo", "HazelRUS", "George, Apollo"], 25 | "en-IE" : ["Sean"], 26 | "en-IN" : ["Heera, Apollo", "PriyaRUS", "Ravi, Apollo"], 27 | "en-US" : ["ZiraRUS", "JessaRUS", "BenjaminRUS"], 28 | "es-ES" : ["Laura, Apollo", "HelenaRUS", "Pablo, Apollo"], 29 | "es-MX" : ["HildaRUS", "Raul, Apollo"], 30 | "fi-FI" : ["HeidiRUS"], 31 | "fr-CA" : ["Caroline", "HarmonieRUS"], 32 | "fr-CH" : ["Guillaume"], 33 | "fr-FR" : ["Julie, Apollo", "HortenseRUS", "Paul, Apollo"], 34 | "he-IL" : ["Asaf"], 35 | "hi-IN" : ["Kalpana, Apollo", "Kalpana", "Hemant"], 36 | "hr-HR" : ["Matej"], 37 | "hu-HU" : ["Szabolcs"], 38 | "id-ID" : ["Andika"], 39 | "it-IT" : ["Cosimo, Apollo","LuciaRUS"], 40 | "ja-JP" : ["Ayumi, Apollo", "Ichiro, Apollo", "HarukaRUS"], 41 | "ko-KR" : ["HeamiRUS"], 42 | "ms-MY" : ["Rizwan"], 43 | "nb-NO" : ["HuldaRUS"], 44 | "nl-NL" : ["HannaRUS"], 45 | "pl-PL" : ["PaulinaRUS"], 46 | "pt-BR" : ["HeloisaRUS", "Daniel, Apollo"], 47 | "pt-PT" : ["HeliaRUS"], 48 | "ro-RO" : ["Andrei"], 49 | "ru-RU" : ["Irina, Apollo", "Pavel, Apollo","EkaterinaRUS"], 50 | "sk-SK" : ["Filip"], 51 | "sl-SI" : ["Lado"], 52 | "sv-SE" : ["HedvigRUS"], 53 | "ta-IN" : ["Valluvar"], 54 | "th-TH" : ["Pattara"], 55 | "tr-TR" : ["SedaRUS"], 56 | "vi-VN" : ["An"], 57 | "zh-CN" : ["HuihuiRUS", "Yaoyao, Apollo", "Kangkang, Apollo"], 58 | "zh-HK" : ["Tracy, Apollo", "TracyRUS", "Danny, Apollo"], 59 | "zh-TW" : ["Yating, Apollo", "HanHanRUS", "Zhiwei, Apollo"] 60 | } 61 | 62 | formatmap = { 63 | "raw-8khz-8bit-mono-mulaw" : None, 64 | "raw-16khz-16bit-mono-pcm" : None, 65 | "riff-8khz-8bit-mono-mulaw" : None, 66 | "riff-16khz-16bit-mono-pcm" : None 67 | } 68 | 69 | # Get config from CLI args 70 | i = 0 71 | argsDict = {} 72 | for item in sys.argv: 73 | if i == 0: 74 | i = i + 1 75 | pass 76 | else: 77 | i = i + 1 78 | paramname, paramval = item.partition( 79 | "=" 80 | )[::2] 81 | argsDict[paramname] = paramval 82 | 83 | try: 84 | helpReq = argsDict['--help'] 85 | print ("") 86 | print ( 87 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 88 | sys.argv[0] 89 | ) 90 | ) 91 | print ("") 92 | print ("--cache : Location of file cache. Useful if you repeatedly request") 93 | print (" the same text to speech and wish to conserve bandwidth") 94 | print (" and reduce Azure costs. Must be writeable.") 95 | print ("--dest : Location for the output file. Must be writeable.") 96 | print ("--lang : Language to be spoken. Case sensitive.") 97 | print ("--voice : Voice to be used. Case sensitive.") 98 | print ("--fileformat : Format of the output file.") 99 | print ("--apikey : API key generated by Azure.") 100 | print ("--text : Text to convert to speech. MUST be enclosed in double quotes.") 101 | print ("") 102 | print ("Available language combinations (--lang --voice):") 103 | for item, value in namemap.items(): 104 | for voiceval in value: 105 | print ( 106 | "Language: {} Voice: {}".format( 107 | item, 108 | voiceval 109 | ) 110 | ) 111 | print ("") 112 | print ("Available file formats:") 113 | for item, value in formatmap.items(): 114 | print (item) 115 | sys.exit(1) 116 | except: 117 | pass 118 | 119 | 120 | 121 | try: 122 | destLoc = argsDict['--dest'] 123 | except: 124 | print ("") 125 | print ("Error: destination location not specified.") 126 | print ("") 127 | print ( 128 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 129 | sys.argv[0] 130 | ) 131 | ) 132 | print ("") 133 | sys.exit(1) 134 | 135 | try: 136 | ttsLang = argsDict['--lang'] 137 | except: 138 | print ("") 139 | print ("Error: language not specified.") 140 | print ("") 141 | print ( 142 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 143 | sys.argv[0] 144 | ) 145 | ) 146 | print ("") 147 | sys.exit(1) 148 | 149 | try: 150 | ttsVoice = argsDict['--voice'] 151 | except: 152 | print ("") 153 | print ("Error: voice not specified.") 154 | print ("") 155 | print ( 156 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 157 | sys.argv[0] 158 | ) 159 | ) 160 | print ("") 161 | sys.exit(1) 162 | 163 | try: 164 | ttsFormat = argsDict['--fileformat'] 165 | except: 166 | print ("") 167 | print ("Error: file format not specified.") 168 | print ("") 169 | print ( 170 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 171 | sys.argv[0] 172 | ) 173 | ) 174 | print ("") 175 | sys.exit(1) 176 | 177 | try: 178 | ttsText = argsDict['--text'] 179 | except: 180 | print ("") 181 | print ("Error: text not specified.") 182 | print ("") 183 | print ( 184 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 185 | sys.argv[0] 186 | ) 187 | ) 188 | print ("") 189 | sys.exit(1) 190 | 191 | try: 192 | ttsApiKey = argsDict['--apikey'] 193 | except: 194 | print ("") 195 | print ("Error: API key not specified.") 196 | print ("") 197 | print ( 198 | "Usage: {} --cache=/path/to/cache --dest=/path/to/destination --lang=en-US --voice=\"ZiraUS\" --fileformat=riff-8khz-8bit-mono-mulaw --apikey=YOUR-API-KEY --text=\"Hello World\"".format( 199 | sys.argv[0] 200 | ) 201 | ) 202 | print ("") 203 | sys.exit(1) 204 | 205 | try: 206 | ttsCache = argsDict['--cache'] 207 | except: 208 | ttsCache = None 209 | 210 | if ttsVoice not in namemap[ttsLang]: 211 | print ("") 212 | print ("Error: invalid language or Voice specified, or invalid combination: The following language/Voice combinations are available:") 213 | print ("") 214 | for item, value in namemap.items(): 215 | for voiceval in value: 216 | print ( 217 | "Language: {}".format( 218 | item 219 | ) 220 | ) 221 | print ( 222 | "Language: {} Voice: {}".format( 223 | item, voiceval 224 | ) 225 | ) 226 | sys.exit(1) 227 | 228 | try: 229 | formatname = formatmap[ttsFormat] 230 | except: 231 | print ("") 232 | print ("Error: invalid format specified: The following formats are available:") 233 | print ("") 234 | for item, value in formatmap.items(): 235 | print (item) 236 | sys.exit(1) 237 | 238 | if ttsCache: 239 | cacheaccess = os.access(ttsCache, os.W_OK) 240 | if not cacheaccess: 241 | print ("Cannot write to cache location, ignoring --cache setting...") 242 | ttsCache = None 243 | 244 | m = hashlib.md5() 245 | # Hash lang+Voice+text 246 | m.update( 247 | ( 248 | "{}-{}-{}".format( 249 | ttsLang, 250 | ttsVoice, 251 | ttsText 252 | ) 253 | ).encode( 254 | 'utf-8' 255 | ) 256 | ) 257 | # create filename base on MD5 hash 258 | filename = "{}.wav".format( 259 | m.hexdigest() 260 | ) 261 | if ttsCache: 262 | # If our file already exists, just return it so we don't have to do an API call... 263 | if os.path.isfile(os.path.join(ttsCache, filename)): 264 | try: 265 | shutil.copy2( 266 | os.path.join( 267 | ttsCache, 268 | filename 269 | ), 270 | destLoc 271 | ) 272 | sys.exit(0) 273 | except (Exception) as e: 274 | print ( 275 | "Could not copy cached file: {}".format( 276 | e 277 | ) 278 | ) 279 | print ("Exiting...") 280 | sys.exit(1) 281 | 282 | # Wait to import this until after we check cache to keep things as speedy as possible... 283 | from bingtts import Translator 284 | translator = Translator(ttsApiKey) 285 | try: 286 | data = translator.speak( 287 | ttsText.encode( 288 | 'utf-8' 289 | ).decode( 290 | 'latin-1' 291 | ), 292 | ttsLang, 293 | ttsVoice, 294 | ttsFormat 295 | ) 296 | except (Exception) as e: 297 | print ( 298 | "Error retrieving speech file: {}".format( 299 | e 300 | ) 301 | ) 302 | sys.exit(1) 303 | 304 | if ttsCache: 305 | try: 306 | with open(destLoc, 'wb') as f: 307 | f.write(data) 308 | except (Exception) as e: 309 | print ( 310 | "Couldn't write to file {}: {}".format( 311 | destLoc, 312 | e 313 | ) 314 | ) 315 | sys.exit(1) 316 | try: 317 | with open(os.path.join(ttsCache, filename), 'w') as f: 318 | f.write(data) 319 | except (Exception) as e: 320 | cacheFileLoc = os.path.join( 321 | ttsCache, 322 | filename 323 | ) 324 | print ( 325 | "Couldn't write to file {}: {}".format( 326 | cacheFileLoc, 327 | e 328 | ) 329 | ) 330 | sys.exit(1) 331 | else: 332 | try: 333 | with open(destLoc, 'wb') as f: 334 | f.write(data) 335 | except (Exception) as e: 336 | print ( 337 | "Couldn't write to file {}: {}".format( 338 | destLoc, 339 | e 340 | ) 341 | ) 342 | sys.exit(1) 343 | 344 | sys.exit(0) 345 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-Bing-TTS 2 | Microsoft Bing Text to Speech library for Python 3 | 4 | # Installation 5 | To install using pip, run the following command: 6 | 7 | pip install git+https://github.com/westparkcom/Python-Bing-TTS.git 8 | 9 | To install using pipenv, run the following command: 10 | 11 | pipenv install git+https://github.com/westparkcom/Python-Bing-TTS.git#egg=bingtts 12 | # Usage 13 | The following is the usage of the library 14 | 15 | translator.speak(text, language, voice, fileformat) 16 | 17 | Variable | Description | Note 18 | --- | --- | --- 19 | text | The text that you wish to convert to speech | 20 | language | The language/country you wish to hear the speech in | Case sensitive. See [Bing TTS API Reference](https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/bingvoiceoutput#SupLocales) for list 21 | voice | The name of the voice to use | Case sensitive 22 | fileformat | File format to encode the speech to | See [Bing TTS API Reference](https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/bingvoiceoutput#Http) for list of formats 23 | 24 | # Example 25 | from bingtts import Translator 26 | translator = Translator('YOUR-API-KEY-HERE') 27 | output = translator.speak("This is a text to speech translation", "en-US", "JessaRUS", "riff-16khz-16bit-mono-pcm") 28 | with open("file.wav", "wb") as f: 29 | f.write(output) 30 | -------------------------------------------------------------------------------- /bingtts/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __init__ 4 | 5 | A library to get text to speech from micrsoft translation engine. 6 | 7 | See: https://www.microsoft.com/cognitive-services/en-us/speech-api/documentation/api-reference-rest/bingvoiceoutput 8 | 9 | """ 10 | 11 | try: 12 | import simplejson as json 13 | except ImportError: 14 | import json 15 | import logging 16 | 17 | try: 18 | import httplib 19 | except ImportError: 20 | import http.client as httplib 21 | 22 | 23 | class BadRequestException(Exception): 24 | def __init__(self, message): 25 | self.message = "{} {}".format( 26 | message.status, 27 | message.reason 28 | ) 29 | super( 30 | BadRequestException, 31 | self 32 | ).__init__( 33 | self.message 34 | ) 35 | 36 | class AuthException(Exception): 37 | def __init__(self, message): 38 | self.message = "{} {}".format( 39 | message.status, 40 | message.reason 41 | ) 42 | super( 43 | AuthException, 44 | self 45 | ).__init__( 46 | self.message 47 | ) 48 | 49 | class LanguageException(Exception): 50 | def __init__(self, message): 51 | self.message = "{}".format( 52 | message 53 | ) 54 | super( 55 | LanguageException, 56 | self 57 | ).__init__( 58 | self.message 59 | ) 60 | 61 | 62 | class Translator(object): 63 | """ 64 | Implements API for the Microsoft Translator service 65 | """ 66 | auth_host = 'api.cognitive.microsoft.com' 67 | auth_path = '/sts/v1.0/issueToken' 68 | base_host = 'speech.platform.bing.com' 69 | base_path = '' 70 | def __init__(self, client_secret, debug=False): 71 | """ 72 | :param clien_secret: The API key provided by Azure 73 | :param debug: If true, the logging level will be set to debug 74 | """ 75 | self.client_secret = client_secret 76 | self.debug = debug 77 | self.logger = logging.getLogger( 78 | "bingtts" 79 | ) 80 | self.access_token = None 81 | if self.debug: 82 | self.logger.setLevel( 83 | level=logging.DEBUG 84 | ) 85 | 86 | def get_access_token(self): 87 | """ 88 | Retrieve access token from Azure. 89 | 90 | :return: Text of the access token to be used with requests 91 | """ 92 | headers={ 93 | 'Ocp-Apim-Subscription-Key' : self.client_secret 94 | } 95 | conn = httplib.HTTPSConnection( 96 | self.auth_host 97 | ) 98 | conn.request( 99 | method="POST", 100 | url=self.auth_path, 101 | headers=headers, 102 | body="" 103 | ) 104 | response = conn.getresponse() 105 | if int(response.status) != 200: 106 | raise AuthException( 107 | response 108 | ) 109 | return response.read() 110 | 111 | def call(self, headerfields, path, body): 112 | """ 113 | Calls Bing API and retrieved audio 114 | 115 | :param headerfields: Dictionary of all headers to be sent 116 | :param path: URL path to be appended to requests 117 | :param body: Content body to be posted 118 | """ 119 | 120 | # If we don't have an access token, get one 121 | if not self.access_token: 122 | self.access_token = self.get_access_token() 123 | 124 | # Set authorization header to token we just retrieved 125 | headerfields["Authorization"] = "Bearer {}".format( 126 | self.access_token.decode( 127 | 'utf-8' 128 | ) 129 | ) 130 | # Post to Bing API 131 | urlpath = "/".join( 132 | [ 133 | self.base_path, 134 | path 135 | ] 136 | ) 137 | conn = httplib.HTTPSConnection( 138 | self.base_host 139 | ) 140 | conn.request( 141 | method="POST", 142 | url=urlpath, 143 | headers=headerfields, 144 | body=body.encode('utf-8') 145 | ) 146 | resp = conn.getresponse() 147 | # If token was expired, get a new one and try again 148 | if int(resp.status) == 401: 149 | self.access_token = None 150 | return self.call( 151 | headerfields, 152 | path, 153 | body 154 | ) 155 | 156 | # Bad data or problem, raise exception 157 | if int(resp.status) != 200: 158 | raise BadRequestException( 159 | resp 160 | ) 161 | 162 | return resp.read() 163 | 164 | def speak(self, text, lang, voice, fileformat): 165 | """ 166 | Gather parameters and call. 167 | 168 | :param text: Text to be sent to Bing TTS API to be 169 | converted to speech 170 | :param lang: Language to be spoken 171 | :param voice: Voice of the speaker 172 | :param fileformat: File format (see link below) 173 | 174 | Name maps and file format specifications can be found here: 175 | https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/bingvoiceoutput 176 | """ 177 | 178 | namemap = { 179 | "ar-EG" : ["Hoda"], 180 | "ar-SA" : ["Naayf"], 181 | "bg-BG" : ["Ivan"], 182 | "ca-ES" : ["HerenaRUS"], 183 | "cs-CZ" : ["Jakub"], 184 | "da-DK" : ["HelleRUS"], 185 | "de-AT" : ["Michael"], 186 | "de-CH" : ["Karsten"], 187 | "de-DE" : ["Hedda", "HeddaRUS", "Stefan, Apollo"], 188 | "el-GR" : ["Stefanos"], 189 | "en-AU" : ["Catherine", "HayleyRUS"], 190 | "en-CA" : ["Linda", "HeatherRUS"], 191 | "en-GB" : ["Susan, Apollo", "HazelRUS", "George, Apollo"], 192 | "en-IE" : ["Sean"], 193 | "en-IN" : ["Heera, Apollo", "PriyaRUS", "Ravi, Apollo"], 194 | "en-US" : ["ZiraRUS", "JessaRUS", "BenjaminRUS"], 195 | "es-ES" : ["Laura, Apollo", "HelenaRUS", "Pablo, Apollo"], 196 | "es-MX" : ["HildaRUS", "Raul, Apollo"], 197 | "fi-FI" : ["HeidiRUS"], 198 | "fr-CA" : ["Caroline", "HarmonieRUS"], 199 | "fr-CH" : ["Guillaume"], 200 | "fr-FR" : ["Julie, Apollo", "HortenseRUS", "Paul, Apollo"], 201 | "he-IL" : ["Asaf"], 202 | "hi-IN" : ["Kalpana, Apollo", "Kalpana", "Hemant"], 203 | "hr-HR" : ["Matej"], 204 | "hu-HU" : ["Szabolcs"], 205 | "id-ID" : ["Andika"], 206 | "it-IT" : ["Cosimo, Apollo","LuciaRUS"], 207 | "ja-JP" : ["Ayumi, Apollo", "Ichiro, Apollo", "HarukaRUS"], 208 | "ko-KR" : ["HeamiRUS"], 209 | "ms-MY" : ["Rizwan"], 210 | "nb-NO" : ["HuldaRUS"], 211 | "nl-NL" : ["HannaRUS"], 212 | "pl-PL" : ["PaulinaRUS"], 213 | "pt-BR" : ["HeloisaRUS", "Daniel, Apollo"], 214 | "pt-PT" : ["HeliaRUS"], 215 | "ro-RO" : ["Andrei"], 216 | "ru-RU" : ["Irina, Apollo", "Pavel, Apollo","EkaterinaRUS"], 217 | "sk-SK" : ["Filip"], 218 | "sl-SI" : ["Lado"], 219 | "sv-SE" : ["HedvigRUS"], 220 | "ta-IN" : ["Valluvar"], 221 | "th-TH" : ["Pattara"], 222 | "tr-TR" : ["SedaRUS"], 223 | "vi-VN" : ["An"], 224 | "zh-CN" : ["HuihuiRUS", "Yaoyao, Apollo", "Kangkang, Apollo"], 225 | "zh-HK" : ["Tracy, Apollo", "TracyRUS", "Danny, Apollo"], 226 | "zh-TW" : ["Yating, Apollo", "HanHanRUS", "Zhiwei, Apollo"] 227 | } 228 | if not text: 229 | raise LanguageException( 230 | "Text to convert is not defined!" 231 | ) 232 | if not voice and not lang: 233 | # Default to English voice if nothing is defined 234 | voice = 'ZiraRUS' 235 | lang = 'en-US' 236 | if voice and not lang: 237 | raise LanguageException( 238 | "Voice defined witout defining language!" 239 | ) 240 | if lang not in namemap: 241 | raise LanguageException( 242 | "Requested language {} not available!".format( 243 | lang 244 | ) 245 | ) 246 | if lang and not voice: 247 | # Default to first voice in array 248 | voice = namemap[lang][0] 249 | if voice not in namemap[lang]: 250 | raise LanguageException( 251 | "Requested language {} does not have voice {}!".format( 252 | lang, 253 | voice 254 | ) 255 | ) 256 | if not fileformat: 257 | fileformat = 'riff-8khz-8bit-mono-mulaw' 258 | # Set the service name sent to Bing TTS 259 | servicename = "Microsoft Server Speech Text to Speech Voice ({}, {})".format( 260 | lang, 261 | voice 262 | ) 263 | 264 | headers = { 265 | "Content-type" : "application/ssml+xml", 266 | "X-Microsoft-OutputFormat" : fileformat, 267 | "X-Search-AppId" : "07D3234E49CE426DAA29772419F436CA", 268 | "X-Search-ClientID" : "1ECFAE91408841A480F00935DC390960", 269 | "User-Agent" : "TTSForPython" 270 | } 271 | 272 | body = "{}".format( 273 | lang, 274 | lang, 275 | voice, 276 | servicename, 277 | text 278 | ) 279 | 280 | return self.call(headers, "synthesize", body) 281 | -------------------------------------------------------------------------------- /freeswitch/tts_commandline.conf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='bingtts', 5 | packages=['bingtts'], 6 | install_requires=[ 7 | ], 8 | version='0.1.6', 9 | description='Python library to access Microsoft Bing Text to Speech API', 10 | author='jpattWPC', 11 | author_email='jpatten@westparkcom.net', 12 | url='https://github.com/westparkcom/Python-Bing-TTS', 13 | download_url='https://github.com/westparkcom/Python-Bing-TTS/tarball/master', 14 | keywords=['microsoft', 'bing', 'text to speech', 'cognitive'], 15 | classifiers=[], 16 | ) 17 | --------------------------------------------------------------------------------