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)文章
1秒鐘實(shí)現(xiàn)Springboot?替換/寫入?word文檔里面的文字、圖片功能
這篇文章主要介紹了Springboot?替換/寫入?word文檔里面的文字、圖片,1秒鐘實(shí)現(xiàn),本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12spring通過(guò)filter,Interceptor統(tǒng)一處理ResponseBody的返回值操作
這篇文章主要介紹了spring通過(guò)filter,Interceptor統(tǒng)一處理ResponseBody的返回值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09對(duì)ArrayList和LinkedList底層實(shí)現(xiàn)原理詳解
今天小編就為大家分享一篇對(duì)ArrayList和LinkedList底層實(shí)現(xiàn)原理詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10SpringBoot使用Jwt處理跨域認(rèn)證問(wèn)題的教程詳解
這篇文章主要介紹了SpringBoot使用Jwt處理跨域認(rèn)證問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06