使用 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 文件中提取振幅数组的主要内容,如果未能解决你的问题,请参考以下文章
在 C++ 中使用 libsndfile 从 WAV 文件中提取原始音频数据