Skip to content

Commit

Permalink
Merge pull request #100 from astahlman/master
Browse files Browse the repository at this point in the history
Platform independent audio output. Stop hardcoding /home/jasper.
  • Loading branch information
shbhrsaha committed Jul 11, 2014
2 parents 659e276 + 4391496 commit bca2361
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 25 deletions.
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")

addendum = ""
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

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

0 comments on commit bca2361

Please sign in to comment.