Skip to content
This repository has been archived by the owner on Feb 9, 2023. It is now read-only.

Commit

Permalink
Restore changes from the voicekit branch
Browse files Browse the repository at this point in the history
Summary of changes:
- Fix instructions for using the Voice Kit on Stretch (#187)
- Make it easier to turn the LED off (#167)
- Fix crash with trigger sound (#163)
- Let user code control TTS (#185)
- Enable hotwording with Cloud Speech (0b4972b)
  • Loading branch information
drigz committed Jan 3, 2018
1 parent c22ea5b commit bc95e07
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 7 deletions.
11 changes: 11 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,21 @@ on Raspbian 2017-07-05 and later. You'll also need to configure ALSA:

``` shell
sudo scripts/configure-driver.sh
sudo reboot
```

After your Pi has rebooted with the driver enabled, run:

```
cd ~/AIY-projects-python
sudo scripts/install-alsa-config.sh
python3 checkpoints/check_audio.py
sudo reboot
```

Don't skip running `check_audio.py` before rebooting, as it has an important
effect on the state of ALSA, the sound architecture.

## Get cloud credentials

To access the cloud services you need to register a project and generate
Expand Down
2 changes: 1 addition & 1 deletion src/aiy/_drivers/_led.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def _animate(self):
running = self.running
if not running:
return
if state:
if state is not None:
if not self._parse_state(state):
raise ValueError('unsupported state: %d' % state)
if self.iterator:
Expand Down
1 change: 1 addition & 0 deletions src/aiy/_drivers/_status_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def set_trigger_sound_wave(self, trigger_sound_wave):
"""
if not trigger_sound_wave:
self._trigger_sound_wave = None
return
expanded_path = os.path.expanduser(trigger_sound_wave)
if os.path.exists(expanded_path):
self._trigger_sound_wave = expanded_path
Expand Down
7 changes: 5 additions & 2 deletions src/aiy/_drivers/_tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,24 @@ def create_say(player):
return functools.partial(say, player, lang=lang)


def say(player, words, lang='en-US'):
def say(player, words, lang='en-US', volume=60, pitch=130):
"""Say the given words with TTS.
Args:
player: To play the text-to-speech audio.
words: string to say aloud.
lang: language for the text-to-speech engine.
volume: volume for the text-to-speech engine.
pitch: pitch for the text-to-speech engine.
"""
try:
(fd, tts_wav) = tempfile.mkstemp(suffix='.wav', dir=TMP_DIR)
except IOError:
logger.exception('Using fallback directory for TTS output')
(fd, tts_wav) = tempfile.mkstemp(suffix='.wav')
os.close(fd)
words = '<volume level="60"><pitch level="130">%s</pitch></volume>' % words
words = '<volume level="' + str(volume) + '"><pitch level="' + str(pitch) + \
'">' + words + '</pitch></volume>'
try:
subprocess.call(['pico2wave', '--lang', lang, '-w', tts_wav, words])
player.play_wav(tts_wav)
Expand Down
37 changes: 34 additions & 3 deletions src/aiy/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
_voicehat_recorder = None
_voicehat_player = None
_status_ui = None
_tts_volume = 60
_tts_pitch = 130


class _WaveDump(object):
Expand Down Expand Up @@ -108,15 +110,24 @@ def play_audio(audio_data):
player.play_bytes(audio_data, sample_width=AUDIO_SAMPLE_SIZE, sample_rate=AUDIO_SAMPLE_RATE_HZ)


def say(words, lang=None):
def say(words, lang=None, volume=None, pitch=None):
"""Says the given words in the given language with Google TTS engine.
If lang is specified, e.g. "en-US', it will be used to say the given words.
If lang is specified, e.g. "en-US", it will be used to say the given words.
Otherwise, the language from aiy.i18n will be used.
volume (optional) volume used to say the given words.
pitch (optional) pitch to say the given words.
Example: aiy.audio.say('This is an example', lang="en-US", volume=75, pitch=135)
Any of the optional variables can be left out.
"""

if not lang:
lang = aiy.i18n.get_language_code()
aiy._drivers._tts.say(aiy.audio.get_player(), words, lang=lang)
if not volume:
volume = aiy.audio.get_tts_volume()
if not pitch:
pitch = aiy.audio.get_tts_pitch()
aiy._drivers._tts.say(aiy.audio.get_player(), words, lang=lang, volume=volume, pitch=pitch)


def get_status_ui():
Expand All @@ -129,3 +140,23 @@ def get_status_ui():
if not _status_ui:
_status_ui = aiy._drivers._StatusUi()
return _status_ui


def set_tts_volume(volume):
global _tts_volume
_tts_volume = volume


def get_tts_volume():
global _tts_volume
return _tts_volume


def set_tts_pitch(pitch):
global _tts_pitch
_tts_pitch = pitch


def get_tts_pitch():
global _tts_pitch
return _tts_pitch
36 changes: 35 additions & 1 deletion src/aiy/cloudspeech.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,51 @@ class _CloudSpeechRecognizer(object):
def __init__(self, credentials_file):
self._request = aiy._apis._speech.CloudSpeechRequest(credentials_file)
self._recorder = aiy.audio.get_recorder()
self._hotwords = []

def recognize(self):
"""Recognizes the user's speech and transcript it into text.
This function listens to the user's speech via the VoiceHat speaker. Then it
contacts Google CloudSpeech APIs and returns a textual transcript if possible.
If hotword list is populated this method will only respond if hotword is said.
"""
self._request.reset()
self._request.set_endpointer_cb(self._endpointer_callback)
self._recorder.add_processor(self._request)
return self._request.do_request().transcript
text = self._request.do_request().transcript
if self._hotwords and text:
text = text.lower()
loc_min = len(text)
hotword_found = ''
for hotword in self._hotwords:
loc_temp = text.find(hotword)
if loc_temp > -1 and loc_min > loc_temp:
loc_min = loc_temp
hotword_found = hotword
if hotword_found:
parse_text = text.split(hotword_found)[1]
return parse_text.strip()
else:
return ''
else:
return '' if self._hotwords else text

def expect_hotword(self, hotword_list):
"""Enables hotword detection for a selected list
This method is optional and populates the list of hotwords
to be used for hotword activation.
For example, to create a recognizer for Google:
recognizer.expect_hotword('Google')
recognizer.expect_hotword(['Google','Raspberry Pi'])
"""
if isinstance(hotword_list, list):
for hotword in hotword_list:
self._hotwords.append(hotword.lower())
else:
self._hotwords.append(hotword_list.lower())

def expect_phrase(self, phrase):
"""Explicitly tells the engine that the phrase is more likely to appear.
Expand Down

0 comments on commit bc95e07

Please sign in to comment.