失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > java实现变声器--变声萝莉

java实现变声器--变声萝莉

时间:2019-11-22 20:35:02

相关推荐

java实现变声器--变声萝莉

编写java变声器需要做的前期准备

安装 ffmgeg 下载地址Releases · BtbN/FFmpeg-Builds · GitHub

win系统下载ffmpeg-N-103272-g7bba0dd638-win64-gpl.zip

配置环境变量到 bin目录

新建maven程序加入两个引用

<!-- /artifact/com.github.st-h/TarsosDSP --><dependency><groupId>com.github.st-h</groupId><artifactId>TarsosDSP</artifactId><version>2.4.1</version></dependency><dependency><groupId>ws.schild</groupId><artifactId>jave-core</artifactId><version>2.4.6</version></dependency>

新建两个基础类

AudioOutputToByteArray

import be.tarsos.dsp.AudioEvent;import be.tarsos.dsp.AudioProcessor;import java.io.ByteArrayOutputStream;public class AudioOutputToByteArray implements AudioProcessor {private boolean isDone = false;private byte[] out = null;private ByteArrayOutputStream bos;public AudioOutputToByteArray() {bos = new ByteArrayOutputStream();}public ByteArrayOutputStream getBos() {return bos;}public byte[] getData() {while (!isDone && out == null) {try {Thread.sleep(10);} catch (InterruptedException ignored) {}}return out;}@Overridepublic boolean process(AudioEvent audioEvent) {bos.write(audioEvent.getByteBuffer(),0,audioEvent.getByteBuffer().length);return true;}@Overridepublic void processingFinished() {out = bos.toByteArray().clone();bos = null;isDone = true;}}

WaveHeader

import java.io.ByteArrayOutputStream;import java.io.IOException;public class WaveHeader {public final char fileID[] = {'R', 'I', 'F', 'F'};public int fileLength;public char wavTag[] = {'W', 'A', 'V', 'E'};;public char FmtHdrID[] = {'f', 'm', 't', ' '};public int FmtHdrLeth;public short FormatTag;public short Channels;public int SamplesPerSec;public int AvgBytesPerSec;public short BlockAlign;public short BitsPerSample;public char DataHdrID[] = {'d','a','t','a'};public int DataHdrLeth;public byte[] getHeader() throws IOException {ByteArrayOutputStream bos = new ByteArrayOutputStream();WriteChar(bos, fileID);WriteInt(bos, fileLength);WriteChar(bos, wavTag);WriteChar(bos, FmtHdrID);WriteInt(bos,FmtHdrLeth);WriteShort(bos,FormatTag);WriteShort(bos,Channels);WriteInt(bos,SamplesPerSec);WriteInt(bos,AvgBytesPerSec);WriteShort(bos,BlockAlign);WriteShort(bos,BitsPerSample);WriteChar(bos,DataHdrID);WriteInt(bos,DataHdrLeth);bos.flush();byte[] r = bos.toByteArray();bos.close();return r;}private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {byte[] mybyte = new byte[2];mybyte[1] =(byte)( (s << 16) >> 24 );mybyte[0] =(byte)( (s << 24) >> 24 );bos.write(mybyte);}private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {byte[] buf = new byte[4];buf[3] =(byte)( n >> 24 );buf[2] =(byte)( (n << 8) >> 24 );buf[1] =(byte)( (n << 16) >> 24 );buf[0] =(byte)( (n << 24) >> 24 );bos.write(buf);}private void WriteChar(ByteArrayOutputStream bos, char[] id) {for (int i=0; i<id.length; i++) {char c = id[i];bos.write(c);}}}

现在新建执行类和方法,先在d盘放一个1.mp3的文件。以下代码就是把1.mp3转换成变声后的2.mp3

byte[] pcmBytes = speechPitchShiftMp3("d://1.mp3", 0.73, 0.73); 后面的0.73就是变声参数。最后会给出各种变声参数

import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import javax.sound.sampled.UnsupportedAudioFileException;import be.tarsos.dsp.AudioDispatcher;import be.tarsos.dsp.WaveformSimilarityBasedOverlapAdd;import be.tarsos.dsp.effects.DelayEffect;import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;import be.tarsos.dsp.resample.RateTransposer;public class ceshi {public static void main(String[] args) throws Exception {//这里返回的是pcm格式的音频byte[] pcmBytes = speechPitchShiftMp3("d://1.mp3", 0.73, 0.73);//如果需要转成wav则需要给pcmBytes增加一个头部信息//TarsosDSP中也有输出Wav格式音频的处理器,这里没有使用。byte[] wavHeader = pcm2wav(pcmBytes);OutputStream wavOutPut = new FileOutputStream("d://2.mp3");wavOutPut.write(wavHeader);wavOutPut.write(pcmBytes);wavOutPut.flush();wavOutPut.close();// 对于各种声音类型,以及所需添加的处理器,还有处理器参数代码,将在本文最后给出。//如果需要转mp3格式的,也可以给我留言,我会加上。}/*** 变声* @param speedFactor 变速率 (0,2) 大于1为加快语速,小于1为放慢语速* @param rateFactor 音调变化率 (0,2) 大于1为降低音调(深沉),小于1为提升音调(尖锐)* @return 变声后的MP3数据输入流*/public static byte[] speechPitchShiftMp3(String fileUrl, double rateFactor, double speedFactor) throws IOException, UnsupportedAudioFileException {WaveformSimilarityBasedOverlapAdd w = new WaveformSimilarityBasedOverlapAdd(WaveformSimilarityBasedOverlapAdd.Parameters.speechDefaults(rateFactor, 16000));int inputBufferSize = w.getInputBufferSize();int overlap = w.getOverlap();AudioDispatcher dispatcher = AudioDispatcherFactory.fromPipe(fileUrl,16000,inputBufferSize,overlap);w.setDispatcher(dispatcher);dispatcher.addAudioProcessor(w);/** 采样率转换器。 使用插值更改采样率, 与时间拉伸器一起可用于音高转换。 **/dispatcher.addAudioProcessor(new RateTransposer(speedFactor));AudioOutputToByteArray out = new AudioOutputToByteArray();/** 声音速率转换器 -- 失败 **//*SoundTouchRateTransposer soundTouchRateTransposer = new SoundTouchRateTransposer(2);soundTouchRateTransposer.setDispatcher(dispatcher);dispatcher.addAudioProcessor(soundTouchRateTransposer);*//** 正弦波发生器 -- 无反应 **//*SineGenerator sineGenerator = new SineGenerator(0.5, 0.5);dispatcher.addAudioProcessor(sineGenerator);*//** 音调转换器 -- 无效果 **/// dispatcher.addAudioProcessor(new PitchShifter(0.1,16000,448,overlap));/** 制粒机使用颗粒合成回放样本。方法可用于控制播放速率,音高,颗粒大小, -- 无效果 **/// dispatcher.addAudioProcessor(new OptimizedGranulator(16000, 448));/** 噪音产生器 -- 有效果 **/// dispatcher.addAudioProcessor(new NoiseGenerator(0.2 ));/** 增益处理器 增益为1,则无任何反应。 增益大于1表示音量增加a -- 有反应 **/// dispatcher.addAudioProcessor(new GainProcessor(10));/**镶边效果 -- 有反应 **/// dispatcher.addAudioProcessor(new FlangerEffect(64, 0.3, 16000, 16000));// 回声效果// dispatcher.addAudioProcessor(new FlangerEffect(1 << 4, 0.8, 8000, 2000));// 感冒// dispatcher.addAudioProcessor(new ZeroCrossingRateProcessor());//感冒/** 淡出 --声音慢慢变小 **/// dispatcher.addAudioProcessor(new FadeOut(5));/** 淡入-- 声音慢慢变大 **/// dispatcher.addAudioProcessor(new FadeIn(5));/** 在信号上添加回声效果。echoLength以秒为单位 elay回声的衰减,介于0到1之间的值。1表示无衰减,0表示立即衰减 **/dispatcher.addAudioProcessor(new DelayEffect(0.2, 0.24, 12000) );/** 调幅噪声 -- 将声音转换为噪声**/// dispatcher.addAudioProcessor(new AmplitudeModulatedNoise());/** 振幅LFO -- 声音波动 **/// dispatcher.addAudioProcessor(new AmplitudeLFO());dispatcher.addAudioProcessor(out);dispatcher.run();// return new ByteArrayInputStream(out.getData());return out.getData();}public static byte[] pcm2wav(byte[] bytes) throws IOException {//填入参数,比特率等等。这里用的是16位单声道 8000 hzWaveHeader header = new WaveHeader();//长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)header.fileLength = bytes.length + (44 - 8);header.FmtHdrLeth = 16;header.BitsPerSample = 16;header.Channels = 1;header.FormatTag = 0x0001;header.SamplesPerSec = 16000;header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;header.DataHdrLeth = bytes.length;byte[] h = header.getHeader();assert h.length == 44; //WAV标准,头部应该是44字节return h;}}

各种参数类

LUOLI(0.6, 0.6, "萝莉", 1, dispatcher -> {})

byte[] pcmBytes = speechPitchShiftMp3("d://1.mp3", 0.73, 0.73);改为

byte[] pcmBytes = speechPitchShiftMp3("d://1.mp3", 0.6, 0.6);就可以了

import be.tarsos.dsp.AudioDispatcher;import be.tarsos.dsp.WaveformSimilarityBasedOverlapAdd;import be.tarsos.dsp.ZeroCrossingRateProcessor;import be.tarsos.dsp.effects.DelayEffect;import be.tarsos.dsp.io.jvm.AudioDispatcherFactory;import be.tarsos.dsp.resample.RateTransposer;import java.io.File;import java.io.IOException;import java.util.Optional;import java.util.function.Consumer;public enum SoundEnum {LUOLI(0.6, 0.6, "萝莉", 1, dispatcher -> {}),DASHU(1.2, 1.2, "大叔", 2, dispatcher -> {}),FEIZAI(1.5, 1.5, "肥仔", 3, dispatcher -> {}),GAOGUAI(1.5, 0.8, "搞怪", 4, dispatcher -> {}),XIONGHAIZI(0.73, 0.73, "熊孩子", 5, dispatcher -> {}),MANTUNTUN(0.35,1, "慢吞吞",6 , dispatcher -> {}),WANGHONGNV(1.2,0.7, "网红女",7 , dispatcher -> {}),/*** dispatcher.addAudioProcessor(new DelayEffect(0.2, 0.24, 12000) );*/KUNSHOU(1.55,1.55, "困兽", 8, dispatcher -> dispatcher.addAudioProcessor(new DelayEffect(0.2, 0.24, 12000))),/*** dispatcher.addAudioProcessor(new DelayEffect(0.2, 0.24, 12000) );*/ZHONGJIXIE(1.50,1.50, "重机械", 9, dispatcher -> dispatcher.addAudioProcessor(new DelayEffect(0.2, 0.24, 12000))),/***dispatcher.addAudioProcessor(new FlangerEffect(1 << 4, 0.8, 8000, 2000));* dispatcher.addAudioProcessor(new ZeroCrossingRateProcessor());*/GANMAO(1.05,1.05, "感冒", 10, dispatcher -> {dispatcher.addAudioProcessor(new DelayEffect(0.2, 0.24, 12000));dispatcher.addAudioProcessor(new ZeroCrossingRateProcessor());}),/***dispatcher.addAudioProcessor(new DelayEffect(0.8, 0.5, 12000) );* dispatcher.addAudioProcessor(new DelayEffect(0.5, 0.3, 8000) );*/KONGLING(1, 1, "空灵", 11, dispatcher -> {dispatcher.addAudioProcessor(new DelayEffect(0.8, 0.5, 12000) );dispatcher.addAudioProcessor(new DelayEffect(0.5, 0.3, 8000) );});/*** @param speedFactor 变速率 (0,2) 大于1为加快语速,小于1为放慢语速* @param rateFactor 音调变化率 (0,2) 大于1为降低音调(深沉),小于1为提升音调(尖锐)*/SoundEnum(double rateFactor, double speedFactor, String name, int type, Consumer<AudioDispatcher> consumer){this.rateFactor = rateFactor;this.speedFactor = speedFactor;this.name = name;this.type = type;this.consumer = consumer;}private double rateFactor;private double speedFactor;private String name;private int type;private Consumer consumer;public byte[] run(String fileUrl){WaveformSimilarityBasedOverlapAdd w = new WaveformSimilarityBasedOverlapAdd(WaveformSimilarityBasedOverlapAdd.Parameters.speechDefaults(rateFactor, 16000));int inputBufferSize = w.getInputBufferSize();int overlap = w.getOverlap();AudioDispatcher dispatcher = AudioDispatcherFactory.fromPipe(fileUrl,16000,inputBufferSize,overlap);w.setDispatcher(dispatcher);dispatcher.addAudioProcessor(w);/** 采样率转换器。 使用插值更改采样率, 与时间拉伸器一起可用于音高转换。 **/dispatcher.addAudioProcessor(new RateTransposer(speedFactor));AudioOutputToByteArray out = new AudioOutputToByteArray();consumer.accept(dispatcher);dispatcher.addAudioProcessor(out);dispatcher.run();return out.getData();}public static byte[] pcm2wav(byte[] bytes) {try {//填入参数,比特率等等。这里用的是16位单声道 8000 hzWaveHeader header = new WaveHeader();//长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)header.fileLength = bytes.length + (44 - 8);header.FmtHdrLeth = 16;header.BitsPerSample = 16;header.Channels = 1;header.FormatTag = 0x0001;header.SamplesPerSec = 16000;header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;header.DataHdrLeth = bytes.length;byte[] h = header.getHeader();assert h.length == 44; //WAV标准,头部应该是44字节return h;} catch (IOException e) {//log.error("pcm2wav-error", e);}return null;}public static Optional<SoundEnum> getInstance(int type){for (int i = 0; i < SoundEnum.values().length; i++) {if(SoundEnum.values()[i].type == type)return Optional.of(SoundEnum.values()[i]);}return Optional.empty();}}

如果觉得《java实现变声器--变声萝莉》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。