框架
帧是音频文件中所有通道的样本横截面。因此,16 位立体声(双通道)音频文件将具有 32 位帧(每样本 16 位 * 每帧 2 通道 = 每帧 32 位)。
加载原始数据
Java 以 8 位字节读取原始音频数据,但大多数音频具有更高的样本大小。因此,为了表示音频,您必须组合多个字节以创建音频格式的样本。但首先,在将字节组合成样本之前,您需要将所有音频加载到缓冲区中。
首先从文件中获取音频流:
File file = new File(filename);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
现在您有了 AudioInputStream,您可以读入音频数据。AudioInputStream 有一个 read() 方法,它接受一个未填充的 byte[] 并读入 byte[] 长度的数据。要一次读取整个音频文件,请创建一个字节 [] 整个音频文件的长度。文件的完整长度(以字节为单位)为:
总字节数 = 每帧字节数 * 总帧数
您可以从 AudioInputStream 中获取整个文件的帧数 (frameLength) 和帧的大小 (frameSize):
int frameLength = (int) audioInputStream.getFrameLength(); int frameSize = (int) audioInputStream.getFormat().getFrameSize();
您可以创建长度设置为 frameLength*frameSize 的 byte[]:
byte[] bytes = new byte[frameLength * frameSize];
最后,您可以读取音频,将空字节 [] 传递给 AudioInputStream 并捕获适当的异常:
int result = 0;
try {
result = audioInputStream.read(bytes);
} catch (Exception e) {
e.printStackTrace();
}
转换为样本和通道
原始音频数据不是很有用。它需要分解为通道和样本。从那里,很容易绘制样品。
字节将被转换为样本并表示为整数。您需要一个容器来存储所有通道中的样本。因此,创建一个二维 int[][] 引用通道和每个通道的样本。您已经了解了如何从AuduioInputStream 中获取帧长度,并且您可以通过相同的方式获取通道数。下面是初始化 int[][] 的代码:
int numChannels = audioInputStream.getFormat().getChannels();
int frameLength = (int) audioInputStream.getFrameLength();
int[][] toReturn = new int[numChannels][frameLength];
现在,您需要遍历 byte[],将字节转换为样本,并将样本放在 int[][] 中的适当通道中。byte[] 是按帧组织的,这意味着您将读取每个通道的样本,而不是连续读取特定通道的所有样本。因此,流程是遍历通道并添加样本,直到 byte[] 完全迭代:
int sampleIndex = 0;
for (int t = 0; t < eightBitByteArray.length;) {
for (int channel = 0; channel < numChannels; channel++) {
int low = (int) eightBitByteArray[t];
t++;
int high = (int) eightBitByteArray[t];
t++;
int sample = getSixteenBitSample(high, low);
toReturn[channel][sampleIndex] = sample;
}
sampleIndex++;
}