Java技术问询:能否将音频存入字节数组?如何存储傅里叶变换Y值?
解决思路与代码示例
Got it, let's clear up the core issue first: 直接将音频文件的原始字节存入数组是完全错误的——这些字节是音频的编码数据(比如WAV的文件头+PCM采样,或是MP3的压缩码流),和傅里叶变换后的频域Y值完全不是一回事。我们需要先提取音频的采样数据,做FFT得到频域值,再把这些值编码成字节数组,同时避开javax包。
步骤拆解
读取音频采样数据(无javax依赖)
如果你处理的是WAV格式(最容易手动解析),可以直接读取文件并解析WAV头,提取出PCM采样数据。WAV头固定44字节,之后的内容就是原始采样(比如16位单声道的话,每2字节对应一个采样值)。执行傅里叶变换(FFT)
实现一个纯Java的FFT算法(不需要任何外部依赖),将时域的采样数据转换成频域的复数数组,你要的Y值通常指的是频域的幅度值(sqrt(实部² + 虚部²))。将FFT结果编码为字节数组
根据你的需求选择编码方式:比如把每个幅度值(浮点数)转成4字节的二进制形式,或是将其缩放为整数后转成对应字节数,存入字节数组。
代码示例
1. 手动解析WAV获取采样数据
import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class AudioFFTProcessor { // 解析WAV文件,获取16位单声道采样数据 public static short[] readWavSamples(String filePath) throws IOException { try (FileInputStream fis = new FileInputStream(filePath)) { // 跳过44字节的WAV头 fis.skip(44); byte[] buffer = new byte[fis.available()]; fis.read(buffer); // 将字节数组转成16位短整数数组(小端序,WAV标准) short[] samples = new short[buffer.length / 2]; ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples); return samples; } }
2. 纯Java实现FFT(简化版)
// 简化版FFT实现(仅处理2的幂次长度的采样) public static void fft(Complex[] x) { int n = x.length; if (n == 1) return; // 分治 Complex[] even = new Complex[n/2]; Complex[] odd = new Complex[n/2]; for (int i = 0; 2*i < n; i++) { even[i] = x[2*i]; odd[i] = x[2*i+1]; } fft(even); fft(odd); // 合并 for (int k = 0; 2*k < n; k++) { Complex t = Complex.exp(-2 * Math.PI * k / n).multiply(odd[k]); x[k] = even[k].add(t); x[k + n/2] = even[k].subtract(t); } } // 复数类 public static class Complex { private final double re; private final double im; public Complex(double re, double im) { this.re = re; this.im = im; } public Complex add(Complex b) { return new Complex(re + b.re, im + b.im); } public Complex subtract(Complex b) { return new Complex(re - b.re, im - b.im); } public Complex multiply(Complex b) { return new Complex(re*b.re - im*b.im, re*b.im + im*b.re); } public static Complex exp(double theta) { return new Complex(Math.cos(theta), Math.sin(theta)); } public double magnitude() { return Math.sqrt(re*re + im*im); } }
3. 核心getBytes()函数:将FFT幅度值转成字节数组
// 将FFT后的幅度值存入字节数组(这里把每个幅度值转成4字节float) public static byte[] getBytes(short[] samples) { // 确保采样长度是2的幂次(FFT要求) int fftSize = 1; while (fftSize < samples.length) fftSize *= 2; Complex[] fftInput = new Complex[fftSize]; // 填充采样数据,补零到2的幂次长度 for (int i = 0; i < samples.length; i++) { fftInput[i] = new Complex(samples[i], 0); } for (int i = samples.length; i < fftSize; i++) { fftInput[i] = new Complex(0, 0); } // 执行FFT fft(fftInput); // 提取幅度值并转成字节数组 ByteBuffer buffer = ByteBuffer.allocate(fftSize * 4); // 每个float占4字节 for (Complex c : fftInput) { buffer.putFloat((float) c.magnitude()); } return buffer.array(); }
4. 测试用main函数
public static void main(String[] args) throws IOException { short[] samples = readWavSamples("your_audio.wav"); byte[] fftBytes = getBytes(samples); // 打印前10个幅度值的浮点形式(方便验证) ByteBuffer buffer = ByteBuffer.wrap(fftBytes); for (int i = 0; i < 10; i++) { System.out.println(buffer.getFloat()); } } }
关键说明
- 如果你处理的是MP3等压缩格式,手动解析会很复杂,这时可以考虑用无javax依赖的轻量级库,但上面的示例针对WAV格式完全不需要额外依赖。
- 你之前打印的"大型整数数组"是原始音频字节的十进制值,和FFT结果无关,现在的代码会输出频域幅度值的字节编码后的对应数值。
内容的提问来源于stack exchange,提问作者UndercoverCoder
相关产品推荐
相关产品推荐

