详解python的webrtc库实现语音端点检测


Posted in Python onMay 31, 2017

引言

语音端点检测最早应用于电话传输和检测系统当中,用于通信信道的时间分配,提高传输线路的利用效率.端点检测属于语音处理系统的前端操作,在语音检测领域意义重大.

但是目前的语音端点检测,尤其是检测 人声 开始和结束的端点始终是属于技术难点,各家公司始终处于 能判断,但是不敢保证 判别准确性 的阶段.

详解python的webrtc库实现语音端点检测 

现在基于云端语义库的聊天机器人层出不穷,其中最著名的当属amazon的 Alexa/Echo 智能音箱.

详解python的webrtc库实现语音端点检测

国内如雨后春笋般出现了各种搭载语音聊天的智能音箱(如前几天在知乎上广告的若琪机器人)和各类智能机器人产品.国内语音服务提供商主要面对中文语音服务,由于语音不像图像有分辨率等等较为客观的指标,很多时候凭主观判断,所以较难判断各家语音识别和合成技术的好坏.但是我个人认为,国内的中文语音服务和国外的英文语音服务,在某些方面已经有超越的趋势.

详解python的webrtc库实现语音端点检测

通常搭建机器人聊天系统主要包括以下三个方面: 

  1.  语音转文字(ASR/STT)
  2.  语义内容(NLU/NLP)
  3.  文字转语音(TTS)

语音转文字(ASR/STT)

在将语音传给云端API之前,是本地前端的语音采集,这部分主要包括如下几个方面: 

  1.  麦克风降噪
  2.  声源定位
  3.  回声消除
  4.  唤醒词
  5.  语音端点检测
  6.  音频格式压缩

python 端点检测

由于实际应用中,单纯依靠能量检测特征检测等方法很难判断人声说话的起始点,所以市面上大多数的语音产品都是使用唤醒词判断语音起始.另外加上声音回路,还可以做语音打断.这样的交互方式可能有些傻,每次必须喊一下 唤醒词 才能继续聊天.这种方式聊多了,个人感觉会嘴巴疼:-O .现在github上有snowboy唤醒词的开源库,大家可以登录snowboy官网训练自己的唤醒词模型. 

  1.  Kitt-AI : Snowboy 
  2.  Sensory : Sensory

考虑到用唤醒词嘴巴会累,所以大致调研了一下,Python拥有丰富的库,直接import就能食用.这种方式容易受强噪声干扰,适合一个人在家玩玩. 

  1.  pyaudio: pip install pyaudio 可以从设备节点读取原始音频流数据,音频编码是PCM格式;
  2.  webrtcvad: pip install webrtcvad 检测判断一组语音数据是否为空语音;

当检测到持续时间长度 T1 vad检测都有语音活动,可以判定为语音起始;

当检测到持续时间长度 T2 vad检测都没有有语音活动,可以判定为语音结束;

完整程序代码可以从我的github下载

程序很简单,相信看一会儿就明白了

'''
Requirements:
+ pyaudio - `pip install pyaudio`
+ py-webrtcvad - `pip install webrtcvad`
'''
import webrtcvad
import collections
import sys
import signal
import pyaudio

from array import array
from struct import pack
import wave
import time

FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
CHUNK_DURATION_MS = 30    # supports 10, 20 and 30 (ms)
PADDING_DURATION_MS = 1500  # 1 sec jugement
CHUNK_SIZE = int(RATE CHUNK_DURATION_MS / 1000) # chunk to read
CHUNK_BYTES = CHUNK_SIZE 2 # 16bit = 2 bytes, PCM
NUM_PADDING_CHUNKS = int(PADDING_DURATION_MS / CHUNK_DURATION_MS)
# NUM_WINDOW_CHUNKS = int(240 / CHUNK_DURATION_MS)
NUM_WINDOW_CHUNKS = int(400 / CHUNK_DURATION_MS) # 400 ms/ 30ms ge
NUM_WINDOW_CHUNKS_END = NUM_WINDOW_CHUNKS 2

START_OFFSET = int(NUM_WINDOW_CHUNKS CHUNK_DURATION_MS 0.5 RATE)

vad = webrtcvad.Vad(1)

pa = pyaudio.PyAudio()
stream = pa.open(format=FORMAT,
         channels=CHANNELS,
         rate=RATE,
         input=True,
         start=False,
         # input_device_index=2,
         frames_per_buffer=CHUNK_SIZE)


got_a_sentence = False
leave = False


def handle_int(sig, chunk):
  global leave, got_a_sentence
  leave = True
  got_a_sentence = True


def record_to_file(path, data, sample_width):
  "Records from the microphone and outputs the resulting data to 'path'"
  # sample_width, data = record()
  data = pack('<' + ('h' len(data)), data)
  wf = wave.open(path, 'wb')
  wf.setnchannels(1)
  wf.setsampwidth(sample_width)
  wf.setframerate(RATE)
  wf.writeframes(data)
  wf.close()


def normalize(snd_data):
  "Average the volume out"
  MAXIMUM = 32767 # 16384
  times = float(MAXIMUM) / max(abs(i) for i in snd_data)
  r = array('h')
  for i in snd_data:
    r.append(int(i times))
  return r

signal.signal(signal.SIGINT, handle_int)

while not leave:
  ring_buffer = collections.deque(maxlen=NUM_PADDING_CHUNKS)
  triggered = False
  voiced_frames = []
  ring_buffer_flags = [0] NUM_WINDOW_CHUNKS
  ring_buffer_index = 0

  ring_buffer_flags_end = [0] NUM_WINDOW_CHUNKS_END
  ring_buffer_index_end = 0
  buffer_in = ''
  # WangS
  raw_data = array('h')
  index = 0
  start_point = 0
  StartTime = time.time()
  print(" recording: ")
  stream.start_stream()

  while not got_a_sentence and not leave:
    chunk = stream.read(CHUNK_SIZE)
    # add WangS
    raw_data.extend(array('h', chunk))
    index += CHUNK_SIZE
    TimeUse = time.time() - StartTime

    active = vad.is_speech(chunk, RATE)

    sys.stdout.write('1' if active else '_')
    ring_buffer_flags[ring_buffer_index] = 1 if active else 0
    ring_buffer_index += 1
    ring_buffer_index %= NUM_WINDOW_CHUNKS

    ring_buffer_flags_end[ring_buffer_index_end] = 1 if active else 0
    ring_buffer_index_end += 1
    ring_buffer_index_end %= NUM_WINDOW_CHUNKS_END

    # start point detection
    if not triggered:
      ring_buffer.append(chunk)
      num_voiced = sum(ring_buffer_flags)
      if num_voiced > 0.8 NUM_WINDOW_CHUNKS:
        sys.stdout.write(' Open ')
        triggered = True
        start_point = index - CHUNK_SIZE 20 # start point
        # voiced_frames.extend(ring_buffer)
        ring_buffer.clear()
    # end point detection
    else:
      # voiced_frames.append(chunk)
      ring_buffer.append(chunk)
      num_unvoiced = NUM_WINDOW_CHUNKS_END - sum(ring_buffer_flags_end)
      if num_unvoiced > 0.90 NUM_WINDOW_CHUNKS_END or TimeUse > 10:
        sys.stdout.write(' Close ')
        triggered = False
        got_a_sentence = True

    sys.stdout.flush()

  sys.stdout.write('\n')
  # data = b''.join(voiced_frames)

  stream.stop_stream()
  print(" done recording")
  got_a_sentence = False

  # write to file
  raw_data.reverse()
  for index in range(start_point):
    raw_data.pop()
  raw_data.reverse()
  raw_data = normalize(raw_data)
  record_to_file("recording.wav", raw_data, 2)
  leave = True

stream.close()

程序运行方式sudo python vad.py

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python ip正则式
May 07 Python
Python安装Imaging报错:The _imaging C module is not installed问题解决方法
Aug 22 Python
使用Python脚本操作MongoDB的教程
Apr 16 Python
python中split方法用法分析
Apr 17 Python
Python的Tornado框架实现异步非阻塞访问数据库的示例
Jun 30 Python
Python机器学习之决策树算法
Dec 22 Python
解决python使用open打开文件中文乱码的问题
Dec 29 Python
python爬虫自动创建文件夹的功能
Aug 01 Python
python实现滑雪游戏
Feb 22 Python
python GUI库图形界面开发之PyQt5单行文本框控件QLineEdit详细使用方法与实例
Feb 27 Python
python+adb命令实现自动刷视频脚本案例
Apr 23 Python
Pycharm Available Package无法显示/安装包的问题Error Loading Package List解决
Sep 18 Python
python实现决策树C4.5算法详解(在ID3基础上改进)
May 31 #Python
基于ID3决策树算法的实现(Python版)
May 31 #Python
Python基础知识_浅谈用户交互
May 31 #Python
python数据类型_字符串常用操作(详解)
May 30 #Python
python数据类型_元组、字典常用操作方法(介绍)
May 30 #Python
node.js获取参数的常用方法(总结)
May 29 #Python
老生常谈python函数参数的区别(必看篇)
May 29 #Python
You might like
不重新编译PHP为php增加openssl模块的方法
2011/06/14 PHP
PHP命名空间(namespace)的使用基础及示例
2014/08/18 PHP
PHP实现通过URL提取根域名
2016/03/31 PHP
PHP简单获取网站百度搜索和搜狗搜索收录量的方法
2016/08/23 PHP
PHP入门教程之面向对象基本概念实例分析
2016/09/11 PHP
PHP使用strrev翻转中文乱码问题的解决方法
2017/01/13 PHP
CSS心形加载的动画源码的实现
2021/03/09 HTML / CSS
js变形金刚文字特效代码分享
2015/08/20 Javascript
基于jquery实现一个滚动的分步注册向导-附源码
2015/08/26 Javascript
jQuery实现带分组数据的Table表头排序实例分析
2015/11/24 Javascript
微信公众号-获取用户信息(网页授权获取)实现步骤
2016/10/21 Javascript
关于使用axios的一些心得技巧分享
2017/07/02 Javascript
vue增删改查的简单操作
2017/07/15 Javascript
Angularjs渲染的 using 指令的星级评分系统示例
2017/11/09 Javascript
JavaScript正则表达式函数总结(常用)
2018/02/22 Javascript
仿vue-cli搭建属于自己的脚手架的方法步骤
2019/04/17 Javascript
微信小程序添加插屏广告并设置显示频率(一天一次)
2019/12/06 Javascript
Vue中axios拦截器如何单独配置token
2019/12/27 Javascript
详解ES6 中的Object.assign()的用法实例代码
2021/01/11 Javascript
python有证书的加密解密实现方法
2014/11/19 Python
按日期打印Python的Tornado框架中的日志的方法
2015/05/02 Python
django 创建过滤器的实例详解
2017/08/14 Python
Python爬虫抓取代理IP并检验可用性的实例
2018/05/07 Python
Python 在OpenCV里实现仿射变换—坐标变换效果
2019/08/30 Python
python文件绝对路径写法介绍(windows)
2019/12/25 Python
flask框架渲染Jinja模板与传入模板变量操作详解
2020/01/25 Python
JINS眼镜官方网站:日本最大的眼镜邮购
2016/10/14 全球购物
教师自我评价范例
2013/09/24 职场文书
实施意见格式范本
2015/06/05 职场文书
农村老人去世追悼词
2015/06/23 职场文书
校园安全教育心得体会
2016/01/15 职场文书
mysql死锁和分库分表问题详解
2021/04/16 MySQL
Vue通过懒加载提升页面响应速度
2021/05/10 Vue.js
css背景和边框标签实例详解
2021/05/21 HTML / CSS
Oracle中DBLink的详细介绍
2022/04/29 Oracle
使用Nginx+Tomcat实现负载均衡的全过程
2022/05/30 Servers