【Streamlit学习】关于Streamlit和Python多线程

声明:本人并非职业程序员,编程能力有限,但是热爱程序开发,随便记录些学习和跳坑心得,不一定完全正确。

最近在写一个网页端录音和文字识别的工具,网页实现使用的是Streamlit,期间遇到了不少坑,目前正在一个一个地跳。

第一坑:如何控制录音线程的开始与停止

录音线程作为子线程,需要接收主线程的停止信号,一开始想用st.session_state,因为它不会在Streamlit刷新页面时被重置。但是,st.session_state作为主线程的变量,是没法用在录音子线程中的。与此相似,也不能在录音子线程中调用st.xxx。比如,当子线程中的录音停止时,我想在页面上显示“录音完毕”的提示,是不能这样写的:

#  录音线程
def record_audio_thread(fs, filename):
    mutex.acquire()
    global stop_or_rec, audio_data
    stop_or_rec_confirm = False
    try:
        while stop_or_rec is False or stop_or_rec_confirm is False:
            with sd.InputStream(fs, channels=1) as stream:
                global duration
                audio_data.extend(stream.read(int(duration * fs)))  # 读取一定数量的音频数据
                time.sleep(0.001)  # 等待一小段时间,避免过快的循环
            if stop_or_rec:
                stop_or_rec_confirm = True
        if stop_or_rec_confirm:
            try:
                sd.stop()
                st.info("正在停止录音")  #这里的写法是不对的
                audio_data[:] = [item for item in audio_data if not isinstance(item, bool)]
                combined_array = np.vstack(audio_data)
                sf.write(filename, combined_array, fs, subtype='PCM_16')
                print("录音完成!已保存为 {}".format(filename))
            except:
                print("保存出错")
    finally:
        mutex.release()  # 释放互斥锁

这里的st.info("正在停止录音")是不对的,会导致以下问题:

Thread 'Thread-14 (record_audio_thread)': missing ScriptRunContext

因此,想要停止录音线程,只能使用全局变量(上面代码中的stop_or_rec)。

第二坑:不限制录音时长,随时结束录音。

我使用的是sounddevice库,但是它有个问题,就是需要将录制的数据长度作为参数输入,才可以记录数据。比如这段代码:

with sd.InputStream(fs, channels=1) as stream:
     global duration
     audio_data.extend(stream.read(int(duration * fs)))  # 读取一定数量的音频数据
     time.sleep(0.001)  # 等待一小段时间,避免过快的循环

读取音频流时,需要定义好duration(时长,秒)和fs(采样率),由此计算出读取的样本点数。但是问题来了,如果我并不确定需要录制多久,而是想随时停止录制,这该怎么办呢?

目前采用的方法是将duration设置为5s,每录制5秒就向总的录音数据变量audio_data中添加一次。这引出了另一个问题,就是当我点击停止录制的按钮时,可能刚好在读取新一轮5秒片段中的第2秒,此时如果立即停止就会导致这最后读的2秒数据没能加入到audio_data中。

因此,我有设置了一个局部变量stop_or_rec_confirm,当stop_or_rec变量被置为True时(即停止录音信号),此时继续让程序把正在读的这5秒数据读完,之后再将stop_or_rec_confirm置为True。当检测到stop_or_rec_confirm为True时,便不再录音,而是保存数据,接收录音。下面是录音线程的完整代码:

def record_audio_thread(fs, filename):
    mutex.acquire()
    global stop_or_rec, audio_data
    stop_or_rec_confirm = False
    try:
        while stop_or_rec is False or stop_or_rec_confirm is False:
            with sd.InputStream(fs, channels=1) as stream:
                global duration
                audio_data.extend(stream.read(int(duration * fs)))  # 读取一定数量的音频数据
                time.sleep(0.001)  # 等待一小段时间,避免过快的循环
            if stop_or_rec:
                stop_or_rec_confirm = True
        if stop_or_rec_confirm:
            try:
                sd.stop()
                print("正在停止录音")
                audio_data[:] = [item for item in audio_data if not isinstance(item, bool)]
                combined_array = np.vstack(audio_data)
                sf.write(filename, combined_array, fs, subtype='PCM_16')
                print("录音完成!已保存为 {}".format(filename))
            except:
                print("保存出错")
    finally:
        mutex.release()  # 释放互斥锁

目前来看,还有一个问题未能解决。尽管录音线程能够将每个5秒片段拼接在一起,形成完整的录音,但是在每个5秒片段的开头,由于录音设备启动时延,导致开头的近半秒钟录不进去声音。这个问题一直困扰着我。还请各位大佬指点。

以下是目前的页面效果,比较简单,还在完善中:

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值