SpeechRecognition listen()源码解析

class Recognizer(AudioSource):
    def __init__(self):
        """
        Creates a new ``Recognizer`` instance, which represents a collection of speech recognition functionality.
        """
        self.energy_threshold = 300  # minimum audio energy to consider for recording
        self.dynamic_energy_threshold = True
        self.dynamic_energy_adjustment_damping = 0.15
        self.dynamic_energy_ratio = 1.5
        self.pause_threshold = 0.8  # seconds of non-speaking audio before a phrase is considered complete
        self.operation_timeout = None  # seconds after an internal operation (e.g., an API request) starts before it times out, or ``None`` for no timeout

        self.phrase_threshold = 0.3  # minimum seconds of speaking audio before we consider the speaking audio a phrase - values below this are ignored (for filtering out clicks and pops)
        self.non_speaking_duration = 0.5  # seconds of non-speaking audio to keep on both sides of the recording

self.energy_threshold,这个参数代表能量阈值,如果麦克风识别到的声音的能量低于这个值,则忽略不再考虑

self.dynamic_energy_threshold,是否动态调整能量阈值,如果是True,能量阈值会变动,以满足不同噪音背景下的能量阈值动态调整,后面会专门介绍调整细节

self.dynamic_energy_adjustment_damping = 0.15、 self.dynamic_energy_ratio = 1.5  是关于动态调整能量阈值的参数,后面会专门介绍调整细节

self.pause_threshold = 0.8 ,这个是暂停时间,如果声音能量连续低于能量阈值超过0.8秒,则认为这一句已经说完了,listen()将会把音频数据返回,结束这个方法。

self.operation_timeout 没有用到,略过

self.phrase_threshold ,短语阈值,意思是低于0.3秒的超过能量阈值的声音,也会被忽略掉

self.non_speaking_duration,静音持续时长,这个意思是,我们最多会在一句话的两端加上0.5秒的声音,这个声音,不是真的静音,而是没有达到能量阈值的声音

listen()工作原理

seconds_per_buffer = float(source.CHUNK) / source.SAMPLE_RATE
pause_buffer_count = int(math.ceil(self.pause_threshold / seconds_per_buffer))  # number of buffers of non-speaking audio during a phrase, before the phrase should be considered complete
phrase_buffer_count = int(math.ceil(self.phrase_threshold / seconds_per_buffer))  # minimum number of buffers of speaking audio before we consider the speaking audio a phrase
non_speaking_buffer_count = int(math.ceil(self.non_speaking_duration / seconds_per_buffer))  # maximum number of buffers of non-speaking audio to retain before and after a phrase

先说一下我们的麦克风,是16000的采样率,也就是说,一秒钟采集声音16000次数据,对应上面的source.SAMPLE_RATE,这些采样的数据,汇聚成一个流,而source.CHUNK 为1024,也就是说,我们每次从流中取出来1024个声音数据,作为一个buffer。 

seconds_per_buffer 的值为1024 / 16000 = 0.065,也就是说,在listen() 方法中,最小的数据流是一个buffer,最短的时间单位就是0.065s

pause_buffer_count ,就是 int(math.ceil(0.8 / 0.065)) ,结果为13, 这意味着,当我们的listen()方法,连续读取到的13  + 1 总计14 个buffer(为什么是14个,代码里面用了 ' > ',14比13大 )都是低于能量阈值的话,就会认为这句话结束了,listen()方法就会终止,并返回声音数据。 当然,前提是这句话要满足时长,这样可以有效的过滤掉砰砰砰这种瞬时噪音,于是引出来了下一个参数 phrase_buffer_count 

phrase_buffer_count, 就是int(math.ceil(0.3 / 0.065)),结果为5,也就是说,如果一句话的时长(不包含结尾暂停的0.8秒声音)短于或者等于五个buffer,则不认为是一句话,会被直接忽略掉。

non_speaking_buffer_count,就是int(math.ceil(0.5 / 0.065)) ,结果为8,也就是说。 在静音环境下,持续低于能量阈值,声音一直被忽略,突然第n个buffer高于了能量阈值,此时如果n <=  8 (n从1开始),则会把前面第 1 ~ n-1的buffer 全部记录到,也当作正式数据。 如果n >=9 ,则会把n- 8 - 1 ~ n - 1 个buffer记录到,n - 8-1 - 1 个buffer、包括其之前的丢掉。 如果是9,那就剔除第一个,保留2 - 9 这八个。 

assert self.pause_threshold >= self.non_speaking_duration >= 0 ,这个是在干嘛?

当我们把self.non_speaking_duration大于self.pause_threshold,会提示错误,不让这样做,为什么呢?

pause_threshold是连续不满足能量阈值的时间,就是被程序认为是无声,但是不一定是无声,只是不满足能量阈值。 non_speaking_duration就是为了把这一段声音给拯救回来(这段声音,会发生在一句话的最前面,也会发生在最后面),所以说,拯救的声音pause_threshold肯定不能大于pause_threshold,要不然不就拯救了空气....

但是我们为什么要把这个给注释掉呢?

因为我们想要更短的时间间隔就把声音截断,比如,现在我们设置self.pause_threshold 为 0.065 ,那就是连续监测到 1 + 1 = 2个0.065秒,就认为声音就断了(就是下方代码的pause_count = 2),这个时候,安装源代码的要求,我们必须设置non_speaking_duration = 0.065 s,0.065 / 0.065 向上取整等于1,根据下面的代码for i in range(2-1),弹出最后一个buffer,我们最多保留结尾的1个能量阈值的0.065秒,结尾处这样保留有问题,这样已经少了一个buffer了。

for i in range(pause_count - non_speaking_buffer_count):

    print("结尾弹出音频,不会执行到")

    frames.pop() # remove extra non-speaking frames at the end

更重要的是,起始处受到影响了,起始处最多也只能保留1个(因为len(frames)为2就会弹出开头的低于能量阈值的buffer了,超过能量阈值的buffer需要占用一个)最新的不足能量阈值的0.065s了

起始处不能只保留1个不足能量阈值的buffer,我们想要让起始处最多保留一秒钟,也就是16个buffer(不足一秒的则全部保存)的音频。 所以我们注释掉这个地方,设置self.non_speaking_duration = 1,这样会让给让结尾不再弹出任何音频,同时开头保留16个buffer。

if len(frames) > (non_speaking_buffer_count):  # ensure we only keep the needed amount of non-speaking buffers
    print(f"len(frames):{len(frames)} > non_speaking_buffer_count:{non_speaking_buffer_count}") # 打印调试信息
    frames.popleft()
    print(f"弹出之后,frames中还有{len(frames)}个buffer") # 打印调试信息

这样做会有什么问题?

I wonder how they make it entertaining.

entertaining假设长达一秒种,那就是被切分为16个buffer,这个单词的音比较轻,在第8,9 (从1开始)连续两个buffer处已经检测到了小于能量阈值,此处就会被断开,当作一句话的结束,此时我们知道,结尾的第8,9个不足能量阈值的buffer会被保留。 第10,11,12,13,14,15,16个buffer,就会被下一句话采集到。 虽然单词被切分了,但是没有丢音,而且之所以被切分,是能量阈值的问题......

能量阈值目前存在的问题

如果不设置动态调整,设置定值,第一是不能确定每个环境的能量阈值,第二是每个人说话的声音能量阈值不同,有人声音重,有人声音轻。 第三个是麦克风在任何条件都不变的情况下,自己对相同声音的能量感知都有问题。  能量阈值存在问题,则会丢音,如果能量阈值过低,过于敏感,一句话持续录入,无法中断;如果能量阈值过高,声音起始阶段超过一秒低于阈值,则会丢音。

如果能量阈值动态调整,那这个调整有时候会调整的把能量阈值调整的特别低,麦克风周围即使任何声音都没有,也会被采集到;如果调整的有点低,那周围突然有一阵动静,则会一直采集,导致断音不理想。 

关于能量阈值的动态调整

能量阈值只是在一句话开始之前,没有到达能量阈值的时候,会动态调整,如果一旦有一个buffer的能量到了能量阈值,此时开始记录数据,在整个记录的过程中,能量阈值是固定的,不会发生改变。 只有这一句话说完了,准备录制下一句话的时候,在没有满足能量阈值的时候,才会动态调整。 

调整的规则不清晰,有时候能增大能量阈值,有时候降低能量阈值。官方给的注释是:采用非对称加权平均动态调整能量阈值。影响调整规则的参数有,当前能量阈值、当前能量两个变量。 

但是就实验而言,有时候,动态阈值的调整会特别低,这个时候,周围保持了很安静的状态,也低于这个阈值,从而导致,一句话无法结束,持续录制。

代码中的while 循环结构

listen中有一个大的while true 循环

这个是处理每一句话的话,只有出了这个大的while true循环的句子才能采集到数据

第一个子while循环,是判断流中依次读取的buffer是否达到了能量阈值

如果到达了能量阈值,则能出了第一个子while循环,进入第二个子while循环

这是第二个子while循环,主要是检测连续到2次低于能量阈值的buffer,则能跳出第二个子while循环

在跳出了第二个子while循环之后,还有最后一步,如果满足这个要求,就能跳出主while循环,从而是采集数据成功。 这个要求就是,需要需要声音时长足够,如果不足够,则重新执行主while循环,重现执行主while循环,那就是全部东西开始从头算起了。

如果non_speaking_duration > pause_threshold,一句话中有多少不足能量阈值的buffer呢?

开头最多有non_speaking_buffer_count - 1 个

声音的过程中有x(不确定)个,因为不满足连续pause_buffer_count + 1个buffer低于能量阈值,就可以一直被记录

声音的结尾会有pause_buffer_count + 1个 

暂时没有想清楚的问题:

  • 同一个麦克风对周围相同声音的感知是不同的,有时候感知是10,有时候是50,有时候是150,有时候是300,在同一次代码运行就就会出现此问题,如果前期对声音的感知比较弱,那么能量阈值就会自动调整的比较低,但是一分钟后,对声音能量感知又较强了,周围安静的声音的能量都比能量阈值高了,这个时候,持续监听,永远无法结束
  • 即使在代码中的逻辑设置了不丢任何一帧音,在机器人身上,一体机麦克风,小麦克风也会丢音。 在虚拟机身上,一体机麦克风和小麦克风都没有这种情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: speechrecognition是一个用于语音识别的Python库。它提供了一个简单而强大的接口,可以将人类语言转换为文本形式。 speechrecognition库支持多种语音识别API,包括百度语音识别、离线语音识别以及Google语音识别等。这使得我们可以选择适合自己需求的API进行语音识别。 使用speechrecognition库进行中文语音识别非常简单。首先,我们需要安装speechrecognition库及其相关依赖。然后,我们可以使用recognize_google函数来进行语音识别: import speech_recognition as sr # 创建一个语音识别的实例 r = sr.Recognizer() # 读取音频文件 with sr.AudioFile('audio.wav') as source: audio = r.record(source) # 将音频文件转换为音频对象 # 使用Google语音识别API进行语音识别 text = r.recognize_google(audio, language='zh-CN') print(text) 在上面的代码中,我们将音频文件读取为一个音频对象,并使用Google语音识别API进行语音识别。最后,我们将识别结果打印出来。 除了从音频文件中进行语音识别,speechrecognition库还支持从麦克风输入进行实时语音识别。我们只需要调用recognize_microphone函数即可实现。例如: import speech_recognition as sr # 创建一个语音识别的实例 r = sr.Recognizer() # 使用麦克风进行实时语音识别 with sr.Microphone() as source: audio = r.listen(source) # 使用Google语音识别API进行语音识别 text = r.recognize_google(audio, language='zh-CN') print(text) 使用speechrecognition库进行中文语音识别非常便捷,无论是从音频文件中进行识别还是实时录音进行识别,都可以通过简单的几行代码实现。 ### 回答2: speechrecognition库是一个用于语音识别的Python库。它提供了多种语音识别引擎的支持,包括Google Speech Recognition、CMU Sphinx、Wit.ai等。虽然speechrecognition库最初是为英语语音识别而设计的,但它也可以用于中文语音识别。 在使用speechrecognition库进行中文语音识别时,我们首先需要导入库并创建一个Recognizer对象。我们可以通过Recognizer对象来调用不同的语音识别引擎。例如,使用Google Speech Recognition引擎进行中文语音识别,我们可以使用以下代码: ``` import speech_recognition as sr r = sr.Recognizer() with sr.Microphone() as source: print("请开始说话...") audio = r.listen(source) try: print("Google Speech Recognition识别结果: " + r.recognize_google(audio, language="zh-CN")) except sr.UnknownValueError: print("Google Speech Recognition无法识别音频") except sr.RequestError as e: print("无法从Google Speech Recognition服务获取结果:" + str(e)) ``` 在上述代码中,我们使用了`recognize_google()`方法来调用Google Speech Recognition引擎进行中文语音识别。`.listen(source)`方法用于从输入设备(麦克风)获取音频输入。接着,我们使用`try except`结构来处理识别结果和异常情况。 除了Google Speech Recognition引擎,speechrecognition库还支持其他引擎。例如,我们可以使用Wit.ai引擎进行中文语音识别,只需将上述代码中的`recognize_google()`改为`recognize_wit()`。 总之,speechrecognition库提供了中文语音识别的功能,我们可以通过简单的代码来实现中文语音识别应用。 ### 回答3: speechrecognition库是一个用于语音识别的Python库。它提供了一种简单的方式来将语音转换为文本数据。该库支持多种语音识别服务,包括Google Speech Recognition、Wit.ai、Bing Speech Recognition等。 使用speechrecognition库非常简单。首先,需要安装该库,可以使用pip命令进行安装。接下来,可以使用代码对语音进行识别。首先,需要创建一个Recognizer对象,然后使用该对象进行语音录制。录制完成后,调用Recognizer对象的recognize方法将语音转换为文本。 使用speechrecognition库进行中文语音识别也非常方便。可以通过设置recognize_google或recognize_bing等方法的参数lang为"zh-CN"来指定使用中文识别。除了指定语言外,还可以设置一些参数,如调整语音识别的准确度、音量阈值等。 speechrecognition库还支持实时语音识别,它可以从麦克风或音频流中实时识别语音。使用实时语音识别时,可以通过设置partial_results参数来获取部分结果,这对于处理长时间语音非常有用。 总而言之,speechrecognition是一个功能强大且易于使用的语音识别库,可以帮助我们将语音转换为文本数据,并支持多种语音识别服务。无论是进行中文语音识别还是实时语音识别,speechrecognition库都是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值