Skip to content

Commit 4364435

Browse files
committed
Merge branch 'master' of github.com:watson-developer-cloud/python-sdk into compare-comply
2 parents 8d81e2d + 9d8ea23 commit 4364435

21 files changed

+591
-34
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 2.4.1
2+
current_version = 2.4.4
33
commit = True
44

55
[bumpversion:file:watson_developer_cloud/version.py]

.travis.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
language: python
2-
sudo: required
3-
dist: trusty
42
python:
53
- '2.7'
64
- '3.4'
75
- '3.5'
86
- '3.6'
7+
matrix:
8+
include:
9+
- python: '3.7'
10+
dist: xenial # required for Python >= 3.7 (travis-ci/travis-ci#9069)
911
cache: pip
1012
before_install:
1113
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && openssl aes-256-cbc -K $encrypted_cebf25e6c525_key

README.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
[![Build Status](https://travis-ci.org/watson-developer-cloud/python-sdk.svg?branch=master)](https://travis-ci.org/watson-developer-cloud/python-sdk)
44
[![Slack](https://wdc-slack-inviter.mybluemix.net/badge.svg)](https://wdc-slack-inviter.mybluemix.net)
5-
[![codecov.io](https://codecov.io/github/watson-developer-cloud/python-sdk/coverage.svg?branch=master)](https://codecov.io/github/watson-developer-cloud/python-sdk?branch=master)
65
[![Latest Stable Version](https://img.shields.io/pypi/v/watson-developer-cloud.svg)](https://pypi.python.org/pypi/watson-developer-cloud)
76
[![CLA assistant](https://cla-assistant.io/readme/badge/watson-developer-cloud/python-sdk)](https://cla-assistant.io/watson-developer-cloud/python-sdk)
87

@@ -251,13 +250,37 @@ This would give an output of `DetailedResponse` having the structure:
251250
```
252251
You can use the `get_result()`, `get_headers()` and get_status_code() to return the result, headers and status code respectively.
253252

253+
## Using Websockets
254+
The Text to Speech service supports synthesizing text to spoken audio using web sockets with the `synthesize_using_websocket`. The Speech to Text service supports recognizing speech to text using web sockets with the `recognize_using_websocket`. These methods need a custom callback class to listen to events. Below is an example of `synthesize_using_websocket`. Note: The service accepts one request per connection.
255+
256+
```py
257+
from watson_developer_cloud.websocket import SynthesizeCallback
258+
259+
class MySynthesizeCallback(SynthesizeCallback):
260+
def __init__(self):
261+
SynthesizeCallback.__init__(self)
262+
263+
def on_audio_stream(self, audio_stream):
264+
return audio_stream
265+
266+
def on_data(self, data):
267+
return data
268+
269+
my_callback = MySynthesizeCallback()
270+
service.synthesize_using_websocket('I like to pet dogs',
271+
my_callback,
272+
accept='audio/wav',
273+
voice='en-US_AllisonVoice'
274+
)
275+
```
276+
254277
## Dependencies
255278

256279
* [requests]
257280
* `python_dateutil` >= 2.5.3
258281
* [responses] for testing
259282
* Following for web sockets support in speech to text
260-
* `websocket-client` 0.47.0
283+
* `websocket-client` 0.48.0
261284

262285
## Contributing
263286

examples/speaker_text_to_speech.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# You need to install pyaudio to run this example
2+
# pip install pyaudio
3+
4+
# In this example, the websocket connection is opened with a text
5+
# passed in the request. When the service responds with the synthesized
6+
# audio, the pyaudio would play it in a blocking mode
7+
8+
from __future__ import print_function
9+
from watson_developer_cloud import TextToSpeechV1
10+
from watson_developer_cloud.websocket import SynthesizeCallback
11+
import pyaudio
12+
13+
# If service instance provides API key authentication
14+
service = TextToSpeechV1(
15+
## url is optional, and defaults to the URL below. Use the correct URL for your region.
16+
url='https://stream.watsonplatform.net/text-to-speech/api',
17+
iam_apikey='your_apikey')
18+
19+
# service = TextToSpeechV1(
20+
# ## url is optional, and defaults to the URL below. Use the correct URL for your region.
21+
# # url='https://stream.watsonplatform.net/text-to-speech/api,
22+
# username='YOUR SERVICE USERNAME',
23+
# password='YOUR SERVICE PASSWORD')
24+
25+
class Play(object):
26+
"""
27+
Wrapper to play the audio in a blocking mode
28+
"""
29+
def __init__(self):
30+
self.format = pyaudio.paInt16
31+
self.channels = 1
32+
self.rate = 22050
33+
self.chunk = 1024
34+
self.pyaudio = None
35+
self.stream = None
36+
37+
def start_streaming(self):
38+
self.pyaudio = pyaudio.PyAudio()
39+
self.stream = self._open_stream()
40+
self._start_stream()
41+
42+
def _open_stream(self):
43+
stream = self.pyaudio.open(
44+
format=self.format,
45+
channels=self.channels,
46+
rate=self.rate,
47+
output=True,
48+
frames_per_buffer=self.chunk,
49+
start=False
50+
)
51+
return stream
52+
53+
def _start_stream(self):
54+
self.stream.start_stream()
55+
56+
def write_stream(self, audio_stream):
57+
self.stream.write(audio_stream)
58+
59+
def complete_playing(self):
60+
self.stream.stop_stream()
61+
self.stream.close()
62+
self.pyaudio.terminate()
63+
64+
class MySynthesizeCallback(SynthesizeCallback):
65+
def __init__(self):
66+
SynthesizeCallback.__init__(self)
67+
self.play = Play()
68+
69+
def on_connected(self):
70+
print('Opening stream to play')
71+
self.play.start_streaming()
72+
73+
def on_error(self, error):
74+
print('Error received: {}'.format(error))
75+
76+
def on_timing_information(self, timing_information):
77+
print(timing_information)
78+
79+
def on_audio_stream(self, audio_stream):
80+
self.play.write_stream(audio_stream)
81+
82+
def on_close(self):
83+
print('Completed synthesizing')
84+
self.play.complete_playing()
85+
86+
test_callback = MySynthesizeCallback()
87+
88+
# An example SSML text
89+
SSML_sorry_text = """<speak version=\"1.0\">
90+
<emphasis> I am sorry, I know how it feels.</emphasis>
91+
</speak>"""
92+
93+
# Another example of SSML text
94+
SSML_text = """
95+
<speak>
96+
I have been assigned to handle your order status request.
97+
<express-as type=\"Apology\">
98+
I am sorry to inform you that the items you requested are backordered.
99+
We apologize for the inconvenience.
100+
</express-as>
101+
<express-as type=\"Uncertainty\">
102+
We don't know when the items will become available. Maybe next week,
103+
but we are not sure at this time.
104+
</express-as>
105+
<express-as type=\"GoodNews\">
106+
But because we want you to be a satisfied customer, we are giving you
107+
a 50% discount on your order!
108+
</express-as>
109+
</speak>"""
110+
111+
service.synthesize_using_websocket(SSML_text,
112+
test_callback,
113+
accept='audio/wav',
114+
voice="en-US_AllisonVoice"
115+
)

examples/text_to_speech_v1.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
from os.path import join, dirname
55
from watson_developer_cloud import TextToSpeechV1
6+
from watson_developer_cloud.websocket import SynthesizeCallback
67

78
# If service instance provides API key authentication
89
# service = TextToSpeechV1(
@@ -63,3 +64,36 @@
6364

6465
# response = service.delete_voice_model('YOUR CUSTOMIZATION ID').get_result()
6566
# print(response)
67+
68+
# Synthesize using websocket. Note: The service accepts one request per connection
69+
file_path = join(dirname(__file__), "../resources/dog.wav")
70+
class MySynthesizeCallback(SynthesizeCallback):
71+
def __init__(self):
72+
SynthesizeCallback.__init__(self)
73+
self.fd = open(file_path, 'ab')
74+
75+
def on_connected(self):
76+
print('Connection was successful')
77+
78+
def on_error(self, error):
79+
print('Error received: {}'.format(error))
80+
81+
def on_content_type(self, content_type):
82+
print('Content type: {}'.format(content_type))
83+
84+
def on_timing_information(self, timing_information):
85+
print(timing_information)
86+
87+
def on_audio_stream(self, audio_stream):
88+
self.fd.write(audio_stream)
89+
90+
def on_close(self):
91+
self.fd.close()
92+
print('Done synthesizing. Closing the connection')
93+
94+
my_callback = MySynthesizeCallback()
95+
service.synthesize_using_websocket('I like to pet dogs',
96+
my_callback,
97+
accept='audio/wav',
98+
voice='en-US_AllisonVoice'
99+
)

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ Sphinx>=1.3.1
1717
bumpversion>=0.5.3
1818

1919
# Web sockets
20-
websocket-client==0.47.0
20+
websocket-client==0.48.0

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
requests>=2.0,<3.0
22
python_dateutil>=2.5.3
3-
websocket-client==0.47.0
3+
websocket-client==0.48.0

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import os
2020
import sys
2121

22-
__version__ = '2.4.1'
22+
__version__ = '2.4.4'
2323

2424
if sys.argv[-1] == 'publish':
2525
# test server
@@ -64,7 +64,7 @@ def run_tests(self):
6464
version=__version__,
6565
description='Client library to use the IBM Watson Services',
6666
license='Apache 2.0',
67-
install_requires=['requests>=2.0, <3.0', 'python_dateutil>=2.5.3', 'websocket-client==0.47.0'],
67+
install_requires=['requests>=2.0, <3.0', 'python_dateutil>=2.5.3', 'websocket-client==0.48.0'],
6868
tests_require=['responses', 'pytest', 'python_dotenv', 'pytest-rerunfailures', 'tox'],
6969
cmdclass={'test': PyTest},
7070
author='Jeffrey Stylos',

test/integration/test_speech_to_text_v1.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ def on_error(self, error):
9797
def on_transcription(self, transcript):
9898
self.transcript = transcript
9999

100-
testCallback = MyRecognizeCallback()
100+
test_callback = MyRecognizeCallback()
101101
with open(os.path.join(os.path.dirname(__file__), '../../resources/speech.wav'), 'rb') as audio_file:
102102
audio_source = AudioSource(audio_file, False)
103-
t = threading.Thread(target=self.speech_to_text.recognize_using_websocket, args=(audio_source, "audio/l16; rate=44100", testCallback))
103+
t = threading.Thread(target=self.speech_to_text.recognize_using_websocket, args=(audio_source, "audio/l16; rate=44100", test_callback))
104104
t.start()
105105
t.join()
106-
assert testCallback.error is None
107-
assert testCallback.transcript is not None
108-
assert testCallback.transcript[0]['transcript'] == 'thunderstorms could produce large hail isolated tornadoes and heavy rain '
106+
assert test_callback.error is None
107+
assert test_callback.transcript is not None
108+
assert test_callback.transcript[0]['transcript'] == 'thunderstorms could produce large hail isolated tornadoes and heavy rain '

test/integration/test_text_to_speech_v1.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# coding: utf-8
22
import unittest
33
import watson_developer_cloud
4+
from watson_developer_cloud.websocket import SynthesizeCallback
45
import pytest
56
import os
67

@@ -67,3 +68,34 @@ def test_custom_words(self):
6768
self.text_to_speech.delete_word(customization_id, 'ACLs')
6869
word = self.text_to_speech.get_word(customization_id, 'MACLs').get_result()
6970
assert word['translation'] == 'mackles'
71+
72+
def test_synthesize_using_websocket(self):
73+
file = 'tongue_twister.wav'
74+
class MySynthesizeCallback(SynthesizeCallback):
75+
def __init__(self):
76+
SynthesizeCallback.__init__(self)
77+
self.fd = None
78+
self.error = None
79+
80+
def on_connected(self):
81+
self.fd = open(file, 'ab')
82+
83+
def on_error(self, error):
84+
self.error = error
85+
86+
def on_audio_stream(self, audio_stream):
87+
self.fd.write(audio_stream)
88+
89+
def on_close(self):
90+
self.fd.close()
91+
92+
test_callback = MySynthesizeCallback()
93+
self.text_to_speech.synthesize_using_websocket('She sells seashells by the seashore',
94+
test_callback,
95+
accept='audio/wav',
96+
voice='en-GB_KateVoice'
97+
)
98+
assert test_callback.error is None
99+
assert test_callback.fd is not None
100+
assert os.stat(file).st_size > 0
101+
os.remove(file)

0 commit comments

Comments
 (0)