【问题标题】:Java: retrieving byte array from an 8 bit wav file and normalizing it to -1.0 to 1.0Java:从 8 位 wav 文件中检索字节数组并将其规范化为 -1.0 到 1.0
【发布时间】:2012-10-01 09:43:22
【问题描述】:

请耐心等待,因为我是音频方面的新手,我已经搜索了几天的解决方案,但没有找到任何解决方案。

所以我用这个检索 .wav 文件的字节数组(来源:Wav file convert to byte array in java

ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(WAV_FILE));

int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
     out.write(buff, 0, read);
}
out.flush();
byte[] audioBytes = out.toByteArray();

然后我将字节数组转换为浮点数组并将其从 -1.0 标准化为 1.0。 (来源:Convert wav audio format byte array to floating point

ShortBuffer sbuf =
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);

float[] audioFloats = new float[audioShorts.length];
for (int i = 0; i < audioShorts.length; i++) {
    audioFloats[i] = ((float)audioShorts[i])/0x8000;
}
return audioFloats;

稍后我将其转换为使用 java.swing 输出波形的线条图

class Panel2 extends JPanel {
float[] audioFloats;

    Dimension d;
    public Panel2(Dimension d, float[] audioFloats) {
        // set a preferred size for the custom panel.
        this.d = d;
        setPreferredSize(d);
        this.audioFloats = audioFloats;
    }


    @Override
    public void paint(Graphics g) {
        //super.paintComponent(g);
        super.paint(g); 

        //shift by 45 because first 44 bytes used for header
        for (int i = 45; i<audioFloats.length; i++){

            Graphics2D g2 = (Graphics2D) g;
            float inc = (i-45)*((float)d.width)/((float)(audioFloats.length-45-1));
            Line2D lin = new Line2D.Float(inc, d.height/2, inc, (audioFloats[i]*d.height+d.height/2));
            g2.draw(lin);

        }


    }
}

波形仅适用于 16 位 wav 文件(我与 goldwave 交叉检查,我的波形和它们的波形对于 16 位看起来都相似)。

如何为 8 位 .wav 文件执行此操作?

因为这是作业,我唯一的限制是逐字节读取wav文件。

我也知道 wav 文件是 PCM 编码的,并且前 44 个字节保留为标题

【问题讨论】:

    标签: java audio bytearray wav waveform


    【解决方案1】:

    你需要修改这部分代码:

    ShortBuffer sbuf =
      ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
    short[] audioShorts = new short[sbuf.capacity()];
    sbuf.get(audioShorts);
    
    float[] audioFloats = new float[audioShorts.length];
    for (int i = 0; i < audioShorts.length; i++) {
        audioFloats[i] = ((float)audioShorts[i])/0x8000;
    }
    

    你根本不需要ByteBuffer——你已经有了你的字节数组。所以只需将其转换为浮点数:

    float[] audioFloats = new float[audioBytes.length];
    for (int i = 0; i < audioBytes.length; i++) {
        audioFloats[i] = ((float)audioBytes[i])/0x80;
    }
    

    【讨论】:

    • 感谢您的回复。它仍然没有正确显示。我检查了浮点数组中的值,几乎所有值似乎都非常接近 1 或 -1。 16 位数组中的值更多地分布在 -1 和 1 之间。这与我一开始检索字节数组的方式有关吗?
    • 为了诊断,打印出原始字节值。看看它们是如何传播的。
    • 我有,它们也没有那么传播。这些值要么非常接近 -128 要么 127。所以当我输出波形时,它就像一个巨大的矩形。
    • 如果那是原始数据,就不能指望别的了。是什么让您如此确定这实际上不是记录的实际波形?
    • 因为当我输出它时,它看起来像一个巨大的矩形。我使用 goldwave 来查看实际波形,它甚至与我所拥有的波形不接近。所以我知道我一定做错了什么
    【解决方案2】:

    音频流通常与一个数据通道交错,然后是相反的数据通道。例如,前 16 位是左声道,接下来的 16 位是右声道。这些中的每一个都被认为是一帧数据。我会确保您的 8 位流只有一个通道,因为看起来这些方法只设置为读取一个通道。

    在您的示例中,还将您正在抓取单个通道的帧转换为短帧,然后通过将其除以 0x8000 十六进制或有符号短帧的最大值来找到小数。

    short[] audioShorts = new short[sbuf.capacity()];
    sbuf.get(audioShorts);
    ...
    audioFloats[i] = ((float)audioShorts[i])/0x8000;
    

    我的猜测是,您需要将 8 字节流作为“字节”类型而不是短类型读取,然后将其除以 128 或有符号 8 位值的最大值。这将涉及制作一种全新的方法来处理 8 位流而不是 16 位流。进行以下更改。

    byte[] audioBytes = new byte[sbuf.capacity()];
    sbuf.get(audioBytes);
    ...
    audioFloats[i] = ((float)audioBytes[i])/0x80;
    

    【讨论】:

    • 行不通。解决方案实际上要简单得多。但是关于立体声 WAV 的观点是正确的。
    • 你知道是单通道还是双通道流?
    • 对不起这是什么意思?正如我所说,我对音频的东西还是很陌生。你的意思是它的立体声还是单声道?我认为这将是单声道
    猜你喜欢
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多