From 84a92e5327997815488276021e5498f7681ab2d7 Mon Sep 17 00:00:00 2001 From: schneefux Date: Sun, 8 Jun 2014 20:39:35 -0700 Subject: Stop hardcoding /home/jasper and abstract out mic.say for multiplatform support --- client/speaker.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 client/speaker.py (limited to 'client/speaker.py') diff --git a/client/speaker.py b/client/speaker.py new file mode 100644 index 0000000..304b426 --- /dev/null +++ b/client/speaker.py @@ -0,0 +1,35 @@ +""" +A Speaker handles output from Jasper to the user. +""" +import os, json + +class PiSpeaker: + + @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.playSound("say.wav") + + def playSound(self, filename): + os.system("aplay -D hw:1,0 " + filename) + +class MacSpeaker: + + @classmethod + def isAvailable(cls): + return os.system("which say") == 0 + + def say(self, phrase): + os.system("say " + phrase) + + def playSound(self, filename): + os.system("afplay " + filename) + +def newSpeaker(): + for cls in [PiSpeaker, MacSpeaker]: + if cls.isAvailable(): + return cls() + raise ValueError("Platform is not supported") -- cgit v1.3.1 From 18c02563c04f77e1732194d74e537787247918b9 Mon Sep 17 00:00:00 2001 From: schneefux Date: Sat, 21 Jun 2014 13:11:56 -0700 Subject: Clean up before pull request. --- boot/boot.py | 3 ++- client/mic.py | 4 ++-- client/speaker.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'client/speaker.py') diff --git a/boot/boot.py b/boot/boot.py index fcaeedd..245bd4d 100755 --- a/boot/boot.py +++ b/boot/boot.py @@ -1,6 +1,7 @@ #!/usr/bin/env python -import os, sys +import os +import sys import urllib2 import yaml import vocabcompiler diff --git a/client/mic.py b/client/mic.py index 0c41df9..f14ebc6 100644 --- a/client/mic.py +++ b/client/mic.py @@ -27,7 +27,7 @@ class Mic: Initiates the pocketsphinx instance. Arguments: - speaker -- used to output audio + 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') @@ -268,7 +268,7 @@ class Mic: if average < THRESHOLD * 0.8: break - self.speaker.playSound("beep_lo.wav") + self.speaker.playSound("../static/audio/beep_lo.wav") # save the audio data stream.stop_stream() diff --git a/client/speaker.py b/client/speaker.py index 304b426..f2d4ab7 100644 --- a/client/speaker.py +++ b/client/speaker.py @@ -1,5 +1,5 @@ """ -A Speaker handles output from Jasper to the user. +A Speaker handles audio output from Jasper to the user """ import os, json -- cgit v1.3.1 From 02f3505cd6d585a24d79cca033f30ce88e972563 Mon Sep 17 00:00:00 2001 From: schneefux Date: Sat, 21 Jun 2014 14:27:05 -0700 Subject: Escape phrases before 'say'ing them in OS X --- boot/test.py | 1 - client/speaker.py | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'client/speaker.py') diff --git a/boot/test.py b/boot/test.py index 4c79f39..b390d5e 100644 --- a/boot/test.py +++ b/boot/test.py @@ -5,7 +5,6 @@ if os.environ.get('JASPER_HOME') is None: import sys import unittest -from sets import Set from mock import patch import vocabcompiler diff --git a/client/speaker.py b/client/speaker.py index f2d4ab7..e7dc075 100644 --- a/client/speaker.py +++ b/client/speaker.py @@ -22,8 +22,11 @@ class MacSpeaker: def isAvailable(cls): return os.system("which say") == 0 + def shellquote(self, s): + return "'" + s.replace("'", "'\\''") + "'" + def say(self, phrase): - os.system("say " + phrase) + os.system("say " + self.shellquote(phrase)) def playSound(self, filename): os.system("afplay " + filename) -- cgit v1.3.1 From 3419eee951be8660f13b5d750a33b8e8ed64b425 Mon Sep 17 00:00:00 2001 From: schneefux Date: Sat, 28 Jun 2014 15:16:57 -0700 Subject: Changes suggested in CR. --- .gitignore | 1 + boot/test.py | 4 ++-- client/mic.py | 4 ++-- client/notifier.py | 2 +- client/speaker.py | 35 +++++++++++++++++++++++++++-------- 5 files changed, 33 insertions(+), 13 deletions(-) (limited to 'client/speaker.py') diff --git a/.gitignore b/.gitignore index 08cf7c5..a5affb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ env/ client/env client/venv +client/modules/* *.pyc client/active.wav client/passive.wav diff --git a/boot/test.py b/boot/test.py index b390d5e..af5f982 100644 --- a/boot/test.py +++ b/boot/test.py @@ -16,7 +16,7 @@ sys.path.append(mod_path) import g2p -class ListWhereOrderDoesNotMatter(list): +class UnorderedList(list): def __eq__(self, other): return sorted(self) == sorted(other) @@ -41,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(ListWhereOrderDoesNotMatter(words)) + translateWords.assert_called_once_with(UnorderedList(words)) self.assertTrue(text2lm.called) os.remove(sentences) os.remove(dictionary) diff --git a/client/mic.py b/client/mic.py index f14ebc6..f4b70df 100644 --- a/client/mic.py +++ b/client/mic.py @@ -238,7 +238,7 @@ class Mic: if THRESHOLD == None: THRESHOLD = self.fetchThreshold() - self.speaker.playSound("../static/audio/beep_hi.wav") + self.speaker.play("../static/audio/beep_hi.wav") # prepare recording stream audio = pyaudio.PyAudio() @@ -268,7 +268,7 @@ class Mic: if average < THRESHOLD * 0.8: break - self.speaker.playSound("../static/audio/beep_lo.wav") + self.speaker.play("../static/audio/beep_lo.wav") # save the audio data stream.stop_stream() diff --git a/client/notifier.py b/client/notifier.py index 341ae04..7e6b0a2 100644 --- a/client/notifier.py +++ b/client/notifier.py @@ -1,5 +1,5 @@ import Queue -from modules import Gmail +#from modules import Gmail from apscheduler.scheduler import Scheduler import logging logging.basicConfig() diff --git a/client/speaker.py b/client/speaker.py index e7dc075..1b1255a 100644 --- a/client/speaker.py +++ b/client/speaker.py @@ -1,22 +1,33 @@ """ A Speaker handles audio output from Jasper to the user -""" -import os, json -class PiSpeaker: +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.playSound("say.wav") + self.play("say.wav") - def playSound(self, filename): + def play(self, filename): os.system("aplay -D hw:1,0 " + filename) -class MacSpeaker: +class saySpeaker: + """ + Uses the OS X built-in 'say' command + """ @classmethod def isAvailable(cls): @@ -28,11 +39,19 @@ class MacSpeaker: def say(self, phrase): os.system("say " + self.shellquote(phrase)) - def playSound(self, filename): + def play(self, filename): os.system("afplay " + filename) def newSpeaker(): - for cls in [PiSpeaker, MacSpeaker]: + """ + 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") -- cgit v1.3.1