windows 使用 Java 将 WAV 文件读取为样本数组时即时转换采样率

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2276027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 13:56:41  来源:igfitidea点击:

Converting the sample rate on-the-fly when reading a WAV file into a samples array with Java

javawindowsaudiosignal-processingjavasound

提问by pako

I've got a collection of short WAV files that I would like to process in Java using various digital signal processing algorithms. I need to get an array of int valued samples for this purpose, encoded at the 11025 Hz frame rate.

我有一组简短的 WAV 文件,我想使用各种数字信号处理算法在 Java 中处理这些文件。为此,我需要获取一组 int 值样本,以 11025 Hz 帧速率编码。

The source files have several different sample rates, including 11025 Hz and 44100 Hz. Here's the code I'm trying to use to read them:

源文件有几种不同的采样率,包括 11025 Hz 和 44100 Hz。这是我试图用来阅读它们的代码:

// read the WAV file
FileInputStream fileInputStream = new FileInputStream(new File("test.wav"));
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(fileInputStream );

// copy the AudioInputStream to a byte array called buffer
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] data = new byte[4096];
int tempBytesRead = 0;
int byteCounter = 0;
while ((tempBytesRead = audioInputStream.read(data, 0, data.length)) != -1) {
  bos.write(data, 0, tempBytesRead);
            byteCounter += tempBytesRead;
}
bos.close();
byte[] buffer = bos.toByteArray();

AudioFileFormat audioFileFormat = new AudioFileFormat(AudioFileFormat.Type.WAVE, audioInputStream.getFormat(), (int)audioInputStream.getFrameLength());

// get the resulting sample array
int[] samples = new int[audioFileFormat.getFrameLength()];
for (int i = 0; i < samples.length; i++) {
  samples[i] = getSampleValue(i); // the getSampleValue method reads the sample values from the "buffer" array, handling different encoding types like PCM unsigned/signed, mono/stereo, 8 bit/16 bit
}

// RESULT: the "samples" array

The problem is, that the code doesn't handle different sample rates properly. So for the 44100 Hz frame rate I get four times as many samples as for the 11025 Hz frame rate. I would like the resulting sample array to use the 11025 Hz frame rate, regardless of the frame rate of the source file. I tried to force Java to convert the frame rate for me when reading the AudioInputStream, but I get an exception similar to the following one:

问题是,代码不能正确处理不同的采样率。因此,对于 44100 Hz 帧速率,我获得的样本数量是 11025 Hz 帧速率的四倍。我希望生成的样本数组使用 11025 Hz 帧速率,而不管源文件的帧速率如何。在读取 AudioInputStream 时,我试图强制 Java 为我转换帧速率,但我收到了类似于以下异常的异常:

java.lang.IllegalArgumentException: Unsupported conversion: PCM_SIGNED 11025.0 Hz, 16 bit, mono, 2 bytes/frame, 44100.0 frames/second, little-endian from PCM_SIGNED 44100.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian
    at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:955)

I read the Java Sound API tutorial: http://java.sun.com/docs/books/tutorial/sound/converters.html. It seems that the Java Sound API doesn't support this kind of conversion of my operating system (Windows 7). And I would like to avoid dependencies on any external libraries. Is there any way to do the sampling rate conversion on my own?

我阅读了 Java Sound API 教程:http: //java.sun.com/docs/books/tutorial/sound/converters.html。Java Sound API 似乎不支持我的操作系统(Windows 7)的这种转换。我想避免对任何外部库的依赖。有什么办法可以自己进行采样率转换吗?

采纳答案by Paul R

For sample rates > 11025 Hz you need to downsample, which is a two stage process. First you need to low pass filter to satisfy the Nyquist criterion, and then you can decimate, e.g. for 44.1 kHz sample rate data you would need to low pass filter with a cut-off frequency of 5.5 kHz and then you can throw away 3 out of every 4 samples for a 4:1 downsampling ratio. You'll need a different filter for each downsampling ratio that you want to support.

对于 > 11025 Hz 的采样率,您需要进行下采样,这是一个两阶段过程。首先,您需要低通滤波器以满足奈奎斯特准则,然后您可以抽取,例如,对于 44.1 kHz 采样率数据,您需要使用截止频率为 5.5 kHz 的低通滤波器,然后您可以丢弃 3 4:1 下采样率的每 4 个样本。对于要支持的每个下采样率,您都需要一个不同的过滤器。

回答by Claude

I believe the accepted answer answers another question -- it solves the same problem (downsampling the audio) but in another way (manually in stead of using the java sound API). I had the same thing and dug into it.

我相信接受的答案回答了另一个问题——它解决了同样的问题(对音频进行下采样),但以另一种方式(手动而不是使用 java 声音 API)。我有同样的事情并深入研究。

The correct way (or java sound API way) to do this is indeed (as suggested in http://docs.oracle.com/javase/tutorial/sound/converters.html)

这样做的正确方法(或 java 声音 API 方法)确实是(如http://docs.oracle.com/javase/tutorial/sound/converters.html 中所建议的)

AudioFormat outDataFormat = new AudioFormat((float) 8000.0, (int) 8, (int) 1, true, false);
AudioInputStream lowResAIS = AudioSystem.getAudioInputStream(outDataFormat, inFileAIS);

Problem is that standard java doesn't ship with resampling (or even stereo-mono conversion) code (or at least not in that part of the code -- see http://www.jsresources.org/faq_audio.html#convert_sample_rate).

问题是标准 java 不附带重采样(甚至立体声单声道转换)代码(或至少不在代码的那部分 - 请参阅http://www.jsresources.org/faq_audio.html#convert_sample_rate) .

The jsresources pages point to the answers as well: simply installing 2 plugins does the trick. Easiest is to install these plugins in the Extensions directory, on OSX Lion this will do the trick (provided you have wget):

jsresources 页面也指向了答案:只需安装 2 个插件即可解决问题。最简单的方法是将这些插件安装在 Extensions 目录中,在 OSX Lion 上,这可以解决问题(前提是您有 wget):

wget http://www.tritonus.org/tritonus_share-0.3.6.jar -O /Library/Java/Extensions/tritonus_share-0.3.6.jar
wget http://www.tritonus.org/tritonus_remaining-0.3.6.jar -O /Library/Java/Extensions/tritonus_remaining-0.3.6.jar

After adding these 2 jar files, everything worked (just one extra warning: if you want to change both the number of channels and the sample rate, it's still not supported as one step).

添加这 2 个 jar 文件后,一切正常(只是一个额外的警告:如果您想同时更改通道数和采样率,仍然不支持将其作为一个步骤)。