ffmpeg+wavesurfer.js开发一个音频切割网页版工具

场景

有时想处理一些音频文件的切割,又不想专门下载audition之类的专业软件。于是我就打开百度寻找这样的网页工具,发现大部分只显示音频的时间,不带波形图,给切割带来不便。为了解决这个问题,那就自己实现一个这样的工具吧

技术栈

前端:ElementUI,wavesurfer.js
后端:ffmpeg,php

思路

服务端安装ffmpeg,前端上传文件到服务器之后,服务端运行切割命令,返回切割好的文件给前端下载

界面如下
在这里插入图片描述

前端代码

index.html

<!DOCTYPE html>
<html>
<head>
    <title>音频切割</title>
    <script src="https://unpkg.com/wavesurfer.js"></script>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 先引入 Vue -->
    <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>

    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <style>
        #waveform {
            width: 600px;
            height: 200px;
            position: relative;
        }

        .block {
            margin-bottom: 20px;
        }
        .overlay {
            position: absolute;
            width: 600px;
            height: 200px;
            z-index: 100;
            border: 1px solid #0B102C;
            opacity: 0.3;
        }

    </style>
</head>
<body>


<div id="app">
    <!-- 音频波形展示容器 -->
    <div class="block">
        <div id="waveform">
            <canvas id="myCanvas" width="600" height="100" class="overlay"
                    style="border:1px solid #000000;">
            </canvas>

        </div>
    </div>

    <div class="block">
        <el-slider
                style="width: 600px"
                v-model="rangeValue"
                range
                show-stops
                @change="changeSlider"
                :max="1000">
        </el-slider>
    </div>

    <div class="block">
        <el-upload
                class="upload-demo"
                action="upload.php"
                :on-preview="handlePreview"
                :on-remove="handleRemove"
                :before-remove="beforeRemove"
                :on-success="uploadSuccess"
                :file-list="fileList">
            <el-button size="small" type="primary">点击上传音频文件</el-button>
            <div slot="tip" class="el-upload__tip">只能上传wav/mp3/ogg文件,且不超过100mb</div>
        </el-upload>
    </div>

    <div class="block">
        <el-button size="small" type="primary" @click="cutAudio">剪裁并导出</el-button>
    </div>

</div>

</body>
</html>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                divWidth: 600,
                overlayLeft: '50px',
                filePath: '',
                maxValue: 1000,
                audioDuration: 0,
                rangeValue: [1, 1000],
                wavesurfer: null,
                fileList: []
            };
        },
        mounted() {
            this.wavesurfer = WaveSurfer.create({
                container: '#waveform',
                waveColor: 'violet',
                progressColor: 'purple'
            });
            this.drawRect(0, 0, this.divWidth, 100)
        },
        methods: {
            drawRect(x, y, w, h) {
                var c = document.getElementById("myCanvas");
                var ctx = c.getContext("2d");
                ctx.clearRect(0, 0, c.width, c.height)
                ctx.fillStyle = "#FF0000";
                ctx.fillRect(x, y, w, 100);
            },
            cutAudio() {
                // 计算切割时间点
                this.$message.error('没有可以切割的文件');
                let duration = this.wavesurfer.getDuration();
                if(!duration){
                    return ;
                }
                this.$message('切割中,稍后导出...');
                let startTime = duration * parseFloat(this.rangeValue[0]) / this.maxValue;
                let endTime = duration * parseFloat(this.rangeValue[1]) / this.maxValue;
                $.ajax({
                    url: 'cut.php',
                    method: 'POST',
                    data: {start_time: startTime, end_time: endTime, filePath: this.filePath},
                    success: (res) => {
                        // 处理响应结果
                        let res1 = JSON.parse(res)
                        let a = document.createElement('a')
                        var filePath = res1.file_path
                        let path = filePath.lastIndexOf('/')
                        a.download = filePath.substr(path + 1)//设置下载的文件名
                        a.href = res1.file_path; // 设置图片的下载地址
                        a.click();//触发下载事件
                    },
                    error: (xhr, status, error) => {
                        // 处理错误
                        console.log('切割失败');
                        console.log(error);
                    }
                });
            },
            changeSlider(e) {

                let x = Math.ceil(this.rangeValue[0] / this.maxValue * this.divWidth)
                let tmpWidth = this.rangeValue[1] - this.rangeValue[0];
                let width = Math.ceil(tmpWidth / this.maxValue * this.divWidth)
                this.drawRect(x, 0, width, 100)

            },
            uploadSuccess(res, file, fileLis) {
                if(res.code ==500){
                    this.$message.error(res.msg);
                    this.fileList = [];
                    return;
                }
                this.wavesurfer.load(res.path);
                this.filePath = res.path
                this.audioDuration = this.wavesurfer.getDuration();
            },
            handleRemove(file, fileList) {
                console.log(file, fileList);
            },
            handlePreview(file) {
                console.log(file);
            },
            beforeRemove(file, fileList) {
                return this.$confirm(`确定移除 ${file.name}`);
            }
        }
    })


</script>


代码解析

1.音频文件的显示我用到了wavesurfer.js这个库,只要传入文件路径即可显示波形
2.截取的起点终点用到了elementUI的slider组件,
拖动选择起点终点
3.我在波形图上覆盖了一层canvas,用于绘制遮罩,用slider的onchange事件触发绘制

服务端

环境安装

安装ffmpeg
本地开发,安装windows版本的,百度即可,必须这个
添加链接描述

放到linux服务器上时,自行百度linux ffmpeg方法

php代码

upload.php

<?php

class UploadFile
{
    /**
     * 上传路径
     * @var string
     */
    public $upload_path = 'uploads';

    /**
     * 获取文件开证明
     * @param $file
     * @return mixed|string
     */
    function get_extension($file)
    {
        $info = pathinfo($file);
        return $info['extension'];
    }

    /**
     * 执行上传
     * @return false|string
     */
    function doUpload()
    {
        $file = $_FILES['file'];

        if ($_FILES["file"]["error"] > 0) {
            exit( json_encode(['code' => 500, 'msg' => $_FILES["file"]["error"]]));
        }
        $allowedMimeTypes = ['audio/mpeg', 'audio/ogg', 'audio/wav'];

        // 检查上传文件的 MIME 类型
        if (!in_array($file['type'], $allowedMimeTypes)) {
            exit( json_encode(['code' => 500, 'msg' => '文件类型错误']));
        }
        $ext = $this->get_extension($_FILES["file"]["name"]);

        if (!is_dir($this->upload_path)) {
            mkdir($this->upload_path, 777);
        }

        $path = "uploads/" . uniqid() . '.' . $ext;
        // 如果 upload 目录不存在该文件则将文件上传到 upload 目录下
        move_uploaded_file($_FILES["file"]["tmp_name"], $path);
        exit( json_encode(['code' => 200, 'path' => $path]));

    }
}


$upload = new UploadFile();
return $upload->doUpload();


一个很常见的上传类,用于接收前端传过来的文件,并返回服务端文件路径用于前端展示波形

cut.php

<?php


$start_time = isset($_POST['start_time']) ? $_POST['start_time'] : 0; // 默认为 0
$end_time = isset($_POST['end_time']) ? $_POST['end_time'] : 0; // 默认为 0
$input_file = isset($_POST['filePath']) ? $_POST['filePath'] : 0; // 默认为 0
// 定义输入和输出文件路径
$output_file = 'output/'.date('Y-m-d-H-i-s').'.wav';

// 调用 FFmpeg 进行音频切割
$ffmpeg_command = "ffmpeg -i {$input_file} -ss $start_time -to $end_time -c copy {$output_file}";
exec($ffmpeg_command);

// 返回切割后的文件路径给前端
$response = array('file_path' => $output_file);
exit(json_encode($response));

点击导出按钮时,执行ffmpeg的切割命令,并且返回切割好的文件给前端

总结

至此,一个简单的ffmpeg音频切割web版已实现,ffmpeg还有很多其他功能,音视频转格式,给视频加水印等等,可以用这个demo去改造

示例项目地址

https://gitee.com/ghjkg546/ffmpeg-audio-cut

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值