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

我正在尝试从音频文件(WAV 文件)中提取振幅数组。我将使用这个幅度数组来绘制给定 wav 文件的幅度与时间关系图。我可以自己绘制图表,但不知道如何从 java 中的给定音频(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) 测试了您之前发布的代码,它给了我正确的值
    猜你喜欢
    • 2011-06-20
    • 2013-11-22
    • 2020-05-29
    • 2012-05-04
    • 2015-04-17
    • 2012-09-05
    • 2019-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多