cloudlanguagetools 17.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. cloudlanguagetools/__init__.py +0 -0
  2. cloudlanguagetools/alibaba.py +270 -0
  3. cloudlanguagetools/amazon.py +286 -0
  4. cloudlanguagetools/audio_processing.py +29 -0
  5. cloudlanguagetools/azure.py +804 -0
  6. cloudlanguagetools/cereproc.py +111 -0
  7. cloudlanguagetools/chatapi.py +360 -0
  8. cloudlanguagetools/constants.py +99 -0
  9. cloudlanguagetools/deepl.py +109 -0
  10. cloudlanguagetools/dictionarylookup.py +48 -0
  11. cloudlanguagetools/easypronunciation.py +254 -0
  12. cloudlanguagetools/elevenlabs.py +290 -0
  13. cloudlanguagetools/encryption.py +38 -0
  14. cloudlanguagetools/epitran.py +117 -0
  15. cloudlanguagetools/errors.py +37 -0
  16. cloudlanguagetools/forvo.py +684 -0
  17. cloudlanguagetools/fptai.py +172 -0
  18. cloudlanguagetools/gemini.py +288 -0
  19. cloudlanguagetools/google.py +312 -0
  20. cloudlanguagetools/keys.py +1 -0
  21. cloudlanguagetools/languages.py +519 -0
  22. cloudlanguagetools/libretranslate.py +84 -0
  23. cloudlanguagetools/mandarincantonese.py +87 -0
  24. cloudlanguagetools/naver.py +286 -0
  25. cloudlanguagetools/openai.py +292 -0
  26. cloudlanguagetools/options.py +15 -0
  27. cloudlanguagetools/pythainlp.py +102 -0
  28. cloudlanguagetools/service.py +110 -0
  29. cloudlanguagetools/servicemanager.py +395 -0
  30. cloudlanguagetools/spacy.py +76 -0
  31. cloudlanguagetools/test_services.py +167 -0
  32. cloudlanguagetools/tokenization.py +19 -0
  33. cloudlanguagetools/translationlanguage.py +19 -0
  34. cloudlanguagetools/transliterationlanguage.py +42 -0
  35. cloudlanguagetools/ttsvoice.py +53 -0
  36. cloudlanguagetools/vocalware.py +288 -0
  37. cloudlanguagetools/voicen.py +137 -0
  38. cloudlanguagetools/watson.py +181 -0
  39. cloudlanguagetools/wenlin.py +167 -0
  40. cloudlanguagetools-17.0.0.dist-info/METADATA +41 -0
  41. cloudlanguagetools-17.0.0.dist-info/RECORD +43 -0
  42. cloudlanguagetools-17.0.0.dist-info/WHEEL +4 -0
  43. cloudlanguagetools-17.0.0.dist-info/licenses/LICENSE +674 -0
File without changes
@@ -0,0 +1,270 @@
1
+ import json
2
+ import requests
3
+ import time
4
+ import datetime
5
+ import uuid
6
+ import urllib
7
+ import hmac
8
+ import base64
9
+ import logging
10
+ import tempfile
11
+ import pprint
12
+ from typing import List
13
+
14
+ import cloudlanguagetools.service
15
+ import cloudlanguagetools.options
16
+ import cloudlanguagetools.constants
17
+ import cloudlanguagetools.languages
18
+ import cloudlanguagetools.ttsvoice
19
+ import cloudlanguagetools.constants
20
+ import cloudlanguagetools.languages
21
+ import cloudlanguagetools.ttsvoice
22
+ import cloudlanguagetools.errors
23
+ from cloudlanguagetools.options import AudioFormat
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ ALIBABA_VOICE_SPEED_DEFAULT = 0
28
+ ALIBABA_VOICE_PITCH_DEFAULT = 0
29
+
30
+ VOICE_OPTIONS = {
31
+ 'speed': {'type': 'number_int', 'min': -500, 'max': 500, 'default': ALIBABA_VOICE_SPEED_DEFAULT},
32
+ 'pitch': {'type': 'number_int', 'min': -500, 'max': 500, 'default': ALIBABA_VOICE_PITCH_DEFAULT},
33
+ 'volume': {'type': 'number_int', 'min': 0, 'max': 100, 'default': 50},
34
+ cloudlanguagetools.options.AUDIO_FORMAT_PARAMETER: {
35
+ 'type': cloudlanguagetools.options.ParameterType.list.name,
36
+ 'values': ['pcm', 'wav', 'mp3'],
37
+ 'default': 'mp3'
38
+ }
39
+ }
40
+
41
+
42
+ class AlibabaService(cloudlanguagetools.service.Service):
43
+ def __init__(self):
44
+ self.access_token = None
45
+
46
+ def configure(self, config):
47
+ self.access_key_id = config['access_key_id']
48
+ self.access_key_secret = config['access_key_secret']
49
+ self.app_key = config['app_key']
50
+
51
+ def refresh_token(self):
52
+ logger.info("refreshing token")
53
+ params = {
54
+ "AccessKeyId": self.access_key_id,
55
+ "Action": "CreateToken",
56
+ "Version": "2019-07-17",
57
+ "Format": "JSON",
58
+ "RegionId": "ap-southeast-1",
59
+ "Timestamp": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
60
+ "SignatureMethod": "HMAC-SHA1",
61
+ "SignatureVersion": "1.0",
62
+ "SignatureNonce": str(uuid.uuid4())
63
+ }
64
+
65
+ # sort by keys alphabetically
66
+ params = dict(sorted(params.items()))
67
+ # timestamp needs to be double-quoted by the end
68
+ params["Timestamp"] = urllib.parse.quote(params["Timestamp"], safe='')
69
+
70
+ # urlencode with noop lambda for no quoting - we will quote later
71
+ params_str = urllib.parse.urlencode(params, quote_via=lambda a, b, c, d: a)
72
+
73
+ # this is always /, as we always hit the path / on the API
74
+ url_encoded = urllib.parse.quote("/", safe='')
75
+ str_to_sign = f"GET&{url_encoded}&{urllib.parse.quote(params_str, safe='')}"
76
+ str_to_sign = str_to_sign.encode("utf-8")
77
+
78
+ key = self.access_key_secret + "&"
79
+ key = key.encode("utf-8")
80
+
81
+ # calculate HMAC-SHA1 digest and convert to base64
82
+ dig = hmac.new(key, str_to_sign, "sha1").digest()
83
+ dig = base64.standard_b64encode(dig).decode("utf-8")
84
+
85
+ # signature also needs to be quoted
86
+ signature = urllib.parse.quote(dig, safe='')
87
+ params_str = f"Signature={signature}&{params_str}"
88
+
89
+ response = requests.get(f"http://nlsmeta.ap-southeast-1.aliyuncs.com/?{params_str}")
90
+
91
+ if response.status_code != 200:
92
+ logger.warning(f"Token request failed: {response.text}")
93
+ raise cloudlanguagetools.errors.RequestError("Token request failed", None, response.text)
94
+
95
+ data = response.json()
96
+ self.access_token = data["Token"]
97
+ logger.info(f"Got access token: {self.access_token}")
98
+
99
+ def get_tts_audio(self, text, voice_key, options):
100
+ logger.debug(f'get_tts_audio, text: {text} voice_key: {voice_key}')
101
+ if not self.access_token or self.access_token["ExpireTime"] <= int(time.time()):
102
+ self.refresh_token()
103
+
104
+ speed = int(options.get('speed', ALIBABA_VOICE_SPEED_DEFAULT))
105
+ pitch = int(options.get('pitch', ALIBABA_VOICE_PITCH_DEFAULT))
106
+ voice = voice_key['name']
107
+
108
+ params = {
109
+ "format": "mp3",
110
+ "appkey": self.app_key,
111
+ "speech_rate": speed,
112
+ "pitch_rate": pitch,
113
+ "text": text,
114
+ "token": self.access_token["Id"],
115
+ "voice": voice
116
+ }
117
+
118
+ response = requests.get(
119
+ "https://nls-gateway-ap-southeast-1.aliyuncs.com/stream/v1/tts",
120
+ params=params,
121
+ timeout=cloudlanguagetools.constants.RequestTimeout
122
+ )
123
+
124
+ logger.debug(f'response status code: {response.status_code} headers: {pprint.pformat(response.headers)} length of response: {len(response.content)}')
125
+
126
+ if response.status_code != 200:
127
+ data = response.json()
128
+ error_message = f'could not retrieve audio using Alibaba: {response.content}'
129
+ logger.error(error_message)
130
+ raise cloudlanguagetools.errors.RequestError(text, voice, error_message)
131
+
132
+ if response.headers['Content-Type'] != 'audio/mpeg':
133
+ logger.error(f'Unexpected response type. Response as text: {response.text}')
134
+ raise cloudlanguagetools.errors.RequestError(
135
+ text, voice,
136
+ f'Got bad content type in response: {response.headers["Content-Type"]}'
137
+ )
138
+
139
+ # Create a temporary file and write the audio content to it
140
+ output_temp_file = tempfile.NamedTemporaryFile(prefix=f'cloudlanguage_tools_{self.__class__.__name__}_audio', suffix='.mp3')
141
+ output_temp_filename = output_temp_file.name
142
+ with open(output_temp_filename, 'wb') as audio:
143
+ audio.write(response.content)
144
+ logger.debug(f'result temporary file name: {output_temp_file.name}')
145
+ return output_temp_file
146
+
147
+ def get_tts_voice_list(self):
148
+ # returns list of TtsVoice
149
+ return []
150
+
151
+ def build_tts_voice_v3(self, name: str, voice_id: str, gender: cloudlanguagetools.constants.Gender, audio_languages: List[cloudlanguagetools.languages.AudioLanguage]):
152
+ return cloudlanguagetools.ttsvoice.TtsVoice_v3(
153
+ name=name,
154
+ voice_key={'name': voice_id},
155
+ options=VOICE_OPTIONS,
156
+ service=cloudlanguagetools.constants.Service.Alibaba,
157
+ gender=gender,
158
+ audio_languages=audio_languages,
159
+ service_fee=cloudlanguagetools.constants.ServiceFee.paid
160
+ )
161
+
162
+ def get_tts_voice_list_v3(self) -> List[cloudlanguagetools.ttsvoice.TtsVoice_v3]:
163
+ result = []
164
+
165
+ # Standard voices for all scenarios
166
+ result.extend([
167
+ # Standard Chinese voices
168
+ self.build_tts_voice_v3('Xiaoyun (Standard)', 'Xiaoyun',
169
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
170
+ self.build_tts_voice_v3('Xiaogang (Standard)', 'Xiaogang',
171
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
172
+ self.build_tts_voice_v3('Ruoxi (Gentle)', 'Ruoxi',
173
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
174
+ self.build_tts_voice_v3('Siqi (Gentle)', 'Siqi',
175
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
176
+ self.build_tts_voice_v3('Sijia (Standard)', 'Sijia',
177
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
178
+ self.build_tts_voice_v3('Sicheng (Standard)', 'Sicheng',
179
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
180
+ self.build_tts_voice_v3('Aiqi (Gentle)', 'Aiqi',
181
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
182
+ self.build_tts_voice_v3('Aijia (Standard)', 'Aijia',
183
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
184
+ self.build_tts_voice_v3('Aicheng (Standard)', 'Aicheng',
185
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
186
+ self.build_tts_voice_v3('Aida (Standard)', 'Aida',
187
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
188
+ self.build_tts_voice_v3("Ning'er", 'Ninger',
189
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
190
+ self.build_tts_voice_v3('Ruilin', 'Ruilin',
191
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
192
+
193
+ # Customer service voices
194
+ self.build_tts_voice_v3('Siyue (Gentle)', 'Siyue',
195
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
196
+ self.build_tts_voice_v3('Aiya (Harsh)', 'Aiya',
197
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
198
+ self.build_tts_voice_v3('Aixia (Amiable)', 'Aixia',
199
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
200
+ self.build_tts_voice_v3('Aimei (Sweet)', 'Aimei',
201
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
202
+ self.build_tts_voice_v3('Aiyu (Natural)', 'Aiyu',
203
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
204
+ self.build_tts_voice_v3('Aiyue (Gentle)', 'Aiyue',
205
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
206
+ self.build_tts_voice_v3('Aijing (Harsh)', 'Aijing',
207
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
208
+ self.build_tts_voice_v3('Xiaomei (Sweet)', 'Xiaomei',
209
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
210
+
211
+ # Regional accent voices
212
+ self.build_tts_voice_v3('Aina (Zhejiang)', 'Aina',
213
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
214
+ self.build_tts_voice_v3('Yina (Zhejiang)', 'Yina',
215
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
216
+ self.build_tts_voice_v3('Sijing (Harsh)', 'Sijing',
217
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
218
+
219
+ # Child voices
220
+ self.build_tts_voice_v3('Sitong (Child)', 'Sitong',
221
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
222
+ self.build_tts_voice_v3('Xiaobei (Little Girl)', 'Xiaobei',
223
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
224
+ self.build_tts_voice_v3('Aitong (Child)', 'Aitong',
225
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
226
+ self.build_tts_voice_v3('Aiwei (Little Girl)', 'Aiwei',
227
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
228
+ self.build_tts_voice_v3('Aibao (Little Girl)', 'Aibao',
229
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
230
+
231
+ # English voices
232
+ self.build_tts_voice_v3('Harry (British)', 'Harry',
233
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
234
+ self.build_tts_voice_v3('Abby (American)', 'Abby',
235
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.en_US]),
236
+ self.build_tts_voice_v3('Andy (American)', 'Andy',
237
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.en_US]),
238
+ self.build_tts_voice_v3('Eric (British)', 'Eric',
239
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
240
+ self.build_tts_voice_v3('Emily (British)', 'Emily',
241
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
242
+ self.build_tts_voice_v3('Luna (British)', 'Luna',
243
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
244
+ self.build_tts_voice_v3('Luca (British)', 'Luca',
245
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
246
+ self.build_tts_voice_v3('Wendy (British)', 'Wendy',
247
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
248
+ self.build_tts_voice_v3('William (British)', 'William',
249
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
250
+ self.build_tts_voice_v3('Olivia (British)', 'Olivia',
251
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.en_GB]),
252
+
253
+ # Special dialect voices
254
+ self.build_tts_voice_v3('Shanshan (Cantonese)', 'Shanshan',
255
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_HK]),
256
+ self.build_tts_voice_v3('Xiaoyue (Sichuan)', 'Xiaoyue',
257
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
258
+ self.build_tts_voice_v3('Lydia (Bilingual)', 'Lydia',
259
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.en_US]),
260
+ self.build_tts_voice_v3('Aishuo (Natural)', 'Aishuo',
261
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.zh_CN, cloudlanguagetools.languages.AudioLanguage.en_US]),
262
+ self.build_tts_voice_v3('Qingqing (Taiwanese)', 'Qingqing',
263
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
264
+ self.build_tts_voice_v3('Cuijie (Northeastern)', 'Cuijie',
265
+ cloudlanguagetools.constants.Gender.Female, [cloudlanguagetools.languages.AudioLanguage.zh_CN]),
266
+ self.build_tts_voice_v3('Xiaoze (Hunan)', 'Xiaoze',
267
+ cloudlanguagetools.constants.Gender.Male, [cloudlanguagetools.languages.AudioLanguage.zh_CN])
268
+ ])
269
+
270
+ return result
@@ -0,0 +1,286 @@
1
+ import json
2
+ import requests
3
+ import tempfile
4
+ import os
5
+ import boto3
6
+ import botocore.exceptions
7
+ import contextlib
8
+ import logging
9
+ import pprint
10
+
11
+ import cloudlanguagetools.service
12
+ import cloudlanguagetools.constants
13
+ import cloudlanguagetools.options
14
+ import cloudlanguagetools.languages
15
+ import cloudlanguagetools.ttsvoice
16
+ import cloudlanguagetools.translationlanguage
17
+ import cloudlanguagetools.transliterationlanguage
18
+ import cloudlanguagetools.errors
19
+ import cloudlanguagetools.audio_processing
20
+
21
+ from cloudlanguagetools.options import AudioFormat
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ DEFAULT_VOICE_PITCH = 0
26
+ DEFAULT_VOICE_RATE = 100
27
+
28
+ def get_audio_language_enum(language_code):
29
+ language_map = {
30
+ 'arb': 'ar_XA',
31
+ 'cmn-CN': 'zh_CN',
32
+ 'yue-CN': 'zh_HK'
33
+ }
34
+
35
+ language_enum_name = language_code.replace('-', '_')
36
+ if language_code in language_map:
37
+ language_enum_name = language_map[language_code]
38
+
39
+ return cloudlanguagetools.languages.AudioLanguage[language_enum_name]
40
+
41
+ class AmazonVoice(cloudlanguagetools.ttsvoice.TtsVoice):
42
+ def __init__(self, voice_data, engine: str):
43
+ # print(voice_data)
44
+ # {'Gender': 'Female', 'Id': 'Lotte', 'LanguageCode': 'nl-NL', 'LanguageName': 'Dutch', 'Name': 'Lotte', 'SupportedEngines': ['standard']}
45
+ self.service = cloudlanguagetools.constants.Service.Amazon
46
+ self.service_fee = cloudlanguagetools.constants.ServiceFee.paid
47
+ self.gender = cloudlanguagetools.constants.Gender[voice_data['Gender']]
48
+ self.voice_id = voice_data['Id']
49
+ self.name = voice_data['Name']
50
+ self.audio_language = get_audio_language_enum(voice_data['LanguageCode'])
51
+ self.engine = engine
52
+
53
+ def get_voice_key(self):
54
+ return {
55
+ 'voice_id': self.voice_id,
56
+ 'engine': self.engine
57
+ }
58
+
59
+ def get_voice_shortname(self):
60
+ return f'{self.name} ({self.engine.capitalize()})'
61
+
62
+ def get_options(self):
63
+ return {
64
+ 'rate' : {
65
+ 'type': 'number',
66
+ 'min': 20,
67
+ 'max': 200,
68
+ 'default': DEFAULT_VOICE_RATE
69
+ },
70
+ 'pitch': {
71
+ 'type': 'number',
72
+ 'min': -50,
73
+ 'max': 50,
74
+ 'default': DEFAULT_VOICE_PITCH
75
+ },
76
+ cloudlanguagetools.options.AUDIO_FORMAT_PARAMETER: {
77
+ 'type': cloudlanguagetools.options.ParameterType.list.name,
78
+ 'values': [
79
+ cloudlanguagetools.options.AudioFormat.mp3.name,
80
+ cloudlanguagetools.options.AudioFormat.ogg_vorbis.name,
81
+ cloudlanguagetools.options.AudioFormat.wav.name
82
+ ],
83
+ 'default': cloudlanguagetools.options.AudioFormat.mp3.name
84
+ }
85
+ }
86
+
87
+ class AmazonTranslationLanguage(cloudlanguagetools.translationlanguage.TranslationLanguage):
88
+ def __init__(self, language, language_id):
89
+ self.service = cloudlanguagetools.constants.Service.Amazon
90
+ self.service_fee = cloudlanguagetools.constants.ServiceFee.paid
91
+ self.language = language
92
+ self.language_id = language_id
93
+
94
+ def get_language_id(self):
95
+ return self.language_id
96
+
97
+ class AmazonService(cloudlanguagetools.service.Service):
98
+ def __init__(self):
99
+ pass
100
+
101
+ def configure(self, config):
102
+ aws_access_key_id = config['AWS_ACCESS_KEY_ID']
103
+ aws_secret_access_key = config['AWS_SECRET_ACCESS_KEY']
104
+ region_name = config['AWS_DEFAULT_REGION']
105
+
106
+ self.polly_client = boto3.client("polly",
107
+ region_name=region_name,
108
+ aws_access_key_id=aws_access_key_id,
109
+ aws_secret_access_key=aws_secret_access_key,
110
+ config=botocore.config.Config(connect_timeout=cloudlanguagetools.constants.RequestTimeout, read_timeout=cloudlanguagetools.constants.RequestTimeout))
111
+ self.translate_client = boto3.client("translate",
112
+ region_name=region_name,
113
+ aws_access_key_id=aws_access_key_id,
114
+ aws_secret_access_key=aws_secret_access_key,
115
+ config=botocore.config.Config(connect_timeout=cloudlanguagetools.constants.RequestTimeout, read_timeout=cloudlanguagetools.constants.RequestTimeout))
116
+
117
+ def get_translation(self, text, from_language_key, to_language_key):
118
+ result = self.translate_client.translate_text(Text=text,
119
+ SourceLanguageCode=from_language_key, TargetLanguageCode=to_language_key)
120
+ return result.get('TranslatedText')
121
+
122
+ def get_tts_audio(self, text, voice_key, options):
123
+ response_format_parameter, audio_format = self.get_request_audio_format({
124
+ AudioFormat.mp3: 'mp3',
125
+ AudioFormat.ogg_vorbis: 'ogg_vorbis',
126
+ AudioFormat.wav: 'pcm'
127
+ }, options, AudioFormat.mp3)
128
+
129
+ logger.info(f'generating audio with voice {voice_key}')
130
+
131
+ # wav, we need to convert as described here:
132
+ # https://aws.amazon.com/blogs/machine-learning/integrating-amazon-polly-with-legacy-ivr-systems-by-converting-output-to-wav-format/
133
+
134
+ output_temp_file = tempfile.NamedTemporaryFile()
135
+ output_temp_filename = output_temp_file.name
136
+
137
+ pitch = options.get('pitch', DEFAULT_VOICE_PITCH)
138
+ pitch_str = f'{pitch:+.0f}%'
139
+ rate = options.get('rate', DEFAULT_VOICE_RATE)
140
+ rate_str = f'{rate:0.0f}%'
141
+
142
+ prosody_tags = f'pitch="{pitch_str}" rate="{rate_str}"'
143
+ if voice_key['engine'] == 'neural':
144
+ # pitch not supported on neural voices
145
+ prosody_tags = f'rate="{rate_str}"'
146
+
147
+ ssml_str = f"""<speak>
148
+ <prosody {prosody_tags} >
149
+ {text}
150
+ </prosody>
151
+ </speak>"""
152
+
153
+
154
+ try:
155
+ if audio_format == cloudlanguagetools.options.AudioFormat.wav:
156
+ response = self.polly_client.synthesize_speech(Text=ssml_str,
157
+ TextType="ssml",
158
+ OutputFormat=response_format_parameter,
159
+ VoiceId=voice_key['voice_id'],
160
+ Engine=voice_key['engine'],
161
+ SampleRate="16000")
162
+ else:
163
+ if voice_key['engine'] in ['generative', 'long-form']:
164
+ logger.info(f'voice: {voice_key}, generating text format: {text}')
165
+ response = self.polly_client.synthesize_speech(Text=text, TextType="text", OutputFormat=response_format_parameter, VoiceId=voice_key['voice_id'], Engine=voice_key['engine'])
166
+ else:
167
+ logger.info(f'voice: {voice_key}, generating ssml format: {ssml_str}')
168
+ response = self.polly_client.synthesize_speech(Text=ssml_str, TextType="ssml", OutputFormat=response_format_parameter, VoiceId=voice_key['voice_id'], Engine=voice_key['engine'])
169
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as error:
170
+ logger.warning(f'Amazon Polly exception: {type(error).__name__}: {error}')
171
+ raise cloudlanguagetools.errors.RequestError(str(error)) from error
172
+
173
+ if "AudioStream" in response:
174
+ # Note: Closing the stream is important because the service throttles on the
175
+ # number of parallel connections. Here we are using contextlib.closing to
176
+ # ensure the close method of the stream object will be called automatically
177
+ # at the end of the with statement's scope.
178
+ with contextlib.closing(response["AudioStream"]) as stream:
179
+ with open(output_temp_filename, 'wb') as audio:
180
+ audio.write(stream.read())
181
+
182
+ if audio_format == cloudlanguagetools.options.AudioFormat.wav:
183
+ return cloudlanguagetools.audio_processing.wrap_pcm_data_wave(output_temp_file,
184
+ num_channels=1,
185
+ sample_width=2,
186
+ framerate=16000)
187
+ return output_temp_file
188
+
189
+ else:
190
+ # The response didn't contain audio data, exit gracefully
191
+ raise cloudlanguagetools.errors.RequestError('no audio stream')
192
+
193
+
194
+ def get_tts_voice_list(self):
195
+ result = []
196
+ response = self.polly_client.describe_voices()
197
+ # print(response['Voices'])
198
+ for voice in response['Voices']:
199
+ logger.debug(f'voice: {pprint.pformat(voice)}')
200
+ for engine in voice['SupportedEngines']:
201
+ result.append(AmazonVoice(voice, engine))
202
+ return result
203
+
204
+
205
+ def get_translation_language_list(self):
206
+ result = [
207
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.af, 'af'),
208
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.sq, 'sq'),
209
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.am, 'am'),
210
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ar, 'ar'),
211
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.hy, 'hy'),
212
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.az, 'az'),
213
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.bn, 'bn'),
214
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.bs, 'bs'),
215
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.bg, 'bg'),
216
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ca, 'ca'),
217
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.zh_cn, 'zh'),
218
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.zh_tw, 'zh-TW'),
219
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.hr, 'hr'),
220
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.cs, 'cs'),
221
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.da, 'da'),
222
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.prs, 'fa-AF'),
223
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.nl, 'nl'),
224
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.en, 'en'),
225
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.et, 'et'),
226
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.fa, 'fa'),
227
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.tl, 'tl'),
228
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.fi, 'fi'),
229
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.fr, 'fr'),
230
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.fr_ca, 'fr-CA'),
231
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ka, 'ka'),
232
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.de, 'de'),
233
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.el, 'el'),
234
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.gu, 'gu'),
235
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ht, 'ht'),
236
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ha, 'ha'),
237
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.he, 'he'),
238
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.hi, 'hi'),
239
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.hu, 'hu'),
240
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.is_, 'is'),
241
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.id_, 'id'),
242
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.it, 'it'),
243
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ja, 'ja'),
244
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.kn, 'kn'),
245
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.kk, 'kk'),
246
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ko, 'ko'),
247
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.lv, 'lv'),
248
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.lt, 'lt'),
249
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.mk, 'mk'),
250
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ms, 'ms'),
251
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ml, 'ml'),
252
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.mt, 'mt'),
253
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.mn, 'mn'),
254
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.no, 'no'),
255
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.fa, 'fa'),
256
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ps, 'ps'),
257
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.pl, 'pl'),
258
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.pt_pt, 'pt'),
259
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ro, 'ro'),
260
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ru, 'ru'),
261
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.sr_cyrl, 'sr'),
262
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.si, 'si'),
263
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.sk, 'sk'),
264
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.sl, 'sl'),
265
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.so, 'so'),
266
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.es, 'es'),
267
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.es_mx, 'es-MX'),
268
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.sw, 'sw'),
269
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.sv, 'sv'),
270
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.tl, 'tl'),
271
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ta, 'ta'),
272
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.te, 'te'),
273
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.th, 'th'),
274
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.tr, 'tr'),
275
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.uk, 'uk'),
276
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.ur, 'ur'),
277
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.uz, 'uz'),
278
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.vi, 'vi'),
279
+ AmazonTranslationLanguage(cloudlanguagetools.languages.Language.cy, 'cy')
280
+ ]
281
+ return result
282
+
283
+ def get_transliteration_language_list(self):
284
+ result = []
285
+ return result
286
+
@@ -0,0 +1,29 @@
1
+ import wave
2
+ import tempfile
3
+
4
+ # audio utilities
5
+
6
+
7
+ # take PCM audio and wrap it in a WAV container
8
+ def wrap_pcm_data_wave(audio_temp_file: tempfile.NamedTemporaryFile,
9
+ num_channels,
10
+ sample_width,
11
+ framerate) -> tempfile.NamedTemporaryFile:
12
+ # read the audio data
13
+ f = open(audio_temp_file.name, 'rb')
14
+ data = f.read()
15
+ f.close()
16
+
17
+ wav_frames = []
18
+ wav_frames.append(data)
19
+
20
+ wav_audio_temp_file = tempfile.NamedTemporaryFile(prefix='clt_wav_audio_', suffix='.wav')
21
+
22
+ WAVEFORMAT = wave.open(wav_audio_temp_file.name,'wb')
23
+ WAVEFORMAT.setnchannels(num_channels) # one channel, mono
24
+ WAVEFORMAT.setsampwidth(sample_width) # Polly's output is a stream of 16-bits (2 bytes) samples
25
+ WAVEFORMAT.setframerate(framerate)
26
+ WAVEFORMAT.writeframes(b''.join(wav_frames))
27
+ WAVEFORMAT.close()
28
+
29
+ return wav_audio_temp_file