webApi06


小概

本章的学习内容:原生js写轮播图(匀速动画应用),缓动动画的封装和使用,导航栏筋斗云案例(缓动动画应用)


提示:以下是本篇文章正文内容,下面案例可供参考

1. 轮播图案例(淘宝滚动型)

  • 轮播图的应用基本在每个电商网站上都可以看见,应用场景非常广泛。下面我们用原生js实现轮播图的制作
  • 图片轮播的原理就是使用上一章我们封装的匀速动画实现的:在css样式中,把所有需要展示的图片连接成一张长图,为父盒子设置overflow: hidden(溢出裁剪), 通过匀速动画修改该图片的letf值达到展示不同图片的效果 下面是css样式
    <style type="text/css">
        * {
            padding: 0;
            margin: 0;
            list-style: none;
            border: 0;
        }

        .all {
            width: 500px;
            height: 200px;
            padding: 7px;
            border: 1px solid #ccc;
            margin: 100px auto;
            position: relative;
        }

        .screen {
            width: 500px;
            height: 200px;
            overflow: hidden;
            position: relative;
        }

        .screen li {
            width: 500px;
            height: 200px;
            overflow: hidden;
            float: left;
        }

        .screen ul {
            position: absolute;
            left: 0;
            top: 0px;
            width: 3000px;
        }

        .all ol {
            position: absolute;
            right: 10px;
            bottom: 10px;
            line-height: 20px;
            text-align: center;
        }

        .all ol li {
            float: left;
            width: 20px;
            height: 20px;
            background: #fff;
            border: 1px solid #ccc;
            margin-left: 10px;
            cursor: pointer;
        }

        .all ol li.current {
            background: yellow;
        }

        #arr {
            display: none;
        }

        #arr span {
            width: 40px;
            height: 40px;
            position: absolute;
            left: 5px;
            top: 50%;
            margin-top: -20px;
            background: #000;
            cursor: pointer;
            line-height: 40px;
            text-align: center;
            font-weight: bold;
            font-family: '黑体';
            font-size: 30px;
            color: #fff;
            opacity: 0.3;
            border: 1px solid #fff;
        }

        #arr #right {
            right: 5px;
            left: auto;
        }
    </style>
</head>

<body>
    <div class="all" id='box'>
        <div class="screen">
            <ul>
                <li><img src="images/01.jpg" width="500" height="200" /></li>
                <li><img src="images/02.jpg" width="500" height="200" /></li>
                <li><img src="images/03.jpg" width="500" height="200" /></li>
                <li><img src="images/04.jpg" width="500" height="200" /></li>
                <li><img src="images/05.jpg" width="500" height="200" /></li>
                <li><img src="images/01.jpg" width="500" height="200" /></li>
            </ul>
            <ol>
                <li class="current">1</li>
                <li>2</li>
                <li>3</li>
                <li>4</li>
                <li>5</li>
            </ol>
        </div>
        <div id="arr">
            <span id="left">&lt;</span>
            <span id="right">&gt;</span>
        </div>
    </div>

1.2 需求分析

1.鼠标移入移出box:显示/隐藏arr箭头盒子
2.鼠标单击上一页/下一页:滚动到对应下标的图片
* 注意点:实现无限循环
3.鼠标单击页码:滚动到对应下标的图片
4.自动轮播:定时器实现

需要用到上期的匀速动画封装函数

/**
* @description:匀速动画
* @param {number}target : 目标位置 
* @param {dom}ele : 目标元素 
* @return: 
*/
function animationMove(target, ele) {
    //1.开始动画之前先清除之前的定时器,以本次移动为准
    clearInterval(ele.timeID);
    //2.开始本次动画
    ele.timeID = setInterval(function () {
        //2.1 获取元素当前位置
        let currentLeft = ele.offsetLeft;
        /* 使用布尔类型存储移动方向 true:从左往右  false:从右往左*/
        let isLeft = target >= currentLeft ? true : false;
        //2.2 开始移动
        isLeft ? currentLeft += 10 : currentLeft -= 10;
        ele.style.left = currentLeft + 'px';
        //2.3 边界检测
        /* 
        这个语法是一个比较表达式,比较两边式子结果是否一致
            左边: isLeft 布尔类型 true:从左往右  false:从右往左
            右边: currentLeft >= target 结果布尔类型
                (1)当isLeft为true(从左往右),只有当右边式子也为true才会成立
                (2)当isLeft为false(从右往左),只有当右边式子也为false才会成立。此时只有当currentLeft < target的时候右边式子才是false
         */
        if (isLeft == currentLeft >= target) {
            //停止动画
            clearInterval(ele.timeID);
            //元素复位
            ele.style.left = target + 'px';
        };
    }, 20);
};

1.3 功能1 功能2

1.鼠标移入移出box:显示/隐藏arr箭头盒子
2.鼠标单击上一页/下一页:滚动到对应下标的图片

        // 1. 获取页面元素
        let box = document.querySelector('#box'); // 父盒子
        let screen = document.querySelector('.screen'); // 轮播图展现父盒子
        let ul = screen.children[0];//图片列表
        let ol = screen.children[1];//页码列表
        let arr = document.querySelector('#arr');//arrow箭头盒子
        let left = arr.children[0];//上一页
        let right = arr.children[1];//上一页

		//声明一个全局变量:存储当前的图片下标
        let index = 0;

        //2.注册事件
        //2.1 鼠标移入box
        box.onmouseover = function () {
            //3.事件处理:显示箭头arr盒子
            arr.style.display = 'block';
        };

        //2.2 鼠标移出box
        box.onmouseout = function () {
            //3.事件处理:隐藏箭头arr盒子
            arr.style.display = 'none';
        };

        //2.3 点击下一页
        right.onclick = function () {
            //3. 事件处理:滚动到对应下标的图片 index++
            // 3.1 计算对应下标
            if (index == ul.children.length - 1) {
                index = 0;
            } else {
                index++;
            }
            console.log(index);
            // 3.2 开始滚动
            animationMove(-index * screen.offsetWidth, ul);
            // 3.3 排他思想修改页码样式
            for (let i = 0; i < ol.children.length; i++) {
                if (i == index) {
                    ol.children[i].className = 'current';
                } else {
                    ol.children[i].className = '';
                }
            }
        };

        //2.4 点击上一页
        left.onclick = function () {
            //3. 事件处理:滚动到对应下标的图片 index--
            // 3.1 计算对应下标
            if (index == 0) {
                index = 4;
            } else {
                index--;
            }
            console.log(index);
            // 3.2 开始滚动
            animationMove(-index * screen.offsetWidth, ul);
            // 3.3 排他思想修改页码样式
            for (let i = 0; i < ol.children.length; i++) {
                if (i == index) {
                    ol.children[i].className = 'current';
                } else {
                    ol.children[i].className = '';
                }
            }
        };

1.4 实现循环轮播

其实上已经实现了无限轮播,但是出现了一个问题,第5张下一张到第一张的时候,切换方式是直接左移到第一张,这种切换方式有问题。

解决方法:视觉欺骗
将第一张图片复制到最后一张图片的后面,实际上就有6张图片了,只不过第一张与最后一张是同一张,判断下标为5时,index=0 ;ul.style.left = -index*(照片宽度),相当于第六张切到第一张的时候 瞬间切换到第一张, 然后展示第一张切换第二张的动画。

还会出现一个问题,就是页码(0 1 2 3 4)与图片的下标不对应(0 1 2 3 4 5);
解决方法:判断当图片[[5]时,给页码[0]设置样式、

        //2.3 点击下一页
        right.onclick = function () {
            //3. 事件处理:滚动到对应下标的图片 index++
            // 3.1 计算对应下标
            if (index == ul.children.length - 1) {
                index = 0;
                ul.style.left = -index*screen.offsetWidth + 'px';        // 重点:轮播图无限轮播的秘密,视觉欺骗
            }
            index++;
            console.log(index);
            // 3.2 开始滚动
            animationMove(-index * screen.offsetWidth, ul);
            // 3.3 排他思想修改页码样式
            for (let i = 0; i < ol.children.length; i++) {
                if (i == index ) {
                    ol.children[i].className = 'current';
                } else {
                    ol.children[i].className = '';
                }
            }
            if(index == ul.children.length-1){
                ol.firstElementChild.className = 'current';
            }
        };
        //2.4 点击上一页
        left.onclick = function () {
            //3. 事件处理:滚动到对应下标的图片 index--
            // 3.1 计算对应下标
            if (index == 0) {
                index = ul.children.length-1;
                ul.style.left = -index*screen.offsetWidth +'px';
            }
            index--;
            console.log(index);
            // 3.2 开始滚动
            animationMove(-index * screen.offsetWidth, ul);
            // 3.3 排他思想修改页码样式
            for (let i = 0; i < ol.children.length; i++) {
                if (i == index) {
                    ol.children[i].className = 'current';
                } else {
                    ol.children[i].className = '';
                }
            }
        };

1.5 功能3 功能4

实现鼠标单击页码,跳到对应的图片上
实现自动轮播,鼠标移入停止轮播,离开继续轮播

        //2.5 页码点击
        for (let i = 0; i < ol.children.length; i++) {
            ol.children[i].onclick = function () {   //i =0 1 2 3 4
                //事件处理 this:当前点击的页码
                console.log(i);
                // 3.1 开始滚动
                animationMove(-i*screen.offsetWidth,ul);
                // 3.2 设置index与当前点击的页码一致
                index =i;
                // 3.3 排他思想修改样式
                for(let j=0;j<ol.children.length;j++){
                    if(i==j){
                        ol.children[j].className = 'current';
                    }else{
                        ol.children[j].className = '';
                    }
                }
            }
        }
        // 开启自动轮播
        let timeID = setInterval(function(){
                right.onclick();
            },2000);
        //2.1 鼠标移入box
        box.onmouseover = function () {
            //3.事件处理:显示箭头arr盒子
            arr.style.display = 'block';
            //清除自动轮播
            clearInterval(timeID);
        };
        //2.2 鼠标移出box
        box.onmouseout = function () {
            //3.事件处理:隐藏箭头arr盒子
            arr.style.display = 'none';
            //开启自动轮播
            timeID = setInterval(function(){
                right.onclick();
            },2000);
        };

2. 缓速动画

  1. 什么是缓动动画 : 速度由快到慢的动画
    匀速: 速度不变
    变速: 速度变化,更符合现实世界物体运动规律
    2. 缓动动画核心原理 : 本次移动距离 = (目标位置-当前位置)/10
    3. 缓动动画特点
    3.1 需要取整 : 因为缓动动画公式是除法,而除法会产生小数。 像素一般不给小数的,所以需要取整。
    3.2 没有误差 : 因为缓动动画公式计算到最后每次只移动1px
    * 不需要边界检测: current >= target , 元素复位
    * 需要终点检测 : current == target

缓速动画与匀速动画的区别,
缓速动画;每次移动的值 =(目标距离-当前距离)/10 ,不需要边界检测,移动方向不用判断(向右移动移动距离向上取整, 向左移动移动距离向下取整)
匀速动画:每次移动固定的值,需要边界检测,需要判断移动方向,需要复位、

        function animationSlow(target,ele){
            //1.先清除以前的定时器,以本次移动为准
            clearInterval(ele.timeID);
            //2.开始本次动画
            ele.timeID = setInterval(function(){
                //2.1.获取当前位置
                let currentLeft = ele.offsetLeft;
                //2.2.计算本次移动距离 = (目标位置-当前位置)/10
                let step = (target-currentLeft)/10;
                //取整 ; 正数:从左往右,向上取整  负数:从右往左,向下取整
                step =   step > 0 ? Math.ceil(step) : Math.floor(step);
                //2.3.开始移动
                currentLeft += step;
                ele.style.left = currentLeft + 'px';
                //2.4.终点检测
                if( currentLeft == target){
                    clearInterval(ele.timeID);
                };
            },20);
        };

3. 筋斗云案例

  • 缓速动画的金典案例
   <style>
        * {
            margin: 0;
            padding: 0;
        }
        ul {
            list-style: none;
        }
        body {
            background-color: #333;
        }
        .nav {
            width: 800px;
            height: 42px;
            margin: 100px auto;
            /*background-color: red;*/
            background: #fff url(images/rss.png) no-repeat right center;
            border-radius: 10px;
            position: relative;
        }
        .nav li {
            width: 83px;
            height: 42px;
            /*background-color: red;*/
            text-align: center;
            line-height: 42px;
            float: left;
            cursor: pointer;
        }
        ul {
            position: relative;
        }
        .nav span {
            position: absolute;
            top: 0;
            left: 0;
            width: 83px;
            height: 42px;
            background: url(images/cloud.gif) no-repeat;
        }
    </style>
</head>

<body>
    <div class="nav">
        <span id="cloud"></span>
        <ul id="navBar">
            <li>首页</li>
            <li>博客</li>
            <li>程序员学院</li>
            <li>下载</li>
            <li>论坛</li>
            <li>问答</li>
            <li>代码</li>
            <li>直播</li>
        </ul>
    </div>
</body>
</html>
<script src="./animation.js"></script>   //封装了匀速和缓速的两个函数方法

<script>
    /*需求分析 
    1.鼠标移入每一个li元素: 筋斗云缓动到移入的li元素位置
    2.鼠标移出每一个li元素: 筋斗云缓动到  主人的位置
    3.鼠标单击每一个li元素: 筋斗云 主人变成单击的li元素
    */

    //1.获取元素
    let cloud = document.querySelector('#cloud');
    let liList = document.querySelectorAll('#navBar>li');
    //声明一个全局变量存储筋斗云的主人(默认第一个li元素)
    let zhuren = liList[0];


    //2.注册事件
    for(let i = 0;i<liList.length;i++){
        
        //2.1 移入
        liList[i].onmouseover = function(){
            //3.事件处理  this: 移入的li元素
            animationSlow(this.offsetLeft,cloud);
        };
        //2.2 移出
        liList[i].onmouseout = function(){
             //3.事件处理  this: 移出的li元素
             animationSlow(zhuren.offsetLeft,cloud);
        };
        //2.3 单击
        liList[i].onclick = function(){
            //3.事件处理 this: 单击的li元素
            zhuren = this;
        };
    };
</script>

总结

匀速动画和缓速动画两个知识点很重要,尤其是利用匀速动画制作轮播图和筋斗云这两个案例,非常的金典 肥常的好用。虽然现在组件很多,写项目的时候直接调用就可以,但是这并不影响我准备敲他‘10’遍的。好记性不如敲烂指头,奥力给!

<think>我们需要在Unity中连接科大讯飞的语音评测WebAPI。科大讯飞语音评测(流式版)通常用于对语音进行实时评测,比如发音准确度等。由于Unity可以使用C#进行开发,我们将使用C#的HttpWebRequest或UnityWebRequest来发送音频数据并接收评测结果。 步骤: 1. 注册科大讯飞开放平台账号,创建应用,获取APPID和APIKey。 2. 根据API文档,构建请求。注意,讯飞的流式评测API要求将音频分片发送,并且使用WebSocket协议(部分API)或者HTTP分片上传(具体看API版本)。 3. 但是,目前讯飞的实时语音评测(流式版)WebAPI主要使用WebSocket协议,因此我们需要在Unity中使用WebSocket来连接。 然而,Unity本身不直接支持WebSocket,我们可以使用第三方库,比如WebSocketSharp(注意:需要兼容Unity的版本)或者使用Unity的WebGL WebSocket(但仅限于WebGL平台)。对于多平台支持,我们可以使用一个跨平台的WebSocket库。 这里我们选择使用一个在Unity中常用的WebSocket库:NativeWebSocket(可以在Asset Store找到)或者使用WebSocketSharp。但为了简单和跨平台,我们这里使用一个简单的WebSocket实现,比如使用BestHTTP(商业插件)或者自己封装。 由于版权和代码简洁性,我们这里使用Unity自带的WebGL.WebSocket(仅限WebGL平台)或者使用一个开源的WebSocket实现。不过,在Unity2018以上版本,我们可以使用.Net Standard 2.0,这样我们可以使用System.Net.WebSockets.ClientWebSocket(需要Unity2018.2以上版本,并且设置API Compatibility Level为.NET 4.x或.NET Standard 2.0)。 因此,我们将使用C#的System.Net.WebSockets.ClientWebSocket(需要Unity版本支持)来连接科大讯飞的WebSocket服务器。 主要步骤: 1. 构建请求的URL,包括鉴权参数(根据讯飞要求,鉴权参数需要生成签名)。 2. 建立WebSocket连接。 3. 按照协议格式发送握手消息(包括参数,如appid, apiKey等,以及音频参数)。 4. 分片发送音频数据(PCM格式)。 5. 接收服务器返回的评测结果。 6. 关闭连接。 注意:科大讯飞的流式评测API要求音频为PCM格式,采样率16k,单声道,16位深。 由于代码较长,我们将分步骤实现。 首先,我们需要生成请求的URL,讯飞的WebSocket地址为:ws://rtasr.xfyun.cn/v1/ws?{参数},其中参数需要包括: appid: 应用ID ts: 当前时间戳(UTC时间) signa: 签名,生成方式为:base64(hmac_sha256(apiKey, ts)) 具体步骤(参考讯飞文档): 1. 生成ts,格式为YYYY-MM-DDTHH:mm:ssZ(如2018-06-11T09:00:00Z),并转换为UTC时间。 2. 拼接字符串:apiKey + ts 3. 使用HMAC-SHA256算法计算签名,然后base64编码。 然后,我们需要按照协议发送数据帧。讯飞流式评测协议中,客户端发送的数据包括: - 握手消息(第一个消息):一个JSON字符串,包含业务参数(如评测文本、音频格式等)和公共参数(appid等)。 - 音频数据:分片发送,每个分片包含一个二进制数据帧。 - 结束消息:发送一个结束帧(内容为{"end": true})。 评测结果会通过WebSocket异步返回。 由于Unity中主线程和网络线程不同,我们需要使用异步并处理回调,并在Unity主线程中更新结果。 考虑到Unity的生命周期,我们将创建一个MonoBehaviour脚本来管理连接和发送。 由于代码量较大,我们只实现核心部分,并注意以下几点: 1. 使用async/await(需要Unity2018.3以上版本支持,且.NET 4.x) 2. 处理连接、发送、接收和关闭事件。 下面是一个简化版本的实现: 步骤: 1. 创建脚本,例如XunFeiSpeechEvaluation。 2. 在脚本中定义WebSocket客户端。 3. 实现连接、发送音频数据、接收结果和关闭连接的方法。 注意:由于科大讯飞流式评测的详细协议请参考官方文档,我们这里仅实现基本流程。 代码示例(请根据实际API文档调整参数):</think>以下是使用Unity连接科大讯飞语音评测Web API的详细解决方案: ### 解决方案概述 1. **准备工作**:注册讯飞开放平台账号,创建应用获取APPID和APIKey 2. **核心流程**: - 生成鉴权签名 - 构建HTTP请求头 - 发送音频数据并处理评测结果 3. **关键点**: - 使用HMAC-SHA256生成签名 - 处理分块传输的音频数据 - 解析返回的JSON评测数据 ### 完整实现代码 ```csharp using UnityEngine; using UnityEngine.Networking; using System; using System.Collections; using System.Text; using System.Security.Cryptography; public class XunfeiSpeechEvaluator : MonoBehaviour { // 从讯飞控制台获取的配置信息 private const string APP_ID = "YOUR_APP_ID"; private const string API_KEY = "YOUR_API_KEY"; private const string HOST = "rtasr.xfyun.cn"; private const string PATH = "/v1/ws"; // 音频录制配置 private const int SAMPLE_RATE = 16000; // 16kHz采样率 private const int CHANNELS = 1; // 单声道 private AudioClip recordingClip; private bool isRecording = false; void Start() { // 初始化音频录制 Microphone.GetDeviceCaps(null, out int minFreq, out int maxFreq); Debug.Log($"Microphone supported frequencies: {minFreq}-{maxFreq}"); } public void StartRecording() { if (isRecording) return; recordingClip = Microphone.Start(null, true, 10, SAMPLE_RATE); isRecording = true; Debug.Log("Recording started..."); } public void StopRecordingAndEvaluate() { if (!isRecording) return; Microphone.End(null); isRecording = false; Debug.Log("Recording stopped. Preparing evaluation..."); // 获取录制的音频数据 float[] samples = new float[recordingClip.samples * recordingClip.channels]; recordingClip.GetData(samples, 0); // 转换为16位PCM字节数组 byte[] pcmData = ConvertAudioClipToPCM(samples); // 发送评测请求 StartCoroutine(SendEvaluationRequest(pcmData)); } private byte[] ConvertAudioClipToPCM(float[] samples) { byte[] pcmData = new byte[samples.Length * 2]; for (int i = 0; i < samples.Length; i++) { short pcmValue = (short)(samples[i] * short.MaxValue); byte[] bytes = BitConverter.GetBytes(pcmValue); pcmData[i * 2] = bytes[0]; pcmData[i * 2 + 1] = bytes[1]; } return pcmData; } private IEnumerator SendEvaluationRequest(byte[] pcmData) { // 1. 生成鉴权签名 string date = DateTime.UtcNow.ToString("r"); string signature = GenerateSignature(API_KEY, date); // 2. 构建WebSocket连接URL string wsUrl = $"wss://{HOST}{PATH}?authorization={signature}&date={Uri.EscapeDataString(date)}&host={HOST}"; // 3. 创建WebSocket连接 using (var ws = new WebSocket(wsUrl)) { ws.Connect(); // 等待连接建立 yield return new WaitUntil(() => ws.IsConnected); // 4. 发送业务参数 string bizParams = $"{{\"common\":{{\"app_id\":\"{APP_ID}\"}},\"business\":{{\"language\":\"en_us\",\"accent\":\"mandarin\",\"domain\":\"iat\",\"sample_rate\":{SAMPLE_RATE}}}}}}"; ws.Send(bizParams); // 5. 分块发送音频数据(每6400字节一个数据帧) int chunkSize = 6400; for (int offset = 0; offset < pcmData.Length; offset += chunkSize) { int length = Math.Min(chunkSize, pcmData.Length - offset); byte[] chunk = new byte[length]; Array.Copy(pcmData, offset, chunk, 0, length); ws.Send(chunk); yield return null; // 避免阻塞主线程 } // 6. 发送结束标记 ws.Send("{\"end\": true}"); // 7. 接收评测结果 while (true) { if (ws.ReceiveQueue.Count > 0) { byte[] msg = ws.Recv(); string result = Encoding.UTF8.GetString(msg); Debug.Log($"Received evaluation: {result}"); // 解析结果(示例) var json = JsonUtility.FromJson<XunfeiResponse>(result); if (json.code == 0 && json.data != null) { Debug.Log($"Final Score: {json.data.total_score}"); Debug.Log($"Accuracy: {json.data.accuracy}"); Debug.Log($"Fluency: {json.data.fluency}"); } } yield return null; } } } private string GenerateSignature(string apiKey, string date) { // 签名格式: base64(hmac-sha256(API_KEY, "host: date: request-line")) string signatureOrigin = $"host: {HOST}\ndate: {date}\nGET {PATH} HTTP/1.1"; using (var hmac = new HMACSHA256(Encoding.ASCII.GetBytes(apiKey))) { byte[] signatureBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(signatureOrigin)); string signature = Convert.ToBase64String(signatureBytes); return Convert.ToBase64String(Encoding.ASCII.GetBytes($"api_key=\"{API_KEY}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"{signature}\"")); } } // JSON解析类 [System.Serializable] public class XunfeiResponse { public int code; public string message; public string sid; public EvaluationData data; } [System.Serializable] public class EvaluationData { public float total_score; public float accuracy; public float fluency; public float integrity; public float rhythm; } } ``` ### 代码解释 1. **音频录制与转换**: - 使用Unity的`Microphone`类录制音频 - `ConvertAudioClipToPCM`将Unity的float数组转换为16位PCM字节数组 - 符合讯飞要求的音频格式:16kHz采样率、单声道、16位深 2. **鉴权签名生成**: - `GenerateSignature`方法实现讯飞要求的HMAC-SHA256签名 - 签名包含API密钥、时间戳和请求信息 - 符合讯飞WebAPI的鉴权规范 3. **WebSocket通信**: - 建立WebSocket连接(需导入WebSocket库) - 分块发送音频数据(每6400字节一帧) - 发送结束标记`{"end": true}`表示数据传输完成 4. **结果解析**: - 使用`JsonUtility`解析返回的JSON数据 - 提取关键评测指标:总分、准确度、流畅度等 - 处理错误代码和异常情况 ### 注意事项 1. **WebSocket库选择**: - 推荐使用[Best HTTP](https://assetstore.unity.com/packages/tools/network/best-http-108109)或[WebSocketSharp-NET](https://github.com/sta/websocket-sharp)等成熟库 - 需在Unity中导入对应插件 2. **音频格式要求**: - 必须使用16kHz/16bit/PCM格式 - 建议添加噪音抑制预处理 - 最大音频长度不超过60秒 3. **错误处理**: - 处理网络连接异常 - 检查返回代码(非0表示错误) - 实现超时重试机制 4. **平台兼容性**: - Android/iOS需添加麦克风权限 - WebGL需使用浏览器兼容的WebSocket实现
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值