用python协程设计语音通信类后端程序

本文探讨了使用Python协程设计TSAPI程序的方法,重点介绍了call模块的设计及其与其他模块的交互方式。讨论了如何通过协程管理复杂的业务流程,并提出了一种用户管理模块user_man来处理用户在不同流程间的切换。此外,还详细分析了协程间的通信机制及用户状态管理,以确保业务流程的正确性和一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发中的一些总结,有些乱。

主要描述,如果使用Python协程,该如何设计tsapi程序。为最终的调度设计积累一部分的经验,并且,最终形成一个框架,在这个框架上,可以方便的新增新的功能,让其他的员工不再对Python协程望而生畏。


1、整个框架主要的部分是call模块,主要负责呼叫流程的控制。它把对asapi的调用全部集中到一个模块中,可以实现对模块的封装,便于以后的替换。

2、call模块和上层模块的交互对象我本来考虑使用用户号码,但是考虑到方便性,以及这样的话必须基于一个假设:所有的用户号码必须是唯一标识:不利于后期的维护。所以,初步考虑采用asobj模块中的user和meet对象作为交互对象。对User和meet类的使用,建议用组合,然后再考虑继承。

3、poc server使用的是一种callinfo的机制,即所有处于稳定状态的用户都有一个callinfo来记录其当前的状态。本来我不想用这样的一个对象来记录,原因是不同的关系会有不同的callinfo定义,最终callinfo会多起来。不用callinfo就要求协程能够管理一个流程的全部生命周期,包括开始和结束,这样如果两个流程间有相互关系,将会难于处理。所以,还是要使用callinfo,不过,callinfo只有两个,p2p和meetcallinfo。

4、考虑一种业务流程:A,B,C三个用户正在进行一个呼叫流程,比如转接,c正在振铃,还未接通。此时另外一个更高优先级流程需要把C拉入新的流程中。要求最好c能够不用挂机再发起呼叫,并且原有的流程能够正常处理。实现这个功能需要增加一个user_man模块,负责用户管理。所有对用户的呼出都调用此模块的接口进行呼出,每次呼出都申请一个新的协程,这个协程结束后在返回原先的协程进行处理后续操作。如果呼叫未成功时就有新的流程操作此用户,则停止原先的协程,makecall协程结束后直接进入新的协程处理。

5、当一个协程在处理过程中,如何向这个协程发送一个消息,并且让协程进行不同处理?本来考虑使用类似golang的方法,每个协程对外提供一个通道,所有的协程对外通信都使用通道的消息。可以实现,不过实现后,整个协程机制就会复杂起来。因为每个协程都有一个栈,每次的消息处理要停止原先的栈,处理完毕后要返回原先的栈,如果在消息处理时调用了协程接口,则对栈以及消息的处理会陷入混乱。所以,针对类似的需求,采用如下机制:
     1)协程处理过程中发生异常,需要终止协程,可以通过协程的throw接口向协程发送不同类型的异常。

     2)比较难于处理的是,如果一个用户在流程过程中被其他的协程拉去处理其他事情,而此用户离开后,不影响原有的处理流程,所以也无法终止原有流程(如果要能够终止就好处理了)。当原有流程需要处理此用户是,可能会和用户当前处理的协程造成冲突。比如:强插后一用户保持,强插结束用户要恢复通话,当此时此用户已经被其他用户拉入了其他的处理流程中,如何好好的处理。如下方案:
               1、一个用户只运行一个协程进行处理,每次协程开始是,都要设置用户的锁(或者标志),设置锁后,其他的协程不可以在处理此用户,除非是被强制解锁。缺点:提高编程复杂度,一个用户在被协程使用前,一定要设置过才可以,不使用的时候 要解锁。容易出错。确定方案,就用锁的方案:在协程开始的时候加锁,协程结束后解锁。只有对用户加锁的协程才可以处理用户。
               2、取消和补偿函数也要调整:目前是没有锁的方案的,现在要增加锁,补偿是人工不是此协程锁定此用户了,那么也就无法再进行对用户的操作了。
               3、最终解决方案:一个大的模块,负责维护所有的user,实现user的makecall接口(所有的makecall必须从这里走)。实现所有的as 呼叫控制接口,对用户的呼叫控制等操作前,都要判断用户是否被当前操作协程控制。所有的补偿操作也必须经过协程是否控制用户的判断。用户原先被一个协程控制,如果要被另外一个协程控制,则需要清楚用户相关所有状态:放音则停止放音,录音则停止录音,会场中则离开会场,总之,恢复到呼叫接通原始状态。如果出于稳定状态,没有在携程中,则另外处理。
               终极方案:对每调用一个呼叫控制接口则设置一下协程状态,然后,可以在不同的状态下受到不同的事件,进行不同的处理,这是最繁琐的一种方法。不过也是最明显,最简单明了的一种方法。

6、如何对用户进行挂机或呼叫结束操作?用户分两种,主动呼入与呼出,这些用户应该如何挂机那?还是根据业务的需要,由上层业务自己选择,或者在协程的补偿中挂机,或者等待操作员的操作下挂机,或者使用类似垃圾回收机制,如果没有呼叫或者协程在用户上,则自动垃圾回收掉。——根据业务再看看。

拆线操作,第一步流程抢占:把用户从原有流程中拉出,原有流程进行用户拉出操作。第二步,判断用户状态,如果已经通话,则挂机,如果处在新呼叫的协程中,则停止新的协程。

每个流程根据不同的情况,选择对用户的不同处理。同时,考虑增加垃圾回收保护。

7、编码的过程中,发现Greenlet原有的模式:父协程调用子协程,子协程处理完成后,switch到父协程来处理。这种模式非常容易出错,原因是他们在协程交互过程中,如果涉及到交互,很容易出错:父协程想等待,却等不到子协程的运行结果。所以,考虑按照go协程的处理方法来处理协程。

8、重新考虑一个模块管理所有的用户:呼叫,挂机,抢占。

协程的kill有问题:kill时,协程可能已经结束,但是还在协程任务列表中未被处理。

9、用户抢占时要在统一入口中考虑,记录当前正在抢的用户。如果有新的流程对同一个用户抢占,则停止之前的抢占。用户抢占成功后给新的流程。
     抢占就是clear user的所有呼叫操作:(录音,放音,连接,会议等),直到刚呼通,没有任何操作的状态。
     有一个问题是抢占时如果用户正在进行操作,比如connect,不能做到很好的取消。这是需要取消协程才能够取消的。
     协程间发送消息如果要等待响应要能够进行coro操作。
     呼叫的抢占:一定要先停止协程,再情况用户操作。因为协程取消是可能会影响新的操作。
     所有流程的session在流程创建的时候申请,用于保存流程的相关信息,报个各个用户的抢占处理函数。
     

总结:使用python协程简化流程开发,比较难以搞定的是状态和数据的同步问题。如果协程间关联不大,则相对比较简单,但是我们公司面临的挑战往往不是性能,而是业务逻辑复杂度,特别是流程间无时无刻都存在的流程交互。这是我用python协程解决类似问题的第二次尝试,目前已经基本解决,不过仍有优化空间,部分模块复杂度还是有些高。


我想要实现实时通过麦克风说话,从而识别出不同的说话人,并标识出来(例如有两个人说话,将两个人音频文字用Speaker 00和Speaker 01标识),使用FunASR结合pyannote.audio模型,以下是我写的代码(我使用的是conda),我想要在此基础上加 WebSocket 前端,通过前端页面点击"开始录音",允许接通麦克风,之后可直接通过前端页面麦克风直接说话,从而识别出不同的说话人,并将其音频识别出的文字展示在页面中,并标识说明人(例如说话人1、说话人2等),请在文档的基础加上WebSocket 前端,请写出详情的前端代码、路径、样式以及如何启动,以及启动后的结果展示 文档内容: 以下是基于FunASR(语音识别)和pyannote.audio(说话人分离)的实时麦克风多说话人识别完整方案,提供**Conda**部署方式,包含代码和预期运行结果。 ------ ### **一、Conda 部署方案** #### **1. 创建虚拟环境** ```bash conda create -n funasr_pyannote python=3.10 conda activate funasr_pyannote ``` #### **2. 安装FunASR** ```bash pip install -U funasr modelscope torch==2.3.1 # 安装音频处理库 pip install pyaudio ``` #### **3. 安装pyannote.audio** ```bash pip install pyannote.audio # 申请HuggingFace Token(需注册账号) huggingface-cli login # 输入Token ``` > 运行此处后,输入Token,我的HuggingFace的Token为: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 ```bash (funasr_pyannote) D:\FunASR\test4>huggingface-cli login _| _| _| _| _|_|_| _|_|_| _|_|_| _| _| _|_|_| _|_|_|_| _|_| _|_|_| _|_|_|_| _| _| _| _| _| _| _| _|_| _| _| _| _| _| _| _| _|_|_|_| _| _| _| _|_| _| _|_| _| _| _| _| _| _|_| _|_|_| _|_|_|_| _| _|_|_| _| _| _| _| _| _| _| _| _| _| _|_| _| _| _| _| _| _| _| _| _| _|_| _|_|_| _|_|_| _|_|_| _| _| _|_|_| _| _| _| _|_|_| _|_|_|_| To log in, `huggingface_hub` requires a token generated from https://huggingface.co/settings/tokens . Token can be pasted using 'Right-Click'. Enter your token (input will not be visible): Add token as git credential? (Y/n) y Token is valid (permission: fineGrained). The token `FunASR` has been saved to C:\Users\HZH\.cache\huggingface\stored_tokens Your token has been saved in your configured git credential helpers (manager). Your token has been saved to C:\Users\HZH\.cache\huggingface\token Login successful. The current active token is: `FunASR` (funasr_pyannote) D:\FunASR\test4>huggingface-cli whoami HZH520520 ``` #### 4.错误解决方法 > (1)若是在运行下述代码,遇到**` 'NoneType' object is not callable`**则表示 **pyannote/speaker-diarization-3.1** 模型访问被拒绝 > > #### . **接受模型使用协议** > > - 访问模型页面并登录 Hugging Face 账号: > - pyannote/speaker-diarization-3.1→ 点击 **"Agree to access"** > - pyannote/segmentation-3.0→ 点击 **"Agree to access"** > - **等待 5 分钟** 让授权生效(Hugging Face 缓存刷新需要时间) ------ ### **二、核心代码(两种环境通用)** ```python from funasr import AutoModel from pyannote.audio import Pipeline import pyaudio import numpy as np import torch import os # 设置镜像源加速下载(可选) os.environ["HF_ENDPOINT"] = "https://hf-mirror.com" # 替换为其他镜像若下载慢 # 初始化模型 asr_model = AutoModel(model="paraformer-zh", vad_model="fsmn-vad") # 中文识别模型 # 加载pyannote说话人分离模型(关键优化:提示用户输入Token或使用环境变量) try: # 注意:替换以下Token为您的HuggingFace Token(从huggingface-cli login获取) pipeline = Pipeline.from_pretrained( "pyannote/speaker-diarization-3.1", use_auth_token="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" # 替换为您的实际Token ) print("pyannote模型加载成功") except Exception as e: print(f"pyannote模型加载失败: {e}") pipeline = None # 降级处理,避免后续崩溃 # 实时录音参数(每秒处理16000采样点) CHUNK = 16000 # 1秒音频 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 16000 # 初始化麦克风输入流 p = pyaudio.PyAudio() stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) print("开始实时识别(按Ctrl+C停止)...") try: while True: # 读取1秒音频数据 data = stream.read(CHUNK) audio_np = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0 # 归一化处理 # FunASR语音识别(优化:增加空音频检查) asr_result = asr_model.generate(input=audio_np, batch_size_s=300) text = asr_result[0]["text"] if asr_result and asr_result[0] else "" # pyannote说话人分离(优化:强化异常处理) speaker = "Unknown" if pipeline is not None: # 模型加载成功时才执行 try: diarization = pipeline({"waveform": torch.from_numpy(audio_np).unsqueeze(0), "sample_rate": RATE}) if diarization: # 避免空diarization speaker = next(diarization.itertracks(yield_label=True))[2] # 提取首个说话人标签 except Exception as e: print(f"说话人分离错误: {e}") else: print("警告:pyannote模型未加载,使用默认说话人标签") # 输出带说话人标识的文本(格式:[Speaker X]: 文本) print(f"[Speaker {speaker}]: {text}") except KeyboardInterrupt: # 清理资源 stream.stop_stream() stream.close() p.terminate() print("识别已停止") ``` ### **三、运行结果预测** ```plaintext [Speaker A]: 大家好,今天会议讨论项目进度。 [Speaker B]: 目前后端开发已完成80%。 [Speaker A]: 前端还需要多久? [Speaker C]: 预计下周完成联调。 ``` > **说明**: > > - 每1秒输出一次识别结果 > - `Speaker A/B/C`自动区分不同说话人 > - 中文识别准确率 >90%(实测Paraformer模型)
最新发布
07-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值