使用科大讯飞接口实现语音转文字

 一、首先使用科大讯飞平台注册账号选择相应功能进行领取或购买

二、本文运用了语音转写和语音听写功能申请后获得讯飞开放平台提供的 apiKey。

三、 示例代码

 本文代码实现功能:

        代码运行有三个模式:mode 0、mode 1、mode2

        mode 0 对整段语音进行转文字操作,结果输出时间较长

        mode 1 将语音划分为若干份,逐份进行转文字输出

        mode 2 首先进行录音操作,然后再对音频使用mode 1方法转文字输出

# -*- coding: utf-8 -*-
import base64
import hashlib
import hmac
import json
import os
import time
import requests
import urllib
import websocket
import datetime
import hashlib
import base64
import hmac
import json
from urllib.parse import urlencode
import time
import ssl
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
import _thread as thread
import pyaudio
import threading
import wave 
import numpy  as np 

STATUS_FIRST_FRAME = 0  # 第一帧的标识
STATUS_CONTINUE_FRAME = 1  # 中间帧标识
STATUS_LAST_FRAME = 2  # 最后一帧的标识


lfasr_host = 'https://raasr.xfyun.cn/v2/api'
# 请求的接口名
api_upload = '/upload'
api_get_result = '/getResult'

##################################################
# 录音功能函数
#########

def audio_f2i(data, width=16):
    """将浮点数音频数据转换为整数音频数据。"""
    data = np.array(data)
    return np.int16(data * (2 ** (width - 1)))

def audio_i2f(data, width=16):
    """将整数音频数据转换为浮点数音频数据。"""
    data = np.array(data)
    return np.float32(data / (2 ** (width - 1)))

def save_wavfile(path, wave_data):
        """保存音频数据为wav文件。"""
        with wave.open(path, 'wb') as wav_file:
            wav_file.setnchannels(1)
            wav_file.setsampwidth(2)
            wav_file.setframerate(16000)
            wav_file.writeframes(np.array(wave_data).tobytes())
        print(f"Successfully saved wavfile: {path} ..")

# 获取麦克风设备列表
def list_devices():
    p = pyaudio.PyAudio()
    info = p.get_host_api_info_by_index(0)
    numdevices = info.get('deviceCount')
    devices = []
    for i in range(numdevices):
        if p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels') > 0:
            devices.append(p.get_device_info_by_host_api_device_index(0, i))
    p.terminate()

    print("Available recording devices:")
    devices_dict = {}
    for i, device in enumerate(devices):
        print(f"{i}: {device['name']}")
        devices_dict[device['name']] = i
    return devices, devices_dict

def init_device():
    devices, devices_dict = list_devices()
    device_id = int(input("请选择设备:"))
    print("选择设备:",devices[device_id])
    device = devices[device_id]  # Select the first available device, modify as needed
    return device

class Recorder(threading.Thread):
    def __init__(self,
                 format=pyaudio.paInt16,
                channels=1,
                sample_rate=16000,
                frames_per_buffer=1024,
                device = None):
        
        super().__init__()
        self.daemon = True
        self._stop_event = threading.Event()
        self.device = device
        self.init_stream(format=format,
                        channels=channels,
                        sample_rate=sample_rate,
                        frames_per_buffer=frames_per_buffer)
        self.waveform = []
        
    def run(self):
        self.waveform = []
        chunk_size = 1024
        while not self._stop_event.is_set():
            data = self.stream.read(chunk_size)
            data = np.frombuffer(data,dtype='int16')
            self.waveform.extend(data)
        self.deinit_stream()


    def init_stream(self,
                    format=pyaudio.paInt16,
                    channels=1,
                    sample_rate=16000,
                    frames_per_buffer=1024):
        self.p = pyaudio.PyAudio()
        self.stream = self.p.open(
            format=format,
            channels=channels,
            rate=sample_rate,
            input=True,
            input_device_index=self.device['index'],
            frames_per_buffer=frames_per_buffer
        )
        print("Initialized the stream reader successfully.")

    def deinit_stream(self):
        self.stream.stop_stream()
        self.stream.close()
        self.p.terminate()
        print("Deinitialized the stream reader successfully.")

    def stop(self):
        self._stop_event.set()

#########


##################################################
# 语音转写
#########
class RequestApi(object):
    def __init__(self, appid, secret_key, upload_file_path):
        self.appid = appid
        self.secret_key = secret_key
        self.upload_file_path = upload_file_path
        self.ts = str(int(time.time()))
        self.signa = self.get_signa()

    def get_signa(self):
        appid = self.appid
        secret_key = self.secret_key
        m2 = hashlib.md5()
        m2.update((appid + self.ts).encode('utf-8'))
        md5 = m2.hexdigest()
        md5 = bytes(md5, encoding='utf-8')
        # 以secret_key为key, 上面的md5为msg, 使用hashlib.sha1加密结果为signa
        signa = hmac.new(secret_key.encode('utf-8'), md5, hashlib.sha1).digest()
        signa = base64.b64encode(signa)
        signa = str(signa, 'utf-8')
        return signa


    def upload(self):
        # print("上传部分:")
        upload_file_path = self.upload_file_path
        file_len = os.path.getsize(upload_file_path)
        file_name = os.path.basename(upload_file_path)

        param_dict = {}
        param_dict['appId'] = self.appid
        param_dict['signa'] = self.signa
        param_dict['ts'] = self.ts
        param_dict["fileSize"] = file_len
        param_dict["fileName"] = file_name
        param_dict["duration"] = "200"
        # print("upload参数:", param_dict)
        data = open(upload_file_path, 'rb').read(file_len)

        response = requests.post(url =lfasr_host + api_upload+"?"+urllib.parse.urlencode(param_dict),
                                headers = {"Content-type":"application/json"},data=data)
        # print("upload_url:",response.request.url)
        result = json.loads(response.text)
        # print("upload resp:", result)
        return result


    def get_result(self):
        uploadresp = self.upload()
        orderId = uploadresp['content']['orderId']
        param_dict = {}
        param_dict['appId'] = self.appid
        param_dict['signa'] = self.signa
        param_dict['ts'] = self.ts
        param_dict['orderId'] = orderId
        param_dict['resultType'] = "transfer,predict"
        # print("")
        # print("查询部分:")
        # print("get result参数:", param_dict)
        status = 3
        # 建议使用回调的方式查询结果,查询接口有请求频率限制
        while status == 3:
            response = requests.post(url=lfasr_host + api_get_result + "?" + urllib.parse.urlencode(param_dict),
                                     headers={"Content-type": "application/json"})
            # print("get_result_url:",response.request.url)
            result = json.loads(response.text)
            # print(result)
            status = result['content']['orderInfo']['status']
            # print("status=",status)
            if status == 4:
                break
            time.sleep(5)
        # print("get_result resp:",result)
        return result

#########

##################################################
# 语音听写
#########
class Ws_Param(object):
    # 初始化
    def __init__(self, APPID, APIKey, APISecret, AudioFile):
        self.APPID = APPID
        self.APIKey = APIKey
        self.APISecret = APISecret
        self.AudioFile = AudioFile

        # 公共参数(common)
        self.CommonArgs = {"app_id": self.APPID}
        # 业务参数(business),更多个性化参数可在官网查看
        self.BusinessArgs = {"domain": "iat", "language": "zh_cn", "accent": "mandarin", "vinfo":1,"vad_eos":10000}

    # 生成url
    def create_url(self):
        url = 'wss://ws-api.xfyun.cn/v2/iat'
        # 生成RFC1123格式的时间戳
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        # 拼接字符串
        signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
        signature_origin += "date: " + date + "\n"
        signature_origin += "GET " + "/v2/iat " + "HTTP/1.1"
        # 进行hmac-sha256进行加密
        signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
                                 digestmod=hashlib.sha256).digest()
        signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')

        authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
            self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
        # 将请求的鉴权参数组合为字典
        v = {
            "authorization": authorization,
            "date": date,
            "host": "ws-api.xfyun.cn"
        }
        # 拼接鉴权参数,生成url
        url = url + '?' + urlencode(v)
        # print("date: ",date)
        # print("v: ",v)
        # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
        # print('websocket url :', url)
        return url


# 收到websocket消息的处理
def on_message(ws, message):
    try:
        code = json.loads(message)["code"]
        sid = json.loads(message)["sid"]
        if code != 0:
            errMsg = json.loads(message)["message"]
            print("sid:%s call error:%s code is:%s" % (sid, errMsg, code))

        else:
            data = json.loads(message)["data"]["result"]["ws"]
            # print(json.loads(message))
            result = ""
            for i in data:
                for w in i["cw"]:
                    result += w["w"]
            json_str = json.dumps(data, ensure_ascii=False)
            data = json.loads(json_str)
            result = ""
            # 遍历数据列表
            for item in data:
                # 从每个子项中提取字符并添加到结果字符串中
                result += item["cw"][0]["w"]


            # 输出最终结果
            with open('data.txt','a+',encoding='utf-8') as f:
                f.write(result+'\n')
            # print(result)
            print("data:%s is:%s" % (n+1, result))
    except Exception as e:
        print("receive msg,but parse exception:", e)



# 收到websocket错误的处理
def on_error(ws, error):
    print("### error:", error)


# 收到websocket关闭的处理
def on_close(ws,a,b):
    print("### closed ###")


# 收到websocket连接建立的处理
def on_open(ws):
    def run(*args):
        frameSize = 8000  # 每一帧的音频大小
        intervel = 0.04  # 发送音频间隔(单位:s)
        status = STATUS_FIRST_FRAME  # 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧

        with open(wsParam.AudioFile, "rb") as fp:
            while True:
                buf = fp.read(frameSize)
                # 文件结束
                if not buf:
                    status = STATUS_LAST_FRAME
                # 第一帧处理
                # 发送第一帧音频,带business 参数
                # appid 必须带上,只需第一帧发送
                if status == STATUS_FIRST_FRAME:

                    d = {"common": wsParam.CommonArgs,
                         "business": wsParam.BusinessArgs,
                         "data": {"status": 0, "format": "audio/L16;rate=16000",
                                  "audio": str(base64.b64encode(buf), 'utf-8'),
                                  "encoding": "raw"}}
                    d = json.dumps(d)
                    ws.send(d)
                    status = STATUS_CONTINUE_FRAME
                # 中间帧处理
                elif status == STATUS_CONTINUE_FRAME:
                    d = {"data": {"status": 1, "format": "audio/L16;rate=16000",
                                  "audio": str(base64.b64encode(buf), 'utf-8'),
                                  "encoding": "raw"}}
                    ws.send(json.dumps(d))
                # 最后一帧处理
                elif status == STATUS_LAST_FRAME:
                    d = {"data": {"status": 2, "format": "audio/L16;rate=16000",
                                  "audio": str(base64.b64encode(buf), 'utf-8'),
                                  "encoding": "raw"}}
                    ws.send(json.dumps(d))
                    time.sleep(1)
                    break
                # 模拟音频采样间隔
                time.sleep(intervel)
        ws.close()

    thread.start_new_thread(run, ())

#########

##################################################
# main函数
# mode = 0 语音转写
# mode = 1 实时语音转写  (小于30s)
# mode = 2 录音转写     (小于30s)
#########

if __name__ == '__main__':
    # 导入库
    import argparse

    # 1. 定义命令行解析器对象
    parser = argparse.ArgumentParser(description='Usage of argparse')

    # 2. 添加命令行参数,可以简写,也可以写全称
    parser.add_argument('-m','--mode', type=int, default="0")

    # 3. 从命令行中结构化解析参数
    args = parser.parse_args()

    #通过args对象.参数名,获取命令行参数值
    mode = args.mode
    print(f"解析出的参数,mode:{mode}")
    print("-------------------------------识别开始-------------------------------")
    if mode == 0:
        api = RequestApi(appid="xxxx",
                        secret_key="xxxx",
                        upload_file_path=r"audio/lfasr_涉政.wav")

        result = api.get_result()

        text = json.loads(result['content']['orderResult'])['lattice']
        with open('music.txt','w',encoding='utf-8') as f:
            for each in text:
                txt = json.loads(each['json_1best'])['st']['rt']
                for t in txt:
                    #print(t)
                    st = ''
                    for i in range(len(t['ws'])):
                        print(t['ws'][i]['cw'][0]['w'],end='')  
                        st += t['ws'][i]['cw'][0]['w']
                    f.write(st+'\n')
                    print()
        print("-------------------------------识别结束-------------------------------")

    elif mode == 1:
        n = 0
        os.remove("data.txt")
        time1 = datetime.now()
        wsParam = Ws_Param(APPID='xxxx', APISecret='xxxx',
                        APIKey='xxxx',
                        AudioFile=r'audio/lfasr_涉政.wav')
        websocket.enableTrace(False)
        wsUrl = wsParam.create_url()
        ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
        ws.on_open = on_open
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
        time2 = datetime.now()
        print(time2-time1)
        print("-------------------------------识别结束-------------------------------")

    elif mode == 2:
        os.remove("data.txt")
        device = init_device()
        recorder = Recorder(device=device)
        input("按下任意键开始录音")
        recorder.start()
        input("按下任意键结束录音")
        recorder.stop()
        recorder.join()
        save_wavfile('noise.wav',recorder.waveform)

        time1 = datetime.now()
        wsParam = Ws_Param(APPID='xxxx', APISecret='xxxx',
                        APIKey='xxxx',
                        AudioFile=r'noise.wav')
        websocket.enableTrace(False)
        wsUrl = wsParam.create_url()
        ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
        ws.on_open = on_open
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
        time2 = datetime.now()
        print(time2-time1)
        print("-------------------------------识别结束-------------------------------")

#########

可用博主资源里的科大讯飞官方例程音频测试代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值