java 使用 jtransforms 傅里叶库通过时域转频域实现wav 音频的加速与变慢效果

复制代码
引入 maven 依赖:
XML 复制代码
 <dependency>
      <groupId>net.sourceforge.jtransforms</groupId>
      <artifactId>jtransforms</artifactId>
      <version>2.4.0</version>
 </dependency>
复制代码
FourierTransformTest.java:
java 复制代码
import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;

import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;

public class FourierTransformTest {
    public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
        fastTest();
        // slowTest();
    }

    public static void  fastTest() throws UnsupportedAudioFileException, IOException {
        double[] audio_src = AudioUtils.wavToDoubleArray( new File("C:\\E\\素材\\音频\\wav\\audio.wav") );

        // 将 audio_src 进行 时域转频域
        new DoubleFFT_1D(audio_src.length).realForward(audio_src);

        // 截取转换成频域以后得 audio_src 的前半部分 作为 audio_target
        // 这里拷贝频域模式下的 audio_src 的前半部分,因为是频域模式,所以前半部分和后半部分其实都包含 "完整" 的时域信息( 只是失真严重 ),如果是时域模式下直接砍一半,肯定会丢失一半的时域信息( 但是未丢失的时域信息的音质没有任何丢失 )
        // 所以频域下的double[]砍一半其实是音质砍一半( 即频率信息丢失一半 )
        // 这里 除以2实现了2x加速效果
        double[] audio_target = Arrays.copyOfRange(audio_src, 0, audio_src.length / 2);

        // 将 audio_target 进行频域转时域
        new DoubleFFT_1D( audio_target.length ).realInverse( audio_target, true );
        System.out.println("转换完毕");

        // 将audio_target 时域 double[] 转换成 wav 文件
        AudioUtils.doubleArrayToWAV( audio_target,new File( "C:\\E\\素材\\音频\\wav\\audio_target.wav" ) );
        System.out.println("生成完毕");
    }

    public static void slowTest() throws UnsupportedAudioFileException, IOException {
        double[] audio_src = AudioUtils.wavToDoubleArray( new File("C:\\E\\素材\\音频\\wav\\audio.wav") );

        // 将 audio_src 进行 时域转频域
        new DoubleFFT_1D(audio_src.length).realForward(audio_src);

        // 实现了 0.5X 变速效果
        double[] audio_zero = buildZeroDoubleValueArray( audio_src.length );
        // 将转换成频域以后的 audio_src 和一个等长度的 全是0的doublep[]合并( 可以把这个 audio_zero看做一个频域格式的 double[],只是各个频率下的振幅全是0)
        double[] audio_target = combineArray( audio_src,audio_zero );

        // 将 audio_target 频域转时域
        new DoubleFFT_1D( audio_target.length ).realInverse( audio_target, true );
        System.out.println("转换完毕");

        // 将时域格式下的 audio_target 转成 wav 文件
        AudioUtils.doubleArrayToWAV( audio_target,new File( "C:\\E\\素材\\音频\\wav\\audio_target.wav" ) );
        System.out.println("生成完毕");
    }

    private static double[] buildZeroDoubleValueArray(int length) {
        double[] doubleArray = new double[length];
        for (int i = 0; i < length; i++) {
            doubleArray[i]=0d;
        }
        return doubleArray;
    }

    private static double[] combineArray(double[]... arrays) {
        int arrayCount = arrays.length;
        int totalLength = 0;
        for( int i=0;i<arrayCount;i++ ){
            totalLength += arrays[ i ].length;
        }
        double[] array_combine = new double[ totalLength ];
        int index = 0;
        for( int arrayNum=0;arrayNum<arrayCount;arrayNum++ ){
            double[] array = arrays[arrayNum];
            for (double v:array){
                array_combine[index] = v;
                index++;
            }
        }
        return array_combine;
    }
}

用到的工具类 AudioUtils.java:

java 复制代码
import javax.sound.sampled.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;


public class AudioUtils {
    public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
        File file = new File("C:\\E\\素材\\音频\\wav\\一生所爱.wav");
        double[] doubleArray = wavToDoubleArray(file);
        System.out.println( doubleArray.length );

        int partLength = doubleArray.length / 10;


        doubleArrayToWAV(  Arrays.copyOfRange(doubleArray, 0, partLength),new File( "C:\\E\\素材\\音频\\wav\\一生所爱_part1.wav" ) );
        doubleArrayToWAV(  Arrays.copyOfRange(doubleArray, partLength, partLength * 2),new File( "C:\\E\\素材\\音频\\wav\\一生所爱_part2.wav" ) );
        doubleArrayToWAV(  Arrays.copyOfRange(doubleArray, partLength * 2, partLength * 3),new File( "C:\\E\\素材\\音频\\wav\\一生所爱_part3.wav" ) );
        doubleArrayToWAV(  Arrays.copyOfRange(doubleArray, partLength * 3, partLength * 4),new File( "C:\\E\\素材\\音频\\wav\\一生所爱_part4.wav" ) );
        doubleArrayToWAV(  Arrays.copyOfRange(doubleArray, partLength * 4, partLength * 5),new File( "C:\\E\\素材\\音频\\wav\\一生所爱_part5.wav" ) );
        doubleArrayToWAV(  Arrays.copyOfRange(doubleArray, partLength * 5, partLength * 6),new File( "C:\\E\\素材\\音频\\wav\\一生所爱_part6.wav" ) );
    }

    public static double[] wavToDoubleArray(File mp3File) throws UnsupportedAudioFileException, IOException {
        AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(mp3File);
        AudioFormat audioFormat = audioInputStream.getFormat();
        int numChannels = audioFormat.getChannels();
        int sampleSizeInBytes = audioFormat.getSampleSizeInBits() / 8;
        int frameSize = numChannels * sampleSizeInBytes;
        int bufferSize = (int) (audioInputStream.getFrameLength() * frameSize);
        byte[] audioBytes = new byte[bufferSize];
        audioInputStream.read(audioBytes);

        double[] audioData = new double[audioBytes.length / 2];
        for (int i = 0, j = 0; i < audioBytes.length; i += 2, j++) {
            int sample = (audioBytes[i + 1] << 8) | (audioBytes[i] & 0xFF);
            audioData[j] = sample / 32768.0;
        }

        return audioData;
    }

    public static void doubleArrayToWAV(double[] audioData, File outputFile) throws IOException, UnsupportedAudioFileException {
        AudioFormat audioFormat = new AudioFormat(44100, 16, 2, true, false);
        byte[] audioBytes = new byte[audioData.length * 2];
        for (int i = 0, j = 0; i < audioData.length; i++, j += 2) {
            short sample = (short) (audioData[i] * 32767);
            audioBytes[j] = (byte) (sample & 0xFF);
            audioBytes[j + 1] = (byte) ((sample >> 8) & 0xFF);
        }

        AudioInputStream audioInputStream = new AudioInputStream(new ByteArrayInputStream(audioBytes), audioFormat, audioData.length);
        AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, outputFile);
    }
}
相关推荐
程序定小飞9 小时前
基于springboot的学院班级回忆录的设计与实现
java·vue.js·spring boot·后端·spring
AI科技星9 小时前
张祥前统一场论动量公式P=m(C-V)误解解答
开发语言·数据结构·人工智能·经验分享·python·线性代数·算法
海琴烟Sunshine9 小时前
leetcode 345. 反转字符串中的元音字母 python
python·算法·leetcode
攀小黑9 小时前
基于若依-内容管理动态修改,通过路由字典配置动态管理
java·vue.js·spring boot·前端框架·ruoyi
geobuilding10 小时前
将大规模shp白模贴图转3dtiles倾斜摄影,并可单体化拾取建筑
算法·3d·智慧城市·数据可视化·贴图
jghhh0110 小时前
基于高斯伪谱法的弹道优化方法及轨迹仿真计算
算法
青云交10 小时前
Java 大视界 -- 基于 Java 的大数据可视化在城市空气质量监测与污染溯源中的应用
java·spark·lstm·可视化·java 大数据·空气质量监测·污染溯源
开开心心就好10 小时前
微软官方出品:免费数据恢复工具推荐
网络·笔记·microsoft·pdf·word·音视频·symfony
森语林溪10 小时前
大数据环境搭建从零开始(十七):JDK 17 安装与配置完整指南
java·大数据·开发语言·centos·vmware·软件需求·虚拟机
懷淰メ11 小时前
python3GUI--短视频社交软件 By:Django+PyQt5(前后端分离项目)
后端·python·django·音视频·pyqt·抖音·前后端