使用 JAVA 从 wav 文件中提取振幅数组

Posted

技术标签:

【中文标题】使用 JAVA 从 wav 文件中提取振幅数组【英文标题】:Extract amplitude array from a wav File using JAVA 【发布时间】:2017-01-11 19:45:41 【问题描述】:

我正在尝试从音频文件(WAV 文件)中提取振幅数组。我将使用这个幅度数组来绘制给定 wav 文件的幅度与时间关系图。我可以自己绘制图表,但不知道如何从 java 中的给定音频(wav)文件中提取振幅?

【问题讨论】:

【参考方案1】:

这是一个你可以使用的辅助类。 getSampleInt() 方法是获取振幅所需的:

File file = ...;
WavFile wav = new WavFile(file);

int amplitudeExample = wav.getSampleInt(140); // 140th amplitude value.

for (int i = 0; i < wav.getFramesCount(); i++) 
    int amplitude = wav.getSampleInt(i);
    // Plot.

它还可以播放文件,以便您测试它,但只能播放 8bit 或 16bit 文件。对于其他情况,您只能阅读它们。

另外,请查看these diagrams 以了解 WAV 文件包含哪些内容并更好地了解此类的作用。

public class WaveFile 
    public final int NOT_SPECIFIED = Audiosystem.NOT_SPECIFIED; // -1
    public final int INT_SIZE = 4;

    private int sampleSize = NOT_SPECIFIED;
    private long framesCount = NOT_SPECIFIED;
    private int sampleRate = NOT_SPECIFIED;
    private int channelsNum;
    private byte[] data;      // wav bytes
    private AudioInputStream ais;
    private AudioFormat af;

    private Clip clip;
    private boolean canPlay;

    public WaveFile(File file) throws UnsupportedAudioFileException, IOException 
        if (!file.exists()) 
            throw new FileNotFoundException(file.getAbsolutePath());
        

        ais = AudioSystem.getAudioInputStream(file);

        af = ais.getFormat();

        framesCount = ais.getFrameLength();

        sampleRate = (int) af.getSampleRate();

        sampleSize = af.getSampleSizeInBits() / 8;

        channelsNum = af.getChannels();

        long dataLength = framesCount * af.getSampleSizeInBits() * af.getChannels() / 8;

        data = new byte[(int) dataLength];
        ais.read(data);

        AudioInputStream aisForPlay = AudioSystem.getAudioInputStream(file);
        try 
            clip = AudioSystem.getClip();
            clip.open(aisForPlay);
            clip.setFramePosition(0);
            canPlay = true;
         catch (LineUnavailableException e) 
            canPlay = false;
            System.out.println("I can play only 8bit and 16bit music.");
        
    

    public boolean isCanPlay() 
        return canPlay;
    

    public void play() 
        clip.start();
    

    public void stop() 
        clip.stop();
    

    public AudioFormat getAudioFormat() 
        return af;
    

    public int getSampleSize() 
        return sampleSize;
    

    public double getDurationTime() 
        return getFramesCount() / getAudioFormat().getFrameRate();
    

    public long getFramesCount() 
        return framesCount;
    


    /**
     * Returns sample (amplitude value). Note that in case of stereo samples
     * go one after another. I.e. 0 - first sample of left channel, 1 - first
     * sample of the right channel, 2 - second sample of the left channel, 3 -
     * second sample of the rigth channel, etc.
     */
    public int getSampleInt(int sampleNumber) 

        if (sampleNumber < 0 || sampleNumber >= data.length / sampleSize) 
            throw new IllegalArgumentException(
                    "sample number can't be < 0 or >= data.length/"
                            + sampleSize);
        

        byte[] sampleBytes = new byte[4]; //4byte = int

        for (int i = 0; i < sampleSize; i++) 
            sampleBytes[i] = data[sampleNumber * sampleSize * channelsNum + i];
        

        int sample = ByteBuffer.wrap(sampleBytes)
                .order(ByteOrder.LITTLE_ENDIAN).getInt();
        return sample;
    

    public int getSampleRate() 
        return sampleRate;
    

    public Clip getClip() 
        return clip;
    

【讨论】:

8位,16位样本大小有什么区别...我使用的是24位音频...如何修改24位音频的代码?它也给出了线路不可用的异常......请你详细解释一下代码......我是这个音频api和音频的新手...... PS:非常感谢 @Jason 这是一种品质。在尝试阅读之前,您应该阅读 WAV 是什么 :) Google,或在此处发布另一个问题。我从我的示例中删除了一些多余的行 - 可以读取任何 WAV 文件,但不要尝试播放除 8 位或 16 位以外的任何内容。 Java 不支持这一点,您只能阅读它们并根据需要绘制图形。 同一编码不能用于mp3文件?...因为不同格式 不幸的是,Java 不支持开箱即用的 mp3。试试 MP3 SPI 库。据说将它添加到类路径就足够了,Java 将开始识别 mp3 文件(因此您将能够使用我的示例)。 javazoom.net/mp3spi/documents.html 进口肯定有帮助【参考方案2】:

我尝试了您的代码,并通过一些小的更改创建了结果。代码输出的数据有什么问题?

我更改了以下几行:

// create file input stream
      DataInputStream fis = new DataInputStream(new FileInputStream(wavFile));
      // create byte array from file
      arrFile = new byte[(int) wavFile.length()];
      fis.readFully(arrFile); // make sure you always read the full file, you did not check its return value, so you might be missing some data

我改变的第二件事是:

System.out.println(Arrays.toString(s.extractAmplitudeFromFile(f)));

在您的 Main 方法中,因为您只是打印出数组的地址。在这些更改之后,代码输出了一个包含值的数组,这似乎与所需的数据相关。

您究竟缺少什么,或者您对数据有什么期望?能否请您再澄清一下这个问题?

【讨论】:

我想要做的是绘制一个特定 wav 文件的振幅和时间之间的图表......时间与正在播放的音频 wav 文件的时刻/当前时间有关...... ..那么如何在不同时刻从 wav 文件中提取振幅? PS:请忽略我的代码....我认为它可能不正确 实际上,代码运行得相当不错。你为什么要删除它?您收到了一个包含整数的数组。这些是幅度值。由于采样率,时间是已知的。您可以从 audioformat 对象中提取它。它以 hz 给出,假设你有 44100,意味着数组的 44100 值是 1 秒。这就是您计算样本时间的方式 我使用 PCM_SIGNED 44100.0 Hz、16 位、立体声、4 字节/帧、little-endian WAV 文件的 windows (ir_begin.wav) 测试了您之前发布的代码,它给了我正确的值

以上是关于使用 JAVA 从 wav 文件中提取振幅数组的主要内容,如果未能解决你的问题,请参考以下文章

从视频文件中提取wav文件

Python从视频文件中提取wav

在 C++ 中使用 libsndfile 从 WAV 文件中提取原始音频数据

在 django 中使用 FFMPEG 在网络上上传后从视频中提取 WAV 音频文件

如何从 .wav 文件中提取特定频率范围?

从 wav 文件 python 中提取频率