Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platform independent audio output. Stop hardcoding /home/jasper. #100

Merged
merged 8 commits into from
Jul 11, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions boot/boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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()
15 changes: 13 additions & 2 deletions boot/boot.sh
Original file line number Diff line number Diff line change
@@ -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 &
python boot.py &
10 changes: 9 additions & 1 deletion boot/test.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -12,6 +16,10 @@

import g2p

class UnorderedList(list):
def __eq__(self, other):
return sorted(self) == sorted(other)


class TestVocabCompiler(unittest.TestCase):

Expand All @@ -33,7 +41,7 @@ def testWordExtraction(self):

# '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)
Expand Down
11 changes: 6 additions & 5 deletions client/g2p.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

TEMP_FILENAME = "g2ptemp"
PHONE_MATCH = re.compile(r'<s> (.*) </s>')
PHONETISAURUS_PATH = os.environ['JASPER_HOME'] + "/phonetisaurus"


def parseLine(line):
Expand All @@ -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)


Expand All @@ -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:
Expand All @@ -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")
3 changes: 2 additions & 1 deletion client/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import yaml
import sys
import speaker
from conversation import Conversation


Expand All @@ -20,7 +21,7 @@ def isLocal():

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")

mic.say("How can I be of service?")
Expand Down
13 changes: 6 additions & 7 deletions client/mic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -237,7 +238,7 @@ def activeListen(self, THRESHOLD=None, LISTEN=True, MUSIC=False):
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()
Expand Down Expand Up @@ -267,7 +268,7 @@ def activeListen(self, THRESHOLD=None, LISTEN=True, MUSIC=False):
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()
Expand All @@ -291,6 +292,4 @@ def activeListen(self, THRESHOLD=None, LISTEN=True, MUSIC=False):
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)
57 changes: 57 additions & 0 deletions client/speaker.py
Original file line number Diff line number Diff line change
@@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add docstrings to these classes, and take note of the comment on line 9 as well?

class eSpeakSpeaker:
"""
Uses the eSpeak speech synthesizer included in the Jasper disk image
"""
@classmethod
def isAvailable(cls):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a slight misrepresentation to say that, just because you have eSpeak installed, you're on a Raspberry Pi. I would propose renaming these classes to eSpeakSpeaker and saySpeaker, and then noting in the docstring for each class that it's intended to be used on OS X or Raspberry Pi, respectively.

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")
2 changes: 1 addition & 1 deletion client/start.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
cd /home/pi/jasper/client/
cd $JASPER_HOME/jasper/client
rm -rf ../old_client
python main.py &
8 changes: 7 additions & 1 deletion client/test.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -6,6 +11,7 @@
import test_mic
import g2p
import brain
import speaker


def activeInternet():
Expand All @@ -23,7 +29,7 @@ def setUp(self):
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):
Expand Down