【问题标题】:Using Ogg Vorbis SPI for decoding .ogg files fails for small file sizes使用 Ogg Vorbis SPI 解码 .ogg 文件对于小文件大小失败
【发布时间】:2015-09-09 18:45:24
【问题描述】:

我使用Ogg Vorbis SPI 作为解码.ogg 文件的一种方式。

我用于加载.ogg 文件并转换为 PCM 的代码 sn-p 与文档中建议的代码没有太大不同:

// The streams are valid at this point.
try (InputStream bin = new BufferedInputStream(file.getInputStream(entry) );
     AudioInputStream in = AudioSystem.getAudioInputStream(bin) ) {

    AudioFormat baseFormat = in.getFormat();
    // At this point the format is valid:
    // VORBISENC 44100.0 Hz, unknown bits per sample, mono, 1 bytes/frame, 12000.0 frames/second, 
    // I can't make a Clip directly from this or it will be all buzzy, so I convert to PCM

    // Decoding to PCM
    AudioFormat decodedFormat =
        new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                        baseFormat.getSampleRate(),
                        16,
                        baseFormat.getChannels(),
                        baseFormat.getChannels() * 2,
                        baseFormat.getSampleRate(),
                        false);

    // This is where the issues appear...
    AudioInputStream decodedInputStream = AudioSystem.getAudioInputStream(decodedFormat, in);

    Clip clip = AudioSystem.getClip();
    clip.open(decodedInputStream);

    // entry.getName is basically the filename. Any files with '0' frame length didn't get converted properly.
    System.out.println("Clip " + entry.getName() + " loaded at " + clip.getFrameLength() + " frame length");

    return clip;
} catch { ... }

正在发生的事情是我正在加载几个小的声音文件,范围从 5K 到 32K 的高(ogg 格式)。但是,一些文件无法正确转换为 PCM。我注意到他们都有一些共同点。它们的大小范围为 5K - 6K。声音文件似乎在 7K 左右开始正确转换。我测试了一个 11K 的声音并将其剪掉,直到它不再转换 (~6K)

我认为解决它的一种方法是添加一些听不见的噪音来填充大小,但我更喜欢正确转换声音文件而不管大小。除了 API 本身之外,我对声音表示的概念完全陌生,我不知道从哪里开始寻找为什么会发生这种情况。

【问题讨论】:

  • 我想知道 AudioInputStream.getFormat() 是否移动了流中的位置并且不重置它。也许尝试关闭并重新打开流?
  • 感谢您的关注;在我使用 getFormat() 之后,我刚刚尝试了 reset() 并关闭/打开了一个新流。不幸的是,这个问题仍然存在。

标签: java audio ogg


【解决方案1】:

我遇到了和你一样的情况,似乎在编码小文件和不符合原始缓冲区长度的文件时(很幸运)会因为 Vorbis 中的错误而被删减SPI 实现——它真的很老了,我希望他们会支持它并修复它。 作为一种解决方法,您可以在 2 个位置修复 DecodedVorbisAudioInputStream.java:

    private int readFromStream(byte[] buffer, int index, int bufferSize_) {
    int bytes = 0;
    try {
        bytes = oggBitStream_.read(buffer, index, bufferSize_);
    }
    catch (Exception e) {
        if (TDebug.TraceAudioConverter)
            TDebug.out("Cannot Read Selected Song");
        bytes = -1;
    }
    if (bytes >= 0) {
        currentBytes += bytes;
    }
    return bytes;
}

并从缓冲区部分读取:

case playState_ReadData:
            int result;
            index = oggSyncState_.buffer(bufferSize_);
            buffer = oggSyncState_.data;
            bytes = readFromStream(buffer, index, bufferSize_);
            if (TDebug.TraceAudioConverter) {
                TDebug.out("More data : " + bytes);
            }
            // has no data in buffer also
            if (bytes == -1 && bufferSize_ - index == 0) {
                playState = playState_Done;
                if (TDebug.TraceAudioConverter) {
                    TDebug.out("Ogg Stream empty. Settings playState to playState_Done.");
                }
                break;
            }
            else {
                if (bytes != -1) {
                    oggSyncState_.wrote(bytes); 
                }
                if (bytes == 0 && bufferSize_ - index == 0) {
                    if ((oggPage_.eos() != 0) || (oggStreamState_.e_o_s != 0) || (oggPacket_.e_o_s != 0)) {
                        if (TDebug.TraceAudioConverter) {
                            TDebug.out("oggSyncState wrote 0 bytes: settings playState to playState_Done.");
                        }
                        playState = playState_Done;
                    }
                    if (TDebug.TraceAudioConverter) {
                        TDebug.out("oggSyncState wrote 0 bytes: but stream not yet empty.");
                    }
                    break;
                }
            }

            result = oggSyncState_.pageout(oggPage_);
            if (result == 0) {
                if (TDebug.TraceAudioConverter) {
                    TDebug.out("Setting playState to playState_ReadData.");
                }
                playState = playState_ReadData;
                break;
            } // need more data
            if (result == -1) { // missing or corrupt data at this page position
                if (TDebug.TraceAudioConverter)
                    TDebug.out("Corrupt or missing data in bitstream; setting playState to playState_ReadData");
                playState = playState_ReadData;
                break;
            }

            oggStreamState_.pagein(oggPage_);

            if (TDebug.TraceAudioConverter)
                TDebug.out("Setting playState to playState_WriteData.");
            playState = playState_WriteData;
            break;

在读取时,它们会忽略已经从缓冲区读取的字节,因此,它们只会跳过缓冲区中最后读取的字节。

希望有帮助!

【讨论】:

  • 不幸的是,自从提出这个问题后,我已经转向其他项目,因此我还没有付诸实践……但我非常感谢您为解决方案提供的详细信息,我不想这个答案浪费了,所以我接受了。
猜你喜欢
  • 2015-07-13
  • 2013-12-14
  • 2014-08-28
  • 2016-02-25
  • 2023-03-26
  • 1970-01-01
  • 2010-12-27
  • 2014-12-28
  • 2011-04-28
相关资源
最近更新 更多