1

我想在后台播放一个声音文件(.wav)并让一个块振动。所以我已经编写了一个带有面板和画布的 JFrame,其中我有一个可以由键盘的箭头键控制的块。我的主要方法有一个循环,大致如下:

while (play) {
        Date delay_time = new Date();
        delay_time.setTime(delay_time.getTime() + (int) (1000*(1.0/FPS)));
        Graphics g = can.getGraphics();
        can.update(g);

        //Here is my other stuff, like the Motion of the Block etc...


        while(new Date().before(delay_time)) {

        }

    }

我的 FPS 变量是一个静态的 final int,当前设置为 30。

现在我想实现一个不断播放的背景音乐。这将是一些电子/dubstep的东西。

现在我的问题是:我如何实现该音乐以及(最重要的!)如何使块根据音乐振动。(如果你不明白我所说的随着音乐振动是什么意思,也许你可以通过解释我如何获得强度、共振或类似的东西来帮助我,它们应该在音乐的播放时间内不断变化。)

谢谢

4

1 回答 1

2

框架

帧是音频文件中所有通道的样本横截面。因此,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++;
}
于 2014-05-16T20:41:34.437 回答