diff options
| -rwxr-xr-x | boot/boot.py | 18 | ||||
| -rwxr-xr-x | boot/boot.sh | 15 | ||||
| -rw-r--r-- | boot/test.py | 10 | ||||
| -rw-r--r-- | client/g2p.py | 11 | ||||
| -rw-r--r-- | client/main.py | 3 | ||||
| -rw-r--r-- | client/mic.py | 13 | ||||
| -rw-r--r-- | client/speaker.py | 57 | ||||
| -rwxr-xr-x | client/start.sh | 2 | ||||
| -rw-r--r-- | client/test.py | 8 |
9 files changed, 112 insertions, 25 deletions
diff --git a/boot/boot.py b/boot/boot.py index 456dd12..12b60eb 100755 --- a/boot/boot.py +++ b/boot/boot.py @@ -3,12 +3,16 @@ import os import json import urllib2 +import sys import vocabcompiler +import traceback -def say(phrase, OPTIONS = " -vdefault+m3 -p 40 -s 160 --stdout > ../static/audio/say.wav"): - os.system("espeak " + json.dumps(phrase) + OPTIONS) - os.system("aplay -D hw:1,0 ../static/audio/say.wav") +lib_path = os.path.abspath('../client') +sys.path.append(lib_path) + +import speaker as speak +speaker = speak.newSpeaker() def configure(): try: @@ -19,17 +23,17 @@ def configure(): vocabcompiler.compile("../client/sentences.txt", "../client/dictionary.dic", "../client/languagemodel.lm") print "STARTING CLIENT PROGRAM" - os.system("/home/pi/jasper/client/start.sh &") + os.system("$JASPER_HOME/jasper/client/start.sh &") except: - print "COULD NOT CONNECT TO NETWORK" - say("Hello, I could not connect to a network. Please read the documentation to configure your Raspberry Pi.") + traceback.print_exc() + speaker.say("Hello, I could not connect to a network. Please read the documentation to configure your Raspberry Pi.") if __name__ == "__main__": print "==========STARTING JASPER CLIENT==========" print "==========================================" print "COPYRIGHT 2013 SHUBHRO SAHA, CHARLIE MARSH" print "==========================================" - say("Hello.... I am Jasper... Please wait one moment.") + speaker.say("Hello.... I am Jasper... Please wait one moment.") configure() diff --git a/boot/boot.sh b/boot/boot.sh index 5ea1b3d..e712c0d 100755 --- a/boot/boot.sh +++ b/boot/boot.sh @@ -1,6 +1,17 @@ -cd /home/pi/jasper/boot/ +#!/bin/bash +if [[ -z "$JASPER_HOME" ]]; then + if [[ -d "/home/pi" ]]; then + JASPER_HOME="/home/pi" + export JASPER_HOME; + else + echo "Error: \$JASPER_HOME is not set." + exit 0; + fi +fi + +cd $JASPER_HOME/jasper/boot LD_LIBRARY_PATH="/usr/local/lib" export LD_LIBRARY_PATH PATH=$PATH:/usr/local/lib/ export PATH -python boot.py &
\ No newline at end of file +python boot.py & diff --git a/boot/test.py b/boot/test.py index c6bd3ff..af5f982 100644 --- a/boot/test.py +++ b/boot/test.py @@ -1,4 +1,8 @@ import os + +if os.environ.get('JASPER_HOME') is None: + os.environ['JASPER_HOME'] = '/home/pi' + import sys import unittest from mock import patch @@ -12,6 +16,10 @@ sys.path.append(mod_path) import g2p +class UnorderedList(list): + def __eq__(self, other): + return sorted(self) == sorted(other) + class TestVocabCompiler(unittest.TestCase): @@ -33,7 +41,7 @@ class TestVocabCompiler(unittest.TestCase): # 'words' is appended with ['MUSIC', 'SPOTIFY'] # so must be > 2 to have received WORDS from modules - translateWords.assert_called_once_with(words) + translateWords.assert_called_once_with(UnorderedList(words)) self.assertTrue(text2lm.called) os.remove(sentences) os.remove(dictionary) diff --git a/client/g2p.py b/client/g2p.py index 06fef14..6d72bfe 100644 --- a/client/g2p.py +++ b/client/g2p.py @@ -4,6 +4,7 @@ import re TEMP_FILENAME = "g2ptemp" PHONE_MATCH = re.compile(r'<s> (.*) </s>') +PHONETISAURUS_PATH = os.environ['JASPER_HOME'] + "/phonetisaurus" def parseLine(line): @@ -16,7 +17,7 @@ def parseOutput(output): def translateWord(word): out = subprocess.check_output(['phonetisaurus-g2p', '--model=%s' % - os.path.expanduser("~/phonetisaurus/g014b2b.fst"), '--input=%s' % word]) + PHONETISAURUS_PATH + "/g014b2b.fst", '--input=%s' % word]) return parseLine(out) @@ -34,8 +35,8 @@ def translateWords(words): def translateFile(input_filename, output_filename=None): - out = subprocess.check_output(['phonetisaurus-g2p', '--model=%s' % os.path.expanduser( - "~/phonetisaurus/g014b2b.fst"), '--input=%s' % input_filename, '--words', '--isfile']) + out = subprocess.check_output(['phonetisaurus-g2p', '--model=%s' % + PHONETISAURUS_PATH + "/g014b2b.fst", '--input=%s' % input_filename, '--words', '--isfile']) out = parseOutput(out) if output_filename: @@ -51,5 +52,5 @@ def translateFile(input_filename, output_filename=None): if __name__ == "__main__": - translateFile(os.path.expanduser("~/phonetisaurus/sentences.txt"), - os.path.expanduser("~/phonetisaurus/dictionary.dic")) + translateFile(PHONETISAURUS_PATH + "/phonetisaurus/sentences.txt", + PHONETISAURUS_PATH + "/phonetisaurus/dictionary.dic") diff --git a/client/main.py b/client/main.py index 0b29c75..6ba3645 100644 --- a/client/main.py +++ b/client/main.py @@ -1,5 +1,6 @@ import yaml import sys +import speaker from conversation import Conversation @@ -20,7 +21,7 @@ if __name__ == "__main__": profile = yaml.safe_load(open("profile.yml", "r")) - mic = Mic("languagemodel.lm", "dictionary.dic", + mic = Mic(speaker.newSpeaker(), "languagemodel.lm", "dictionary.dic", "languagemodel_persona.lm", "dictionary_persona.dic") addendum = "" diff --git a/client/mic.py b/client/mic.py index d746ba1..f4b70df 100644 --- a/client/mic.py +++ b/client/mic.py @@ -22,17 +22,18 @@ class Mic: speechRec = None speechRec_persona = None - def __init__(self, lmd, dictd, lmd_persona, dictd_persona, lmd_music=None, dictd_music=None): + def __init__(self, speaker, lmd, dictd, lmd_persona, dictd_persona, lmd_music=None, dictd_music=None): """ Initiates the pocketsphinx instance. Arguments: + speaker -- handles platform-independent audio output lmd -- filename of the full language model dictd -- filename of the full dictionary (.dic) lmd_persona -- filename of the 'Persona' language model (containing, e.g., 'Jasper') dictd_persona -- filename of the 'Persona' dictionary (.dic) """ - + self.speaker = speaker hmdir = "/usr/local/share/pocketsphinx/model/hmm/en_US/hub4wsj_sc_8k" if lmd_music and dictd_music: @@ -237,7 +238,7 @@ class Mic: if THRESHOLD == None: THRESHOLD = self.fetchThreshold() - os.system("aplay -D hw:1,0 ../static/audio/beep_hi.wav") + self.speaker.play("../static/audio/beep_hi.wav") # prepare recording stream audio = pyaudio.PyAudio() @@ -267,7 +268,7 @@ class Mic: if average < THRESHOLD * 0.8: break - os.system("aplay -D hw:1,0 ../static/audio/beep_lo.wav") + self.speaker.play("../static/audio/beep_lo.wav") # save the audio data stream.stop_stream() @@ -291,6 +292,4 @@ class Mic: def say(self, phrase, OPTIONS=" -vdefault+m3 -p 40 -s 160 --stdout > say.wav"): # alter phrase before speaking phrase = alteration.clean(phrase) - - os.system("espeak " + json.dumps(phrase) + OPTIONS) - os.system("aplay -D hw:1,0 say.wav") + self.speaker.say(phrase) diff --git a/client/speaker.py b/client/speaker.py new file mode 100644 index 0000000..1b1255a --- /dev/null +++ b/client/speaker.py @@ -0,0 +1,57 @@ +""" +A Speaker handles audio output from Jasper to the user + +Speaker methods: + say - output 'phrase' as speech + play - play the audio in 'filename' + isAvailable - returns True if the platform supports this implementation +""" +import os +import json + +class eSpeakSpeaker: + """ + Uses the eSpeak speech synthesizer included in the Jasper disk image + """ + @classmethod + def isAvailable(cls): + return os.system("which espeak") == 0 + + def say(self, phrase, OPTIONS=" -vdefault+m3 -p 40 -s 160 --stdout > say.wav"): + os.system("espeak " + json.dumps(phrase) + OPTIONS) + self.play("say.wav") + + def play(self, filename): + os.system("aplay -D hw:1,0 " + filename) + +class saySpeaker: + """ + Uses the OS X built-in 'say' command + """ + + @classmethod + def isAvailable(cls): + return os.system("which say") == 0 + + def shellquote(self, s): + return "'" + s.replace("'", "'\\''") + "'" + + def say(self, phrase): + os.system("say " + self.shellquote(phrase)) + + def play(self, filename): + os.system("afplay " + filename) + +def newSpeaker(): + """ + Returns: + A speaker implementation available on the current platform + + Raises: + ValueError if no speaker implementation is supported on this platform + """ + + for cls in [eSpeakSpeaker, saySpeaker]: + if cls.isAvailable(): + return cls() + raise ValueError("Platform is not supported") diff --git a/client/start.sh b/client/start.sh index f3eea8b..cc33f9c 100755 --- a/client/start.sh +++ b/client/start.sh @@ -1,3 +1,3 @@ -cd /home/pi/jasper/client/ +cd $JASPER_HOME/jasper/client rm -rf ../old_client python main.py & diff --git a/client/test.py b/client/test.py index 80191ca..fb7d147 100644 --- a/client/test.py +++ b/client/test.py @@ -1,3 +1,8 @@ +import os + +if os.environ.get('JASPER_HOME') is None: + os.environ['JASPER_HOME'] = '/home/pi' + import unittest import argparse from mock import patch @@ -6,6 +11,7 @@ import yaml import test_mic import g2p import brain +import speaker def activeInternet(): @@ -23,7 +29,7 @@ class TestMic(unittest.TestCase): self.time_clip = "../static/audio/time.wav" from mic import Mic - self.m = Mic("languagemodel.lm", "dictionary.dic", + self.m = Mic(speaker.newSpeaker(), "languagemodel.lm", "dictionary.dic", "languagemodel_persona.lm", "dictionary_persona.dic") def testTranscribeJasper(self): |
