傅立叶变换一个字节数组 [英] Fourier transforming a byte array

查看:148
本文介绍了傅立叶变换一个字节数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不是在Java中那么精通,所以请保持它相当简单。我会,不过,试着去了解你的一切后。这里是我的问题。

我已经写了code录制来自外部麦克风的音频并存储为.wav。保存这个文件归档相关的目的。我需要做的是存储的音频的FFT。

我的这种做法是加载wav文件作为一个字节数组,并改变着,与1有一个在我需要摆脱的方式头,但我应该能够做到这一点,2的问题。我有一个字节数组,但大多数如果不是全部FFT算法我在网上找到,并试图平息了我的复杂/两个双阵列项目工作。

我试图来解决这两个问题,最后是能够绘制我FFT阵列作为一个图,当我发现这只是给我回0。 .wav文件是罚款不过,我可以打回来没有问题。我想也许转换成字节双打是我的问题,所以这里是我这样的做法(我知道这不是pretty)

 字节的ByteArray [] = Files.readAllBytes(wav_path);
字符串s =新的String(字节阵列);
双[]的doubleArray = toDouble(字节阵列);
//建立2 ^ N阵列,用零填满
布尔EXP = FALSE;
INT I = 0;
INT POW = 0;
而(!EXP){
    POW =(int)的Math.pow(2,i)的;
    如果(POW> ByteArray.length){
        EXP = TRUE;
    }其他{
        我++;
    }
}
的System.out.println(POW);
双[] = Filledup新的双[POW]
对于(INT J = 0; J< D​​oubleArray.length; J ++){
    Filledup [J] =的doubleArray [J]。
    的System.out.println(的doubleArray [J]);
}
对于(INT K = DoubleArray.length; K< Filledup.length; k ++){
    Filledup [K] = 0;
}

这是我使用的字节数组转换成一个双阵列功能:

 公共静态双重[] toDouble(字节[]的字节数组){
    ByteBuffer中的ByteBuffer = ByteBuffer.wrap(字节阵列);
    双[] =双打新的双[byteArray.length / 8]。
    的for(int i = 0; I< doubles.length;我++){
        双打[I] = byteBuffer.getDouble(我* 8);
    }
    返回双打;
}

头仍然在那里,我知道,但应该是最小的问题现在。我改变了我的字节数组双阵列,然后填补了该阵列的2零接下来的功率,从而FFT实际上可以正常工作(需要2 ^ n的值的数组)。我使用的FFT算法得到两个双阵列作为输入,一个是真实的,另一个是虚部。我阅读,这个工作,我必须保持虚阵列空(但其长度是一样的真正的数组)。

值得一提:我与44100千赫,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
...

我知道的地方,问题在于我忽视的东西很简单,我应该知道,但我似乎无法找出问题所在。我的问题总算是:我如何能得到这个工作。


解决方案

  

有一个在我需要摆脱的方式头[…]


您需要使用 javax.sound.sampled.AudioInputStream中 来,如果你想跳过的头读取的文件。这是非常有用反正去学习,因为你需要在头间preT字节的数据。如果你不知道时间提前的确切格式。


  

我与44100 kHz的录音, 16位并单。


所以,这几乎肯定意味着该文件中的数据连接在Java中的命名codeD为16位的的整数的( )。

现在,你的的ByteBuffer code使得假设它已经64位浮点,这就是为什么你得到奇怪的结果。换句话说,你的 reinter preting 的二进制数据,就好像它是双击

您需要做的就是读取数据,然后什么的转换的它双击

例如,这里有一个基本的程序来完成的,比如说你正在试图做的(支持8位,16位,32位和64位有符号整数PCM):

 进口javax.sound.sampled中*。
进口javax.sound.sampled.AudioFormat.Encoding;
进口java.io. *;
导入java.nio中*。静态双[]的readFully(档案文件)
抛出UnsupportedAudioFileException,IOException异常{
    在的AudioInputStream = AudioSystem.getAudioInputStream(文件);
    FMT AudioFormat的= in.getFormat();    字节[]字节;
    尝试{
        如果(fmt.getEncoding()!= Encoding.PCM_SIGNED){
            抛出新UnsupportedAudioFileException();
        }        //完全读取数据
        字节=新的字节[in.available()];
        in.read(字节);
    } {最后
        附寄();
    }    INT位= fmt.getSampleSizeInBits();
    双上限= Math.pow(2位 - 1);    ByteBuffer的BB = ByteBuffer.wrap(字节);
    bb.order(fmt.isBigEndian()?
        ByteOrder.BIG_ENDIAN:ByteOrder.LITTLE_ENDIAN);    双[] =样品新的双[bytes.length * 8 /位]
    //样品逐样本转换成的比例
    // -1.0&下; =样品[1] - ; 1.0
    的for(int i = 0; I< samples.length ++我){
        开关(比特){
            案例8:样本[I] =(bb.get()/最大);
                     打破;
            案例16:样本[I] =(bb.getShort()/最大);
                     打破;
            案例32:样本[I] =(bb.getInt()/最大);
                     打破;
            案例64:样本[I] =(bb.getLong()/最大);
                     打破;
            默认:抛出新UnsupportedAudioFileException();
        }
    }    返回样本;
}


  

我使用的FFT算法得到两个双阵列作为输入,一个是真实的,另一个是虚部。我阅读,这个工作,我必须保持虚阵列空(但其长度是一样的真正的数组)。


这是正确的。该的真正的部分是从文件中的音频采样阵,的想象的部分是相等长度的数组,充满了0例如:

 双击[] = realPart mySamples;
双[] = imagPart新的双[realPart.length]
myFft(realPart,imagPart);


更多信息... 我如何使用从Java声音频采样数据吗?

I am not so proficient in Java, so please keep it quite simple. I will, though, try to understand everything you post. Here's my problem.

I have written code to record audio from an external microphone and store that in a .wav. Storing this file is relevant for archiving purposes. What I need to do is a FFT of the stored audio.

My approach to this was loading the wav file as a byte array and transforming that, with the problem that 1. There's a header in the way I need to get rid of, but I should be able to do that and 2. I got a byte array, but most if not all FFT algorithms I found online and tried to patch into my project work with complex / two double arrays.

I tried to work around both these problems and finally was able to plot my FFT array as a graph, when I found out it was just giving me back "0"s. The .wav file is fine though, I can play it back without problems. I thought maybe converting the bytes into doubles was the problem for me, so here's my approach to that (I know it's not pretty)

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;
}

This is the function I'm using to convert the byte array into a double array:

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;
}

The header still is in there, I know that, but that should be the smallest problem right now. I transformed my byte array to a double array, then filled up that array to the next power of 2 with zeroes, so that the FFT can actually work (it needs an array of 2^n values). The FFT algorithm I'm using gets two double arrays as input, one being the real, the other being the imaginary part. I read, that for this to work, I'd have to keep the imaginary array empty (but its length being the same as the real array).

Worth to mention: I'm recording with 44100 kHz, 16 bit and mono.

If necessary, I'll post the FFT I'm using.

If I try to print the values of the double array, I get kind of weird results:

...
-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
...

I know that somewhere the problem lies with me overlooking something very simple I should be aware of, but I can't seem to find the problem. My question finally is: How can I get this to work?

解决方案

There's a header in the way I need to get rid of […]

You need to use javax.sound.sampled.AudioInputStream to read the file if you want to "skip" the header. This is useful to learn anyway, because you would need the data in the header to interpret the bytes if you did not know the exact format ahead of time.

I'm recording with 44100 kHz, 16 bit and mono.

So, this almost certainly means the data in the file is encoded as 16-bit integers (short in Java nomenclature).

Right now, your ByteBuffer code makes the assumption that it's already 64-bit floating point and that's why you get strange results. In other words, you are reinterpreting the binary short data as if it were double.

What you need to do is read in the short data and then convert it to double.

For example, here's a rudimentary routine to do such as you're trying to do (supporting 8-, 16-, 32- and 64-bit signed integer 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;
}

The FFT algorithm I'm using gets two double arrays as input, one being the real, the other being the imaginary part. I read, that for this to work, I'd have to keep the imaginary array empty (but its length being the same as the real array).

That's right. The real part is the audio sample array from the file, the imaginary part is an array of equal length, filled with 0's e.g.:

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


More info... "How do I use audio sample data from Java Sound?"

这篇关于傅立叶变换一个字节数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆