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

Adding test automation allowing a unit test to feed in audio files an… #568

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
Empty file added __init__.py
Empty file.
66 changes: 66 additions & 0 deletions client/automated_mic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# -*- coding: utf-8-*-
"""
A drop-in replacement for the Mic class that allows for input I/O to
happen via provided audio file and output via string return. This is
intended to drive automated testing.

Unlike test_mic.py this does include both active and passive listening
in order to support testing and evaluation of wake words and related
configuration.
"""

import logging
import pyaudio


class Mic:
prev = None

def __init__(self, speaker, passive_stt_engine, active_stt_engine):
"""
Initiates the pocketsphinx instance.

Arguments:
speaker -- handles platform-independent audio output
passive_stt_engine -- performs STT while Jasper is in passive listen
mode
acive_stt_engine -- performs STT while Jasper is in active listen mode
"""
self._logger = logging.getLogger(__name__)
self.speaker = speaker
self.passive_stt_engine = passive_stt_engine
self.active_stt_engine = active_stt_engine
self._logger.info("Initializing PyAudio. ALSA/Jack error messages " +
"that pop up during this process are normal and " +
"can usually be safely ignored.")
self._audio = pyaudio.PyAudio()
self._logger.info("Initialization of PyAudio completed.")

def listen(self, stt_engine):
with open(self.audio_input, 'rb') as f:
return stt_engine.transcribe(f)

def passiveListen(self, PERSONA):
# check if PERSONA was said
transcribed = self.listen(self.passive_stt_engine)

if any(PERSONA in phrase for phrase in transcribed):
return (False, PERSONA)

return (False, transcribed)

def activeListenToAllOptions(self, THRESHOLD=None, LISTEN=True,
MUSIC=False):
return [self.activeListen(THRESHOLD=THRESHOLD, LISTEN=LISTEN,
MUSIC=MUSIC)]

def activeListen(self, THRESHOLD=None, LISTEN=True, MUSIC=False):
if not LISTEN:
return self.prev

input = self.listen(self.active_stt_engine)
self.prev = input
return input

def say(self, phrase, OPTIONS=None):
self.text_output = phrase
7 changes: 7 additions & 0 deletions client/jasperpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
DATA_PATH = os.path.join(APP_PATH, "static")
LIB_PATH = os.path.join(APP_PATH, "client")
PLUGIN_PATH = os.path.join(LIB_PATH, "modules")
TEST_PATH = os.path.join(APP_PATH, "tests")
TEST_AUDIO_PATH = os.path.join(TEST_PATH, "audio")

CONFIG_PATH = os.path.expanduser(os.getenv('JASPER_CONFIG', '~/.jasper'))

Expand All @@ -18,3 +20,8 @@ def config(*fname):

def data(*fname):
return os.path.join(DATA_PATH, *fname)


# Convenience method to join a filename to a path
def join(path, *fname):
return os.path.join(path, *fname)
35 changes: 22 additions & 13 deletions jasper.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,29 @@
# Add jasperpath.LIB_PATH to sys.path
sys.path.append(jasperpath.LIB_PATH)

parser = argparse.ArgumentParser(description='Jasper Voice Control Center')
parser.add_argument('--local', action='store_true',
help='Use text input instead of a real microphone')
parser.add_argument('--no-network-check', action='store_true',
help='Disable the network connection check')
parser.add_argument('--diagnose', action='store_true',
help='Run diagnose and exit')
parser.add_argument('--debug', action='store_true', help='Show debug messages')
args = parser.parse_args()

if args.local:
from client.local_mic import Mic
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Jasper Voice Control Center')
parser.add_argument('--local', action='store_true',
help='Use text input instead of a real microphone')
parser.add_argument('--no-network-check', action='store_true',
help='Disable the network connection check')
parser.add_argument('--automated', action='store_true',
help='Use audio input from files and text output')
parser.add_argument('--diagnose', action='store_true',
help='Run diagnose and exit')
parser.add_argument('--debug', action='store_true',
help='Show debug messages')
args = parser.parse_args()

if args.local:
from client.local_mic import Mic
elif args.automated:
from client.automated_mic import Mic
else:
from client.mic import Mic
else:
from client.mic import Mic
# Running this as a module, so assume an automated test scenario
from client.automated_mic import Mic


class Jasper(object):
Expand Down
Binary file added tests/audio/jasper.wav
Binary file not shown.
Binary file added tests/audio/meaning_of_life.wav
Binary file not shown.
44 changes: 44 additions & 0 deletions tests/test_audio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python2
# -*- coding: utf-8-*-
import unittest
from client import jasperpath
from client.modules import Life
from jasper import Jasper

DEFAULT_PROFILE = {
'prefers_email': False,
'location': 'Cape Town',
'timezone': 'US/Eastern',
'phone_number': '012344321'
}

WAKE_AUDIO_FILE = jasperpath.join(jasperpath.TEST_AUDIO_PATH, 'jasper.wav')

# set input file path to mic.audio_input
# read output text from mic.text_output


class TestAudio(unittest.TestCase):

def setUp(self):
self.profile = DEFAULT_PROFILE
self.send = False
self.persona = "Jasper"
self.jasper = Jasper()
self.mic = self.jasper.mic

def testLife(self):
# Wake up
self.mic.audio_input = WAKE_AUDIO_FILE
threshold, transcribed = self.mic.passiveListen(self.persona)
print(transcribed)

# Run the meaning of life module
self.mic.audio_input = jasperpath.join(jasperpath.TEST_AUDIO_PATH,
'meaning_of_life.wav')
transcribed = self.mic.activeListen()
print(transcribed)
Life.handle(transcribed, self.mic, self.profile)

print(self.mic.text_output)
self.assertTrue("42" in self.mic.text_output)