Java實(shí)現(xiàn)生成自定義時(shí)長(zhǎng)的靜音音頻
Maven依賴
<dependency>
<groupId>org</groupId>
<artifactId>jaudiotagger</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
代碼
以16k采樣率,單聲道,16bits采樣分辨率為例,具體參數(shù)原理,下面有說(shuō)明。
import com.google.common.primitives.Bytes;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
/** @Author huyi @Date 2021/9/30 15:33 @Description: 靜音音頻工具類 */
public class SilenceAudioUtils {
? /**
? ?* 根據(jù)PCM文件構(gòu)建wav的header字段
? ?*
? ?* @param srate Sample rate - 8000, 16000, etc.
? ?* @param channel Number of channels - Mono = 1, Stereo = 2, etc..
? ?* @param format Number of bits per sample (16 here)
? ?* @throws IOException
? ?*/
? public static byte[] buildWavHeader(int dataLength, int srate, int channel, int format)
? ? ? throws IOException {
? ? byte[] header = new byte[44];
? ? long totalDataLen = dataLength + 36;
? ? long bitrate = srate * channel * format;
? ? header[0] = 'R';
? ? header[1] = 'I';
? ? header[2] = 'F';
? ? header[3] = 'F';
? ? header[4] = (byte) (totalDataLen & 0xff);
? ? header[5] = (byte) ((totalDataLen >> 8) & 0xff);
? ? header[6] = (byte) ((totalDataLen >> 16) & 0xff);
? ? header[7] = (byte) ((totalDataLen >> 24) & 0xff);
? ? header[8] = 'W';
? ? header[9] = 'A';
? ? header[10] = 'V';
? ? header[11] = 'E';
? ? header[12] = 'f';
? ? header[13] = 'm';
? ? header[14] = 't';
? ? header[15] = ' ';
? ? header[16] = (byte) format;
? ? header[17] = 0;
? ? header[18] = 0;
? ? header[19] = 0;
? ? header[20] = 1;
? ? header[21] = 0;
? ? header[22] = (byte) channel;
? ? header[23] = 0;
? ? header[24] = (byte) (srate & 0xff);
? ? header[25] = (byte) ((srate >> 8) & 0xff);
? ? header[26] = (byte) ((srate >> 16) & 0xff);
? ? header[27] = (byte) ((srate >> 24) & 0xff);
? ? header[28] = (byte) ((bitrate / 8) & 0xff);
? ? header[29] = (byte) (((bitrate / 8) >> 8) & 0xff);
? ? header[30] = (byte) (((bitrate / 8) >> 16) & 0xff);
? ? header[31] = (byte) (((bitrate / 8) >> 24) & 0xff);
? ? header[32] = (byte) ((channel * format) / 8);
? ? header[33] = 0;
? ? header[34] = 16;
? ? header[35] = 0;
? ? header[36] = 'd';
? ? header[37] = 'a';
? ? header[38] = 't';
? ? header[39] = 'a';
? ? header[40] = (byte) (dataLength & 0xff);
? ? header[41] = (byte) ((dataLength >> 8) & 0xff);
? ? header[42] = (byte) ((dataLength >> 16) & 0xff);
? ? header[43] = (byte) ((dataLength >> 24) & 0xff);
? ? return header;
? }
? /**
? ?* 默認(rèn)寫入的pcm數(shù)據(jù)是16000采樣率,16bit,可以按照需要修改
? ?*
? ?* @param filePath
? ?* @param pcmData
? ?*/
? public static boolean writeToFile(String filePath, byte[] pcmData) {
? ? BufferedOutputStream bos = null;
? ? try {
? ? ? bos = new BufferedOutputStream(new FileOutputStream(filePath));
? ? ? byte[] header = buildWavHeader(pcmData.length, 16000, 1, 16);
? ? ? bos.write(header, 0, 44);
? ? ? bos.write(pcmData);
? ? ? bos.close();
? ? ? return true;
? ? } catch (Exception e) {
? ? ? e.printStackTrace();
? ? } finally {
? ? ? if (bos != null) {
? ? ? ? try {
? ? ? ? ? bos.close();
? ? ? ? } catch (IOException e) {
? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? }
? ? }
? ? return false;
? }
? /**
? ?* 生成靜音音頻
? ?*
? ?* @param filePath 輸出文件地址
? ?* @param duration 音頻時(shí)長(zhǎng)
? ?*/
? public static void makeSilenceWav(String filePath, Long duration) {
? ? List<Byte> oldBytes = new ArrayList<>();
? ? IntStream.range(0, (int) (duration * 32)).forEach(x -> oldBytes.add((byte) 0));
? ? writeToFile(filePath, Bytes.toArray(oldBytes));
? }
? public static void main(String[] args) {
? ? makeSilenceWav("E:/csdn/1.wav", 5000L);
? }運(yùn)行結(jié)果:
使用ffmpeg查看

參數(shù)說(shuō)明和使用方法
1、構(gòu)造原理
構(gòu)造一個(gè)wav主要分為兩個(gè)部分,頭文件和數(shù)據(jù)文件。那么只需要構(gòu)造一個(gè)全是靜音的音頻數(shù)據(jù)包,在加上頭就可以構(gòu)造出一個(gè)靜音文件。
所以代碼的主要邏輯就是構(gòu)造數(shù)據(jù)包->封裝文件頭。
2、怎么1毫秒的靜音包如何構(gòu)建呢?
這里首先要知道的是,音頻采樣率、采樣分辨率和聲道的概念。
這里分享一個(gè)簡(jiǎn)單的說(shuō)明鏈接: 詳解RIFF和WAVE音頻文件格式
而靜音就是每個(gè)采樣到的音頻包里面的內(nèi)容都是由(byte)0構(gòu)成的。
3、那么每毫秒的音頻包對(duì)應(yīng)多少個(gè)(byte)0呢?
這里有個(gè)公式:采樣率 x 聲道數(shù) x 采樣分辨率 / 8
參考鏈接: http://soundfile.sapp.org/doc/WaveFormat/

舉個(gè)例子:如果你要生成32k采樣率、雙聲道、16bits的靜音,每毫秒需要構(gòu)建幾個(gè)比特0呢?
按照公式: result = 32000 x 2 x 16 / 8000 = 128 (為什么是8000,因?yàn)槲覀兯愕氖呛撩?,原公式單位為?
那么就可以把代碼中的
IntStream.range(0, (int) (duration * 32)).forEach(x -> oldBytes.add((byte) 0));
修改為:
IntStream.range(0, (int) (duration * 128)).forEach(x -> oldBytes.add((byte) 0));
同時(shí)需要把頭文件的格式也調(diào)整一下:
byte[] header = buildWavHeader(pcmData.length, 16000, 1, 16);
修改為:
byte[] header = buildWavHeader(pcmData.length, 32000, 2, 16);
總結(jié)
當(dāng)然生成靜音的方法有很多,比如使用sox在ubuntu上一行命令就行。不過(guò)如果需要在工程化項(xiàng)目中,對(duì)原始音頻做靜音拼接組裝,那么你看懂了我上面的邏輯,就應(yīng)該知道如何實(shí)現(xiàn)了吧。只要讀取音頻中的數(shù)據(jù)包,然后往后面添加需要靜音時(shí)長(zhǎng)的靜音數(shù)據(jù)包,重新封裝頭,就可以得到了。
這里附上ubuntu上sox生成靜音的命令供大家參考.
sox -n -r 16000 -b 16 -c 1 -L silence.wav trim 0.0 5.000
以上就是Java實(shí)現(xiàn)生成自定義時(shí)長(zhǎng)的靜音音頻的詳細(xì)內(nèi)容,更多關(guān)于Java自定義靜音音頻的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java設(shè)計(jì)模式——工廠設(shè)計(jì)模式詳解
這篇文章主要介紹了Java設(shè)計(jì)模式——工廠設(shè)計(jì)模式詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
Java?將PDF轉(zhuǎn)為HTML時(shí)保存到流的方法和步驟
本文介紹如何通過(guò)Java后端程序代碼將PDF文件轉(zhuǎn)為HTML,并將轉(zhuǎn)換后的HTML文件保存到流,下面是實(shí)現(xiàn)轉(zhuǎn)換的方法和步驟,感興趣的朋友一起看看吧2022-01-01
SpringMVC中的DispatcherServlet初始化流程詳解
這篇文章主要介紹了SpringMVC中的DispatcherServlet初始化流程詳解,DispatcherServlet這個(gè)前端控制器是一個(gè)Servlet,所以生命周期和普通的Servlet是差不多的,在一個(gè)Servlet初始化的時(shí)候都會(huì)調(diào)用該Servlet的init()方法,需要的朋友可以參考下2023-12-12
Java使用OpenFeign管理多個(gè)第三方服務(wù)調(diào)用
最近開(kāi)發(fā)了一個(gè)統(tǒng)一調(diào)度類的項(xiàng)目,需要依賴多個(gè)第三方服務(wù),這些服務(wù)都提供了HTTP接口供我調(diào)用。感興趣的可以了解一下2021-06-06
Java由淺入深細(xì)數(shù)數(shù)組的操作下
數(shù)組對(duì)于每一門編程語(yǔ)言來(lái)說(shuō)都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語(yǔ)言對(duì)數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java?語(yǔ)言中提供的數(shù)組是用來(lái)存儲(chǔ)固定大小的同類型元素2022-04-04
java字符串比較獲取字符串出現(xiàn)次數(shù)的示例
java獲取一個(gè)字符串在整個(gè)字符串出現(xiàn)的次數(shù),下面寫出我的思路和二個(gè)實(shí)現(xiàn)方法,大家參考使用吧2014-01-01
Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程
Hibernate可以將Java中幾個(gè)內(nèi)置的集合結(jié)構(gòu)映射為數(shù)據(jù)庫(kù)使用的關(guān)系模型,下面我們就來(lái)看一下Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程:2016-07-07
SpringBoot項(xiàng)目中JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的使用詳解
JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理都是SpringBoot中實(shí)現(xiàn)AOP的重要技術(shù),JDK動(dòng)態(tài)代理通過(guò)反射生成代理類,適用于目標(biāo)類實(shí)現(xiàn)了接口的場(chǎng)景,性能較好,易用性高,但必須實(shí)現(xiàn)接口且不能代理final方法,CGLIB動(dòng)態(tài)代理通過(guò)生成子類實(shí)現(xiàn)代理2025-03-03
詳解使用MyBatis Generator自動(dòng)創(chuàng)建代碼
這篇文章主要介紹了使用MyBatis Generator自動(dòng)創(chuàng)建代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12

