def start(self): """ Start Zeroconf inside thread """ self.threads = ThreadGroup() self.threads.targets = [self.run] self.threads.start_all()
def wakeword_loop(): """ Run wakeword listener loop """ th = ThreadGroup() th.targets = [[subprocess.call, ['python', '-m', 'voiceplay.recognition.wakeword.sender']]] th.start_all() return th
def start(self): """ Start TTS as a separate thread """ logger.debug('Starting TTS engine...') self.threads = ThreadGroup() self.threads.targets = [self.poll_loop] self.threads.start_all()
def start(self): """ Start VickiPlayer and auxiliary loops using thread group """ # non-blocking start self.player.start(debug=self.debug) self.thread_group = ThreadGroup() self.thread_group.targets = [ self.task_loop, self.cmd_loop, self.prefetch_loop ] self.thread_group.start_all()
def wakeword_loop(): """ Run wakeword listener loop """ th = ThreadGroup() th.targets = [[ subprocess.call, ['python', '-m', 'voiceplay.recognition.wakeword.sender'] ]] th.start_all() return th
def vicki_loop(vicki, noblock=False): """ Run Vicki loop """ vicki.player.start() vicki.tts.start() ThreadedRequestHandler.callback = vicki.wakeword_callback address = ('127.0.0.1', 63455) server = WakeWordReceiver(address, ThreadedRequestHandler) threads = ThreadGroup() threads.targets = [server.serve_forever] threads.start_all() vicki.run_forever_new(server, noblock=noblock)
def run_forever_new(self, wakeword_receiver, noblock=False): """ Recognition main loop """ self.wakeword_receiver = wakeword_receiver if noblock: threads = ThreadGroup() threads.targets = [self.background_listener] threads.start_all() return while not self.shutdown: try: self.background_listener() except (KeyboardInterrupt, SystemExit): self.stop() except Exception as exc: logger.debug('Background listener failed with %r, restarting...', exc)
def run_forever_new(self, wakeword_receiver, noblock=False): """ Recognition main loop """ self.wakeword_receiver = wakeword_receiver if noblock: threads = ThreadGroup() threads.targets = [self.background_listener] threads.start_all() return while not self.shutdown: try: self.background_listener() except (KeyboardInterrupt, SystemExit): self.stop() except Exception as exc: logger.debug( 'Background listener failed with %r, restarting...', exc)
def start(self): """ Start VickiPlayer and auxiliary loops using thread group """ # non-blocking start self.player.start(debug=self.debug) self.thread_group = ThreadGroup() self.thread_group.targets = [self.task_loop, self.cmd_loop, self.prefetch_loop] self.thread_group.start_all()
def player_console(vicki, queue=None): """ Start VickiPlayer console """ helper = Help() console = Console() console.add_handler(Command.PLAY, vicki.player.play_from_parser, Command().CONTROLS) console.add_handler('what', vicki.player.play_from_parser) console.add_handler('current_track', vicki.player.play_from_parser) helper.register(console) console.set_queue(queue) th = ThreadGroup(restart=False) th.targets = [console.run_bg_queue] th.start_all() console.run_console() console.set_exit() th.stop_all()
def wakeword_listener(self): """ Wakeword listener """ logger.warning('starting detector!') threads = ThreadGroup() threads.targets = [self.async_worker] threads.start_all() sensitivity = [0.5, 0.3, 0.3] #[0.5] * len(self.models) self.detector = snowboydecoder.HotwordDetector( [os.path.join(self.basedir, model) for model in self.models], sensitivity=sensitivity, audio_gain=1) try: self.detector.start(detected_callback=self.wakeword_callback, interrupt_check=self.interrupt_check, sleep_time=0.03) except (KeyboardInterrupt, SystemExit): self.exit = True threads.stop_all()
def player_console(self, vicki, queue=None): """ Start VickiPlayer console """ helper = Help() #self.console = Console() self.console.add_handler(Command.PLAY, vicki.player.play_from_parser, Command().CONTROLS) self.console.add_handler('what', vicki.player.play_from_parser) self.console.add_handler('current_track', vicki.player.play_from_parser) helper.register(self.console) self.console.set_queue(queue) th = ThreadGroup(restart=False) th.targets = [self.console.run_bg_queue] th.start_all() self.console.run_console() self.console.set_exit() th.stop_all()
def play_station(cls, station): """ Play top tracks for station TODO: Fix in https://github.com/tb0hdan/voiceplay/issues/22 """ sc = StationCrawl() sc.put_genre(station) tg = ThreadGroup(restart=False) tg.targets = [sc.genre_loop, sc.playlist_loop] tg.start_all() already_played = [] while True: for track in cls.tracks_with_prefetch(sc.playlist): if cls.get_exit(): # pylint:disable=no-member sc.set_exit(True) tg.stop_all() break cls.play_full_track(track) already_played.append(track) if sorted(already_played) == sorted(sc.playlist) or cls.get_exit(): break
class VickiPlayer(object): """ Vicki player class """ def __init__(self, tts=None, debug=False, player_backend='vlc'): self.debug = debug self.tts = tts self.queue = Queue() self.p_queue = Queue() self.prefetch_q = Queue() if player_backend == 'vlc': from .backend.vlc import VLCPlayer self.player = VLCPlayer() elif player_backend == 'mplayer': from .backend.mpl import MPlayer self.player = MPlayer() else: raise NotImplementedError('Specified player backend %r is unknown' % player_backend) self.shutdown_flag = False self.exit_task = False self._argparser = None self.player_tasks = sorted(PluginLoader().find_classes('voiceplay.player.tasks', BasePlayerTask), key=cmp_to_key(lambda x, y: cmp(x.__priority__, y.__priority__))) self.known_actions = self.get_actions(self.player_tasks) self.task_pool = [] @property def argparser(self): """ Managed property, returns argument parser """ return self._argparser @argparser.setter def argparser(self, argobj): """ Managed property, sets argument parser """ self._argparser = argobj self.player.set_argparser(argobj) @staticmethod def get_actions(tasks): """ Returns list of available actions """ actions = [] for task in tasks: for action in task.__group__: if not action in actions: actions.append(action) return actions def get_exit(self): """ Returns true if exit was requested """ return self.exit_task def put(self, message): """ Put message in queue for processing """ self.queue.put(message) def parse(self, message): """ Parse incoming message """ start = False action_phrase = [] for word in message.split(' '): if word.lower() in self.known_actions: start = True if start and word: action_phrase.append(word) response = ' '.join(action_phrase) return response def play_from_parser(self, message): """ Handle message and call respective actions """ orig_message = message command = Command(message) if command.IN_ALL_SET: if message == command.STOP: self.player.stop() self.exit_task = True elif message == command.NEXT: self.player.resume() self.player.stop() elif message == command.PAUSE: self.player.pause() elif message == command.RESUME: self.player.resume() elif message == command.SHUTDOWN: self.player.shutdown() self.shutdown_flag = True self.exit_task = True # pass other messages elif command.COMMAND in [command.LOVE, command.BAN]: self.p_queue.put(orig_message) else: self.exit_task = True if message.startswith('play'): self.player.stop() self.p_queue.put(orig_message) return None, False def cmd_loop(self): """ Run command loop / periodically check for commands from queue """ while not self.shutdown_flag: if not self.queue.empty(): message = self.queue.get() else: time.sleep(0.01) continue self.play_from_parser(message) self.queue.task_done() logger.debug('Vickiplayer.cmd_loop exit') def run_player_tasks(self, message): """ Run player tasks """ ran = False for task in self.player_tasks: for regexp in task.__regexp__: if re.match(regexp, message, re.I) is not None: start = time.time() ran = True task.prefetch_callback = self.add_to_prefetch_q task.argparser = self._argparser task.get_exit = self.get_exit task.player = self.player task.tts = self.tts th = threading.Thread(target=task.process, args=(regexp, message)) th.daemon = True th.start() self.task_pool.append(th) logger.debug('Task %r completed in %ss', task, '{0:.2f}'.format(int(time.time()) - start)) break if ran: break return ran def task_loop(self): """ Check periodically for tasks from queue """ while not self.shutdown_flag: if not self.p_queue.empty(): parsed = self.parse(self.p_queue.get()) else: time.sleep(0.01) continue logger.debug('task_loop got from queue: %r', parsed) if not parsed: continue self.exit_task = False ran = self.run_player_tasks(parsed) if not ran: msg = 'I think you said ' + parsed self.tts.say_put(msg) logger.warning(msg) self.p_queue.task_done() logger.debug('VickiPlayer.task_loop exit') def prefetch_loop(self): """ Prefetch loop allows significant speedup in playback as it fetches tracks in background """ while not self.shutdown_flag: if not self.prefetch_q.empty(): trackname = self.prefetch_q.get() else: time.sleep(0.01) continue logger.debug('prefetch_loop got from queue: %r', trackname.encode('utf-8')) start = time.time() BasePlayerTask.search_full_track(trackname, download=True) elapsed = round(time.time() - start, 3) logger.debug('prefetch_loop finished downloading, took %ss', elapsed) def add_to_prefetch_q(self, item): """ Add track to prefetch queue """ self.prefetch_q.put(item) def start(self): """ Start VickiPlayer and auxiliary loops using thread group """ # non-blocking start self.player.start(debug=self.debug) self.thread_group = ThreadGroup() self.thread_group.targets = [self.task_loop, self.cmd_loop, self.prefetch_loop] self.thread_group.start_all() def shutdown(self): """ Shutdown VickiPlayer and auxiliary loops """ self.shutdown_flag = True self.exit_task = True for th in self.task_pool: th.join(timeout=1) self.player.shutdown() self.thread_group.stop_all()
def __run_bg__(self): self.th = ThreadGroup(restart=False) self.th.targets = [self.__run_console__] self.th.start_all()
class TextToSpeech(object): """ MAC/Linux TTS """ def __init__(self): self.thread = None self.shutdown = False system = platform.system() if system == 'Darwin': try: from AppKit import NSSpeechSynthesizer voice = 'Vicki' base = 'com.apple.speech.synthesis.voice' self.voice = base + '.' + voice self.speech = NSSpeechSynthesizer.alloc().initWithVoice_(self.voice) # sierra? if not self.speech: self.speech = NSSpeechSynthesizer.alloc().initWithVoice_(base + '.' + 'Victoria') self.say = self.__say_mac except ImportError: # osx lacks appkit support for python3 (sigh) self.say = self.__say_dummy elif system == 'Linux': self.say = self.__say_linux else: raise NotImplementedError('Platform not supported') self.queue = Queue() @staticmethod def __say_linux(message): """ Read message aloud (Linux) """ from festival import sayText # pylint:disable=import-error sayText(message) def __say_mac(self, message): """ Read message aloud (MAC) """ self.speech.startSpeakingString_(message) while self.speech.isSpeaking(): time.sleep(0.1) def __say_dummy(self, message): """ Dummy TTS, does nothing """ pass def poll_loop(self): """ Poll speech queue """ while not self.shutdown: if not self.queue.empty(): message = self.queue.get() logger.debug('TTS: %r', message) self.say(message) self.queue.task_done() else: time.sleep(0.01) logger.debug('TTS poll_loop exit') def start(self): """ Start TTS as a separate thread """ logger.debug('Starting TTS engine...') self.threads = ThreadGroup() self.threads.targets = [self.poll_loop] self.threads.start_all() def stop(self): """ Set shutdown flag and wait for thread to exit """ self.shutdown = True self.threads.stop_all() def say_put(self, message): """ Add message to speech queue """ self.queue.put(message)
class VoicePlayZeroConf(object): """ Very basic mDNS/Zeroconf check/registration """ def __init__(self): self.zeroconf = None self.known_servers = [] self.info = None self.threads = None self.exit = False @staticmethod def get_ip_address(): """ Get my IP """ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) return s.getsockname()[0] def service_info(self, hostname): """ Prepare service information for (my) hostname """ port = int(Config.cfg_data().get('webapp_port')) info = ServiceInfo("_http._tcp.local.", "{0!s}._http._tcp.local.".format(hostname), socket.inet_aton(self.get_ip_address()), port, 0, 0, {'path': '/'}, "{0!s}.local.".format(hostname)) return info def on_service_state_change(self, zeroconf, service_type, name, state_change): """ Service state change callback """ if state_change is ServiceStateChange.Added: info = zeroconf.get_service_info(service_type, name) if info and not info.server in self.known_servers: self.known_servers.append(info.server) def get_others(self): """ Wait for other services to make themselves visible """ zeroconf = Zeroconf() _ = ServiceBrowser(zeroconf, "_http._tcp.local.", handlers=[self.on_service_state_change]) for _ in range(1, 10 + 1): time.sleep(1) zeroconf.close() return self.known_servers def run(self): """ Actual Zeroconf runner """ # has to be here, otherwise quick start/stop will not provide self.zeroconf descriptor self.zeroconf = Zeroconf() servers = self.get_others() hostname = __title__.lower() if '{0!s}.local'.format(hostname) in servers: hostname = '{0!s}-{1!s}'.format(__title__.lower(), str(uuid.uuid4())) self.info = self.service_info(hostname) self.zeroconf.register_service(self.info) while not self.exit: try: time.sleep(0.1) except Exception as _: break def unregister(self): """ Unregister (remove) service from local network """ if self.info: self.zeroconf.unregister_service(self.info) self.zeroconf.close() def start(self): """ Start Zeroconf inside thread """ self.threads = ThreadGroup() self.threads.targets = [self.run] self.threads.start_all() def stop(self): """ Stop and unregister Zeroconf service """ self.exit = True self.unregister() if self.threads: self.threads.stop_all()
class VoicePlayApp(rumps.App): def __init__(self): super(VoicePlayApp, self).__init__(vp_title, quit_button=None) self.menu = ['Pause/Resume', 'Quit'] @rumps.clicked('Pause/Resume') def pause_resume(self, _): try: self.console.parse_command('pause') except Exception as exc: logger.error(repr(exc)) @rumps.clicked('Quit') def menu_quit(self, _): try: self.console.set_exit() self.vicki.player.shutdown() self.th.stop_all() except Exception as exc: logger.error(repr(exc)) rumps.quit_application() def player_console(self, vicki, queue=None): """ Start VickiPlayer console """ helper = Help() #self.console = Console() self.console.add_handler(Command.PLAY, vicki.player.play_from_parser, Command().CONTROLS) self.console.add_handler('what', vicki.player.play_from_parser) self.console.add_handler('current_track', vicki.player.play_from_parser) helper.register(self.console) self.console.set_queue(queue) th = ThreadGroup(restart=False) th.targets = [self.console.run_bg_queue] th.start_all() self.console.run_console() self.console.set_exit() th.stop_all() def __run_console__(self): self.parser.player_console(self.vicki, queue=self.queue) def __run_bg__(self): self.th = ThreadGroup(restart=False) self.th.targets = [self.__run_console__] self.th.start_all() def run_app(self): signal_handler = SignalHandler() signal_handler.register() message = check_update(suppress_uptodate=True) if message: logger.error(message) parser = MyArgumentParser(signal_handler=signal_handler) parser.configure() # first parse is just for debug result = parser.parser.parse_args(sys.argv[1:]) debug = result.debug # rumps.debug_mode(debug) # result = parser.parser.parse_args(['-c']) vicki = Vicki(debug=debug, player_backend=result.player_backend) vicki.player.player.set_argparser(result) # self.console = Console() # self.queue = multiprocessing.Queue() vicki.player.start() self.vicki = vicki self.parser = parser self.__run_bg__() self.run()
class VickiPlayer(object): """ Vicki player class """ def __init__(self, tts=None, debug=False, player_backend='vlc'): self.debug = debug self.tts = tts self.queue = Queue() self.p_queue = Queue() self.prefetch_q = Queue() if player_backend == 'vlc': from .backend.vlc import VLCPlayer self.player = VLCPlayer() elif player_backend == 'mplayer': from .backend.mpl import MPlayer self.player = MPlayer() else: raise NotImplementedError( 'Specified player backend %r is unknown' % player_backend) self.shutdown_flag = False self.exit_task = False self._argparser = None self.player_tasks = sorted( PluginLoader().find_classes('voiceplay.player.tasks', BasePlayerTask), key=cmp_to_key(lambda x, y: cmp(x.__priority__, y.__priority__))) self.known_actions = self.get_actions(self.player_tasks) self.task_pool = [] @property def argparser(self): """ Managed property, returns argument parser """ return self._argparser @argparser.setter def argparser(self, argobj): """ Managed property, sets argument parser """ self._argparser = argobj self.player.set_argparser(argobj) @staticmethod def get_actions(tasks): """ Returns list of available actions """ actions = [] for task in tasks: for action in task.__group__: if not action in actions: actions.append(action) return actions def get_exit(self): """ Returns true if exit was requested """ return self.exit_task def put(self, message): """ Put message in queue for processing """ self.queue.put(message) def parse(self, message): """ Parse incoming message """ start = False action_phrase = [] for word in message.split(' '): if word.lower() in self.known_actions: start = True if start and word: action_phrase.append(word) response = ' '.join(action_phrase) return response def play_from_parser(self, message): """ Handle message and call respective actions """ orig_message = message command = Command(message) if command.IN_ALL_SET: if message == command.STOP: self.player.stop() self.exit_task = True elif message == command.NEXT: self.player.resume() self.player.stop() elif message == command.PAUSE: self.player.pause() elif message == command.RESUME: self.player.resume() elif message == command.SHUTDOWN: self.player.shutdown() self.shutdown_flag = True self.exit_task = True # pass other messages elif command.COMMAND in [command.LOVE, command.BAN]: self.p_queue.put(orig_message) else: self.exit_task = True if message.startswith('play'): self.player.stop() self.p_queue.put(orig_message) return None, False def cmd_loop(self): """ Run command loop / periodically check for commands from queue """ while not self.shutdown_flag: if not self.queue.empty(): message = self.queue.get() else: time.sleep(0.01) continue self.play_from_parser(message) self.queue.task_done() logger.debug('Vickiplayer.cmd_loop exit') def run_player_tasks(self, message): """ Run player tasks """ ran = False for task in self.player_tasks: for regexp in task.__regexp__: if re.match(regexp, message, re.I) is not None: start = time.time() ran = True task.prefetch_callback = self.add_to_prefetch_q task.argparser = self._argparser task.get_exit = self.get_exit task.player = self.player task.tts = self.tts th = threading.Thread(target=task.process, args=(regexp, message)) th.daemon = True th.start() self.task_pool.append(th) logger.debug('Task %r completed in %ss', task, '{0:.2f}'.format(int(time.time()) - start)) break if ran: break return ran def task_loop(self): """ Check periodically for tasks from queue """ while not self.shutdown_flag: if not self.p_queue.empty(): parsed = self.parse(self.p_queue.get()) else: time.sleep(0.01) continue logger.debug('task_loop got from queue: %r', parsed) if not parsed: continue self.exit_task = False ran = self.run_player_tasks(parsed) if not ran: msg = 'I think you said ' + parsed self.tts.say_put(msg) logger.warning(msg) self.p_queue.task_done() logger.debug('VickiPlayer.task_loop exit') def prefetch_loop(self): """ Prefetch loop allows significant speedup in playback as it fetches tracks in background """ while not self.shutdown_flag: if not self.prefetch_q.empty(): trackname = self.prefetch_q.get() else: time.sleep(0.01) continue logger.debug('prefetch_loop got from queue: %r', trackname.encode('utf-8')) start = time.time() BasePlayerTask.search_full_track(trackname, download=True) elapsed = round(time.time() - start, 3) logger.debug('prefetch_loop finished downloading, took %ss', elapsed) def add_to_prefetch_q(self, item): """ Add track to prefetch queue """ self.prefetch_q.put(item) def start(self): """ Start VickiPlayer and auxiliary loops using thread group """ # non-blocking start self.player.start(debug=self.debug) self.thread_group = ThreadGroup() self.thread_group.targets = [ self.task_loop, self.cmd_loop, self.prefetch_loop ] self.thread_group.start_all() def shutdown(self): """ Shutdown VickiPlayer and auxiliary loops """ self.shutdown_flag = True self.exit_task = True for th in self.task_pool: th.join(timeout=1) self.player.shutdown() self.thread_group.stop_all()