Web系统中Mic设备的应用实例
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>欢迎转载,转载请注明出处-VirgoArt,www.cnblogs.com
感谢xiangyuecn同学在GitHub上提供的音频操作组件,点击传送!
一、客户端使用音频设备
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html>
<head>
<title>HTML5采集麦克风音频</title>
<meta charset="UTF-8">
<meta name="content-type" content="text/html; charset=UTF-8">
<script src="http://static.runoob.com/assets/jquery-validation-1.14.0/lib/jquery.js"></script>
<script type="text/javascript" src="recorder-core.js"></script>
<script type="text/javascript" src="wav.js"></script>
<script type="text/javascript">
var index = 1; //录音后行的索引(table) function hasGetUserMedia() {
return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
} if (!hasGetUserMedia()) {
alert('您的浏览器不支持录音');
}
// 录音配置
set = {
type: "wav" //输出类型:mp3,wav等,使用一个类型前需要先引入对应的编码引擎
, bitRate: 16 //比特率 wav(位):16、8,MP3(单位kbps):8kbps时文件大小1k/s,16kbps 2k/s,录音文件很小
, sampleRate: 16000 //采样率,wav格式文件大小=sampleRate*时间;mp3此项对低比特率文件大小有影响,高比特率几乎无影响。wav任意值,mp3取值范围:48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000
, bufferSize: 4096 //AudioContext缓冲大小。会影响onProcess调用速度,相对于AudioContext.sampleRate=48000时,4096接近12帧/s
}
var rec = Recorder(set); function start() {
//打开麦克风授权获得相关资源
rec.open(function () {
rec.start();//开始录音 setTimeout(function () {
rec.stop(function (blob, duration) {//到达指定条件停止录音,拿到blob对象想干嘛就干嘛:立即播放、上传
addInfo(blob);
rec.close();//释放录音资源
}, function (msg) {
alert.log("录音失败:" + msg);
});
}, 1000 * $("#audiotime").val()); }
, function (msg) {//未授权或不支持
alert.log("无法录音:" + msg);
}
);
} var autioList = []; function addInfo(blob) {
var table = $("#table");
var ts = Date.parse(new Date());
var dom = "<tr>";
dom += "<td>" + index + "</td>";
dom += "<td>" + ts + ".wav</td>";
dom += "<td>" + "<video height='40px' width='300px' controls = 'controls' name = 'video' src = '" + URL.createObjectURL(blob) + "'></video>" + "</td>";
dom += "<td>" + "<a download='audio' href='" + URL.createObjectURL(blob) + "'>下载</a>" + "</td>";
dom += "<td>" + "<button onclick='upload(this," + index + ")'>上传</button>" + "</td>";
dom += "<td>未上传</td>";
dom += "</tr>";
table.append(dom); autioList.push({filename: ts + ".wav", blob: blob});
index++;
} function upload(my, index) {
var formData = new FormData();
formData.append("audioData", autioList[index - 1].blob, autioList[index - 1].filename);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/upload");
xhr.send(formData);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
//若响应完成且请求成功
my.parentElement.nextElementSibling.textContent = xhr.responseText;
}
}
} function recognition() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/recognition");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
//若响应完成且请求成功
alert(xhr.responseText);
}
}
}
</script>
</head>
<body>
Web音频采集 <br>
<button id="start" onclick="start()"> 点击录音</button>
<span>录音时长,默认为三秒</span><input type="text" value="3" id="audiotime">
<button id="confirm" onclick="recognition()">确认</button>
<hr/>
<table id="table">
<tr>
<td>序号</td>
<td>音频文件</td>
<td>播放</td>
<td>下载</td>
<td>上传</td>
<td>状态</td>
</tr>
</table>
</body>
</html>
其中,项目需要引用xiangyuecn/Recorder工程中的recorder-core.js、wav.js(个人测试只用到Wav格式,如有其他需求,参见工程ReadMe)。
二、后端数据通信
package cn.virgo.audio.controller; import cn.virgo.audio.utils.RemoteShellExecutor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List; @Controller
public class IndexController { @Value("${system.audiofile.path}")
private String AUDIO_FILES_PATH;
@Value("${system.ffmpeg.path}")
private String AUDIO_FFMPEG_PATH;
@Value("${system.ffmpeg.splittime}")
private String AUDIO_SPLIT_TIME; /**
* 首页跳转
*
* @param request
* @return
*/
@RequestMapping(path = {"/index"}, method = RequestMethod.GET)
public ModelAndView defaultPage1(HttpServletRequest request) throws IOException {
ModelAndView modelAndView = new ModelAndView("/mic");
return modelAndView;
}
/**
* 音频文件上传
*
* @param file
* @return
*/
@RequestMapping(path = {"/upload"}, method = RequestMethod.POST)
@ResponseBody
public String upload1(@RequestParam("audioData") MultipartFile file) {
if (file.isEmpty()) {
return "Error";
}
try {
Files.createDirectories(Paths.get(AUDIO_FILES_PATH));
byte[] bytes = file.getBytes();
Path path = Paths.get(AUDIO_FILES_PATH + file.getOriginalFilename());
Files.write(path, bytes);
} catch (IOException e) {
e.printStackTrace();
}
return "Success";
}
/**
* 音频文件上传
*
* @param file
* @return
*/
@RequestMapping(path = {"/upload2"}, method = RequestMethod.POST)
@ResponseBody
public String upload2(@RequestParam("audioData") MultipartFile file) {
if (file.isEmpty()) {
return "Error";
}
try {
Files.createDirectories(Paths.get(AUDIO_FILES_PATH));
byte[] bytes = file.getBytes();
Path path = Paths.get(AUDIO_FILES_PATH + file.getOriginalFilename());
Files.write(path, bytes);
File srcFile = path.toFile();
//TODO:上传完成后,调用FFMPEG将音频分片,并删除源文件
run_exe(AUDIO_FFMPEG_PATH + "ffmpeg.exe -i " + AUDIO_FILES_PATH + file.getOriginalFilename() + " -f segment -segment_time " + AUDIO_SPLIT_TIME + " -c copy " + AUDIO_FILES_PATH + "out%03d.wav");
srcFile.delete();
} catch (IOException e) {
e.printStackTrace();
}
return "Success";
}
/**
* 准备就绪
*
* @return
*/
@RequestMapping(path = {"/recognition"}, method = RequestMethod.GET)
@ResponseBody
public List<String> recognition1() {
//TODO:return null;
}
/**
* 获取到项目路径
*
* @return
*/
public static String getRootPath() {
return ClassUtils.getDefaultClassLoader().getResource("").getPath();
}
/**
* 调用EXE
*
* @param cmd
*/
public static void run_exe(String cmd) {
System.out.println("-------------Cmd info-------------");
System.out.println("CMD:" + cmd);
String s1;
String s2;
StringBuilder sb1 = new StringBuilder();
sb1.append("ProcessInfo:");
StringBuilder sb2 = new StringBuilder();
sb2.append("ErrorInfo:");
Process proc = null;
try {
// 使用绝对路径
proc = Runtime.getRuntime().exec(cmd);
InputStream pInfo = proc.getInputStream();
BufferedReader bufferedReader1 = new BufferedReader(new InputStreamReader(pInfo));
while ((s1 = bufferedReader1.readLine()) != null) {
sb1.append(s1);
}
bufferedReader1.close();
System.out.println(sb1.toString());
InputStream pErr = proc.getErrorStream();
BufferedReader bufferedReader2 = new BufferedReader(new InputStreamReader(pErr));
while ((s2 = bufferedReader2.readLine()) != null) {
sb2.append(s2);
}
bufferedReader2.close();
System.out.println(sb2.toString());
System.out.println("ExitCode:" + proc.waitFor());
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
其中,项目中upload1对上传的音频不做二次加工,upload2对上传的音频使用FFMPEG进行二次加工。
三、备注
项目实例基于SpringBoot项目,需引用pom为:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>cn.study</groupId>
<artifactId>audio</artifactId>
<version>1.0-SNAPSHOT</version> <name>audio</name>
<description>Demo project for audio</description> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/>
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties> <dependencies>
<!--Spring Web 相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除默认的日志框架-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> <!--第三方工具-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!--log4j2 日志框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.audio.App</mainClass>
<layout>JAR</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Web系统中Mic设备的应用实例的更多相关文章
- Linux系统中存储设备的两种表示方法
转:https://blog.csdn.net/holybin/article/details/38637381 一.对于IDE接口的硬盘的两种表示方法: 1.IDE接口硬盘,对于整块硬盘的两种表示方 ...
- VLC在web系统中应用(x-vlc-plugin 即如何把VLC嵌入HTML中)第一篇
VLC毫无疑问是优秀的一款播放软件,子B/S机构的web项目中,如果能把它嵌入页面,做页面预览或者其他,是非常棒的. 第一步:下载VLC安装程序:(推荐1.0.3或者是1.0.5版本,比较稳定) ht ...
- 使用SetupDI* API列举系统中的设备
原文链接地址:https://blog.csdn.net/clteng/article/details/801012?utm_source=blogxgwz8 在Windows系统中提供一组有用的函数 ...
- 如何在施工物料管理Web系统中处理大量数据并显示
最近在开发施工物料管理系统,其中涉及大量的物料信息需要管理和汇总,数据量非常庞大.之前尝试自己通过将原始数据,加工处理建模,在后台代码中通过分组.转置再显示到 Web 页面中,但自己编写的代码量非常大 ...
- B/S(WEB)系统中使用Activex插件调用扫描仪实现连续扫描并上传图像(IE文件扫描并自动上传)
IE浏览器下使用Activex插件调用客户端扫描仪扫描文件并山传,可以将纸质档案(如合同.文件.资料等)扫描并将扫描图像保存到服务器,可以用于合同管理.档案管理等. 通过插件方式调用扫描仪扫描并获取图 ...
- JAVA WEB项目中生成验证码及验证实例(附源码及目录结构)
[我是一个初学者,自己总结和网上搜索资料,代码是自己敲了一遍,亲测有效,现将所有的目录结构和代码贴出来分享给像我一样的初学者] 作用 验证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计 ...
- web开发中不同设备浏览器的区分
通常区分不同设备浏览器是用JavaScript中的navigator.userAgent.toLowerCase()方式获取浏览器的userAgent信息 //使用javascript判断是否是iPh ...
- java web系统中时间比sql server中的日期少2天的解决办法
系统环境 jdk:1.7 数据库:sql server 2008 问题描述 升级1.7之后查询出来的日期就比数据库中的少2天,降回1.6版本的jdk就正常了. 问题原因及解决办法 国内网站有很多不靠谱 ...
- 重新指派usb转串口模块在linux系统中的设备调用名称
How to remap /dev/ttyUSB* to a specific name to be called by my program. How to map /dev/ttyUSB* to ...
随机推荐
- pwn-ROP(2)
通过int80系统只对静态编译有效,动态编译需要用其他方法 本题提供了一个地址输入端,输入函数地址会返回该函数的实际地址,我们用得到的实际地址-偏移地址=基地址,然后用基地址+任意函数的偏移地址就可以 ...
- 我眼中的Adaboost
步骤: def buildStump(dataArr,classLabels,D): 1.循环取出数据集中的一个特征(一列)输入 (for:) 2.循环调整阀值threshVal (for:) 3, ...
- 八.django模型系统(二)之常用查询及表关系的实现
Ⅰ.常用查询 1.几个概念 每一个django模型类,都有一个默认的管理器,objects,查询就是依赖于objects管理器进行的(在创建时就被添加了). QuerySet表示数据库中对象的列表( ...
- blaze介绍
sklearn实战-乳腺癌细胞数据挖掘 https://study.163.com/course/introduction.htm?courseId=1005269003&utm_campai ...
- oracle not in 改为 not exist
修改前 SELECT pageID, permissionID FROM tableA WHERE userID=#{userID} AND projectCode=#{projectCode} AN ...
- HDU 2717(* bfs)
题意是在一个数轴上,每次可以一步到达当前位置数值的 2 倍的位置或者数值 +1 或数值 -1 的位置,给定 n 和 k,问从数值为 n 的位置最少多少步可以到达数值为 k 的位置. 用广搜的方法,把已 ...
- 定时调度系列之Quartz.Net详解
一. 背景 我们在日常开发中,可能你会遇到这样的需求:"每个月的3号给用户发信息,提醒用户XXX "."每天的0点需要统计前一天的考勤记录"."每个月 ...
- js 正则表达式,分组,非捕获或 环视的使用
定位一个字符串中,匹配与定位重复字符中的最后一个字符: 例子: <script type="text/javascript"> var str="http:/ ...
- AtCoder Grand Contest 030题解
第一次套刷AtCoder 体验良好 传送门 Poisonous Cookies cout<<b+min(c,a+b+); Tree Burning 难度跨度有点大啊 可以证明当第一次转向之 ...
- Python+Selenium+Unittest+HTMLTestRunner生成测试报告+发送至邮箱,记一次完整的cnblog登录测试示例,
测试思路:单个测试集.单个测试汇成多个测试集.运行测试集.生成测试报告.发送至邮箱. 第一步:建立单个测试集,以cnblog登录为例. 测试用例: cnblog的登录测试,简单分下面几种情况:(1)用 ...