def test_wait_while_speaking(self): # Check that test terminates create_signal('isSpeaking') Thread(target=wait_while_speaking_thread).start() sleep(2) self.assertFalse(done_waiting) check_for_signal('isSpeaking') sleep(2) self.assertTrue(done_waiting)
def test_check_signal(self): if exists('/tmp/OwO'): rmtree('/tmp/OwO') # check that signal is not found if file does not exist self.assertFalse(check_for_signal('test_signal')) # Check that the signal is found when created create_signal('test_signal') self.assertTrue(check_for_signal('test_signal')) # Check that the signal is removed after use self.assertFalse(isfile('/tmp/OwO/ipc/signal/test_signal'))
def end_audio(self): """ Helper function for child classes to call in execute(). Sends the recognizer_loop:audio_output_end message, indicating that speaking is done for the moment. It also checks if cache directory needs cleaning to free up disk space. """ self.bus.emit(Message("recognizer_loop:audio_output_end")) # Clean the cache as needed cache_dir = owo.util.get_cache_directory("tts") owo.util.curate_cache(cache_dir, min_free_percent=100) # This check will clear the "signal" check_for_signal("isSpeaking")
def main(): """ Main function. Run when file is invoked. """ reset_sigint_handler() check_for_signal("isSpeaking") bus = WebsocketClient() # Connect to the OwO Messagebus Configuration.init(bus) speech.init(bus) LOG.info("Starting Audio Services") bus.on('message', create_echo_function('AUDIO', ['owo.audio.service'])) audio = AudioService(bus) # Connect audio service instance to message bus create_daemon(bus.run_forever) wait_for_exit_signal() speech.shutdown() audio.shutdown()
def handle_stop(event): """ handle stop message """ global _last_stop_signal if check_for_signal("isSpeaking", -1): _last_stop_signal = time.time() tts.playback.clear_queue() tts.playback.clear_visimes() bus.emit(Message("owo.stop.handled", {"by": "TTS"}))
def _skip_wake_word(self): # Check if told programatically to skip the wake word, like # when we are in a dialog with the user. if check_for_signal('startListening'): return True # Pressing the Mark 1 button can start recording (unless # it is being used to mean 'stop' instead) if check_for_signal('buttonPress', 1): # give other processes time to consume this signal if # it was meant to be a 'stop' sleep(0.25) if check_for_signal('buttonPress'): # Signal is still here, assume it was intended to # begin recording LOG.debug("Button Pressed, wakeword not needed") return True return False
def handle_speak(event): """ Handle "speak" message """ config = Configuration.get() Configuration.init(bus) global _last_stop_signal # Get conversation ID if event.context and 'ident' in event.context: ident = event.context['ident'] else: ident = 'unknown' with lock: stopwatch = Stopwatch() stopwatch.start() utterance = event.data['utterance'] if event.data.get('expect_response', False): # When expect_response is requested, the listener will be restarted # at the end of the next bit of spoken audio. bus.once('recognizer_loop:audio_output_end', _start_listener) # This is a bit of a hack for Picroft. The analog audio on a Pi blocks # for 30 seconds fairly often, so we don't want to break on periods # (decreasing the chance of encountering the block). But we will # keep the split for non-Picroft installs since it give user feedback # faster on longer phrases. # # TODO: Remove or make an option? This is really a hack, anyway, # so we likely will want to get rid of this when not running on Mimic if (config.get('enclosure', {}).get('platform') != "picroft" and len(re.findall('<[^>]*>', utterance)) == 0): start = time.time() chunks = re.split(r'(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)\s', utterance) for chunk in chunks: try: mute_and_speak(chunk, ident) except KeyboardInterrupt: raise except Exception: LOG.error('Error in mute_and_speak', exc_info=True) if (_last_stop_signal > start or check_for_signal('buttonPress')): break else: mute_and_speak(utterance, ident) stopwatch.stop() report_timing(ident, 'speech', stopwatch, { 'utterance': utterance, 'tts': tts.__class__.__name__ })
def _record_phrase(self, source, sec_per_buffer): """Record an entire spoken phrase. Essentially, this code waits for a period of silence and then returns the audio. If silence isn't detected, it will terminate and return a buffer of RECORDING_TIMEOUT duration. Args: source (AudioSource): Source producing the audio chunks sec_per_buffer (float): Fractional number of seconds in each chunk Returns: bytearray: complete audio buffer recorded, including any silence at the end of the user's utterance """ num_loud_chunks = 0 noise = 0 max_noise = 25 min_noise = 0 silence_duration = 0 def increase_noise(level): if level < max_noise: return level + 200 * sec_per_buffer return level def decrease_noise(level): if level > min_noise: return level - 100 * sec_per_buffer return level # Smallest number of loud chunks required to return min_loud_chunks = int(self.MIN_LOUD_SEC_PER_PHRASE / sec_per_buffer) # Maximum number of chunks to record before timing out max_chunks = int(self.RECORDING_TIMEOUT / sec_per_buffer) num_chunks = 0 # Will return if exceeded this even if there's not enough loud chunks max_chunks_of_silence = int(self.RECORDING_TIMEOUT_WITH_SILENCE / sec_per_buffer) # bytearray to store audio in byte_data = get_silence(source.SAMPLE_WIDTH) phrase_complete = False while num_chunks < max_chunks and not phrase_complete: chunk = self.record_sound_chunk(source) byte_data += chunk num_chunks += 1 energy = self.calc_energy(chunk, source.SAMPLE_WIDTH) test_threshold = self.energy_threshold * self.multiplier is_loud = energy > test_threshold if is_loud: noise = increase_noise(noise) num_loud_chunks += 1 else: noise = decrease_noise(noise) self._adjust_threshold(energy, sec_per_buffer) if num_chunks % 10 == 0: with open(self.mic_level_file, 'w') as f: f.write("Energy: cur=" + str(energy) + " thresh=" + str(self.energy_threshold)) f.close() was_loud_enough = num_loud_chunks > min_loud_chunks quiet_enough = noise <= min_noise if quiet_enough: silence_duration += sec_per_buffer if silence_duration < self.MIN_SILENCE_AT_END: quiet_enough = False # gotta be silent for min of 1/4 sec else: silence_duration = 0 recorded_too_much_silence = num_chunks > max_chunks_of_silence if quiet_enough and (was_loud_enough or recorded_too_much_silence): phrase_complete = True # Pressing top-button will end recording immediately if check_for_signal('buttonPress'): phrase_complete = True return byte_data
def test_is_speaking(self): create_signal('isSpeaking') self.assertTrue(owo.audio.is_speaking()) # Check that the signal hasn't been removed self.assertTrue(check_for_signal('isSpeaking')) self.assertFalse(owo.audio.is_speaking())
def on_stop_handled(self, event): # A skill performed a stop check_for_signal('buttonPress')