zoukankan      html  css  js  c++  java
  • 麦克风静音检测,只保存非静音部分

    #-*- coding: utf-8 -*-
    import os
    import wave
    from time import sleep
    import numpy as np
    import pyaudio
    import matplotlib.pyplot as plt
    
    SUCCESS = 0
    FAIL = 1
    
    audio = pyaudio.PyAudio()
    audio2 = ""
    stream2 = ""
    FORMAT = pyaudio.paInt16
    stream = audio.open(format=FORMAT,
                        channels=1,
                        rate=16000,
                        input=True,
                        frames_per_buffer=256
                        )
    
    
    """
    语音和噪声的区别可以体现在他们的能量上,语音段的能量比噪声段的能量大,如果环境噪声和系统输入的噪声比较小,
    只要计算输入信号的短时能量就能够把语音段和噪声背景区分开,除此之外,用基于能量的算法来检测浊音通常效果也是比较理想的,
    因为浊音的能量值比清音大得多,可以判断浊音和清音之间过渡的时刻[3],但对清音来说,效果不是很好,因此还需要借助短时过零率来表征。 
    短时能量可以近似为互补的情况,短时能量大的地方过零率小,短时能量小的地方过零率较大。  
    
    基于短时能量和过零率的检测方法  尽管基于短时能量和过零率的检测方法各有其优缺点,但是若将这两种基本方法相结合起来使用也可以实现对语音信号可靠的端点检测。
    无声段的短时能量为零,清音段的短时能量又比浊音段的短时能量大,
    而在过零率方面,理想的情况是无声段的过零率为零,浊音段的过零率比清音段的过零率要大的多,
    假设有一段语音,
    如果某部分短时能量和过零率都为零或者为很小的值,就可以认为这部分为无声段,
    如果该部分语音短时能量很大但是过零率很小,则认为该部分语音为浊音段,
    如果该部分短时能量很小但是过零率很大,则认为该部分语音为清音段。
    正如前面提到,语音信号具有短时性,因此在对语音信号进行分析时,
    ,需要将语音信号以30ms为一段分为若干帧来进行分析,则两帧起始点之间的间隔为10ms。
    
    短时能量,无声<浊音<清音
    过零率,无声<清音<浊音
    """
    
    """
    事迹测试
    说话时的过零率 在0-3之间   轻呼吸时   重呼吸呼吸时的过零率在2-7之间
    
    说话时的短时能量 在940-12000之间    轻呼吸时   重呼吸15000-23000之间
    """
    
    
    # 需要添加录音互斥功能能,某些功能开启的时候录音暂时关闭
    def ZCR(curFrame):
        # 过零率
        tmp1 = curFrame[:-1]
        tmp2 = curFrame[1:]
        sings = (tmp1 * tmp2 <= 0)
        diffs = (tmp1 - tmp2) > 0.02
        zcr = np.sum(sings * diffs)
        return zcr
    
    
    def STE(curFrame):
        # 短时能量
        amp = np.sum(np.abs(curFrame))
        return amp
    
    
    class Vad(object):
        def __init__(self):
            # 初始短时能量高门限
            self.amp1 = 940
            # 初始短时能量低门限
            self.amp2 = 120
            # 初始短时过零率高门限
            self.zcr1 = 30
            # 初始短时过零率低门限
            self.zcr2 = 2
            # 允许最大静音长度
            self.maxsilence = 45     #允许换气的最长时间
            # 语音的最短长度
            self.minlen = 40        #  过滤小音量
            # 偏移值
            self.offsets = 40
            self.offsete = 40
            # 能量最大值
            self.max_en = 20000
            # 初始状态为静音
            self.status = 0
            self.count = 0
            self.silence = 0
            self.frame_len = 256
            self.frame_inc = 128
            self.cur_status = 0
            self.frames = []
            # 数据开始偏移
            self.frames_start = []
            self.frames_start_num = 0
            # 数据结束偏移
            self.frames_end = []
            self.frames_end_num = 0
            # 缓存数据
            self.cache_frames = []
            self.cache = ""
            # 最大缓存长度
            self.cache_frames_num = 0
            self.end_flag = False
            self.wait_flag = False
            self.on = True
            self.callback = None
            self.callback_res = []
            self.callback_kwargs = {}
    
            self.frames = []
            self.x = []
            self.y = []
    
        def check_ontime(self,num,cache_frame):  # self.cache的值为空   self.cache_frames的数据长度为744
    
            global audio2,stream2
            wave_data = np.frombuffer(cache_frame, dtype=np.int16)  # 这里的值竟然是256
            wave_data = wave_data * 1.0 / self.max_en  # max_en  为20000
            data = wave_data[np.arange(0, self.frame_len)]  # 取前frame_len个值   这个值为256
            # speech_data = self.cache_frames.pop(0)    #删除第一个元素,并把第一个元素给speech_data  ,长度为256
            # 获得音频过零率
            zcr = ZCR(data)
            # 获得音频的短时能量, 平方放大
            amp = STE(data)**2
            # 返回当前音频数据状态
            res = self.speech_status(amp, zcr)
    
            self.cur_status = res
    
            if res ==2:
            #开始截取音频
                if not audio2:
                    audio2 = pyaudio.PyAudio()
                    stream2 = audio2.open(format=FORMAT,
                                          channels=1,
                                          rate=16000,
                                          input=True,
                                          frames_per_buffer=256
                                          )
                stream_data = stream2.read(256)
                wave_data = np.frombuffer(stream_data, dtype=np.int16)
                # print(num, wave_data, len(stream_data))
                print(num, "正在说话ing...")
                self.frames.append(stream_data)
    
            if res ==3 and len(self.frames)>25:
    
                    # print(len(self.frames))
                wf = wave.open(str(num)+".wav", 'wb')
                wf.setnchannels(1)
                wf.setsampwidth(audio2.get_sample_size(FORMAT))
                wf.setframerate(16000)
                wf.writeframes(b"".join(self.frames))
                self.frames = []
                stream2.stop_stream()
                stream2.close()
                audio2.terminate()
                audio2 = ""
                stream2 = ""
                wf.close()
    
            # if num <=100:
            #     self.x.append(num)
            #     self.y.append(res)
            # elif num > 100:
            #     self.x.append(num)
            #     self.y.append(res)
            #     self.x.pop(0)
            #     self.y.pop(0)
            #
            # plt.cla()
            # # plt.scatter(num, res)
            # plt.plot(self.x, self.y, 'r-', lw=1)
            # plt.pause(0.01)
            # plt.show()
    
            # print("idx={}	过零率={}	短时能量={}	res={}".format(num,zcr,amp,res))
    
        # 短时能量,无声 < 浊音 < 清音
        # 过零率,无声 < 清音 < 浊音
    
        """
        实际测试
        说话时的过零率 在0-3之间    呼吸时的过零率在0-5之间
    
        说话时的短时能量 在940-12000之间    15000-23000之间
        """
    
        def speech_status(self, amp, zcr):
            status = 0
            # 0= 静音, 1= 可能开始, 2=确定进入语音段   3语音结束
            if self.cur_status in [0, 1]:    #如果在静音状态或可能的语音状态,则执行下面操作
                # 确定进入语音段
                if amp > self.amp1 or zcr > self.zcr1:    #超过最大  短时能量门限了
                    status = 2
                    self.silence = 0
                    self.count += 1
                # 可能处于语音段   能量处于浊音段,过零率在清音或浊音段
                elif amp > self.amp2 or zcr > self.zcr2:
                    status = 2
                    self.count += 1
                # 静音状态
                else:
                    status = 0
                    self.count = 0
                    self.count = 0
            # 2 = 语音段
            elif self.cur_status == 2:
                # 保持在语音段    能量处于浊音段,过零率在清音或浊音段
                if amp > self.amp2 or zcr > self.zcr2:
                    self.count += 1
                    status = 2
                # 语音将结束
                else:
                    # 静音还不够长,尚未结束
                    self.silence += 1
                    if self.silence < self.maxsilence:
                        self.count += 1
                        status = 2
                    # 语音长度太短认为是噪声
                    elif self.count < self.minlen:
                        status = 0
                        self.silence = 0
                        self.count = 0
                    # 语音结束
                    else:
                        status = 3
                        self.silence = 0
                        self.count = 0
            return status
    
    
    
    
    class FileParser(Vad):
        def __init__(self):
            self.block_size = 256
            Vad.__init__(self)
    
    
    if __name__ == "__main__":
    
        # plt.ion()
        stream_test = FileParser()
        num = 0
        while True:
            byte_obj = stream.read(256)
            stream_test.check_ontime(0,byte_obj)
            num = num+1
    

      

  • 相关阅读:
    2019-2020-2 20175227张雪莹《网络对抗技术》 Exp3 免杀原理与实践
    2019-2020-2 20175227张雪莹《网络对抗技术》 Exp2 后门原理与实践
    2019-2020-2 20175227张雪莹《网络对抗技术》 Exp1 PC平台逆向破解
    USCOSII
    office word excel等图标显示异常
    Mysql事务
    Mysql 主备配置
    查看mysql已有用户并删除
    在linux下面安装mysql 确认 配置文件路径 my.cnf
    yum提示Another app is currently holding the yum lock; waiting for it to exit...
  • 原文地址:https://www.cnblogs.com/LiuXinyu12378/p/13741218.html
Copyright © 2011-2022 走看看