使用 Java Sound API 跳转到音频文件中的位置

Posted

技术标签:

【中文标题】使用 Java Sound API 跳转到音频文件中的位置【英文标题】:Jump to position in audio file using Java Sound API 【发布时间】:2011-08-18 20:25:11 【问题描述】:

我正在使用 AudioInputStream 将字节提供给 SourceDataLine 以播放 PCM 文件。我想让用户能够移动滑块以跳转到文件中的某个点。

我遇到的问题:

markSupported() 在我的 AudioInputStream 上返回 false。所以我 不能使用我最初的方法来调用reset() 然后调用skip()(其中 我已经觉得有点丑了……) 我真的不希望拆除 InputStream 并创建一个新的,只是为了跳到我当前标记之前的位置。 SourceLineData.getLongFramePosition() 似乎不太可靠......我知道它有一个缓冲区,但即使考虑到缓冲区中留下的bytes,我也不明白这种行为

我考虑过使用Memory-Mapped File 将字节提供给行,这样我就可以跳到任何我想要的地方,但是如果我不需要的话,我不想增加函数的复杂性。有没有我想念的好方法?还有谁能解释getLongFramePosition()返回的帧号实际上是什么意思?是通过扬声器的帧数吗(似乎不是)?

【问题讨论】:

因为我正在播放的文件可能是未指定的长度。据我了解,剪辑适用于非常短的声音。 '未指定'或'大'?区别非常重要。例如。 BigClip 可以处理 large 剪辑。 (是的,您对Clip 的理解是正确的。) 【参考方案1】:

BigClip 对你有用吗?

如果没有,这里有一些可行的方法。警告,这有点令人费解。 http://hexara.com/VSL/VSL2.htm

使用这个小程序,您可以加载 wav——我已经加载了超过 5 分钟的 wav,它运行良好,但音频数据确实占用了大量 RAM。

加载 WAV 后,您可以将鼠标移动到任意点并通过按住鼠标并拖动来播放。显然,这并不是你想要做的,因为你想从一个点回放,而不用担心拖动或拖动速度。但是我为使数据处于可播放状态所采取的方法仍然适用于您。

如果您有一个正常工作的 AudioInputStream 和 AudioFileFormat,您可以设置一个内部数组并将数据直接读入其中。音频文件格式为您提供编码和帧长度,可用于计算数组维度。

然后,创建一个 TargetDataLine,从您创建的数组中获取其音频数据。 TDL 必须有一个变量来标记下一次读取的开始位置。您可以使用您的 JSlider 更改该变量的内容以指向您想要的任何框架。

如果您没有可用的 AudioInputStream,有办法获得...但它更复杂,涉及 ByteArrayOutput 和 Input Stream 对象。应该没必要去吧。 (我用它们来读取上述小程序中的客户端数据。)

至于您关于当前 Frame 位置的问题,关键问题是 JVM 以块的形式处理音频数据,并且它往往会在听到的内容之前运行。我在这个问题上撞了很久,然后想出了这个: http://www.java-gaming.org/index.php/topic,24605.0.html

【讨论】:

谢谢你的例子,其实我已经想出了一个解决方案,只是忘记贴了。【参考方案2】:

我能想出的最干净的方法是使用FileChannel 将字节读入SourceDataLine 这种方式基于用户移动滑块我可以确定文件中的字节位置(确保调整或将其舍入以与帧对齐)然后在FileChannel 中设置该位置,然后继续播放。当发生这种情况时,我刷新了线路以阻止播放剩余的字节。

【讨论】:

以上是关于使用 Java Sound API 跳转到音频文件中的位置的主要内容,如果未能解决你的问题,请参考以下文章

Java getAudioInputStream 试图读取音频文件,得到 javax.sound.sampled.UnsupportedAudioFileException,

java 怎么获取音频文件时长

Java Wav 文件错误(javax.sound.sampled.UnsupportedAudioFileException:无法从输入文件获取音频输入流)

使用 Java Sound API 进行多通道 USB 录音?

在java中怎么获取音频的总时长?

Java Sound API - 如何获取麦克风设备信息?