def start_new_process(self, sound_arg): """ Start mplayer process with the given sounds to play :param sound_arg: :type sound_arg: list of dicts [{name: link}, {name: link}, {name: link}] :return: """ mplayer_exec_path = [self.mplayer_path] mplayer_options = ['-slave', '-quiet', '-af', 'volume=-15', '-loop'] mplayer_options.append("0" if self.loop_option == "loop" else "1") mplayer_command = list() mplayer_command.extend(mplayer_exec_path) mplayer_command.extend(mplayer_options) for sound in sound_arg: for sound_name, sound_link in sound.items(): mplayer_command.append(sound_link) logger.debug("[Background_sound_player] Mplayer cmd: %s" % str(mplayer_command)) Cortex.save("current_playing_background_sound", sound_name) # give the current file name played to the neuron template self.message['sound_name'] = sound_name self.message["sound_link"] = sound_link # run mplayer in background inside a new process fnull = open(os.devnull, 'w') pid = subprocess.Popen(mplayer_command, stdout=fnull, stderr=fnull).pid # store the pid in a file to be killed later self.store_pid(pid) logger.debug("[Background_sound_player] Mplayer started, pid: %s" % pid)
def __init__(self, **kwargs): super(Editor, self).__init__(**kwargs) # the args from the neuron configuration listen_ip = kwargs.get('listen_ip', '0.0.0.0') port = kwargs.get('port', 8000) ignore_pattern = kwargs.get('ignore_pattern', None) dir_first = kwargs.get('dir_first', False) hide_hidden = kwargs.get('hide_hidden', False) page_title = kwargs.get('page_title', "Kalliope Editor") stop_server = kwargs.get('stop_server', False) if stop_server: self.stop_http_server() Utils.print_info("[ Editor ] Editor stopped") else: global IGNORE_PATTERN, DIRSFIRST, HIDEHIDDEN, PAGE_TITLE IGNORE_PATTERN = ignore_pattern DIRSFIRST = dir_first HIDEHIDDEN = hide_hidden PAGE_TITLE = page_title if self.stop_http_server(): server = EditorThread(listen_ip, int(port)) server.daemon = True server.start() Cortex.save('EditorServerThread', server)
def __init__(self, **kwargs): super(Background_sound_player, self).__init__(**kwargs) self.state = kwargs.get('state', None) # "on" / "off" self.sounds = kwargs.get( 'sounds', None) # "[{'title1': 'link1'}, {'title2': 'link2'}, ...]" self.random_option = kwargs.get( 'random_option', "random-select-one" ) # "random-order-play" / "random-select-one" / "no-random" self.loop_option = kwargs.get('loop_option', 'no-loop') # "loop" / "no-loop" self.mplayer_path = kwargs.get('mplayer_path', "/usr/bin/mplayer") self.auto_stop_minutes = kwargs.get('auto_stop_minutes', None) self.currently_playing_sound = None # a dict of parameters the user ask to save in short term memory self.kalliope_memory = kwargs.get('kalliope_memory', None) # parameters loaded from the order can be save now Cortex.save_parameter_from_order_in_memory(self.kalliope_memory) Cortex.save("current_playing_background_sound", "Aucun fond sonore lancé actuellement") # message dict that will be passed to the neuron template self.message = dict() # check if sent parameters are in good state if self._is_parameters_ok(): if self.state == "off": self.stop_last_process() self.clean_pid_file() Cortex.save("current_playing_background_sound", "Aucun fond sonore lancé actuellement") else: # we stop the last process if exist self.stop_last_process() # pick one sound randomly in all sounds entered if self.random_option == "random-select-one": self.currently_playing_sound = [random.choice(self.sounds)] # play all sounds in random order elif self.random_option == "random-order-play": random.shuffle(self.sounds) self.currently_playing_sound = self.sounds # play all sounds the specified order else: self.currently_playing_sound = self.sounds # then we can start a new process self.start_new_process(self.currently_playing_sound) # run auto stop thread if self.auto_stop_minutes: thread_auto_stop = threading.Thread( target=self.wait_before_stop) thread_auto_stop.start() # give the message dict to the neuron template self.say(self.message)
def test_save(self): key_to_save = "key1" value_to_save = "value1" expected_memory = {"key1": "value1"} Cortex.save(key=key_to_save, value=value_to_save) self.assertDictEqual(expected_memory, Cortex.memory)
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("[NeuronModule] Say() called with message: %s" % message) tts_message = None # we can save parameters from the neuron in memory Cortex.save_neuron_parameter_in_memory(self.kalliope_memory, message) if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # save in kalliope memory the last tts message Cortex.save("kalliope_last_tts_message", tts_message) # process the audio only if the mute flag is false if self.settings.options.mute: logger.debug("[NeuronModule] mute is True, Kalliope is muted") else: logger.debug( "[NeuronModule] mute is False, make Kalliope speaking") HookManager.on_start_speaking() # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation( package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message) HookManager.on_stop_speaking()
def run_synapse_by_order(self): """ Give an order to Kalliope via API like it was from a spoken one Test with curl curl -i --user admin:secret -H "Content-Type: application/json" -X POST \ -d '{"order":"my order"}' http://localhost:5000/synapses/start/order In case of quotes in the order or accents, use a file cat post.json: {"order":"j'aime"} curl -i --user admin:secret -H "Content-Type: application/json" -X POST \ --data @post.json http://localhost:5000/order/ Can be used with mute flag curl -i --user admin:secret -H "Content-Type: application/json" -X POST \ -d '{"order":"my order", "mute":"true"}' http://localhost:5000/synapses/start/order :return: """ if not request.get_json() or 'order' not in request.get_json(): data = { "Error": "Wrong parameters, 'order' not set" } return jsonify(error=data), 400 order = request.get_json('order') # Store the mute value, then apply depending of the request parameters old_mute_value = self.settings.options.mute mute = utils.get_value_flag_from_request(http_request=request, flag_to_find="mute", is_boolean=True) if mute is not None: SettingEditor.set_mute_status(mute=mute) if order is not None: # get the order order_to_run = order["order"] logger.debug("[FlaskAPI] run_synapse_by_order: order to run -> %s" % order_to_run) api_response = SynapseLauncher.run_matching_synapse_from_order(order_to_run, self.brain, self.settings, is_api_call=True) Cortex.save('kalliope_last_order', order_to_run) data = jsonify(api_response) if mute is not None: SettingEditor.set_mute_status(mute=old_mute_value) return data, 201 else: data = { "error": "order cannot be null" } if mute is not None: SettingEditor.set_mute_status(mute=old_mute_value) return jsonify(error=data), 400
def test_save(self): key_to_save = "key1" value_to_save = "value1" expected_memory = { "key1": "value1" } Cortex.save(key=key_to_save, value=value_to_save) self.assertDictEqual(expected_memory, Cortex.memory)
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("[NeuronModule] Say() called with message: %s" % message) tts_message = None # we can save parameters from the neuron in memory Cortex.save_neuron_parameter_in_memory(self.kalliope_memory, message) if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # save in kalliope memory the last tts message Cortex.save("kalliope_last_tts_message", tts_message) # process the audio only if the mute flag is false if self.settings.options.mute: logger.debug("[NeuronModule] mute is True, Kalliope is muted") else: logger.debug("[NeuronModule] mute is False, make Kalliope speaking") HookManager.on_start_speaking() # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation(package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message) HookManager.on_stop_speaking()
def order_listener_callback(self, order): """ Receive an order, try to retrieve it in the brain.yml to launch to attached plugins :param order: the sentence received :type order: str """ logger.debug("[Order] Order listener callback called. Order to process: %s" % order) HookManager.on_stop_listening() self.order_to_process = order self.order_listener_callback_called = True # save in kalliope memory the last order Cortex.save('kalliope_last_order', order)
def order_listener_callback(self, order): """ Receive an order, try to retrieve it in the brain.yml to launch to attached plugins :param order: the sentence received :type order: str """ logger.debug("[Order] Order listener callback called. Order to process: %s" % order) HookManager.on_stop_listening() self.order_to_process = order self.order_listener_callback_called = True # save in kalliope memory the last order Cortex.save('kalliope_last_order', order)
def start_new_process(self, sound_arg): """ Start mplayer process with the given sounds to play :param sound_arg: :type sound_arg: list of dicts [{name: link}, {name: link}, {name: link}] :return: """ mplayer_exec_path = [self.mplayer_path] mplayer_options = [ '-slave', '-quiet', '-af', 'volume=-10', '-volume', str(self.launch_volume), '-loop' ] mplayer_options.append("0" if self.loop_option == "loop" else "1") mplayer_command = list() mplayer_command.extend(mplayer_exec_path) mplayer_command.extend(mplayer_options) for sound in sound_arg: for sound_name, sound_link in sound.items(): mplayer_command.append(sound_link) logger.debug("[Background_sound_player] Mplayer cmd: %s" % str(mplayer_command)) # give the current file name played to the neuron template self.message['sound_name'] = sound_name self.message["sound_link"] = sound_link # run mplayer in background inside a new process fnull = open(os.devnull, 'w') process = subprocess.Popen(mplayer_command, stdout=fnull, stderr=fnull, stdin=subprocess.PIPE, universal_newlines=True) self.lower_sound_for_speaking() Cortex.save("current_playing_background_sound", sound_name) Cortex.save("background_mplayer_popen", process) logger.debug("[Background_sound_player] Mplayer started, pid: %s" % process.pid)
def stop_last_process(self): """ stop the last mplayer process launched by this neuron :return: """ if self.mplayer_popen_obj is not None: logger.debug("[Background_sound_player] loaded pid: %s" % self.mplayer_popen_obj.pid) self.mplayer_popen_obj.kill() logger.debug( "[Background_sound_player] mplayer process with pid %s killed" % self.mplayer_popen_obj.pid) Cortex.save("current_playing_background_sound", "Aucun fond sonore lancé actuellement") Cortex.save("background_mplayer_popen", 'NIL') else: logger.debug( "[Background_sound_player] Popen object is None. Process already stopped" )
def audio_analyser_callback(self, order): """ Callback of the OrderListener. Called after the processing of the audio file This method will - call the Order Analyser to analyse the order and launch corresponding synapse as usual. - get a list of launched synapse. - give the list to the main process via self.launched_synapses - notify that the processing is over via order_analyser_return :param order: string order to analyse :return: """ logger.debug("[FlaskAPI] audio_analyser_callback: order to process -> %s" % order) api_response = SynapseLauncher.run_matching_synapse_from_order(order, self.brain, self.settings, is_api_call=True) self.api_response = api_response Cortex.save('kalliope_last_order', order) # this boolean will notify the main process that the order have been processed self.order_analyser_return = True
def start_new_process(self, sound_arg): #Start mplayer process """ Start mplayer process with the given sounds to play :param sound_arg: :type sound_arg: list of dicts [{name: link}, {name: link}, {name: link}] :return: """ currently_playing_sound = None mplayer_exec_path = [self.mplayer_path] mplayer_options = ['-slave', '-quiet', '-af'] mplayer_volume = ['volume'] mplayer_volume[0] = "volume=" + self.volume mplayer_loop = ['-loop'] mplayer_loop.append("0" if self.loop_option == "loop" else "1") # mplayer { 1.avi - loop 2 2.avi } -loop 3 > La commande jouera les fichiers dans cet ordre: 1, 1, 2, 1, 1, 2, 1, 1, 2. "-loop 0" tournera a l'infini first_sound_name, first_sound_link = list(sound_arg[0].items())[0] first_sound_link = str(first_sound_link) # Pick one sound randomly in all sounds entered. currently_playing_sound will have only one entry # Need anyway to add "-playlist" if the link is a TXT playlist. But this playlist will be read with no shuffle option if self.random_option == "random-select-one": currently_playing_sound = [random.choice(sound_arg)] if (first_sound_link)[-4:] == ".txt": mplayer_loop.append("-playlist") # play all sounds in random order if there is no TXT playlist # at this stage, the list either only TXT or playable files (if not it should has raised an error in _check_sounds function # if the links are then TXT playlist, one will be selected. Otherwise, shuffle parameter is added to MPLAYER commands > need "-playlist" for TXT playlist elif self.random_option == "random-order-play": mplayer_loop.append("-shuffle") if str( first_sound_link )[-4:] == ".txt": # if it is a TXT (then like all others), need to randomly select one currently_playing_sound = [random.choice(sound_arg)] mplayer_loop.append("-playlist") else: currently_playing_sound = sound_arg # play all sounds the specified order else: if str(first_sound_link )[-4:] == ".txt": # if it is a TXT (then like all others) mplayer_loop.append( "-playlist") # then indicate that this is a playlist currently_playing_sound = sound_arg[ 0] # and select the first playlist else: currently_playing_sound = sound_arg # else the list is simply copied mplayer_command = list() mplayer_command.extend(mplayer_exec_path) mplayer_command.extend(mplayer_options) mplayer_command.extend(mplayer_volume) mplayer_command.extend(mplayer_loop) for sound in currently_playing_sound: for sound_name, sound_link in sound.items(): mplayer_command.append(sound_link) logger.debug("[Background_sound_player] Mplayer cmd: %s" % str(mplayer_command)) Cortex.save("current_playing_background_sound", sound_name) # give the current file name played to the neuron template self.message['sound_name'] = sound_name self.message["sound_link"] = sound_link # run mplayer in background inside a new process fnull = open(os.devnull, 'w') # mplayer_command = "mplayer -slave -quiet -af volume=-20 -loop 0 -shuffle -playlist /home/pi/kalliope_starter_fr/resources/sounds/MP3/Florian/*.*" pid = subprocess.Popen(mplayer_command, stdout=fnull, stderr=fnull).pid # store the pid in a file to be killed later self.store_pid(pid) logger.debug("[Background_sound_player] Mplayer started, pid: %s" % pid)
def run(self): """ Start the voice detector. For every `sleep_time` second it checks the audio buffer for triggering keywords. If detected, then call corresponding function in `detected_callback`, which can be a single function (single model) or a list of callback functions (multiple models). Every loop it also calls `interrupt_check` -- if it returns True, then breaks from the loop and return. :param detected_callback: a function or list of functions. The number of items must match the number of models in `decoder_model`. :param interrupt_check: a function that returns True if the main loop needs to stop. :param float sleep_time: how much time in second every loop waits. :param audio_recorder_callback: if specified, this will be called after a keyword has been spoken and after the phrase immediately after the keyword has been recorded. The function will be passed the name of the file where the phrase was recorded. :param silent_count_threshold: indicates how long silence must be heard to mark the end of a phrase that is being recorded. :param recording_timeout: limits the maximum length of a recording. :return: None """ if self.interrupt_check(): logger.debug("detect voice return") return tc = type(self.detected_callback) if tc is not list: self.detected_callback = [self.detected_callback] if len(self.detected_callback) == 1 and self.num_hotwords > 1: self.detected_callback *= self.num_hotwords assert self.num_hotwords == len(self.detected_callback), \ "Error: hotwords in your models (%d) do not match the number of " \ "callbacks (%d)" % (self.num_hotwords, len(self.detected_callback)) logger.debug("detecting...") SR = SpeechRecorder() while not self.kill_received: #if not self.paused: if self.interrupt_check(): logger.debug("detect voice break") break data = self.ring_buffer.get() if len(data) == 0: time.sleep(self.sleep_time) continue self.saveMessage( data ) # Save trigger data so it can be append to the record for STT status = self.detector.RunDetection(data) if status > 0: #key word found SR.start() # Start the speech recorder Utils.print_info("Keyword " + self.keyword_names[status] + " detected") Cortex.save( 'kalliope_trigger_called', self.keyword_names[status] ) # I save it to the Cortex, to use it by another neuron # for changing the tts acording to the trigger name HookManager.on_triggered() callback = self.detected_callback[status - 1] if callback is not None: callback() if status == -1: logger.warning( "Error initializing streams or reading audio data") logger.debug("finished.")