【h5】实现语音转文字

news2025/2/27 23:10:21

【h5】实现语音转文字

  • 一、需求
    • 功能概述是:
  • 二、实现过程
    • 1、实现按住录音,松开发送。有两个录音按钮。
      • a. 获取用户的麦克风声音和创建一个MediaRecorder对象:
      • b. 启动和停止录音:
      • c. 将音频数据上传到服务器,并在处理完后返回结果并在前端展示:
    • 2. 上移到特定区域取消。
      • a. 获取要监听区域的坐标范围:
      • b. 在touchmove事件中判断用户手指的位置是否在指定区域内:
      • c. 在被判断为取消区域的情况下,需要为用户提供取消的提示信息。最好的做法是记录用户的取消动作并复原已有界面。例如:
    • 3. 略微上移是ASR和展示为文字。
      • a. 监听用户手指移动事件。通过touchstart、touchmove、touchend事件获取用户移动手指的位置。
      • b. 监听录音停止事件,获取录音的Blob数据,并将其转化为Base64格式。
      • c. 将Base64格式的语音数据上传到服务器进行处理,并在处理完成后将结果展示在前端。
      • d. 在前端页面上展示语音识别结果。
    • 4、查看历史消息。
      • a. 在LocalStorage中保存历史消息:
      • b. 在前端页面上显示历史消息列表:
      • c. 在前端页面上展示历史记录并且提供删除功能。(代码在b步骤中已经给出了)。
  • 三、延伸:前端H5实现调用麦克风,录音功能
    • 下面是一个简单的实现流程:
      • 1. 首先,我们需要请求用户授权使用麦克风。调用Navigator.mediaDevices.getUserMedia()方法即可。在授权成功后,可以通过回调函数来获取到用户的音频输入流。
      • 2. 通过获取到的音频输入流,我们可以创建一个MediaRecorder对象,用于进行录音操作。MediaRecorder对象支持多种不同的配置选项,可以根据实际需要进行设置。
      • 3. 开始录音:通过调用MediaRecorder对象的start()方法来开始录音。同时通过start()方法的参数可以设置录音的时长限制。
      • 4. 停止录音:调用MediaRecorder对象的stop()方法停止录音。在停止录音之后,可以通过ondataavailable事件来获取录音的数据,这里的数据是一个Blob类型的对象。

一、需求

在这里插入图片描述

功能概述是:

  1. 按住录音,松开发送。有两个录音按钮。
  2. 上移到特定区域取消。
  3. 略微上移是asr和展示为文字。
  4. 查看历史消息。

需求是:可以h5开发,即:可以网页版,也打包为apk的安卓包

二、实现过程

1、实现按住录音,松开发送。有两个录音按钮。

a. 获取用户的麦克风声音和创建一个MediaRecorder对象:

const audioBuffer = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
const recorder = new MediaRecorder(audioBuffer);

b. 启动和停止录音:

const startRecordButton = document.getElementById('start-record');
const stopRecordButton = document.getElementById('stop-record');

function handleStartRecording() {
  recorder.start();
}

function handleStopRecording() {
  recorder.stop();
  recorder.ondataavailable = (e) => {
    const audioBlob = e.data;
    const reader = new FileReader();
    reader.readAsDataURL(audioBlob);
    reader.onloadend = () => {
      const audioBase64 = reader.result.split(',')[1];
      // 将录制好的音频数据上传到服务器进行处理,并在处理完成后将结果展示在前端
      postAudioDataToServer(audioBase64);
    }
  }
}

startRecordButton.addEventListener('touchstart', handleStartRecording);
stopRecordButton.addEventListener('touchend', handleStopRecording);

c. 将音频数据上传到服务器,并在处理完后返回结果并在前端展示:

async function postAudioDataToServer(audioBase64) {
  const response = await fetch(SERVER_URL, {
    headers: {
      'Content-Type': 'application/json'
    },
    method: 'POST',
    body: JSON.stringify({ audio: audioBase64 })
  });
  const result = await response.json();
  // 在前端页面上展示处理后的语音识别结果
  showAsrResult(result.result);
}

2. 上移到特定区域取消。

a. 获取要监听区域的坐标范围:

const cancelArea = document.getElementById('cancel-area');
const cancelAreaRect = cancelArea.getBoundingClientRect();

b. 在touchmove事件中判断用户手指的位置是否在指定区域内:

const recordedArea = document.getElementById('recorded-area');

function handleTouchMove(e) {
  const touchY = e.touches[0].clientY;
  if (touchY < cancelAreaRect.top) {
    // 用户的手指已经滑出了取消所在的区域
    recordedArea.classList.remove('cancel');
  } else {
    // 用户的手指仍在取消所在区域内
    recordedArea.classList.add('cancel');
  }
}

document.addEventListener('touchmove', handleTouchMove);

c. 在被判断为取消区域的情况下,需要为用户提供取消的提示信息。最好的做法是记录用户的取消动作并复原已有界面。例如:

function handleTouchEnd(e) {
  if (recordedArea.classList.contains('cancel')) {
    // 用户已经划出了取消区域
    recorder.stop();
    recorder.ondataavailable = null;
    recorder = null;

    // 给用户展示取消信息
    alert('录音已取消');
  } else {
    // 用户完成了一次完整的录音,并且已经成功上传到服务器
    alert('录音上传成功');
  }
}

document.addEventListener('touchend', handleTouchEnd);

3. 略微上移是ASR和展示为文字。

a. 监听用户手指移动事件。通过touchstart、touchmove、touchend事件获取用户移动手指的位置。

let startY = 0;
let currentY = 0;

function handleTouchStart(e) {
  startY = e.touches[0].clientY;
}

function handleTouchMove(e) {
  currentY = e.touches[0].clientY;
}

function handleTouchEnd(e) {
  const deltaY = startY - currentY;
  if (deltaY > MIN_SLIDE_DISTANCE) {
    // 触发ASR操作
    shouldAsr = true;
  }
}

document.addEventListener('touchstart', handleTouchStart, { passive: true });
document.addEventListener('touchmove', handleTouchMove, { passive: true });
document.addEventListener('touchend', handleTouchEnd, { passive: true });

b. 监听录音停止事件,获取录音的Blob数据,并将其转化为Base64格式。

recorder.ondataavailable = (e) => {
  const audioBlob = e.data;
  const reader = new FileReader();
  reader.readAsDataURL(audioBlob);
  reader.onloadend = () => {
    const audioBase64 = reader.result.split(',')[1];
    // 将录制好的音频数据上传到服务器进行处理,并在处理完成后将结果展示在前端
    postAudioDataToServer(audioBase64);
  }
}

c. 将Base64格式的语音数据上传到服务器进行处理,并在处理完成后将结果展示在前端。

async function postAudioDataToServer(audioBase64) {
  const response = await fetch(SERVER_URL, {
    headers: {
      'Content-Type': 'application/json'
    },
    method: 'POST',
    body: JSON.stringify({ audio: audioBase64 })
  });
  const result = await response.json();
  // 在前端页面上展示语音识别结果
  showAsrResult(result.result);
}

d. 在前端页面上展示语音识别结果。

function showAsrResult(result) {
  const asrResultArea = document.getElementById('asr-result');
  const p = document.createElement('p');
  p.innerText = result;
  asrResultArea.appendChild(p);
}

4、查看历史消息。

a. 在LocalStorage中保存历史消息:

// 保存历史消息列表
let historyList = JSON.parse(localStorage.getItem('historyList')) || [];

// 在按钮点击事件中将当前的录音数据添加到LocalStorage中
function handleStopRecording() {
  // ...
  reader.onloadend = () => {
    const audioBase64 = reader.result.split(',')[1];
    // 将录制好的音频数据上传到服务器进行处理,并在处理完成后将结果展示在前端
    postAudioDataToServer(audioBase64);

    // 将音频数据添加到历史记录中
    historyList.push({ audio: audioBase64 });
    localStorage.setItem('historyList', JSON.stringify(historyList));
  }
}

b. 在前端页面上显示历史消息列表:

// 渲染历史记录列表在页面上
function renderHistoryList() {
  const historyList = JSON.parse(localStorage.getItem('historyList')) || [];
  const historyListArea = document.getElementById('history-list');
  historyListArea.innerHTML = '';
  if (historyList.length > 0) {
    historyList.forEach((item, idx) => {
      const li = document.createElement('li');
      const deleteBtn = document.createElement('button');
      deleteBtn.innerText = 'Delete';

      // 点击删除按钮,从LocalStorage中删除对应的历史记录
      deleteBtn.addEventListener('click', () => {
        historyList.splice(idx, 1);
        localStorage.setItem('historyList', JSON.stringify(historyList));
        renderHistoryList();
      });

      li.appendChild(deleteBtn);
      historyListArea.appendChild(li);
    });
  } else {
    historyListArea.innerHTML = '没有历史记录';
  }
}

// 初始化页面时渲染历史记录
renderHistoryList();

c. 在前端页面上展示历史记录并且提供删除功能。(代码在b步骤中已经给出了)。

三、延伸:前端H5实现调用麦克风,录音功能

我们可以通过Web Audio API以及Navigator.mediaDevices.getUserMedia()方法来实现。

下面是一个简单的实现流程:

1. 首先,我们需要请求用户授权使用麦克风。调用Navigator.mediaDevices.getUserMedia()方法即可。在授权成功后,可以通过回调函数来获取到用户的音频输入流。

navigator.mediaDevices.getUserMedia({ audio: true, video: false })
 .then(stream => {
   console.log('获取到麦克风输入流,可以进行录音操作');
 })
 .catch(error => {
   console.log('获取麦克风输入流失败', error);
 });

2. 通过获取到的音频输入流,我们可以创建一个MediaRecorder对象,用于进行录音操作。MediaRecorder对象支持多种不同的配置选项,可以根据实际需要进行设置。

let recorder = null;

navigator.mediaDevices.getUserMedia({ audio: true, video: false })
 .then(stream => {
   recorder = new MediaRecorder(stream, {
     mimeType: 'audio/webm;codecs=opus',
     audioBitsPerSecond: 128000,
     videoBitsPerSecond: null,
   });

   console.log('获取到麦克风输入流,可以进行录音操作');
 })
 .catch(error => {
   console.log('获取麦克风输入流失败', error);
 });

3. 开始录音:通过调用MediaRecorder对象的start()方法来开始录音。同时通过start()方法的参数可以设置录音的时长限制。

// 开始录音
recorder.start();

4. 停止录音:调用MediaRecorder对象的stop()方法停止录音。在停止录音之后,可以通过ondataavailable事件来获取录音的数据,这里的数据是一个Blob类型的对象。

// 停止录音
recorder.stop();

// 获取录音数据
recorder.ondataavailable = (e) => {
  const audioBlob = e.data;
  // 这里可以上传录音数据等操作
};

在获取到录音数据之后,我们就可以进行 上传 等其他操作。上传数据可以使用XMLHttpRequestfetch等常用的浏览器API进行处理。

需要注意的是,由于不同的浏览器可能存在兼容性问题,所以在开发过程中需要进行兼容性测试
同时,为了避免产生大量无用的录音数据占用用户空间,可以在录音数据被上传至服务器之后尽早地删除掉该数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/581819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】指针 - 定义和使用,所占内存空间,空指针,野指针,const修饰指针,指针和数组,指针和函数

文章目录 1. 定义和使用2. 所占内存空间3. 空指针4. 野指针5. const修饰指针6. 指针和数组7. 指针和函数 1. 定义和使用 数据类型 * 变量名; 指针的作用是&#xff0c;可以通过指针间接访问内存。 内存编号是从 0 开始记录的&#xff0c;一般用十六进制数字表示。可以利用指…

nodejs+vue社区母婴幼儿用品商城系统

本系统实现了管理员对用户、商品信息、交流论坛、订单信息的管理&#xff0c;是为了满足用户更深层次的需求。除了上述优势外&#xff0c;本系统还具有&#xff1a;查询迅速&#xff0c;搜索资料方便&#xff0c;可靠性强等等在如今这个高速发展的时代&#xff0c;效率决定着你…

十款开源测试开发工具推荐(自动化、性能、混沌测试、造数据、流量复制)

在本篇文章中&#xff0c;我将给大家推荐 10 款日常工作中经常用到的测试开发工具神器&#xff0c;涵盖了自动化测试、性能压测、流量复制、混沌测试、造数据等。 1、AutoMeter-API 自动化测试平台 AutoMeter 是一款针对分布式服务&#xff0c;微服务 API 做功能和性能一体化…

可靠可用性基本知识

可靠可用性基本知识 1. 基本概念1.1 可靠性1.2 可用性 2. 可靠和可用性指标3. 可靠性工程实践相关概念4. FEMA相关知识4.1 基本概念4.2 FEMA分析流程 5. 产品开发流程中可靠可用性测试如何开展5.1 测试可靠可用性输入\输出5.2 可靠可用性开展流程5.3 测试设计5.4 测试执行5.4.1…

chatgpt赋能python:Numpy读音:是“num-pie”还是“num-pee”?

Numpy读音&#xff1a;是“num-pie”还是“num-pee”&#xff1f; 你是否曾经在想&#xff0c;“numpy”这个词怎么念&#xff1f;很多人都有不同的看法。有些人说“num-pie”&#xff0c;而另一些人则说“num-pee”。那么&#xff0c;谁是正确的呢&#xff1f;在这篇文章中&a…

ad18学习笔记三:关于测量点对点

如何测量&#xff1f; 方法有很多种&#xff0c;比如 1、 点击 ‘放置’–》‘尺寸’–》‘线性尺寸’ 2、 快捷工具栏 3、 快捷键 AD如何使用测量命令&#xff1f;-凡亿课堂 AD中的三种测量距离的方式 清除测量标线&#xff1f; 这个简单&#xff0c;在显示测量结果的…

【论文阅读】GNN在推荐系统中的应用

【论文阅读】GNN在推荐系统中的应用 参考Graph Neural Networks for Recommender Systems: Challenges, Methods, and Directions 文章目录 【论文阅读】GNN在推荐系统中的应用1、本文结构2、推荐系统的目的&#xff0c;发展和基于GNN模型的挑战3、推荐系统相应背景&#xff0…

GPT-4 插件和插件化的思考

一、前言 最近 ChatGPT 的 Plus 用户在 GPT-4 中新增了插件功能&#xff0c; GPT 在插件的加持下如虎添翼。 那么我常用的插件是哪些&#xff1f;插件化是什么&#xff1f;插件化有什么好处&#xff1f;插件化和我们日常开发中哪些设计模式思想一致&#xff1f;GPT 的插件还存…

竟然还可以这样计算圆周率π?你被惊艳到吗(50)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 什么是圆周率π&#xff1f; 如何用蒙特卡洛法来计算圆周率&#xff1f; 圆周率π 圆周率用希腊字母π&#xf…

TatukGIS Developer Kernel 11.78 for .NETCore Crack

Tatuk GIS Developer Kernel for .NET 是一个变体&#xff0c;它是受控代码和 .NET GIS SDK&#xff0c;用于为用户 Windows 操作系统创建 GIS 专业软件的过程。它被认为是一个完全用于 Win Forms 的 .NET CIL&#xff0c;WPF 的框架是为 C# 以及 VB.NET、VC、oxygen 以及最终与…

chatgpt赋能python:Python个人数据合并:简单优雅地整合您的个人数据

Python个人数据合并&#xff1a;简单优雅地整合您的个人数据 在信息时代&#xff0c;我们收集了大量的个人数据&#xff0c;包括社交媒体、电子邮件、日历事件和其他各种来源。但是&#xff0c;如何以整洁的方式将这些数据整合到同一地方&#xff1f;Python提供了一种简单而优…

chatgpt赋能python:介绍:Python中的jieba.cut

介绍&#xff1a;Python 中的 jieba.cut Jieba 是一个用于中文分词的 Python 库&#xff0c;被广泛应用于自然语言处理、文本分析等领域。其中的 jieba.cut 方法是该库的核心功能之一&#xff0c;对于各类中文文本的分词操作起到至关重要的作用。本文将从以下四个方面对 jieba…

springboot--请求

1. 请求 在本章节呢&#xff0c;我们主要讲解&#xff0c;如何接收页面传递过来的请求数据。 1.1 Postman 当下最为主流的开发模式&#xff1a;前后端分离 在这种模式下&#xff0c;前端技术人员基于"接口文档"&#xff0c;开发前端程序&#xff1b;后端技术人员也…

1.3. 数据类型与变量

数据类型 在Java中&#xff0c;数据类型决定着一个数据的取值范围和操作。Java中的数据类型主要分为两类&#xff1a;基本数据类型和引用数据类型。 基本数据类型 Java中的基本数据类型包括整型、浮点型、字符型和布尔型。 整型&#xff1a;byte、short、int、long。对应的…

【嵌入式烧录/刷写文件】-3.3-Bin文件转换为S19/Hex文件

案例背景(共8页精讲)&#xff1a; 该篇将告诉您&#xff1a;如何使用Vector HexView工具&#xff0c;j将一个bin文件转换为Intel Hex或Motorola S-record(S19/SREC/mot/SX)文件。 目录 1 Intel Hex&#xff0c;Motorola S-record(S19/SREC/mot/SX)&#xff0c;Bin文件之间的…

Flutter 笔记 | Flutter 自定义组件

Flutter 自定义组件的几种方式 当Flutter提供的现有组件无法满足我们的需求&#xff0c;或者我们为了共享代码需要封装一些通用组件&#xff0c;这时我们就需要自定义组件。在Flutter中自定义组件有三种方式&#xff1a;通过组合其他组件、自绘和实现RenderObject。 1. 组合多…

RT1170如何在SRAM/SDRAM运行程序

一般Flash为non-XIP时&#xff0c;我们需要在RAM上运行程序。还有一种情况&#xff0c;就是我们不想每次调试都要将程序写入Flash&#xff0c;然后由BootROM进行代码的拷贝和跳转&#xff0c;这样可以减少Flash的烧写次数。本篇文章就来讨论一下如何实现这两种情形的RAM代码运行…

总结882

每周小结&#xff1a; 暴力英语&#xff1a;一边背单词&#xff0c;一边背文章&#xff0c;背了两篇文章 高等数学&#xff1a;进行了二重积分和矩阵第二讲专题的纠错&#xff0c;刷了微分方程上的相关题目。 每日必复习&#xff08;5分钟&#xff09; 就复习了昨天的一道题…

chatgpt赋能python:Python中的NaN:了解使用方法

Python中的NaN: 了解使用方法 在Python中&#xff0c;NaN代表“Not a Number”&#xff0c;它是一种特殊的数据类型&#xff0c;用于表示一些无法表示为数字的值。 在本文中&#xff0c;我们将深入探讨Python中的NaN以及如何在代码中使用它。 什么是NaN? NaN通常用于表示不…

chatgpt赋能python:Python中的num模块和使用方式

Python中的num模块和使用方式 Python是一种高级的编程语言&#xff0c;它被广泛地应用于各种领域&#xff0c;例如Web开发、数据处理、人工智能等等。在Python中&#xff0c;为了进行各种数字计算&#xff0c;我们会使用num模块。本文将介绍Python中num模块和使用方式&#xf…