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>

猜你喜欢

转载自www.cnblogs.com/virgoart/p/10569401.html