From 0fdbde32aaf3d19f9a49c35dcc057789d923c05f Mon Sep 17 00:00:00 2001 From: Malcolm Jones Date: Wed, 6 Jul 2016 15:23:00 -0400 Subject: [PATCH 1/3] WOOT: full scarlett workflow WORKING. Missing ability to reset pipline when finished. --- generator_commands.py | 226 ++++++++++++++++++++++++++++++++++++++++++ generator_speaker.py | 21 ++-- generator_tasker.py | 93 ++++++++++++++--- 3 files changed, 318 insertions(+), 22 deletions(-) create mode 100644 generator_commands.py diff --git a/generator_commands.py b/generator_commands.py new file mode 100644 index 0000000..0e8f7d4 --- /dev/null +++ b/generator_commands.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import with_statement +from __future__ import division + +import sys +import os + +os.environ[ + "GST_DEBUG_DUMP_DOT_DIR"] = "/home/pi/dev/bossjones-github/scarlett-dbus-poc/_debug" +os.putenv('GST_DEBUG_DUMP_DIR_DIR', + '/home/pi/dev/bossjones-github/scarlett-dbus-poc/_debug') + +import pprint +pp = pprint.PrettyPrinter(indent=4) + +from IPython.core.debugger import Tracer +from IPython.core import ultratb + +sys.excepthook = ultratb.FormattedTB(mode='Verbose', + color_scheme='Linux', + call_pdb=True, + ostream=sys.__stdout__) +import logging +logger = logging.getLogger('scarlettlogger') + +import generator_utils +from generator_utils import trace, abort_on_exception + +import time +import datetime + +CMD_MASTER_LIST_HEX = { + "frizzytv": { + "up": "1,77E1D009,32", + "down": "1,77E1B009,32", + "left": "1,77E11009,32", + "right": "1,77E1E009,32", + "menu": "1,77E1BA09,32", + "pause": "1,77E12009,32", + "play": "1,77E17A09,32", + "circle button": "1,77E1BA09,32" # this too 77E12009 + }, + "appletv": { + "up": "1,77E1D030,32", + "down": "1,77E1B030,32", + "left": "1,77E11030,32", + "right": "1,77E1E030,32", + "menu": "1,77E14030,32", + "pause": "1,77E17A30,32", + "play": "1,77E17A30,32", + "circle button": "1,77E1BA30,32" + }, + "toshiba": { + "channel up": "1,2FDD827,32", + "channel down": "1,2FDF807,32", + "volume up": "1,2FD58A7,32", + "volume down": "1,2FD7887,32", + "mute": "1,2FD08F7,32", + "recall": "1,2FD38C7,32", + "input": "1,2FDF00F,32", + "select up": "1,2FD41BE,32", + "select down": "1,2FDC13E,32", + "select left": "1,2FDB847,32", + "select right": "1,2FD9867,32", + "select enter": "1,2FD916E,32", + "one": "1,2FD807F,32", + "two": "1,2FD40BF,32", + "three": "1,2FDC03F,32", + "four": "1,2FD20DF,32", + "five": "1,2FDA05F,32", + "six": "1,2FD609F,32", + "seven": "1,2FDE01F,32", + "eight": "1,2FD10EF,32", + "nine": "1,2FD906F,32", + "zero": "1,2FD00FF,32", + "power": "1,2FD48B7,32" + } +} + +SPOTIFY_CMDS = { + "SPOTIFY PLAY": "play music", + "SPOTIFY PAUSE": "pause music", + "SPOTIFY SKIP": "skip track", + "SPOTIFY SKIP FORWARD": "track back", + "SPOTIFY SKIP BACK": "track forward", +} + +LIGHT_CMDS = { + "TURN ON THE LIGHTS": "hue lights on", + "TURN ON LIGHTS": "hue lights on", + "LIGHTS ON": "hue lights on", + "TURN OFF THE LIGHTS": "hue lights off", + "TURN OFF LIGHTS": "hue lights off", + "LIGHTS OFF": "hue lights off", + "TURN LIGHTS RED": "hue lights all red", + "LIGHTS RED": "hue lights all red", + "CHANGE LIGHTS RED": "hue lights all red", + "TURN LIGHTS GREEN": "hue lights all green", + "LIGHTS GREEN": "hue lights all green", + "CHANGE LIGHTS GREEN": "hue lights all green", + "TURN LIGHTS WHITE": "hue lights all white", + "LIGHTS WHITE": "hue lights all white", + "CHANGE LIGHTS WHITE": "hue lights all white", + + # TODO: CHANGE THIS BULLSHIT TO USE PROPER PYTHON MODULE + "TURN LIGHTS BRIGHTER": "echo '{\"bri\": 240}' | hue lights 3 state", + "LIGHTS BRIGHTER": "echo '{\"bri\": 240}' | hue lights 3 state", + "TURN LIGHTS DARKER": "echo '{\"bri\": 100}' | hue lights 3 state", + "LIGHTS DARKER": "echo '{\"bri\": 100}' | hue lights 3 state", + "SEXY TIME": "hue lights colorloop", + "GET LIGHT NAMES": "get light names", +} + +TIME_CMDS = { + "WHAT TIME IS IT": "what time is it", + "TIME IS IT": "what time is it", + "TIME IT IS": "what time is it" +} + +TV_CMDS = { + "CHANNEL UP": "channel up", + "CHANNEL DOWN": "channel down", + "TURN TO MTV": "turn to mtv", + "TURN TO BET": "turn to bet", + "TURN TO HBO": "turn to hbo", + "SWITCH TO APPLE TV": "switch to apple tv", + "SWITCH TO PLAY STATION": "switch to play station", + "SWTICH TO REGULAR TV": "switch to regular tv", + "APPLE TV UP": CMD_MASTER_LIST_HEX["appletv"]["up"].lower(), + "APPLE TV CHANNEL UP": CMD_MASTER_LIST_HEX["appletv"]["up"].lower(), + "APPLE TV DOWN": CMD_MASTER_LIST_HEX["appletv"]["down"].lower(), + "APPLE TV CHANNEL DOWN": CMD_MASTER_LIST_HEX["appletv"]["down"].lower(), + "APPLE TV LEFT": CMD_MASTER_LIST_HEX["appletv"]["left"].lower(), + "APPLE TV CHANNEL LEFT": CMD_MASTER_LIST_HEX["appletv"]["left"].lower(), + "APPLE TV RIGHT": CMD_MASTER_LIST_HEX["appletv"]["right"].lower(), + "APPLE TV CHANNEL RIGHT": CMD_MASTER_LIST_HEX["appletv"]["right"].lower(), + "APPLE TV PAUSE": CMD_MASTER_LIST_HEX["appletv"]["pause"].lower(), + "APPLE TV PLAY": CMD_MASTER_LIST_HEX["appletv"]["play"].lower(), + "APPLE TV MENU": CMD_MASTER_LIST_HEX["appletv"]["menu"].lower(), + "APPLE TV MENU BUTTON": CMD_MASTER_LIST_HEX["appletv"]["menu"].lower(), + "APPLE TV ENTER": CMD_MASTER_LIST_HEX["appletv"]["circle button"].lower(), + "APPLE TV ENTER BUTTON": CMD_MASTER_LIST_HEX["appletv"]["circle button"].lower() +} + +GENERAL_CMDS = { + "CANCEL": "cancel", +} + +FORECAST_CMDS = { + "WHAT IS THE FORECAST": "weather", + "WHAT IS THE TEMPATURE": "weather", + "WHAT IS CURRENT TEMPATURE": "weather", + "WHATS THE WEATHER": "weather", + "WHATS TODAYS WEATHER": "weather", + "WHATS THE TEMPATURE": "weather", +} + +NO_OP = '__SCARLETT_NO_OP__' + +###################################################################################################################### +# NOTE: In the future time will be its own plugin class etc, for now we're just going to add arbitrary functions here: +###################################################################################################################### +###################################################################################################################### + + +class TimeCommand(object): + + @staticmethod + def get_current_time(): + now = datetime.datetime.now() + return now.strftime("It is now, %I:%M %p") + + @staticmethod + def get_current_date(): + now = datetime.datetime.now() + return now.strftime("Today's date is, %A, %B %d, %Y") + +###################################################################################################################### +###################################################################################################################### + + +class Command(object): + + @staticmethod + def check_cmd(command_tuple=None): + logger.error("Value of command_tuple: {}".format(command_tuple)) + + if isinstance(command_tuple, tuple): + logger.info("Valid command_tuple: {}".format(command_tuple)) + else: + logger.error("INValid command_tuple: {}".format(command_tuple)) + return NO_OP + + msg, scarlett_sound, command = command_tuple + + if command in SPOTIFY_CMDS.keys(): + logger.debug("** received {}, sending 'spotify {}'".format(command, SPOTIFY_CMDS[command])) + elif command in LIGHT_CMDS.keys(): + logger.debug("** received {}, sending 'light {}'".format(command, LIGHT_CMDS[command])) + # try: + # logger.debug("trying light chit") + # self.get_hue = scarlett.connect_hue(self.voice, self.brain) + # return self.get_hue.get_light_names() + # # REFACTOR ### light_play(LIGHT_CMDS[command]) + # except Exception as e: + # logger.debug("light exception b. \nCMD: {} \nException: {}" %(command, e)) + # # REFACTOR ### general_play("cancel") + elif command in TIME_CMDS.keys(): + logger.debug("** received {}, sending 'time {}'".format(command, TIME_CMDS[command])) + return TimeCommand.get_current_time() + # try: + # from scarlett.features.time import FeatureTime + # self.get_time = FeatureTime(self.voice, self.brain) + # return self.get_time.time_play() + # except Exception as e: + # logger.debug( + # "time exception b. \nCMD: {} \nException: {}" % + # (command, e)) + elif command in GENERAL_CMDS.keys(): + logger.debug("** received {}, sending 'general command: {}'".format(command, GENERAL_CMDS[command])) + elif command in FORECAST_CMDS.keys(): + logger.debug("** received {}, sending 'forecast command: {}'".format(command, FORECAST_CMDS[command])) + elif command in TV_CMDS.keys(): + logger.debug("** received {}, sending 'tv command: {}'".format(command, TV_CMDS[command])) diff --git a/generator_speaker.py b/generator_speaker.py index d97083d..9f849a1 100644 --- a/generator_speaker.py +++ b/generator_speaker.py @@ -45,7 +45,7 @@ class ScarlettSpeaker(object): """Scarlett Speaker Class.""" - def __init__(self, text_to_speak="", wavpath=""): + def __init__(self, text_to_speak="", wavpath="", skip_player=False): """ScarlettSpeaker object. Anything defined here belongs to the INSTANCE of the class.""" self._wavefile = [] self._pitch = 75 @@ -60,6 +60,8 @@ def __init__(self, text_to_speak="", wavpath=""): "-w", self._wavpath, "-v%s" % self._voice, ". %s ." % self._text] + self.path = None + # Write espeak data with generator_utils.time_logger('Espeak Subprocess To File'): self.running = True @@ -70,14 +72,15 @@ def __init__(self, text_to_speak="", wavpath=""): print "Did is run successfully? {}".format(self.res) # Have Gstreamer play it - for path in self._wavefile: - path = os.path.abspath(os.path.expanduser(path)) - with generator_player.ScarlettPlayer(path) as f: - print(f.channels) - print(f.samplerate) - print(f.duration) - for s in f: - pass + if skip_player != True: + for path in self._wavefile: + path = os.path.abspath(os.path.expanduser(path)) + with generator_player.ScarlettPlayer(path) as f: + print(f.channels) + print(f.samplerate) + print(f.duration) + for s in f: + pass # Cleanup. def close(self, force=False): diff --git a/generator_tasker.py b/generator_tasker.py index 1a6b49a..3a5f34a 100644 --- a/generator_tasker.py +++ b/generator_tasker.py @@ -52,6 +52,7 @@ from generator_utils import trace, abort_on_exception, _IdleObject import generator_player import generator_speaker +import generator_commands import logging logger = logging.getLogger('scarlettlogger') @@ -353,6 +354,51 @@ def command_cb(*args, **kwargs): # iface='org.scarlett.Listener', # signal='CommandRecognizedSignal', # params=GLib.Variant('(sss)', (' ScarlettListener caugh...ommand match', 'pi-response', 'what time is it'))) + + # NOTE: THIS IS WHAT FIXED THE GENERATOR NONSENSE + # source: https://www.python.org/dev/peps/pep-0343/ + def player_generator_func(): + for path in wavefile: + path = os.path.abspath(os.path.expanduser(path)) + yield True + print("for path in wavefile") + p = generator_player.ScarlettPlayer(path, False) + while True: + try: + yield p.next() + finally: + time.sleep(p.duration) + p.close(force=True) + yield False + + def run_player(function): + gen = function() + GObject.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_HIGH) + + + def speaker_generator_func(): + for scarlett_text in tts_list: + yield True + print("scarlett_text in tts_list") + _wavepath = "/home/pi/dev/bossjones-github/scarlett-dbus-poc/espeak_tmp.wav" + s = generator_speaker.ScarlettSpeaker(text_to_speak=scarlett_text, + wavpath=_wavepath, + skip_player=True) + p = generator_player.ScarlettPlayer(_wavepath, False) + logger.error("Duration: p.duration: {}".format(p.duration)) + while True: + try: + yield p.next() + finally: + time.sleep(p.duration) + p.close(force=True) + s.close(force=True) + yield False + + def run_speaker(function): + gen = function() + GObject.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_HIGH) + for i, v in enumerate(args): if SCARLETT_DEBUG: logger.debug("Type v: {}".format(type(v))) @@ -366,19 +412,40 @@ def command_cb(*args, **kwargs): logger.warning( " scarlett_sound: {}".format(scarlett_sound)) logger.warning(" command: {}".format(command)) - command_run = True - if command_run: - tts_list = SpeakerType.speaker_to_array( - 'Hello sir. How are you doing this afternoon? I am full lee function nall, andd red ee for your commands') - logger.info('BEGIN PLAYING INTRO') - for scarlett_text in tts_list: - with generator_utils.time_logger('Scarlett Speaks'): - generator_speaker.ScarlettSpeaker(text_to_speak=scarlett_text, - wavpath="/home/pi/dev/bossjones-github/scarlett-dbus-poc/espeak_tmp.wav") - logger.info('FINISHED PLAYING INTRO') - tts_list = None - command_run = False - return True + + # 1. play sound first + wavefile = SoundType.get_path(scarlett_sound) + run_player_result = run_player(player_generator_func) + + # 2. Perform command + command_run_results = generator_commands.Command.check_cmd(command_tuple=v) + + # 3. Verify it is not a command NO_OP + if command_run_results == '__SCARLETT_NO_OP__': + logger.error("__SCARLETT_NO_OP__") + return False + + # 4. Scarlett Speaks + tts_list = SpeakerType.speaker_to_array(command_run_results) + run_speaker_result = run_speaker(speaker_generator_func) + + # 5. Emit signal to reset keyword match + # 6. Finished call back + + # command_run = True + # if command_run: + # tts_list = SpeakerType.speaker_to_array( + # 'Hello sir. How are you doing this afternoon? I am full lee function nall, andd red ee for your commands') + # logger.info('BEGIN PLAYING INTRO') + # for scarlett_text in tts_list: + # with generator_utils.time_logger('Scarlett Speaks'): + # generator_speaker.ScarlettSpeaker(text_to_speak=scarlett_text, + # wavpath="/home/pi/dev/bossjones-github/scarlett-dbus-poc/espeak_tmp.wav", + # skip_player=True) + # logger.info('FINISHED PLAYING INTRO') + # tts_list = None + # command_run = False + # return True else: logger.debug("THIS IS NOT A GLib.Variant: {} - TYPE {}".format(v, type(v))) From d5c5dbd94bc87b4d36fdd80db4bbbea6f9d339b4 Mon Sep 17 00:00:00 2001 From: Malcolm Jones Date: Wed, 6 Jul 2016 15:29:39 -0400 Subject: [PATCH 2/3] chg: currently using emitListenerCancelSignal, need to have that reset Listener --- generator_tasker.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/generator_tasker.py b/generator_tasker.py index 3a5f34a..8492971 100644 --- a/generator_tasker.py +++ b/generator_tasker.py @@ -429,23 +429,12 @@ def run_speaker(function): tts_list = SpeakerType.speaker_to_array(command_run_results) run_speaker_result = run_speaker(speaker_generator_func) - # 5. Emit signal to reset keyword match + # 5. Emit signal to reset keyword match ( need to implement this ) + bus = SessionBus() + ss = bus.get("org.scarlett", object_path='/org/scarlett/Listener') # NOQA + time.sleep(1) + ss.emitListenerCancelSignal() # 6. Finished call back - - # command_run = True - # if command_run: - # tts_list = SpeakerType.speaker_to_array( - # 'Hello sir. How are you doing this afternoon? I am full lee function nall, andd red ee for your commands') - # logger.info('BEGIN PLAYING INTRO') - # for scarlett_text in tts_list: - # with generator_utils.time_logger('Scarlett Speaks'): - # generator_speaker.ScarlettSpeaker(text_to_speak=scarlett_text, - # wavpath="/home/pi/dev/bossjones-github/scarlett-dbus-poc/espeak_tmp.wav", - # skip_player=True) - # logger.info('FINISHED PLAYING INTRO') - # tts_list = None - # command_run = False - # return True else: logger.debug("THIS IS NOT A GLib.Variant: {} - TYPE {}".format(v, type(v))) From e5edac38f73ce60a1a1ff199ab4783c8251767d6 Mon Sep 17 00:00:00 2001 From: Malcolm Jones Date: Wed, 6 Jul 2016 15:35:41 -0400 Subject: [PATCH 3/3] WOOT: cancel listening logic workingggg --- generator_listener.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/generator_listener.py b/generator_listener.py index 9c7109e..e572eb0 100644 --- a/generator_listener.py +++ b/generator_listener.py @@ -371,7 +371,7 @@ def scarlett_reset_listen(self): self.failed = 0 self.kw_found = 0 - def cancel_listening(self): + def cancel_listening(self, *args, **kwargs): logger.debug("Inside cancel_listening function") self.scarlett_reset_listen() logger.debug("self.failed = %i" % (self.failed)) @@ -502,6 +502,13 @@ def _connect_to_dbus(self): self.dbus_proxy.emitConnectedToListener('ScarlettListener') sleep(2) logger.info('_connect_to_dbus') + ss_cancel_signal = self.bus.subscribe(sender=None, + iface="org.scarlett.Listener", + signal="ListenerCancelSignal", + object="/org/scarlett/Listener", + arg0=None, + flags=0, + signal_fired=self.cancel_listening) # NOTE: This function generates the dot file, checks that graphviz in installed and # then finally generates a png file, which it then displays