Uniapp 安卓实现讯飞语音听写(复制即用)

        在移动应用开发中,语音交互功能能够极大提升用户体验,让操作更加便捷自然。讯飞语音听写技术凭借其高准确率和稳定性,成为众多开发者的选择。本文将详细介绍如何在 Uniapp 项目中,实现安卓端的讯飞语音听写功能,帮助你快速为应用增添实用的语音交互能力。
        但是,由于uniapp录音管理器 uni.getRecorderManager()的实时监听音频大小帧的功能onFrameRecorded不支持app,app端只能在录音结束后获取临时录音文件,因此需要把录音文件转成base64,在切片传输给讯飞的接口。

一、准备工作​

1. 注册讯飞开放平台账号​

首先,你需要前往讯飞开放平台注册账号,完成实名认证。认证通过后,你将获得使用讯飞相关服务的权限。​

2. 创建应用并获取 AppID 和密钥​

在讯飞开放平台控制台中,创建一个新的应用。创建成功后,你会得到该应用的 AppID、AppKey 和 AppSecret,这些信息在后续集成过程中至关重要,用于验证应用身份。​

二、实现代码

以下是已经实现的uniapp在app端的一个简单的利用讯飞语音听写api完成的语音识别的demo,复制代码到你的项目中,把你申请的appid、apiSecret、apiKey替换到代码中,即可运行识别。

<template>
    <div class="asr">
        <button class="btn" shape="circle" type="info" @touchstart="openMedia" @touchend="stopMedia">按住说话</button>
        <view v-if="show" class="iating">
            <text text="正在说话中...." color="#49ABFE" size="27rpx" line-height="60rpx" align="center"></text>
        </view>
    </div>
</template>

<script>
import CryptoJS from "crypto-js";

export default {
    data() {
        return {
            config: {
                hostUrl: "wss://iat-api.xfyun.cn/v2/iat",
                host: "iat-api.xfyun.cn",
                appid: "申请的讯飞appid",
                apiSecret: "申请的讯飞apiSecret",
                apiKey: "申请的apiKey",
                uri: "/v2/iat",
                highWaterMark: 1280,
            },
            uniSocketTask: null,
            show: false,
            resultText: "",
            resultTextTemp: "",
            renderText: ""
        };
    },
    methods: {
        // 鉴权签名
        getAuthStr(date) {
            let signatureOrigin = `host: ${this.config.host}\ndate: ${date}\nGET ${this.config.uri} HTTP/1.1`;
            let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, this.config.apiSecret);
            let signature = CryptoJS.enc.Base64.stringify(signatureSha);
            let authorizationOrigin =
                `api_key="${this.config.apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
            let authStr = CryptoJS.enc.Base64.stringify(
                CryptoJS.enc.Utf8.parse(authorizationOrigin)
            );
            return authStr;
        },

        getUrl() {
            let date = new Date().toUTCString();
            let wssUrl =
                this.config.hostUrl +
                "?authorization=" +
                this.getAuthStr(date) +
                "&date=" +
                encodeURIComponent(date) +
                "&host=" +
                this.config.host;
            console.log("websocke科大讯飞的地址为", wssUrl);
            return wssUrl;
        },

        // 打开麦克风
        openMedia() {
            this.connectSocket();
        },

        // 创建连接并返回数据
        connectSocket() {
            if (this.uniSocketTask === null) {
                this.uniSocketTask = uni.connectSocket({
                    url: this.getUrl(),
                    success() {},
                });
                
                this.uniSocketTask.onOpen(() => {
                    console.log("监听到开启连接成功");
                    this.startRecord();
                });
                
                this.uniSocketTask.onClose(() => {
                    console.log("监听到关闭连接成功");
                    this.uniSocketTask = null;
                });
                
                this.uniSocketTask.onError(() => {
                    console.log("监听到连接发生错误");
                });
                
                this.uniSocketTask.onMessage((res) => {
                    const message = JSON.parse(res.data);
                    if (res.data) {
                        console.log("收到服务器消息,并开始渲染", message);
                        this.renderResult(message);

                        if (message.code === 0 && message.data.status === 2) {
                            console.log('最后一条', this.renderText);
                            this.closeSocket();
                            // 注意:这里需要根据实际情况处理事件触发
                            // this.$emit('renderText', this.renderText);
                        }
                        
                        if (message.code !== 0) {
                            this.closeSocket();
                            console.error(message);
                        }
                    } else {
                        console.log("未监听到消息:原因:", JSON.stringify(res));
                    }
                });
            } else {
                console.log("socketTask实例已存在");
            }
        },
        
        // 发送给科大讯飞的第一帧的模板数据格式
        getInitialFrame() {
            return {
                common: {
                    app_id: this.config.appid,
                },
                business: {
                    language: "zh_cn",
                    domain: "iat",
                    accent: "mandarin",
                    dwa: "wpgs", // 可选参数,动态修正
                    vad_eos: 5000,
                },
                data: {
                    status: 0,
                    format: "audio/L16;rate=16000",
                    encoding: "lame"
                },
            };
        },

        // 发送消息
        sendMessage(sendData) {
            console.log('发送', JSON.stringify(sendData));
            this.uniSocketTask.send({
                data: JSON.stringify(sendData),
                success() {},
                fail() {
                    console.log("发送失败");
                },
            });
        },

        // 关闭连接
        closeSocket() {
            console.log("开始尝试关闭连接");
            this.uniSocketTask.close();
        },

        // 开启录音
        startRecord() {
            const recordOption = {
                sampleRate: 16000,
                format: "mp3",
            };
            const recordManager = uni.getRecorderManager();
            
            recordManager.onStart(() => {
                console.log("开始录音");
                this.show = true;
            });
            
            recordManager.onStop((res) => {
                console.log("录音停止,文件路径为:", res.tempFilePath);
                this.sendMessage(this.getInitialFrame());
                
                this.pathToBase64(res.tempFilePath).then(base64 => {
                    let buff = base64.split(",")[1];
                    const arrayBuffer = uni.base64ToArrayBuffer(buff);
                    const audioString = this.toString(arrayBuffer);
                    console.log("文件读取成功", audioString.length);
                    
                    let offset = 0;
                    while (offset < audioString.length) {
                        const subString = audioString.substring(offset, offset + 1280);
                        offset += 1280;
                        const isEnd = offset >= audioString.length;

                        let params = {
                            data: {
                                status: isEnd ? 2 : 1,
                                format: "audio/L16;rate=16000",
                                encoding: "lame",
                                audio: btoa(subString)
                            }
                        };

                        this.sendMessage(params);
                    }
                }).catch(error => {
                    console.error(error);
                });
            });
            
            recordManager.onError((err) => {
                console.log("录音出现错误", err);
            });

            recordManager.start(recordOption);
        },

        // 关闭录音
        stopMedia() {
            const recordManager = uni.getRecorderManager();
            recordManager.stop();
            this.show = false;
        },

        // 工具方法
        toString(buffer) {
            var binary = '';
            var bytes = new Uint8Array(buffer);
            var len = bytes.byteLength;
            for (var i = 0; i < len; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return binary;
        },

        // 录音文件路径转base64
        pathToBase64(path) {
            return new Promise((resolve, reject) => {
                if (typeof plus === 'object') {
                    plus.io.resolveLocalFileSystemURL(path, function(entry) {
                        entry.file(function(file) {
                            var fileReader = new plus.io.FileReader();
                            fileReader.onload = function(evt) {
                                resolve(evt.target.result);
                            };
                            fileReader.onerror = function(error) {
                                reject(error);
                            };
                            fileReader.readAsDataURL(file);
                        }, function(error) {
                            reject(error);
                        });
                    }, function(error) {
                        reject(error);
                    });
                    return;
                }
                reject(new Error('not support'));
            });
        },

        // 讯飞回复字段拼接
        renderResult(jsonData) {
            if (jsonData.data && jsonData.data.result) {
                let data = jsonData.data.result;
                let str = "";
                let ws = data.ws;
                for (let i = 0; i < ws.length; i++) {
                    str = str + ws[i].cw[0].w;
                }
                
                if (data.pgs) {
                    if (data.pgs === "apd") {
                        this.resultText = this.resultTextTemp;
                    }
                    this.resultTextTemp = this.resultText + str;
                } else {
                    this.resultText = this.resultText + str;
                }
                
                this.renderText = this.resultTextTemp || this.resultText || "";
            }
            console.log("渲染后的数据为", this.renderText);
        }
    }
}
</script>

<style scoped>
.asr {
    display: flex;
    justify-content: center;
    padding: 20rpx;
}

.btn {
    width: 200rpx;
    height: 200rpx;
}

.iating {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: rgba(0, 0, 0, 0.7);
    padding: 30rpx;
    border-radius: 20rpx;
    z-index: 999;
}

</style>    

<think>好的,我现在需要回答用户关于如何在UniApp中集成语音识别SDK的问题。用户希望得到方法、示例和教程。首先,我得回顾一下用户提供的引用内容,看看有没有相关的信息可以利用。 根据引用[2],用户提到官网的APP集成方案适用于原生Android和iOS,但他们在使用UniApp时无法直接集成,所以考虑通过离线采集SDK的方式先获取用户照片,再调用人脸认证接口。这可能意味着在UniApp中集成原生SDK需要插件或桥接方法。 引用[3]提到了一个UniApp原生语音插件,YL-SpeechRecognition,说明可能存在第三方插件可以直接使用。同时,安卓和iOS在停止录音时的回调不同,需要注意平台差异。 接下来,我需要整理步骤。首先,用户可能需要安装第三方插件,比如引用中的YL-SpeechRecognition。然后配置原生平台,如Android和iOS的相关设置,包括权限和SDK密钥。接着,在UniApp中编写调用代码,处理开始、停止录音以及回调。此外,还需要考虑不同平台的差异,比如iOS需要描述文件权限。 另外,用户提供的引用[1]虽然关于阿里云的SDK,但可能不适用于UniApp,因为它是微信小程序专用的。所以可能不需要考虑这个。 总结步骤:1. 安装插件;2. 配置原生平台;3. 编写代码;4. 处理权限和平台差异。同时,需要注意插件的文档和示例,确保正确使用。 可能的问题点:插件是否最新,是否支持最新的UniApp版本,以及如何获取等平台的AppKey。需要提醒用户根据实际情况调整参数,并参考插件文档。 最后,用户可能需要示例代码,所以需要提供一个简单的示例,展示初始化、开始和停止录音的方法,以及回调处理。同时,注意事项如网络权限、平台差异等也要提到。</think>### 在UniApp中集成语音识别SDK的方法与示例 #### 1. **选择适配UniApp语音识别插件** 由于UniApp本身无法直接调用原生SDK,需依赖第三方插件实现桥接。例如,可使用**YL-SpeechRecognition插件**(支持语音识别)[^3]: - 插件功能:语音听写、实时回调、停止录音等。 - 平台差异:Android/iOS在停止录音时回调参数可能不同,需分别处理[^3]。 #### 2. **插件安装与配置** **步骤1:安装插件** 在UniApp项目的`manifest.json`中,通过「原生插件配置」添加插件(需从插件市场购买或下载)。 ```json // manifest.json "app-plus": { "plugins": { "YL-SpeechRecognition": { "version": "1.0.0", "provider": "插件供应商ID" } } } ``` **步骤2:配置原生平台** - **Android**:在`build.gradle`中添加SDK依赖,并配置`APPID`和`APPKEY`。 - **iOS**:在`Info.plist`中添加麦克风权限描述`NSSpeechRecognitionUsageDescription`。 #### 3. **代码实现示例** ```javascript // 引入插件 const speech = uni.requireNativePlugin('YL-SpeechRecognition'); // 初始化配置 speech.init({ appId: 'YOUR_APP_ID', // 开放平台申请 apiKey: 'YOUR_API_KEY' }); // 开始录音 function startListening() { speech.startListening({ language: 'zh_cn', // 中文普通话 onResult: (res) => { console.log('实时识别结果:', res.partialResult); }, onError: (err) => { console.error('识别错误:', err); } }); } // 停止录音(需区分平台) function stopListening() { #ifdef APP-PLUS speech.stopListening((res) => { console.log('最终结果:', res.finalResult); // Android返回此处 }); #endif #ifdef IOS // iOS停止后通过事件监听结果 uni.$on('onSpeechResult', (res) => { console.log('iOS最终结果:', res); }); #endif } ``` #### 4. **注意事项** - **权限申请**:需在`manifest.json`中声明麦克风权限(`android.permission.RECORD_AUDIO`和`NSMicrophoneUsageDescription`)。 - **平台差异**:iOS可能需要额外配置`Capabilities`中的语音识别权限。 - **SDK密钥**:需从开放平台注册应用获取。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值