java - 如何使用 Java Sound 缓冲和播放 OGG 文件中的声音数据

标签 java audio buffer javasound ogg

我正在尝试使用 Java Sound(使用 MP3SPIVorbisSPI)从 WAV、MP3 和 OGG 文件中读取音频数据,将其存储到一个字节数组中,以便稍后从那里播放。为此,我使用如下代码:

public Sound loadSound(File file){ 
   AudioInputStream baseStream = AudioSystem.getAudioInputStream(file);
   AudioFormat baseFormat = baseStream.getFormat();
   AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                    baseFormat.getSampleRate(),
                    16,
                    baseFormat.getChannels(),
                    baseFormat.getChannels() * 2,
                    baseFormat.getSampleRate(),
                    false);
    AudioInputStream decodedStream = AudioSystem.getAudioInputStream(decodedFormat, baseStream);
    // Buffer audio data from audio stream
    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    byte[] buffer = new byte[4096];
                int nBytesRead = 0;
                while (nBytesRead != -1) {
                    nBytesRead = decodedStream.read(buffer, 0, buffer.length);
                    // Write read bytes to out stream
                    byteOut.write(buffer);
                }
    // close streams and cleanup
    byte[] samples = byteOut.toByteArray();

    return new Sound(samples,decodedFormat);
}

Sound 类基本上是一对 (byte[] samples, AudioFormat format)。完成后,我尝试按如下方式读取和播放字节数组:

public void play(Sound sound) {
    InputStream source = new ByteArrayInputStream(sound.getSamples());
    AudioFormat format = sound.getFormat();
    // use a short, 100ms (1/10th sec) buffer for real-time changes to the sound stream
    int bufferSize = format.getFrameSize()
            * Math.round(format.getSampleRate() / 10);
    byte[] buffer = new byte[bufferSize];

    // create a line to play to
    SourceDataLine line;
    try {
        DataLine.Info info
                = new DataLine.Info(SourceDataLine.class, format);
        line = (SourceDataLine) AudioSystem.getLine(info);
        line.open(format, bufferSize);
    } catch (LineUnavailableException ex) {
        ex.printStackTrace();
        return;
    }

    // start the line
    line.start();

    // copy data to the line
    try {
        int numBytesRead = 0;
        while (numBytesRead != -1) {
            numBytesRead
                    = source.read(buffer, 0, buffer.length);
            if (numBytesRead != -1) {
                line.write(buffer, 0, numBytesRead);
            }
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    } catch (ArrayIndexOutOfBoundsException ex2) {

    }

    // wait until all data is played, then close the line
    line.drain();
    line.close();

}

对于 WAV 和 MP3 一切正常,但 OGG 播放失真且速度有所降低,就好像格式错误一样。如果有人能指出为什么会这样,我会很高兴。

注意:我通过使用 FileInputStream 将整个音频文件加载为字节数组,然后通过数组上的 ByteArrayInputStream 使用 AudioInputStream 播放它来解决问题。但是,通过这种方式,字节数组保存编码数据而不是解码数据:实际上,对我来说差别不大,但我很好奇为什么以前的方法对 OGG 不起作用。 感谢所有尝试回答的人。

最佳答案

问题出在 loadSound 中,其中 nBytesRead 返回值未在以下 write 调用中使用。 read 函数不需要返回请求的全部字节数。事实上,如果解码器可以沿着其自然边界打破事物,它可能会使解码器更易于实现。这可能就是 ogg 解码器正在做的事情。此外,如果不使用返回值,则 very read 调用不太可能返回请求的确切大小。

            byte[] buffer = new byte[4096];
            int nBytesRead = 0;
            while (nBytesRead != -1) {
                nBytesRead = decodedStream.read(buffer, 0, buffer.length);
                // Write read bytes to out stream
                byteOut.write(buffer, 0, nBytesRead);
                //                    ^^^^^^^^^^^^^
            }

关于java - 如何使用 Java Sound 缓冲和播放 OGG 文件中的声音数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33110772/

相关文章:

java - 网页长时间打开抛出空指针异常

ios - 使用纯正弦音调的 FFT 获得更精确的频率

javascript - 如何正确回收 JavaScript 中的缓冲区

php - php输出缓冲区如何工作(使用$_SERVER ['REQUEST_TIME']计算脚本执行时间的问题)

Java最佳实践或是否有任何设计模式来保存与该类没有直接关系的数据

java - Spring WebFlow 项目。我无法阻止 log4j 打印调试语句

java - 如何请求需要客户端证书进行身份验证的 URL

c# - .Net 如何直接播放音频样本

audio - 如何将声音输入与预先确定的声音进行比较?

c - 从c中的文件流中获取数据