dever 6 anos atrás
pai
commit
b9926c09ff

+ 23 - 15
conf/dev.conf

@@ -30,7 +30,7 @@ name				= office_file
 prefix				= convert_
 
 ;机器人配置 应该加到数据库中
-
+;https://github.com/wzpan/wukong-robot/blob/master/static/default.yml
 ;snowboy配置
 [snowboy]
 hotword				= wukong.pmdl
@@ -44,6 +44,8 @@ recording_timeout	= 5
 bother_enable		= True
 bother_since		= 1
 bother_till			= 100
+;自动聊天
+active_mode			= True
 
 [robot]
 asr					= tencent
@@ -52,36 +54,42 @@ tts 				= baidu
 nlu 				= baidu
 
 [tencent]
-appid				= 1
-secret_id			= 1232
-secret_key			= 22
+;appid				= 1252854083
+;secret_id			= AKIDmQvEPYRP1Rh0CGdLF1b3MTsaBfnjQ5cP
+;secret_key			= wpazQgljXCgycEi18wbAapwsfi8QXSFu
+
+appid				= 1253537070
+secret_id			= AKID7C7JK9QomcWJUjcsKbK8iLQjhju8fC3z
+secret_key			= 2vhKRVSn4mXQ9PiT7eOtBqQhR5Z6IvPn
 region				= ap-guangzhou
 voiceType 			= 0
 language			= 1
 
 [baidu]
-appid				= 1
-api_key				= 1232
-secret_key			= 22
+appid				= 9670645
+api_key				= qg4haN8b2bGvFtCbBGqhrmZy
+secret_key			= 585d4eccb50d306c401d7df138bb02e7
 dev_pid				= 1936
 per 				= 1
 lan 				= zh
 
 [xunfei]
-appid				= 1
-api_key				= 1232
-voice_name 			= vecan
+appid				= 5c540c39
+api_key				= 859bc21e90c64d97fae77695579eb05e
+voice_name 			= xiaoyun
 
 [ali]
-appKey				= 1
-token				= 1232
-voice 				= vecan
+appKey				= x04ujKJ7oRvDgt6h
+token				= 7237029b69f146e89bac004bb61c09ee
+;xiaoyun为女生,xiaogang为男生, 全部可选:http://suo.im/4x8RzQ
+voice 				= xiaoyun
+
 
 [tuling]
-key 				= 1
+key 				= 98f95153fb5c4684a5602b909949ba61
 
 [emotibot]
-appid 				= 1
+appid 				= 32ee80dddb36f1dc5356517503b3f55e
 location			= 深圳
 more				= False
 

+ 1 - 3
install.py

@@ -1,9 +1,7 @@
 # -*- coding: utf-8 -*-
 from demeter.core import *
 
-Shell.popen('pip install pydub')
-Shell.popen('pip install baidu-aip')
-Shell.popen('pip install python-dateutil')
+Shell.popen('apt-get install python-pyaudio python3-pyaudio sox pulseaudio libsox-fmt-all ffmpeg libatlas-base-dev libsox-fmt-mp3 && pip install pydub pyaudio baidu-aip python-dateutil')
 
 #CREATE DATABASE IF NOT EXISTS yourdbname DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
 def manage():

+ 59 - 7
service/__load__.py

@@ -1,21 +1,48 @@
-#!/usr/bin/env python
 # -*- coding: utf-8 -*-
-"""
-	demeter service
-	name:__load__.py
-	author:rabin
-"""
+import sys
+reload(sys)
+sys.setdefaultencoding('utf8')
 from demeter.core import *
 from datetime import *
+import time
 import uuid
 import os
 import os.path
 import wave
 import tempfile
+import cProfile
 from pydub import AudioSegment
 from uuid import getnode as get_mac
 from abc import ABCMeta, abstractmethod
 
+from ctypes import CFUNCTYPE, c_char_p, c_int, cdll
+from contextlib import contextmanager
+
+def py_error_handler(filename, line, function, err, fmt):
+	pass
+
+ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
+
+c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
+
+@contextmanager
+def no_alsa_error():
+	try:
+		asound = cdll.LoadLibrary('libasound.so')
+		asound.snd_lib_error_set_handler(c_error_handler)
+		yield
+		asound.snd_lib_error_set_handler(None)
+	except:
+		yield
+		pass
+
+def getPlayerByFileName(fname):
+	foo, ext = os.path.splitext(fname)
+	if ext == '.mp3':
+		return Demeter.service('sox', 'player')
+	elif ext == '.wav':
+		return Demeter.service('wav', 'player')
+
 """ 
 将 mp3 文件转成 wav
 
@@ -66,4 +93,29 @@ def writeTempFile(data, suffix):
 	with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as f:
 		f.write(data)
 		tmpfile = f.name
-	return tmpfile
+	return tmpfile
+
+# 清理目录
+def clean():
+	temp = Demeter.config['vecan']['temp']
+	temp_files = os.listdir(temp)
+	for f in temp_files:
+		if os.path.isfile(os.path.join(temp, f)) and re.match(r''+Demeter.config['vecan']['outname']+'[\d]*\.wav', os.path.basename(f)):
+			os.remove(os.path.join(temp, f))
+
+""" 获取缓存的语音 """
+def getCache(msg):
+	md5 = Demeter.md5(msg)
+	mp3_cache = os.path.join(Demeter.config['vecan']['temp'], md5 + '.mp3')
+	wav_cache = os.path.join(Demeter.config['vecan']['temp'], md5 + '.wav')
+	if os.path.exists(mp3_cache):
+		return mp3_cache
+	elif os.path.exists(wav_cache):
+		return wav_cache
+	return None
+
+""" 获取缓存的语音 """
+def saveCache(voice, msg):
+	foo, ext = os.path.splitext(voice)
+	md5 = Demeter.md5(msg)
+	shutil.copyfile(voice, os.path.join(Demeter.config['vecan']['temp'], md5+ext))

+ 1 - 0
service/player/__init__.py

@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-

+ 24 - 0
service/player/base.py

@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import division
+from ..__load__ import *
+import subprocess
+import threading
+import pyaudio
+
+class Base(threading.Thread):
+
+	def __init__(self, **kwargs):
+		super(Base, self).__init__()
+		self.logger = Log.init(__name__)
+
+	def play(self):
+		pass
+
+	def play_block(self):
+		pass
+
+	def stop(self):
+		pass
+
+	def is_playing(self):
+		return False

+ 55 - 0
service/player/sox.py

@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+from __future__ import division
+from ..__load__ import *
+from base import Base
+import subprocess
+
+class Sox(Base):
+	def __init__(self, **kwargs):
+		super(self.__class__, self).__init__(**kwargs)
+		self.playing = False
+		self.pipe = None
+		self.delete = False
+		self.volume = 1
+		self.onCompleteds = []
+
+	def run(self):
+		cmd = ['play', '-v', str(self.volume), str(self.src)]
+		self.logger.debug('Executing %s', ' '.join(cmd))
+		print cmd
+		return
+		self.proc = subprocess.Popen(cmd)
+		self.playing = True
+		self.proc.wait()
+		self.playing = False
+		if self.delete:
+			File.remove(self.src)
+		self.logger.debug('play completed')
+		for onCompleted in self.onCompleteds:
+			if onCompleted:				
+				onCompleted()
+		self.onCompleteds = []
+
+	def play(self, src, delete=False, onCompleted=None, volume=1):
+		self.src = src		
+		self.delete = delete
+		self.onCompleteds.append(onCompleted)
+		self.volume = volume
+		self.start()
+
+	def appendOnCompleted(self, onCompleted):
+		if onCompleted:
+			self.onCompleteds.append(onCompleted)
+
+	def play_block(self):
+		self.run()
+
+	def stop(self):
+		if self.proc:
+			self.onCompleteds = []
+			self.proc.terminate()
+			if self.delete:
+				File.remove(self.src)
+
+	def is_playing(self):
+		return self.playing

+ 60 - 0
service/player/wav.py

@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+from __future__ import division
+from ..__load__ import *
+from base import Base
+
+class Wav(Base):
+
+	def __init__(self, **kwargs):
+		super(self.__class__, self).__init__(**kwargs)
+		self.playing = False
+		self.stop = False		
+
+	def run(self):
+		# play a voice
+		CHUNK = 1024
+
+		self.logger.debug("playing wave %s", self.src)
+		f = wave.open(self.src, "rb")
+
+		with no_alsa_error():
+			audio = pyaudio.PyAudio()
+
+		stream = audio.open(
+			format=audio.get_format_from_width(f.getsampwidth()),
+			channels=f.getnchannels(),
+			rate=f.getframerate(),
+			input=False,
+			output=True)
+		
+		self.playing = True
+		stream.start_stream()
+		data = f.readframes(CHUNK)
+		while data != '' and not self.stop:
+			stream.write(data)
+			data = f.readframes(CHUNK)
+			print('data=="": {}, self.stop: {}'.format(data == '', self.stop))
+
+		self.playing = False
+		stream.stop_stream()
+		stream.close()
+		audio.terminate()
+		if self.onCompleteds:
+			for onCompleted in self.onCompleteds:
+				if onCompleted:
+					onCompleted()
+
+	def play(self, src, onCompleted=None):
+		self.src = src
+		self.onCompleted = onCompleted
+		self.start()
+
+	def play_block(self):
+		self.run()
+
+	def stop(self):
+		self.stop = True
+		File.remove(self.src)
+
+	def is_playing(self):
+		return self.playing

+ 166 - 34
service/robot.py

@@ -4,27 +4,88 @@ from .__load__ import *
 
 class Robot(object):
 	tool = {}
-	def __init__(self, profiling=False):
+	def init(self, profiling=False):
 		self.start()
+		# 历史记录
+		self.history = []
+		# 数据文件夹
+		self.data = Demeter.config['vecan']['data']
+		# 日志
+		self.logger = Log.init(__name__)
+		# 是否正在录音
 		self.isRecording = False
+		# 插件验证--暂时无用
+		self.matchPlugin = None
+		# 勿扰模式
+		self.immersiveMode = None
+		# 优化
+		self.profiling = profiling
+		# 询问模式
+		self.hasPardon = False
+		# 播放器
+		self.player = None
+		# snowboy
+		self.snowboy = None
+		# 对话
+		self.onSay = None
+		# 技能
+		self.skill = None
 
-	def say(self, msg, cache=False, plugin='', onCompleted=None):
+	def initSnowboy(self, snowboy):
+		self.snowboy = snowboy
+
+	def getSnowBoy(self):
+		if self.snowboy:
+			return self.snowboy
+		else:
+			self.snowboy = Demeter.service('snowboy')
+			self.snowboy.init(self)
+			return self.snowboy
+
+	def getHistory(self):
+		return self.history
+
+	def say(self, msg, cache=False, plugin='', completed=None):
 		print msg
-		self.player = Player.SoxPlayer()
+		if self.onSay:
+			self.logger.info('onSay: {}'.format(msg))
+			if plugin != '':
+				self.onSay("[{}] {}".format(plugin, msg))
+			else:
+				self.onSay(msg)
+			self.onSay = None
+		if plugin != '':
+			self.appendHistory(1, "[{}] {}".format(plugin, msg))
+		else:
+			self.appendHistory(1, msg)
+		pattern = r'^https?://.+'
+		if re.match(pattern, msg):
+			self.logger.info('内容包含URL,无法读取')
+			return
+		voice = ''
 		voice = self.tool['tts'].tts(msg)
-		if onCompleted is None:
-			onCompleted = lambda: self.onCompleted(msg)
-		self.player.play(voice, not cache, onCompleted)
-
-	def onCompleted(self, msg):
-		if config.get('active_mode', False) and \
-		   (
-			   msg.endswith('?') or 
-			   msg.endswith(u'?') or 
-			   u'告诉我' in msg or u'请回答' in msg
-		   ):
-			query = self.activeListen()
-			self.doResponse(query)
+		if cache:
+			saveCache(voice, msg)
+		'''
+		if getCache(msg):
+			self.logger.info('命中缓存,播放缓存语音')
+			voice = getCache(msg)
+		else:
+			try:
+				voice = self.tool['tts'].tts(msg)
+				if cache:
+					saveCache(voice, msg)
+			except Exception as e:
+				self.logger.error('保存缓存失败:{}'.format(e))
+		'''
+		if completed is None:
+			completed = lambda: self.completed(msg)
+		self.play(voice, not cache, completed=completed)
+
+	def play(self, voice, delete=False, completed=None):
+		if self.player == None:
+			self.player = Demeter.service('sox', 'player')
+		self.player.play(voice, delete=delete, onCompleted=completed)
 
 	def start(self):
 		# 机器人配置,后续改成从数据库读取
@@ -39,31 +100,41 @@ class Robot(object):
 					self.tool[item] = Demeter.service(self.config[item], 'api')
 					self.tool[item].setting(**setting)
 			self.player = None
+			self.skill = Demeter.service('skill')
+			self.skill.init(self)
+			self.skill.printPlugins()
 		except Exception as e:
-			Demeter.logger.critical("机器人初始化失败:{}".format(e))
+			self.logger.critical('机器人初始化失败:{}'.format(e))
+
+	def checkRestore(self):
+		if self.immersiveMode:
+			self.skill.restore()
+
+	def checkPlayer(self):
+		return self.player is not None and self.player.is_playing()
 
 	def setting(self, name):
 		# 后续要改成数据库读取
 		config = Demeter.config[name]
 		return config
 
+	# 中断、停止
 	def interrupt(self):
-		if self.player is not None and self.player.is_playing():
+		if self.checkPlayer():
 			self.player.stop()
 			self.player = None
 		if self.immersiveMode:
-			self.brain.pause()
+			self.skill.pause()
 
-	def converse(self, fp, callback=None):
-		""" 核心对话逻辑 """
-		Player.play(constants.getData('beep_lo.wav'))
-		logger.info('结束录音')
+	def talk(self, fp, callback=None):
+		self.play(self.data + 'beep_lo.wav')
+		self.logger.info('结束录音')
 		self.isRecording = False
 		if self.profiling:
-			logger.info('性能调试已打开')
+			self.logger.info('性能调试已打开')
 			pr = cProfile.Profile()
 			pr.enable()
-			self.doConverse(fp, callback)
+			self.onTalk(fp, callback)
 			pr.disable()
 			s = io.StringIO()
 			sortby = 'cumulative'
@@ -71,22 +142,83 @@ class Robot(object):
 			ps.print_stats()
 			print(s.getvalue())
 		else:
-			self.doConverse(fp, callback)
+			self.onTalk(fp, callback)
 
-	def doConverse(self, fp, callback=None, onSay=None):
+	def onTalk(self, fp, callback=None, onSay=None):
 		try:
 			self.interrupt()
-			query = self.asr.transcribe(fp)
-			utils.check_and_delete(fp)
-			self.doResponse(query, callback, onSay)
+			query = self.tool['asr'].asr(fp)
+			File.remove(fp)
+			self.response(query, callback, onSay)
 		except Exception as e:
-			logger.critical(e)
-			utils.clean()
+			self.logger.critical(e)
+			clean()
+
+	def doParse(self, query, **args):
+		return self.tool['nlu'].nlu(query, **args)
+
+	def setImmersiveMode(self, slug):
+		self.immersiveMode = slug
+
+	def getImmersiveMode(self):
+		return self.immersiveMode
+
+	def response(self, query, uid='', onSay=None):
+		# 统计
+		#statistic.report(1)
+		self.interrupt()
+		self.appendHistory(0, query, uid)
+
+		if onSay:
+			self.onSay = onSay
+
+		if query.strip() == '':
+			self.pardon()
+			return
+
+		lastImmersiveMode = self.immersiveMode
+
+		if not self.skill.query(query):
+			# 没命中技能,使用机器人回复
+			msg = self.tool['ai'].ai(query)
+			self.say(msg, True, completed=self.checkRestore)
+		else:
+			if lastImmersiveMode is not None and lastImmersiveMode != self.matchPlugin:
+				time.sleep(1)
+				if self.checkPlayer():
+					self.logger.debug('等说完再checkRestore')
+					self.player.appendOnCompleted(lambda: self.checkRestore())
+				else:
+					self.logger.debug('checkRestore')
+					self.checkRestore()
+
+	#将会话历史加进历史记录
+	def appendHistory(self, t, text, uid=''):
+		if t in (0, 1) and text != '':
+			if text.endswith(',') or text.endswith(','):
+				text = text[:-1]
+			if uid == '' or uid == None or uid == 'null':
+				uid = Demeter.uuid(__name__)
+			# 将图片处理成HTML
+			pattern = r'https?://.+\.(?:png|jpg|jpeg|bmp|gif|JPG|PNG|JPEG|BMP|GIF)'
+			url_pattern = r'^https?://.+'
+			imgs = re.findall(pattern, text)
+			for img in imgs:
+				text = text.replace(img, '<img src={} class="img"/>'.format(img))
+			urls = re.findall(url_pattern, text)
+			for url in urls:
+				text = text.replace(url, '<a href={} target="_blank">{}</a>'.format(url, url))
+			self.history.append({'type': t, 'text': text, 'time': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())), 'uid': uid})
+
+	def completed(self, msg):
+		if Demeter.config['snowboy']['active_mode'] and (msg.endswith('?') or msg.endswith(u'?') or u'告诉我' in msg or u'请回答' in msg):
+			query = self.getSnowBoy().listen()
+			self.response(query)
 
 	def pardon(self):
 		if not self.hasPardon:
-			self.say("抱歉,刚刚没听清,能再说一遍吗?", onCompleted=lambda: self.doResponse(self.activeListen()))
+			self.say('抱歉,刚刚没听清,能再说一遍吗?', completed=lambda: self.response(self.getSnowBoy().listen()))
 			self.hasPardon = True
 		else:
-			self.say("没听清呢")
+			self.say('没听清呢')
 			self.hasPardon = False

+ 96 - 0
service/skill.py

@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+from __future__ import division
+from .__load__ import *
+
+class Skill(object):
+
+	def init(self, robot):
+		# vecan机器人
+		self.robot = robot
+		self.logger = Log.init(__name__)
+		self.plugins = []
+		#self.plugins = plugin_loader.get_plugins(self.robot)
+		self.handling = False
+
+	def isImmersive(self, plugin, text, parsed):
+		return self.robot.getImmersiveMode() == plugin.SLUG and \
+			plugin.isValidImmersive(text, parsed)
+
+	def printPlugins(self):
+		plugin_list = []
+		for plugin in self.plugins:
+			plugin_list.append(plugin.SLUG)
+		self.logger.info('已激活插件:{}'.format(plugin_list))
+
+	def query(self, text):
+		"""
+		query 模块
+
+		Arguments:
+		text -- 用户输入
+		"""
+
+		args = {
+			"service_id": "S13442",
+			"api_key": 'w5v7gUV3iPGsGntcM84PtOOM',
+			"secret_key": 'KffXwW6E1alcGplcabcNs63Li6GvvnfL'
+		}
+		parsed = self.robot.doParse(text, **args)
+		print parsed
+		return parsed
+
+		for plugin in self.plugins:
+			if not plugin.isValid(text, parsed) and not self.isImmersive(plugin, text, parsed):
+				continue
+
+			self.logger.info("'{}' 命中技能 {}".format(text, plugin.SLUG))
+			self.robot.matchPlugin = plugin.SLUG
+
+			if plugin.IS_IMMERSIVE:
+				self.robot.setImmersiveMode(plugin.SLUG)
+
+			continueHandle = False
+			try:
+				self.handling = True
+				continueHandle = plugin.handle(text, parsed)
+				self.handling = False				
+			except Exception:
+				self.logger.critical('Failed to execute plugin',
+								   exc_info=True)
+				reply = u"抱歉,插件{}出故障了,晚点再试试吧".format(plugin.SLUG)
+				self.robot.say(reply, plugin=plugin.SLUG)
+			else:
+				self.logger.debug("Handling of phrase '%s' by " +
+								   "plugin '%s' completed", text,
+								   plugin.SLUG)					
+			finally:
+				if not continueHandle:
+					return True
+
+		self.logger.debug("No plugin was able to handle phrase {} ".format(text))
+		return False
+
+	def restore(self):
+		""" 恢复某个技能的处理 """
+		if not self.robot.immersiveMode:
+			return
+		for plugin in self.plugins:
+			if plugin.SLUG == self.robot.immersiveMode and plugin.restore:
+				plugin.restore()
+
+	def pause(self):
+		""" 暂停某个技能的处理 """
+		if not self.robot.immersiveMode:
+			return
+		for plugin in self.plugins:
+			if plugin.SLUG == self.robot.immersiveMode and plugin.pause:
+				plugin.pause()
+
+	def understand(self, fp):
+		if self.robot and self.robot.tool['asr']:
+			return self.robot.tool['asr'].asr(fp)
+		return None
+
+	def say(self, msg, cache=False):
+		if self.robot and self.robot.tool['tts']:
+			self.robot.tool['tts'].tts(msg)

+ 69 - 33
service/snowboy.py

@@ -5,46 +5,82 @@ from snowboy import snowboydecoder
 class Snowboy(object):
 
 	def init(self, robot):
+		# 勿扰模式控制 勿扰模式关闭
 		self.bother = False
-		self.robot = robot
+		# snowboy detector
 		self.detector = None
+		# 中断控制
 		self.interrupted = False
-		self.data = File.runtime('data')
+		# vecan机器人
+		self.robot = robot
+		self.hotword()
 
+	# 启动hotword
+	def hotword(self):
 		if self.detector is not None:
 			self.detector.terminate()
-		# snowboy配置,后续改成从数据库读取
-		snowboyConfig = Demeter.config['snowboy']
-		if snowboyConfig['hotword_switch'] == True:
-			models = [
-				self.data + snowboyConfig['hotword'],
-				self.data + snowboyConfig['on_hotword'],
-				self.data + snowboyConfig['off_hotword']
-			]
-		else:
-			models = self.data + snowboyConfig['hotword']
-		self.detector = snowboydecoder.HotwordDetector(models, sensitivity=snowboyConfig['sensitivity'])
+
+		self.getConfig()
+		self.detector = snowboydecoder.HotwordDetector(self.config['models'], sensitivity=self.config['sensitivity'], outname=Demeter.config['vecan']['outname'], temp=Demeter.config['vecan']['temp'])
 		# main loop
 		try:
-			if snowboyConfig['hotword_switch'] == True:
-				callbacks = [self.detectedCallback,
-							 self.onHotwordCallback,
-							 self.offHotwordCallback]
-			else:
-				callbacks = self.detectedCallback
-			self.detector.start(detected_callback=callbacks,
-								audio_recorder_callback=self.robot.converse,
+			self.detector.start(detected_callback=self.config['callbacks'],
+								audio_recorder_callback=self.robot.talk,
 								interrupt_check=self.interruptCallback,
-								silent_count_threshold=int(snowboyConfig['silent_threshold']),
-								recording_timeout=int(snowboyConfig['recording_timeout']) * 4,
+								silent_count_threshold=int(self.config['silent_threshold']),
+								recording_timeout=int(self.config['recording_timeout']) * 4,
 								sleep_time=0.03)
 			self.detector.terminate()
 		except Exception as e:
 			Demeter.logger.critical('离线唤醒机制初始化失败:{}'.format(e))
 
+	# active
+	def listen(self, silent=False):
+		Demeter.logger.debug('activeListen')
+		try:
+			if not silent:
+				time.sleep(1)
+				self.robot.play(self.robot.data + 'beep_hi.wav')
+			listener = snowboydecoder.ActiveListener([self.robot.data + self.config['hotword']], outname=Demeter.config['vecan']['outname'], temp=Demeter.config['vecan']['temp'])
+			voice = listener.listen(
+				silent_count_threshold=int(self.config['silent_threshold']),
+				recording_timeout=int(self.config['recording_timeout']) * 4
+			)
+			if not silent:
+				self.robot.play(self.robot.data + 'beep_lo.wav')
+			if voice:
+				query = self.robot.tool['asr'].asr(voice)
+				Demeter.remove(voice)
+				return query
+		except Exception as e:			
+			Demeter.logger.error(e)
+			return ''
+
+	# snowboy配置,后续改成从数据库读取
+	def getConfig(self):
+		self.config = Demeter.config['snowboy']
+		if self.config['hotword_switch'] == True:
+			self.config['models'] = [
+				self.robot.data + self.config['hotword'],
+				self.robot.data + self.config['on_hotword'],
+				self.robot.data + self.config['off_hotword']
+			]
+
+			self.config['callbacks'] = [
+				self.detectedCallback,
+				self.onHotwordCallback,
+				self.offHotwordCallback
+			]
+		else:
+			self.config['models'] = self.robot.data + self.config['hotword']
+			self.config['callbacks'] = self.detectedCallback
+
+	# stop
 	def stop(self):
 		self.interrupted = True
+		clean()
 
+	# callback
 	def detectedCallback(self):
 		if not self.isProperTime():
 			Demeter.logger.warning('勿扰模式开启中')
@@ -52,21 +88,21 @@ class Snowboy(object):
 		if self.robot.isRecording:
 			Demeter.logger.warning('正在录音中,跳过')
 			return
-		Player.play(self.data + 'beep_hi.wav')
+		self.robot.player.play(self.robot.data + 'beep_hi.wav')
 		Demeter.logger.info('开始录音')
 		self.robot.interrupt()
 		self.robot.isRecording = True;
 
 	def onHotwordCallback(self):
-		if snowboyConfig['hotword_switch'] == True:
+		if self.config['hotword_switch'] == True:
 			self.bother = True
-			Player.play(self.data + 'off.wav')
+			self.robot.player.play(self.robot.data + 'off.wav')
 			Demeter.logger.info('勿扰模式打开')
 
 	def offHotwordCallback(self):
-		if snowboyConfig['hotword_switch'] == True:
+		if self.config['hotword_switch'] == True:
 			self.bother = False
-			Player.play(self.data + 'on.wav')
+			self.robot.player.play(self.robot.data + 'on.wav')
 			Demeter.logger.info('勿扰模式关闭')
 
 	def interruptCallback(self):
@@ -75,14 +111,14 @@ class Snowboy(object):
 	def isProperTime():
 		if self.bother == True:
 			return False
-		if 'bother_enable' not in Demeter.config['snowboy']:
+		if 'bother_enable' not in self.config:
 			return True
-		if Demeter.config['snowboy']['bother_enable'] == False:
+		if self.config['bother_enable'] == False:
 			return True
-		if 'bother_since' not in Demeter.config['snowboy'] or 'bother_till' not in Demeter.config['snowboy']:
+		if 'bother_since' not in self.config or 'bother_till' not in self.config:
 			return True
-		since = Demeter.config['snowboy']['bother_since']
-		till = Demeter.config['snowboy']['bother_till']
+		since = self.config['bother_since']
+		till = self.config['bother_till']
 		current = time.localtime(time.time()).tm_hour
 		if till > since:
 			return current not in range(since, till)

+ 0 - 0
snowboy/__init__.py


BIN
snowboy/_snowboydetect.so


BIN
snowboy/resources/common.res


BIN
snowboy/resources/ding.wav


BIN
snowboy/resources/dong.wav


+ 402 - 0
snowboy/snowboydecoder.py

@@ -0,0 +1,402 @@
+# -*- coding: utf-8 -*-
+from __future__ import division
+from .__load__ import *
+import collections
+import pyaudio
+from . import snowboydetect
+from ctypes import CFUNCTYPE, c_char_p, c_int, cdll
+from contextlib import contextmanager
+
+logger = Log.init('snowboy')
+TOP_DIR = os.path.dirname(os.path.abspath(__file__))
+
+RESOURCE_FILE = os.path.join(TOP_DIR, "resources/common.res")
+DETECT_DING = os.path.join(TOP_DIR, "resources/ding.wav")
+DETECT_DONG = os.path.join(TOP_DIR, "resources/dong.wav")
+
+def py_error_handler(filename, line, function, err, fmt):
+    pass
+
+ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
+
+c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
+
+@contextmanager
+def no_alsa_error():
+    try:
+        asound = cdll.LoadLibrary('libasound.so')
+        asound.snd_lib_error_set_handler(c_error_handler)
+        yield
+        asound.snd_lib_error_set_handler(None)
+    except:
+        yield
+        pass
+
+class RingBuffer(object):
+    """Ring buffer to hold audio from PortAudio"""
+
+    def __init__(self, size=4096):
+        self._buf = collections.deque(maxlen=size)
+
+    def extend(self, data):
+        """Adds data to the end of buffer"""
+        self._buf.extend(data)
+
+    def get(self):
+        """Retrieves data from the beginning of buffer and clears it"""
+        tmp = bytes(bytearray(self._buf))
+        self._buf.clear()
+        return tmp
+
+
+def play_audio_file(fname=DETECT_DING):
+    """Simple callback function to play a wave file. By default it plays
+    a Ding sound.
+
+    :param str fname: wave file name
+    :return: None
+    """
+    ding_wav = wave.open(fname, 'rb')
+    ding_data = ding_wav.readframes(ding_wav.getnframes())
+    with no_alsa_error():
+        audio = pyaudio.PyAudio()
+    stream_out = audio.open(
+        format=audio.get_format_from_width(ding_wav.getsampwidth()),
+        channels=ding_wav.getnchannels(),
+        rate=ding_wav.getframerate(), input=False, output=True)
+    stream_out.start_stream()
+    stream_out.write(ding_data)
+    time.sleep(0.2)
+    stream_out.stop_stream()
+    stream_out.close()
+    audio.terminate()
+
+
+class ActiveListener(object):
+    """ Active Listening with VAD """
+    def __init__(self, decoder_model,
+                 resource=RESOURCE_FILE,
+                 outname = 'output',
+                 temp = ''):
+        logger.debug("activeListen __init__()")
+        self.recordedData = []
+        model_str = ",".join(decoder_model)
+        self.detector = snowboydetect.SnowboyDetect(
+            resource_filename=resource.encode(), model_str=model_str.encode())
+        self.ring_buffer = RingBuffer(
+            self.detector.NumChannels() * self.detector.SampleRate() * 5)
+
+    def listen(self, interrupt_check=lambda: False, sleep_time=0.03, silent_count_threshold=15, recording_timeout=100):
+        """
+        :param interrupt_check: a function that returns True if the main loop
+                                needs to stop.
+        :param silent_count_threshold: indicates how long silence must be heard
+                                       to mark the end of a phrase that is
+                                       being recorded.
+        :param float sleep_time: how much time in second every loop waits.
+        :param recording_timeout: limits the maximum length of a recording.
+        :return: recorded file path
+        """
+        logger.debug("activeListen listen()")
+
+        self._running = True
+
+        def audio_callback(in_data, frame_count, time_info, status):
+            self.ring_buffer.extend(in_data)
+            play_data = chr(0) * len(in_data)
+            return play_data, pyaudio.paContinue
+
+        with no_alsa_error():            
+            self.audio = pyaudio.PyAudio()
+        
+        logger.debug('opening audio stream')
+
+        try:
+            self.stream_in = self.audio.open(
+                input=True, output=False,
+                format=self.audio.get_format_from_width(
+                    self.detector.BitsPerSample() / 8),
+                channels=self.detector.NumChannels(),
+                rate=self.detector.SampleRate(),
+                frames_per_buffer=2048,
+                stream_callback=audio_callback)
+        except Exception as e:
+            logger.critical(e)
+            return 
+
+        logger.debug('audio stream opened')
+
+        if interrupt_check():
+            logger.debug("detect voice return")
+            return
+
+        silentCount = 0
+        recordingCount = 0
+
+        logger.debug("begin activeListen loop")
+        
+        while self._running is True:
+
+            if interrupt_check():
+                logger.debug("detect voice break")
+                break
+            data = self.ring_buffer.get()
+            if len(data) == 0:
+                time.sleep(sleep_time)
+                continue
+            
+            status = self.detector.RunDetection(data)
+            if status == -1:
+                logger.warning("Error initializing streams or reading audio data")
+                
+            stopRecording = False
+            if recordingCount > recording_timeout:
+                stopRecording = True
+            elif status == -2: #silence found
+                if silentCount > silent_count_threshold:
+                    stopRecording = True
+                else:
+                    silentCount = silentCount + 1
+            elif status == 0: #voice found
+                silentCount = 0
+
+            if stopRecording == True:
+                return self.saveMessage()
+
+            recordingCount = recordingCount + 1
+            self.recordedData.append(data)
+
+        logger.debug("finished.")
+
+
+    def saveMessage(self):
+        """
+        Save the message stored in self.recordedData to a timestamped file.
+        """
+        filename = os.path.join(self.temp, self.outname + str(int(time.time())) + '.wav')
+        data = b''.join(self.recordedData)
+
+        #use wave to save data
+        wf = wave.open(filename, 'wb')
+        wf.setnchannels(self.detector.NumChannels())
+        wf.setsampwidth(self.audio.get_sample_size(
+            self.audio.get_format_from_width(self.detector.BitsPerSample() / 8)))
+        wf.setframerate(self.detector.SampleRate())
+        wf.writeframes(data)
+        wf.close()
+        logger.debug("finished saving: " + filename)
+
+        self.stream_in.stop_stream()
+        self.stream_in.close()
+        self.audio.terminate()
+        
+        return filename
+    
+    
+
+
+class HotwordDetector(object):
+    """
+    Snowboy decoder to detect whether a keyword specified by `decoder_model`
+    exists in a microphone input stream.
+
+    :param decoder_model: decoder model file path, a string or a list of strings
+    :param resource: resource file path.
+    :param sensitivity: decoder sensitivity, a float of a list of floats.
+                              The bigger the value, the more senstive the
+                              decoder. If an empty list is provided, then the
+                              default sensitivity in the model will be used.
+    :param audio_gain: multiply input volume by this factor.
+    :param apply_frontend: applies the frontend processing algorithm if True.
+    """
+
+    def __init__(self, decoder_model,
+                 resource=RESOURCE_FILE,
+                 sensitivity=[],
+                 audio_gain=1,
+                 apply_frontend=False,
+                 outname='output',
+                 temp=''):
+
+        self._running = False
+
+        tm = type(decoder_model)
+        ts = type(sensitivity)
+        if tm is not list:
+            decoder_model = [decoder_model]
+        if ts is not list:
+            sensitivity = [sensitivity]
+        model_str = ",".join(decoder_model)
+
+        self.detector = snowboydetect.SnowboyDetect(
+            resource_filename=resource.encode(), model_str=model_str.encode())
+        self.detector.SetAudioGain(audio_gain)
+        self.detector.ApplyFrontend(apply_frontend)
+        self.num_hotwords = self.detector.NumHotwords()
+
+        if len(decoder_model) > 1 and len(sensitivity) == 1:
+            sensitivity = sensitivity * self.num_hotwords
+        if len(sensitivity) != 0:
+            assert self.num_hotwords == len(sensitivity), \
+                "number of hotwords in decoder_model (%d) and sensitivity " \
+                "(%d) does not match" % (self.num_hotwords, len(sensitivity))
+        sensitivity_str = ",".join([str(t) for t in sensitivity])
+        if len(sensitivity) != 0:
+            self.detector.SetSensitivity(sensitivity_str.encode())        
+
+        self.ring_buffer = RingBuffer(
+            self.detector.NumChannels() * self.detector.SampleRate() * 5)
+
+    def start(self, detected_callback=play_audio_file,
+              interrupt_check=lambda: False,
+              sleep_time=0.03,
+              audio_recorder_callback=None,
+              silent_count_threshold=15,
+              recording_timeout=100):
+        """
+        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
+        """
+        self._running = True
+
+        def audio_callback(in_data, frame_count, time_info, status):
+            self.ring_buffer.extend(in_data)
+            play_data = chr(0) * len(in_data)
+            return play_data, pyaudio.paContinue
+
+        with no_alsa_error():
+            self.audio = pyaudio.PyAudio()
+        self.stream_in = self.audio.open(
+            input=True, output=False,
+            format=self.audio.get_format_from_width(
+                self.detector.BitsPerSample() / 8),
+            channels=self.detector.NumChannels(),
+            rate=self.detector.SampleRate(),
+            frames_per_buffer=2048,
+            stream_callback=audio_callback)
+
+        if interrupt_check():
+            logger.debug("detect voice return")
+            return
+
+        tc = type(detected_callback)
+        if tc is not list:
+            detected_callback = [detected_callback]
+        if len(detected_callback) == 1 and self.num_hotwords > 1:
+            detected_callback *= self.num_hotwords
+
+        assert self.num_hotwords == len(detected_callback), \
+            "Error: hotwords in your models (%d) do not match the number of " \
+            "callbacks (%d)" % (self.num_hotwords, len(detected_callback))
+
+        logger.debug("detecting...")
+
+        state = "PASSIVE"
+        while self._running is True:            
+            if interrupt_check():
+                logger.debug("detect voice break")
+                break
+            data = self.ring_buffer.get()
+            if len(data) == 0:
+                time.sleep(sleep_time)
+                continue
+
+            status = self.detector.RunDetection(data)
+            if status == -1:
+                logger.warning("Error initializing streams or reading audio data")
+
+            #small state machine to handle recording of phrase after keyword
+            if state == "PASSIVE":
+                if status > 0: #key word found
+
+                    self.recordedData = []
+                    self.recordedData.append(data)
+                    silentCount = 0
+                    recordingCount = 0
+                    message = "Keyword " + str(status) + " detected at time: "
+                    message += time.strftime("%Y-%m-%d %H:%M:%S",
+                                         time.localtime(time.time()))
+                    logger.info(message)
+                    callback = detected_callback[status-1]                    
+                    if callback is not None:
+                        callback()
+
+                    if audio_recorder_callback is not None and status == 1 and utils.is_proper_time():
+                        state = "ACTIVE"
+                    continue
+
+            elif state == "ACTIVE":
+                stopRecording = False
+                if recordingCount > recording_timeout:
+                    stopRecording = True
+                elif status == -2: #silence found
+                    if silentCount > silent_count_threshold:
+                        stopRecording = True
+                    else:
+                        silentCount = silentCount + 1
+                elif status == 0: #voice found
+                    silentCount = 0
+
+                if stopRecording == True:
+                    fname = self.saveMessage()
+                    audio_recorder_callback(fname)
+                    state = "PASSIVE"                    
+                    continue
+
+                recordingCount = recordingCount + 1
+                self.recordedData.append(data)                
+
+        logger.debug("finished.")
+
+
+    def saveMessage(self):
+        """
+        Save the message stored in self.recordedData to a timestamped file.
+        """
+        filename = os.path.join(self.temp, self.outname + str(int(time.time())) + '.wav')
+        data = b''.join(self.recordedData)
+
+        #use wave to save data
+        wf = wave.open(filename, 'wb')
+        wf.setnchannels(self.detector.NumChannels())
+        wf.setsampwidth(self.audio.get_sample_size(
+            self.audio.get_format_from_width(
+                self.detector.BitsPerSample() / 8)))
+        wf.setframerate(self.detector.SampleRate())
+        wf.writeframes(data)
+        wf.close()
+        logger.debug("finished saving: " + filename)
+        return filename
+
+    def terminate(self):
+        """
+        Terminate audio stream. Users can call start() again to detect.
+        :return: None
+        """
+        if self._running:
+            self.stream_in.stop_stream()
+            self.stream_in.close()
+            self.audio.terminate()
+            self._running = False

+ 193 - 0
snowboy/snowboydetect.py

@@ -0,0 +1,193 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 3.0.12
+#
+# Do not make changes to this file unless you know what you are doing--modify
+# the SWIG interface file instead.
+
+from sys import version_info as _swig_python_version_info
+if _swig_python_version_info >= (2, 7, 0):
+    def swig_import_helper():
+        import importlib
+        pkg = __name__.rpartition('.')[0]
+        mname = '.'.join((pkg, '_snowboydetect')).lstrip('.')
+        try:
+            return importlib.import_module(mname)
+        except ImportError:
+            return importlib.import_module('_snowboydetect')
+    _snowboydetect = swig_import_helper()
+    del swig_import_helper
+elif _swig_python_version_info >= (2, 6, 0):
+    def swig_import_helper():
+        from os.path import dirname
+        import imp
+        fp = None
+        try:
+            fp, pathname, description = imp.find_module('_snowboydetect', [dirname(__file__)])
+        except ImportError:
+            import _snowboydetect
+            return _snowboydetect
+        try:
+            _mod = imp.load_module('_snowboydetect', fp, pathname, description)
+        finally:
+            if fp is not None:
+                fp.close()
+        return _mod
+    _snowboydetect = swig_import_helper()
+    del swig_import_helper
+else:
+    import _snowboydetect
+del _swig_python_version_info
+
+try:
+    _swig_property = property
+except NameError:
+    pass  # Python < 2.2 doesn't have 'property'.
+
+try:
+    import builtins as __builtin__
+except ImportError:
+    import __builtin__
+
+def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
+    if (name == "thisown"):
+        return self.this.own(value)
+    if (name == "this"):
+        if type(value).__name__ == 'SwigPyObject':
+            self.__dict__[name] = value
+            return
+    method = class_type.__swig_setmethods__.get(name, None)
+    if method:
+        return method(self, value)
+    if (not static):
+        if _newclass:
+            object.__setattr__(self, name, value)
+        else:
+            self.__dict__[name] = value
+    else:
+        raise AttributeError("You cannot add attributes to %s" % self)
+
+
+def _swig_setattr(self, class_type, name, value):
+    return _swig_setattr_nondynamic(self, class_type, name, value, 0)
+
+
+def _swig_getattr(self, class_type, name):
+    if (name == "thisown"):
+        return self.this.own()
+    method = class_type.__swig_getmethods__.get(name, None)
+    if method:
+        return method(self)
+    raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name))
+
+
+def _swig_repr(self):
+    try:
+        strthis = "proxy of " + self.this.__repr__()
+    except __builtin__.Exception:
+        strthis = ""
+    return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+try:
+    _object = object
+    _newclass = 1
+except __builtin__.Exception:
+    class _object:
+        pass
+    _newclass = 0
+
+class SnowboyDetect(_object):
+    __swig_setmethods__ = {}
+    __setattr__ = lambda self, name, value: _swig_setattr(self, SnowboyDetect, name, value)
+    __swig_getmethods__ = {}
+    __getattr__ = lambda self, name: _swig_getattr(self, SnowboyDetect, name)
+    __repr__ = _swig_repr
+
+    def __init__(self, resource_filename, model_str):
+        this = _snowboydetect.new_SnowboyDetect(resource_filename, model_str)
+        try:
+            self.this.append(this)
+        except __builtin__.Exception:
+            self.this = this
+
+    def Reset(self):
+        return _snowboydetect.SnowboyDetect_Reset(self)
+
+    def RunDetection(self, *args):
+        return _snowboydetect.SnowboyDetect_RunDetection(self, *args)
+
+    def SetSensitivity(self, sensitivity_str):
+        return _snowboydetect.SnowboyDetect_SetSensitivity(self, sensitivity_str)
+
+    def SetHighSensitivity(self, high_sensitivity_str):
+        return _snowboydetect.SnowboyDetect_SetHighSensitivity(self, high_sensitivity_str)
+
+    def GetSensitivity(self):
+        return _snowboydetect.SnowboyDetect_GetSensitivity(self)
+
+    def SetAudioGain(self, audio_gain):
+        return _snowboydetect.SnowboyDetect_SetAudioGain(self, audio_gain)
+
+    def UpdateModel(self):
+        return _snowboydetect.SnowboyDetect_UpdateModel(self)
+
+    def NumHotwords(self):
+        return _snowboydetect.SnowboyDetect_NumHotwords(self)
+
+    def ApplyFrontend(self, apply_frontend):
+        return _snowboydetect.SnowboyDetect_ApplyFrontend(self, apply_frontend)
+
+    def SampleRate(self):
+        return _snowboydetect.SnowboyDetect_SampleRate(self)
+
+    def NumChannels(self):
+        return _snowboydetect.SnowboyDetect_NumChannels(self)
+
+    def BitsPerSample(self):
+        return _snowboydetect.SnowboyDetect_BitsPerSample(self)
+    __swig_destroy__ = _snowboydetect.delete_SnowboyDetect
+    __del__ = lambda self: None
+SnowboyDetect_swigregister = _snowboydetect.SnowboyDetect_swigregister
+SnowboyDetect_swigregister(SnowboyDetect)
+
+class SnowboyVad(_object):
+    __swig_setmethods__ = {}
+    __setattr__ = lambda self, name, value: _swig_setattr(self, SnowboyVad, name, value)
+    __swig_getmethods__ = {}
+    __getattr__ = lambda self, name: _swig_getattr(self, SnowboyVad, name)
+    __repr__ = _swig_repr
+
+    def __init__(self, resource_filename):
+        this = _snowboydetect.new_SnowboyVad(resource_filename)
+        try:
+            self.this.append(this)
+        except __builtin__.Exception:
+            self.this = this
+
+    def Reset(self):
+        return _snowboydetect.SnowboyVad_Reset(self)
+
+    def RunVad(self, *args):
+        return _snowboydetect.SnowboyVad_RunVad(self, *args)
+
+    def SetAudioGain(self, audio_gain):
+        return _snowboydetect.SnowboyVad_SetAudioGain(self, audio_gain)
+
+    def ApplyFrontend(self, apply_frontend):
+        return _snowboydetect.SnowboyVad_ApplyFrontend(self, apply_frontend)
+
+    def SampleRate(self):
+        return _snowboydetect.SnowboyVad_SampleRate(self)
+
+    def NumChannels(self):
+        return _snowboydetect.SnowboyVad_NumChannels(self)
+
+    def BitsPerSample(self):
+        return _snowboydetect.SnowboyVad_BitsPerSample(self)
+    __swig_destroy__ = _snowboydetect.delete_SnowboyVad
+    __del__ = lambda self: None
+SnowboyVad_swigregister = _snowboydetect.SnowboyVad_swigregister
+SnowboyVad_swigregister(SnowboyVad)
+
+# This file is compatible with both classic and new-style classes.
+
+

+ 23 - 14
vecan.py

@@ -6,19 +6,33 @@ class Vecan(object):
 	def init(self):
 		print('''
 		********************************************************
-		*		  vecan-robot - 神算终端机器人				*
-		*		  (c) 2019 rabin <rabin@dever.cc>			 *
-		*	 https://github.com/shemic/vecan.git			  *
+		*          vecan-robot - 神算终端机器人                *
+		*          (c) 201(c) 2019 rabin <rabin@dever.cc>      *
+		*         https://github.com/shemic/vecan.git          *
 		********************************************************
-					如需退出,可以按 Ctrl-4 组合键。
+                    如需退出,可以按 Ctrl-4 组合键。
 		''')
-		Log.init(__name__)
+		Demeter.initConfig()
+		Demeter.logger = Log.init(__name__)
 		Demeter.logger.info('测试测试')
 
+		self.config()
+
 		self.robot = Demeter.service('robot')
-		self.robot.say('你好')
+		self.robot.init()
+		self.robot.say('你好,我是神算终端机器人vecan,很高兴为你服务')
+
+		Demeter.dog = WatchDog.init()
 
-		WatchDog.init()
+	def config(self):
+		if 'vecan' not in Demeter.config:
+			Demeter.config['vecan'] = {}
+		if 'data' not in Demeter.config['vecan']:
+			Demeter.config['vecan']['data'] = File.runtime('data')
+		if 'temp' not in Demeter.config['vecan']:
+			Demeter.config['vecan']['temp'] = File.runtime('temp')
+		if 'outname' not in Demeter.config['vecan']:
+			Demeter.config['vecan']['outname'] = 'output'
 
 	def stop(self, signal, frame):
 		self.clean()
@@ -29,20 +43,15 @@ class Vecan(object):
 		self.init()
 		signal.signal(signal.SIGINT, self.stop)
 		#Demeter.webInit('front')
+		return
 		try:
 			self.snowboy = Demeter.service('snowboy')
 			self.snowboy.init(self.robot)
+			self.robot.initSnowboy(self.snowboy)
 		except AttributeError:
 			Demeter.logger.error('初始化离线唤醒功能失败')
 			pass
 
-	def clean():
-		temp = File.runtime('temp')
-		temp_files = os.listdir(temp)
-		for f in temp_files:
-			if os.path.isfile(os.path.join(temp, f)) and re.match(r'output[\d]*\.wav', os.path.basename(f)):
-				os.remove(os.path.join(temp, f))
-
 if __name__ == '__main__':
 	vecan = Vecan()
 	vecan.run()