java实现wav的重采样

news2024/12/25 23:35:54

在处理一些用户上传的音频的时候,往往根据用户的设备不通,文件格式难以统一,尤其是涉及到算法模型相关的,更是令人头疼,这里提供两种思路解决这个问题。

不借助三方库

这种采用的是javax.sound.sampled下的包来实现,缺点是需要预先知道目标的采样率等信息。

工具类

import com.example.phoneme.constant.WavConstant;
import lombok.extern.slf4j.Slf4j;

import javax.sound.sampled.*;
import java.io.*;
import java.util.Arrays;

@Slf4j
public class WavUtils {

    public static byte[] toPCM(byte[] src) {
        if (src.length > 44) {
            return Arrays.copyOfRange(src, 44, src.length);
        }
        return new byte[0];
    }

    public static byte[] convertTo16kHzMono16bitPCM(byte[] audioData,int sampleRate,int sampleSizeBits,int channels,boolean signed,boolean bigEndian) {
        try{
            // 创建输入字节数组流
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(audioData);

            // 创建目标音频格式
            AudioFormat targetFormat = new AudioFormat(WavConstant.SAMPLE_RATE, WavConstant.BIT_DEPTH, WavConstant.CHANNELS, WavConstant.SIGNED, WavConstant.BIG_ENDIAN);

            // 创建目标音频输入流
            AudioInputStream audioInputStream = new AudioInputStream(byteArrayInputStream, new AudioFormat(sampleRate, sampleSizeBits, channels, signed, bigEndian), audioData.length / 4);

            // 转换音频格式
            AudioInputStream convertedAudioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream);

            // 将转换后的音频数据写入字节数组
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            AudioSystem.write(convertedAudioInputStream, AudioFileFormat.Type.WAVE, byteArrayOutputStream);

            // 将字节数组返回
            byte[] convertedAudioData = byteArrayOutputStream.toByteArray();

            // 关闭流
            audioInputStream.close();
            convertedAudioInputStream.close();
            byteArrayOutputStream.close();

            return convertedAudioData;

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static boolean checkVideo(byte[] fileBytes) {

        try (InputStream inputStream = new ByteArrayInputStream(fileBytes)) {
            // 使用AudioSystem类获取音频文件的格式
            AudioFileFormat audioFileFormat = AudioSystem.getAudioFileFormat(inputStream);
            // 判断文件格式是否为WAV
            if (audioFileFormat.getType() == AudioFileFormat.Type.WAVE) {
                // 获取WAV文件的属性信息
                AudioFormat audioFormat = audioFileFormat.getFormat();
                double sampleRate = audioFormat.getSampleRate();
                int channels = audioFormat.getChannels();
                int bitDepth = audioFormat.getSampleSizeInBits();

                log.info("上传的音频格式为:Sample Rate:{},Channels:{},Bit Depth:{}", sampleRate, channels, bitDepth);
                if (sampleRate == WavConstant.SAMPLE_RATE
                        && channels == WavConstant.CHANNELS
                        && bitDepth == WavConstant.BIT_DEPTH) {
                    log.info("校验通过");
                    return true;
                }
            } else {
                log.info("不是WAV文件");
                return false;
            }
        } catch (UnsupportedAudioFileException | IOException e) {
            log.info("不是WAV文件");
            return false;
        }
        log.info("不是WAV文件");
        return false;
    }
}

常量类

public interface WavConstant {

    float SAMPLE_RATE = 16000.0;
    int CHANNELS = 1;
    int BIT_DEPTH = 16;
    boolean SIGNED = true;
    boolean BIG_ENDIAN = false;
}

测试类

@Test
public void testTransform(){
    try (FileInputStream fis=new FileInputStream("/path/to/file")){
        byte[] buffer = new byte[1024];
            int bytesRead;
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            while ((bytesRead = fis.read(buffer)) != -1) {
                stream.write(buffer);
            }
            byte[] streamByteArray = stream.toByteArray();
            boolean b1 = checkVideo(streamByteArray);
            log.info("文件是否符合要求: {}", b1);
            byte[] bytes = WavUtils.convertTo16kHzMono16bitPCM(streamByteArray,16000,16,2,true,false);
            log.info("开始转换");
            boolean b2 = checkVideo(bytes);
            log.info("文件的字节长度是: {}", bytes.length);
            log.info("文件是否符合要求: {}", b2);
    }catch (IOException ioe){
        ioe.printStackTrace();
    }
}

运行结果

在这里插入图片描述

借助三方库

比较通用的解决方案是ffmpeg,这里提供一种方法,借助命令行实现,缺点也很明显,就是命令行对于代码来说可控程度不高,依赖环境,不好迁移,需要保存中间文件,但是优点是处理音频更灵活

工具类

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

@Slf4j
public class FfmpegUtil {

    public static void toWav(String flvPath, int sampleRate, int channel, int bitrate, String fileType, String targetFilename) {
        try {
            String command = "ffmpeg -i " + flvPath + " -vn " + " -ar " + sampleRate + " -ac " + channel + " -ab " + bitrate + "k" + " -f " + fileType + " " + targetFilename;
            Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

测试类这里就略去了

总结

这两种方法各有优略,实际中要酌情考虑
调用命令行有时候比较依赖环境,不好迁移,也可以请c++的工程师负责编成jni的形式

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

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

相关文章

数据结构之顺序表的实现(详解!附完整代码)

线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构 常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结…

十五、W5100S/W5500+RP2040树莓派Pico<TFTP Client>

文章目录 1 前言2 简介2 .1 什么是TFTP?2.2 TFTP的优点2.3 TFTP和FTP对比2.4 TFTP应用场景 3 WIZnet以太网芯片4 ARP网络设置示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1 前言 一般来说&#xff0…

金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件

文章目录 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件背景说明业务需求格式BOS配置 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件 背景说明 序列号档案是基础资料,资料里…

delphi程序启动时带参数运行的例子

这里有一个坑,就是参数会减少一个 //需要引用这个单元 uses shellapiprocedure TForm1.Button5Click(Sender: TObject); varParams: string; begin //由于第三个参数不会显示,需要额外的多补充一个参数,而且第一个参数会变成程序的运行路径P…

什么是超级托斯卡纳葡萄酒?

超级托斯卡纳葡萄酒通常被认为是在托斯卡纳用国际葡萄品种制成的葡萄酒,如赤霞珠、品丽珠或梅洛,而不是传统的托斯卡纳葡萄桑娇维塞。来自云仓酒庄品牌雷盛红酒分享这些葡萄酒可能包含一些桑娇维塞,但这通常不是混合中的主要葡萄。这些大胆的…

工程车云管家|叉车智能管家安卓主板方案

工程车云管家是一款功能强大的设备管理和调度系统,它可以实时追踪工程车或机械设备的地理位置、视频、行驶轨迹、油位油耗、工作时长和地点、以及运行状况等信息,并将这些数据通过云平台存储、分析,并发送到管理者的手机上。这使得管理者能够…

Windows安装svn命令

1、svn命令下载地址 https://www.visualsvn.com/downloads/; 2、安装svn命令 3、测试svn命令是否安装成功

Java修仙传之神奇的ES2(巧妙的查询及处理)

SDL语句查询 查询的基本语法 GET /indexName/_search {"query": {"查询类型": {"查询条件": "条件值"}} } 根据文档id查询 #查询文档 GET hotel/_doc/36934 查询所有 会弹出该索引库下所有文档// 查询所有 GET /indexName/_searc…

quickapp_快应用_快应用组件

快应用组件 web组件web页面与快应用页面通信网页接收/发送消息网页接收消息 快应用页面接收/发送消息给网页发送消息 通信前提- trustedurl web组件 作用:用于显示在线的 html 页面(可以嵌入三方页面或者某些不太重要的页面) 缺点:打开会比原生慢一点&…

centos的docker镜像下载ffmpeg的方式

ffmpeg是业界比较好用的开源的音频处理工具,当我们在实际业务中使用ffmpeg的时候,直接使用yum安装回提示找不到ffmpeg的包,遇到这种情况,可以通过以下方式来进行安装(docker环境)。 已经拥有镜像 更新源 …

激光雷达(LiDAR)技术

激光雷达 LiDAR 不久前引发热议的iPhone 12 Pro机型,配备了全新的LiDAR扫描仪,只需点击自带的Measure应用程序,便能立即测量一个人的身高。 在人工智能和自动驾驶领域,神奇的LiDAR又有着怎样的用处?随着汽车巨头们在…

Redis系列-四种部署方式-单机部署+主从模式+哨兵模式【7】

目录 Redis系列-四种部署方式-单机部署主从模式【7】redis-四种部署模式单机模式主从模式数据同步的方式全量数据同步增量数据同步 Redis哨兵模式总结缺点:哨兵模式应用sentinel.conf配置项 REF 个人主页: 【⭐️个人主页】 需要您的【💖 点赞关注】支持…

geoserver发布同一字段的多值渲染

Geoserver之同一字段的多值渲染 有时候我们需要对一个shp的某一字段值中的不同值进行区分展示,但是一般的渲染都是按照统一图层展示的,因此为了更好的效果,我们选择使用uDig等工具处理。 文章目录 Geoserver之同一字段的多值渲染前言一共是分…

【HarmonyOS】HarmonyOS Test测试用例中一些断言API的使用

【关键词】 单元测试框架、HarmonyOS Test、assertThrowError、assertFail、assertEqual 【测试代码及测试结果展示】 这里以新建API9工程自动生成的ohosTest来编写单元测试代码。 1、 测试代码: import { describe, it, expect } from ohos/hypium import abil…

本地电脑如何连接使用腾讯云服务器

如何连接使用腾讯云服务器 在自己的电脑上,单击 ,在搜索中,输入 mstsc,按 Enter,打开远程桌面连接对话框。如下图所示: 在“计算机”后面,输入 服务器的公网 IP,就是上节大图左侧…

十三、W5100S/W5500+RP2040树莓派Pico<FTP Server>

文章目录 1. 前言2. 相关简介2.1 简述2.2 原理2.3 优点2.4 应用 3. WIZnet以太网芯片4. FTP Server运行测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 在当今的信息化时代,互联网已经成为人们生活、工作不可…

7天入门python系列之第二天python 基础语法

第2天主要是学习Python的基础知识 编者打算开一个python 初学主题的系列文章,用于指导想要学习python的同学。关于文章有任何疑问都可以私信作者。对于初学者想在7天内入门Python,这是一个紧凑的学习计划。但并不是不可完成的。第二天开始python 基础知…

查分小程序,教学大作用

数字化时代,技术已经深深的改变了我们的生活和工作方式。当然,教育领域也不例外。如果你是一位老师,你可能会想知道如何利用这些技术工具来提高学生的学习体验和成绩。今天,我们就来聊聊如何用各种代码、Excel等工具,打…

Redis系列-Redis集群模式【8】

目录 Redis系列-Redis集群模式【8】特性Redis的数据分区虚拟槽分区Redis虚拟槽分区的特点客户端如何定位目标节点? 故障转移故障检测故障转移 Redis一致性保证Redis集群的功能限制部署命令REF 个人主页: 【⭐️个人主页】 需要您的【💖 点赞关注】支持 &…

LeetCode148.排序链表

看完题目的想法是,直接把所有节点的值都遍历出来放进优先队列里面,然后从头节点遍历一次,每次把优先队列poll()的值赋给节点的val即可,说实话,想完还觉得估计有问题怎么可能这么简单,但是不管了&#xff0c…