在 Java 中制作具有不同播放速度的声音文件

Making a sound file with varying playback speed in Java

我已经设法使用此处的答案以不同的速度播放声音文件,但我需要能够在播放时调整速度。我想到了两种方法。第一种是将音频文件分割成短片,并在最后一个结束后播放每个短片。我还没有尝试过,但它似乎很容易以文件自己播放或有短间隙结束。

另一种方法是将原始文件作为流,然后使用它制作流,根据需要加快或减慢速度。这看起来会很好用,但是为了构造一个 AudioInputStream,我要么需要一个已知长度的 InptutStream,这是不可能提前计算出来的,要么需要一个 TargetDataLine,它是一个方法比我多得多的接口愿意实施。

有更好的方法吗?另外,为什么 AudioInputStream 需要知道流的长度?

或者,是否有我可以使用的外部库?

如果您只是简单地播放一个音频文件(例如 .wav)并且可以接受声音的音高变化,一个简单的可能性是从 AudioInputStream 中读取数据,翻译到 PCM,以所需的速率插入数据,转换回字节并通过 SourceDataLine.

发送出去

要实时加速或减速,将输入松耦合到变量,该变量保存用于在传入帧中前进的增量。为了尽量减少不连续性,您可以在给定的帧数内平滑从一个音高到另一个音高的过渡。

这样做是为了在开源库中实现实时变频AudioCue, on github. Smoothing there between frequency changes is set to occur over 1028 frames (approx 1/40th of a second). But quicker changes are certainly possible. The sound data in that library is take from an internal float array of PCM values. But a good example of code needed to read the data as a line rather than a fixed array can be seen in the first code example in the Sound Trail, Using File Filters and Converters。您可能想要使用 InputStream 作为 AudioInputStream 的参数。在示例中显示 "Here, do something useful.." 的位置,您将转换为 PCM,然后使用线性插值以所需的频率光标浏览生成的 PCM,然后重新打包并通过 SourceDataLine 发送出去。

如果您希望保留音高(仅时间拉伸或压缩),那么这开始需要更多重型 DSP。 This thread at the StackExchange Digital Processing 网站上有一些相关信息。我在使用 Hamming Window 制作颗粒以帮助它们之间的交叉淡化方面取得了一些成功,但其他一些解决方案超出了我的理解范围(而且我还没有很长一段时间没有回到这个问题)。但是如果我没记错的话,可以实时改变颗粒的间距。虽然听起来不如 Audacity 工具的算法那么好,但这可能对我来说更重要。我几乎是自学和尝试,而不是专业地在该领域工作。

(我相信 的回答会让你顺利进行。我发布这个只是为了补充我关于重采样的两分钱。)

简短回答: 创建一个 AudioInputStream 来删除样本或添加零样本。作为长度,您可以设置 AudioSystem.NOT_SPECIFIED.

长答案:如果添加零样本,您可能想要插值,但不是线性的。您必须为上采样进行插值的原因是 aliasing, which you might want to avoid. You do so, by applying a lowpass filter. The reason for this is simple. The Nyquist-Shannon theorem states that when a signal is sampled at X Hz, you can only unambiguously represent frequencies up to X/2 Hz. When you upsample, you increase the sample frequency, so in theory you can represent a larger frequency range. Indeed, when simply adding zeros you see some energy in those additional frequency ranges—which shouldn't be there, because you have no information about it. So you need to "cut them off" using a low pass filter. More about upsampling can be found on Wikipedia.

长话短说,有一个正确的方法来做到这一点。您似乎对扭曲没问题,所以正确的方法可能没有必要,但浪费时间。

无耻插件:如果你还是想做有点对,你可能会发现Resample class of jipes有用。它不是通用重采样器,即它只支持有限数量的因子,例如 24、...,但它可能对您有用。

import com.tagtraum.jipes.math.MultirateFilters.Resampler;

[...]

float[] original = ... ; // original signal as float

Resampler downsampler2 = new MultirateFilters.Resampler(1, 2);
float[] downsampled = downsampler2.map(original);

Resampler upsampler2 = new MultirateFilters.Resampler(2, 1);
float[] upsampled = upsampler2.map(original);

如果你想time-scale modification (TSM), i.e., changing the tempo without changing the frequencies, you might want to use Rubberband for Java.