sound_tool.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #-*- coding:utf-8 -*-
  2. import os,sys
  3. reload(sys)
  4. sys.setdefaultencoding('utf-8')
  5. import wave
  6. from time import sleep
  7. import time
  8. import numpy
  9. import numpy as np
  10. import pyaudio
  11. # import pylab
  12. # import matplotlib.pyplot as pl
  13. # import matplotlib
  14. import math,random
  15. from ssat_sdk.sound.audio_recorder import ARecorder
  16. from ssat_sdk.sound.audio_recorder import READ_BUF_TIME
  17. from ssat_sdk.sound.audio_analysis import AAnalysis
  18. from ssat_sdk.sat_environment import getSATTmpDIR,getVoicecards
  19. import threading,thread
  20. import Queue
  21. from scipy import fftpack
  22. from scipy import signal
  23. from scipy.io import wavfile
  24. from ssat_sdk.device_manage.sound_manager import *
  25. from threading import Lock
  26. FORMAT = pyaudio.paInt16
  27. RATE = 44100
  28. RECORD_SECONDS = 5
  29. data = []
  30. FFT_LEN = 128
  31. frames = []
  32. counter = 1
  33. isMonitor = True
  34. NoSoundLT = 10#记录连续8次<SoundLevel状态,1秒钟大概抓40次数据。10次代表0.2秒,即表示无声。
  35. NoSoundTLimit = 0.1 #没有声音时长判断标准
  36. SoundTLimit = 0.06 #有声时长判断标准
  37. ExpList = []
  38. t = None
  39. q = Queue.Queue()
  40. # processing block
  41. # window = signal.hamming(CHUNK)
  42. TAG = "SoundTool"
  43. MuTex = Lock()
  44. class AudioIdentify():
  45. # 设备名称,默认拾音器=0, 亚为USB=1;
  46. Sound_Has = 0
  47. Sound_No = 1
  48. Sound_Break = 2
  49. def __init__(self):
  50. print "Init AudioIdentify"
  51. self.aAnalysis = AAnalysis()
  52. self.isSound = False #整个监听过程,是否有声音
  53. self.hasBlock = False #是否有断续现象
  54. self.deviceType = 0
  55. # self.audioChecker = Sound()
  56. self.audioChecker = None
  57. self.audioRecorder = ""
  58. try:
  59. list = getVoicecards()
  60. print u'音频设备列表:',list
  61. self.default = list["default"]
  62. for item in list["devices"]:
  63. if self.default == item["name"]:
  64. self.deviceType = int(item["type"])
  65. break
  66. if self.deviceType == 1: # usb
  67. print u'使用usb声音检测设备'
  68. self.audioChecker = SoundManager()
  69. if self.audioChecker.IsOpen != 0:
  70. print u'初始化声音设备失败'
  71. else:
  72. self.audioChecker = ARecorder()
  73. print u'使用拾音器设备'
  74. except Exception, e:
  75. self.audioChecker = ARecorder()
  76. print u"获取VoiceCards失败", e
  77. # 是否有声音,muteVoltage静音电压值
  78. # 废弃函数
  79. def hasSound(self, duration, muteVoltage = 1.0):
  80. if self.deviceType == 0:
  81. return self.isSound
  82. else:
  83. if self.audioChecker == None:
  84. print u'USB设备未初始化'
  85. return False
  86. # 获取静音百分比,1.0表示完全静音;
  87. percentMute = self.audioChecker.IsMute(duration, muteVoltage)
  88. # 静音百分比小于1.0时认为有声音;
  89. self.isSound = True if percentMute < 1.0 else False
  90. return self.isSound
  91. '''
  92. 返回当前设备状态。设备就绪返回True,失败返回False。
  93. '''
  94. def getStatus(self):
  95. if self.deviceType == 0:
  96. return self.audioChecker.getStatus()
  97. if self.audioChecker == None:
  98. print u'USB设备未初始化'
  99. return False
  100. return True if self.audioChecker.IsOpen == 0 else False
  101. '''
  102. 判断当前声音是否中断,中断返回True,没有中断返回False
  103. collectionTime: 采集时长,单位毫秒
  104. muteVoltage: 静音电压值,单位伏
  105. interruptTime: 停顿时长,单位毫秒
  106. '''
  107. def isInterrupt(self, collectionTime = 5000, muteVoltage = 1.0, interruptTime = 200):
  108. # return self.audioChecker.isInterrupt()
  109. if self.audioChecker == None:
  110. print u'USB设备未初始化'
  111. return False
  112. return self.audioChecker.IsInterrupt( collectionTime, muteVoltage, interruptTime)
  113. '''
  114. 判断当前是否监听到声音,成功返回True,失败返回False。
  115. collectionTime: 采集时长,单位毫秒
  116. muteVoltage: 静音电压值,单位伏
  117. [return] Bool
  118. '''
  119. def isOK(self):
  120. return self.isSound
  121. '''
  122. 采集声音(主线程)
  123. [int] int :采集多少时长的声明,以秒为单位
  124. [return] void
  125. '''
  126. def startCHK(self, seconds,muteVoltage = 1.0):
  127. if self.deviceType == 0:
  128. self.monitorSound(seconds)
  129. elif self.deviceType == 1:
  130. if self.audioChecker == None:
  131. print u'USB设备未初始化'
  132. return False
  133. # 获取静音百分比,1.0表示完全静音;
  134. percentMute = self.audioChecker.IsMute(seconds, muteVoltage)
  135. # 静音百分比小于1.0时认为有声音;
  136. self.isSound = True if percentMute < 1.0 else False
  137. # return self.isSound
  138. '''
  139. 返回monitorSound过程中的声音平均强度,如果LR为True则返回左右声道的声音强度,Freq为频率范围,单位Hz。
  140. 切割音频,进行最大强度累计,然后算平均值。
  141. :param minFreq:频率范围中的最小频率值。
  142. :param maxFreq:频率范围中的最大频率值。
  143. :param LR:左右声道检测选项。True:左右声道分开检测;False:仅检测0声道,可能是左声道,有可能仅有的一个声道。
  144. :param audioFile: 传入新的音频文件,不用monitorSound的音频文件。
  145. :return: LR=True时,返回左右声道声音强度2个值,LR=False时,返回一个声道强度值。
  146. '''
  147. def getSoundPower(self, minFreq=50, maxFreq=20000, LR = False, audioFile="", avg=False):
  148. if audioFile.__len__() > 2:
  149. self.aAnalysis.analyWav(audioFile)
  150. return self.aAnalysis.getTotalAVGPower(LR=LR)
  151. '''
  152. 获取左右声道有无声的状态。
  153. :return result,valueList。
  154. result:-1代表声音检测异常;1表示单声道;2 表示双声道
  155. valueList:-1代表没有声音,0代表仅左声道,1代表仅右声道,2代表左右声道有声
  156. '''
  157. def getLRVariation(self):
  158. return self.aAnalysis.getLRVariation()
  159. '''
  160. 监听声音状态,阻塞式监听
  161. :param seconds:监听声音异常时间
  162. :param expList数组:声音异常记录数组。数组第一个值{"time":time.asctime( time.localtime(time.time()))},记录开始时间。
  163. 后续,碰到一次异常,记录一次。
  164. expList第一个字典数据,有audio 键,取录音文件路径。status键:0 代表无异常;1代表没有声音发出;2代表声音有间断。
  165. :param waveFile:可以指定录制的音频文件路径
  166. :return expList数组。 和带入的expList一样
  167. '''
  168. def monitorSound(self, seconds,expList=None, waveFile = None, buf_time=READ_BUF_TIME):
  169. global ExpList
  170. MuTex.acquire()
  171. print "monitorSound start:",seconds,expList
  172. del ExpList
  173. if expList is not None:
  174. ExpList = expList
  175. else:
  176. ExpList = []
  177. if self.deviceType == 0:
  178. firstItem = {"time":time.asctime(time.localtime(time.time()))}
  179. # analysisLock = threading.Condition()
  180. self.aAnalysis.startFramesAnalysis(self.audioChecker.getFrameQue(),
  181. self.audioChecker.CHANNELS,
  182. self.audioChecker.WIDTH,
  183. buf_time)
  184. self.audioChecker.monitor(seconds,saveWave=True, waveFile=waveFile, buf_time=buf_time)
  185. self.audioRecorder = self.audioChecker.waveFile
  186. time.sleep(1) #预留1秒钟,用于音频分析。
  187. self.aAnalysis.endFramesAnalysis()
  188. firstItem["audio"] = self.audioChecker.getWavFile()
  189. ExpList.append(firstItem)
  190. retExpList = self.changeMonSoundResult(ExpList)
  191. # print u'monitorSound end',retExpList
  192. MuTex.release() # 解锁
  193. print "monitorSound end:", seconds, retExpList
  194. return retExpList
  195. elif self.deviceType == 1:
  196. # 采集时长,单位毫秒;
  197. # collectionTime = 5000
  198. # 静音电压,单位伏;
  199. muteVoltage = 1.0
  200. # 停顿时长,单位毫秒;
  201. interruptTime = 200
  202. ExpList.append({"time":time.asctime( time.localtime(time.time())),"audio":""})
  203. isInterrupt = self.audioChecker.IsInterrupt( seconds, muteVoltage, interruptTime)
  204. if isInterrupt == True:
  205. ExpList.append({"time":time.asctime( time.localtime(time.time()))})
  206. MuTex.release() # 解锁
  207. return ExpList
  208. '''
  209. 启动一个线程,调用monitorSound函数。
  210. 详细说明,查看monitorSound函数。
  211. '''
  212. def monitorSoundTH(self,seconds,expList=None, waveFile = None, buf_time=READ_BUF_TIME):
  213. thread.start_new_thread(self.monitorSound, (seconds, expList, waveFile, buf_time,))
  214. def changeMonSoundResult(self,ExpList):
  215. self.isSound = self.aAnalysis.hasSound
  216. self.hasBlock = self.aAnalysis.hasBlock
  217. soundPower = self.getSoundPower()
  218. print "changeMonSoundResult:", self.aAnalysis.hasSound, self.aAnalysis.hasBlock, self.aAnalysis.hasPlosive,soundPower
  219. if soundPower - self.aAnalysis.TH_POWER >= 0:
  220. self.aAnalysis.hasSound = True
  221. if self.aAnalysis.hasSound is True and self.aAnalysis.hasBlock is True:
  222. ExpList[0]["status"] = self.Sound_Break
  223. elif self.aAnalysis.hasSound is True and self.aAnalysis.hasBlock is False:
  224. ExpList[0]["status"] = self.Sound_Has
  225. else:
  226. ExpList[0]["status"] = self.Sound_No
  227. retExpList = []
  228. for item in ExpList:
  229. retExpList.append(item)
  230. # del ExpList #线程调用,回传参数,需要用到
  231. return retExpList
  232. # print "changeMonSoundResult:",ExpList
  233. '''
  234. 开启一个后台线程,录制音频文件,并返回音频文件路径(保存于临时文件夹)。
  235. :param seconds:录制声音时间
  236. :param fileName:希望命名的文件名
  237. :param CHANNELS:录制单声道时为1,录制双声道时为2
  238. 成功时返回音频文件路径,失败则返回空结果
  239. '''
  240. def recordAudioFileReturnPath(self, seconds, fileName,CHANNELS = 2):
  241. try:
  242. filePath = os.path.join(getSATTmpDIR(), fileName)
  243. audioPath = self.monitorSound(seconds,waveFile=filePath)[0]["audio"]
  244. return audioPath
  245. except Exception,e:
  246. # LoggingUtil.printLog(u"获取录制的音频文件失败!")
  247. print u"获取录制的音频文件失败!",e
  248. return "wav file record fail!!!"
  249. '''
  250. 读取立体声文件并分离左右声道音源,
  251. 并返回左右声道音源的路径
  252. '''
  253. def splitChannel(self, filePath = ""):
  254. # 读取WAV声音文件
  255. if filePath == "":
  256. filePath = self.audioChecker.getWavFile()
  257. sampleRate, musicData = wavfile.read(filePath)
  258. # 提取左右声道数据
  259. left = []
  260. right = []
  261. musicData.shape = -1,2
  262. musicData = musicData.T
  263. left = musicData[0]
  264. right = musicData[1]
  265. # 写入结果文件
  266. section_path = filePath.split(".")[0]
  267. leftPath = section_path + "_left.wav"
  268. rightPath = section_path + "_right.wav"
  269. wavfile.write(leftPath, sampleRate, np.array(left))
  270. wavfile.write(rightPath, sampleRate, np.array(right))
  271. return leftPath, rightPath
  272. if __name__ == "__main__":
  273. aIden = AudioIdentify()
  274. aIden.monitorSound(5)
  275. time.sleep(1)
  276. # sound1 = "D:/1.wav"
  277. # sound2 = "D:/2.wav"
  278. # list1=[]
  279. # expList1 = aIden.monitorSoundTH(10)
  280. # time.sleep(15)
  281. # aIden.recordAudioFileReturnPath(5, "wavetest_%s.wav" % str(1))
  282. # print "expList1:",expList1
  283. # print "soundPower:", aIden.getSoundPower()
  284. # print "fftsoundPower:", aIden.aAnalysis.getFFTPower()
  285. # for i in range(10):
  286. # filePath = aIden.recordAudioFileReturnPath(5,"wavetest_%s.wav"%str(i))
  287. # print "getSoundPower:", aIden.getSoundPower(LR=True)
  288. # print "filePath:", filePath
  289. # time.sleep(3)
  290. # print "getSoundPower:",aIden.getSoundPower(LR=True)
  291. # print "getTotalAVGPower:",aIden.aAnalysis.getTotalAVGPower(LR=True)
  292. # aIden.aAnalysis.analyWav("D:/5.wav")
  293. # print "getLRVariation:",aIden.getLRVariation()
  294. # print "getFFTPower:",aIden.aAnalysis.getFFTPower(LR=True)
  295. # print "aIden.isOK():", aIden.isOK(),list1
  296. # shutil.move(expList1[0]['audio'], sound1)
  297. # for i in range(10):
  298. # expList2 = aIden.monitorSound(1)
  299. # print "expList2:",i, expList2
  300. # shutil.move(expList2[0]['audio'], sound2)
  301. # waveFile = "sound/wav_balance_v15.wav"
  302. # print "getSoundPower:", aIden.getSoundPower(LR=True,audioFile=waveFile)