当前位置: 首页 > 知识库问答 >
问题:

字节数组的傅里叶变换

韩嘉胜
2023-03-14

我不太精通Java,所以请保持简单。不过,我会尽力理解你发布的所有内容。这是我的问题。

我已经编写了代码来记录来自外部麦克风的音频并将其存储在. wave中。存储此文件与存档目的相关。我需要做的是对存储的音频进行FFT。

我的方法是将wav文件作为字节数组加载并对其进行转换,问题是1。我需要摆脱一个标题,但我应该能够做到这一点和2。我得到了一个字节数组,但是我在网上找到的大多数(如果不是全部)FFT算法,并试图用复杂/两个双精度数组修补到我的项目工作中。

我试图解决这两个问题,最终能够将我的FFT阵列绘制成一个图形,当我发现它只是给我返回“0”s。虽然.wav文件很好,但我可以毫无问题地播放它。我认为也许将字节转换为双字节对我来说是个问题,所以我的方法是这样的(我知道这不太好)

byte ByteArray[] = Files.readAllBytes(wav_path);
String s = new String(ByteArray);
double[] DoubleArray = toDouble(ByteArray);
// build 2^n array, fill up with zeroes
boolean exp = false;
int i = 0;
int pow = 0;
while (!exp) {
    pow = (int) Math.pow(2, i);
    if (pow > ByteArray.length) {
        exp = true;
    } else {
        i++;
    }
}
System.out.println(pow);
double[] Filledup = new double[pow];
for (int j = 0; j < DoubleArray.length; j++) {
    Filledup[j] = DoubleArray[j];
    System.out.println(DoubleArray[j]);
}
for (int k = DoubleArray.length; k < Filledup.length; k++) {
    Filledup[k] = 0;
}

这是我用来将字节数组转换为双数组的函数:

public static double[] toDouble(byte[] byteArray) {
    ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
    double[] doubles = new double[byteArray.length / 8];
    for (int i = 0; i < doubles.length; i++) {
        doubles[i] = byteBuffer.getDouble(i * 8);
    }
    return doubles;
}

头球还在,我知道,但这应该是现在最小的问题了。我将我的字节数组转换为一个双数组,然后用零填充该数组的2次方,以便FFT可以实际工作(它需要一个2^n值数组)。我使用的FFT算法得到两个双数组作为输入,一个是实数,另一个是虚数。我了解到,为了实现这一点,我必须保持虚数组为空(但它的长度与实数组相同)。

值得一提的是:我正在用44100kHz、16位和单声道录音。

如果有必要,我会张贴我正在使用的FFT。

如果我尝试打印双精度数组的值,我会得到一些奇怪的结果:

...
-2.0311904060823147E236
-1.3309975624948503E241
1.630738286366793E-260
1.0682002560745842E-255
-5.961832069690704E197
-1.1476447092561027E164
-1.1008407401197794E217
-8.109566204271759E298
-1.6104556241572942E265
-2.2081172620352248E130
NaN
3.643749694745671E-217
-3.9085815506127892E202
-4.0747557114875874E149
...

我知道问题在于我忽略了一些我应该意识到的非常简单的事情,但我似乎找不到问题所在。最后,我的问题是:我如何才能让它发挥作用?

共有2个答案

申屠秦斩
2023-03-14

wave 文件中的示例不会已经是 8 字节的双精度值,可以根据您发布的代码直接复制。

在将样本转换为双精度值之前,您需要查找(部分来自 WAVE 标头格式和 RIFF 规范)样本的数据类型、格式、长度和字节序。

尝试使用 2 字节小端有符号整数作为可能的可能性。

满才
2023-03-14

有一个标题的方式,我需要摆脱〔…〕

如果要“跳过”标头,则需要使用 javax.声音.采样.音频输入流来读取文件。无论如何,这对于学习都很有用,因为如果您事先不知道确切的格式,则需要标头中的数据来解释字节。

我用44100千赫,16位和单声道录音。

因此,这几乎肯定意味着文件中的数据被编码为16位整数(short在Java术语中)。

现在,您的ByteBuffer代码假设它已经是64位浮点数,这就是为什么你会得到奇怪的结果。换句话说,您正在重新解释二进制数据,就好像它是双精度的一样。

您需要做的是读入< code>short数据,然后将其转换为< code>double。

例如,下面是一个基本例程,可以完成您正在尝试的任务(支持8位、16位、32位和64位有符号整数PCM):

import javax.sound.sampled.*;
import javax.sound.sampled.AudioFormat.Encoding;
import java.io.*;
import java.nio.*;

static double[] readFully(File file)
throws UnsupportedAudioFileException, IOException {
    AudioInputStream in = AudioSystem.getAudioInputStream(file);
    AudioFormat     fmt = in.getFormat();

    byte[] bytes;
    try {
        if(fmt.getEncoding() != Encoding.PCM_SIGNED) {
            throw new UnsupportedAudioFileException();
        }

        // read the data fully
        bytes = new byte[in.available()];
        in.read(bytes);
    } finally {
        in.close();
    }

    int   bits = fmt.getSampleSizeInBits();
    double max = Math.pow(2, bits - 1);

    ByteBuffer bb = ByteBuffer.wrap(bytes);
    bb.order(fmt.isBigEndian() ?
        ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);

    double[] samples = new double[bytes.length * 8 / bits];
    // convert sample-by-sample to a scale of
    // -1.0 <= samples[i] < 1.0
    for(int i = 0; i < samples.length; ++i) {
        switch(bits) {
            case 8:  samples[i] = ( bb.get()      / max );
                     break;
            case 16: samples[i] = ( bb.getShort() / max );
                     break;
            case 32: samples[i] = ( bb.getInt()   / max );
                     break;
            case 64: samples[i] = ( bb.getLong()  / max );
                     break;
            default: throw new UnsupportedAudioFileException();
        }
    }

    return samples;
}

我使用的FFT算法得到两个双数组作为输入,一个是实数,另一个是虚数。我了解到,为了实现这一点,我必须保持虚数组为空(但它的长度与实数组相同)。

没错。实部是文件中的音频样本数组,虚部是一个长度相等的数组,用0填充,例如:

double[] realPart = mySamples;
double[] imagPart = new double[realPart.length];
myFft(realPart, imagPart);

更多信息..."如何使用Java Sound中的音频样本数据?"

 类似资料:
  • 目标 在本节中,我们将学习 使用OpenCV查找图像的傅立叶变换 利用Numpy中可用的FFT函数 傅立叶变换的某些应用程序 我们将看到以下函数:cv.dft(),cv.idft()等 理论 傅立叶变换用于分析各种滤波器的频率特性。对于图像,使用2D离散傅里叶变换(DFT)查找频域。一种称为快速傅立叶变换(FFT)的快速算法用于DFT的计算。关于这些的详细信息可以在任何图像处理或信号处理教科书中找

  • 本文向大家介绍Opencv实现傅里叶变换,包括了Opencv实现傅里叶变换的使用技巧和注意事项,需要的朋友参考一下 傅里叶变换将图像分解成其正弦和余弦分量,它将图像由空域转换为时域。任何函数都可以近似的表示为无数正弦和余弦函数的和,傅里叶变换就是实现这一步的,数学上一个二维图像的傅里叶变换为:   公式中,f是图像在空域的值,F是频域的值。转换的结果是复数,但是不可能通过一个真实图像和一个复杂的图

  • 目标 在这一节中,我们将学习 使用OpenCV查找图像的傅立叶变换 利用Numpy中的FFT功能 傅立叶变换的一些应用 我们将学到以下函数:cv2.dft(),cv2.idft()等 理论 傅立叶变换用于分析各种滤波器的频率特性。对于图像,可以使用2D离散傅里叶变换(DFT)来查找频域。被称为快速傅立叶变换(FFT)的快速算法被用于DFT的计算。有关这些的细节可以在任何图像处理或信号处理的教科书中

  • 傅里叶 - 莫茨金消元法的英文名:Fourier-Motzkin Elimination,简称 FME 算法,它是一种用于从线性不等式中消除变量的数学方法。 它的命名源自于在 1827 年和 1936 年独立发现该算法的 Joseph Fourier 和 Theodore Motzkin 的姓氏。 1. 展示 从线性不等式中消除一组变量,是指通过将关系式中的若干个元素有限次地变换,消去其中的某些元

  • 假设我有一个有一百个测量值的时间序列t,每个条目代表每天的测量值。我假设信号中有一些周期性——它可能每天、每周或每月重复。 将时间序列翻译成傅立叶域可能有助于找到这样的周期性? 我如何使用Numpy的fft模块找到我的时间序列最有可能的时期?

  • 从头到尾彻底理解傅里叶变换算法、下 推荐阅读:The Scientist and Engineer’s Guide to Digital Signal Processing,By Steven W. Smith, Ph.D。此书地址:http://www.dspguide.com/pdfbook.htm。 前期回顾,在上一篇里,我们讲了傅立叶变换的由来、和实数形式离散傅立叶变换(Real DFT)