Pergunta

I'm making a rhythm game and I need a quick way to get the length of an ogg file. The only way I could think would be to stream the file really fast without playing it but if I have hundreds of songs this would obviously not be practical. Another way would be to store the length of the file in some sort of properties file but I would like to avoid this. I know there must be some way to do this as most music players can tell you the length of a song.

Foi útil?

Solução

The quickest way to do it is to seek to the end of the file, then back up to the last Ogg page header you find and read its granulePosition (which is the total number of samples per channel in the file). That's not foolproof (you might be looking at a chained file, in which case you're only getting the last stream's length), but should work for the vast majority of Ogg files out there.

If you need help with reading the Ogg page header, you can read the Jorbis source code... The short version is to look for "OggS", read a byte (should be 0), read a byte (only bit 3 should be set), then read a 64-bit little endian value.

Outras dicas

I implemented the solution described by ioctlLR and it seems to work:

double calculateDuration(final File oggFile) throws IOException {
    int rate = -1;
    int length = -1;

    int size = (int) oggFile.length();
    byte[] t = new byte[size];

    FileInputStream stream = new FileInputStream(oggFile);
    stream.read(t);

    for (int i = size-1-8-2-4; i>=0 && length<0; i--) { //4 bytes for "OggS", 2 unused bytes, 8 bytes for length
        // Looking for length (value after last "OggS")
        if (
                t[i]==(byte)'O'
                && t[i+1]==(byte)'g'
                && t[i+2]==(byte)'g'
                && t[i+3]==(byte)'S'
        ) {
            byte[] byteArray = new byte[]{t[i+6],t[i+7],t[i+8],t[i+9],t[i+10],t[i+11],t[i+12],t[i+13]};
            ByteBuffer bb = ByteBuffer.wrap(byteArray);
            bb.order(ByteOrder.LITTLE_ENDIAN);
            length = bb.getInt(0);
        }
    }
    for (int i = 0; i<size-8-2-4 && rate<0; i++) {
        // Looking for rate (first value after "vorbis")
        if (
                t[i]==(byte)'v'
                && t[i+1]==(byte)'o'
                && t[i+2]==(byte)'r'
                && t[i+3]==(byte)'b'
                && t[i+4]==(byte)'i'
                && t[i+5]==(byte)'s'
        ) {
            byte[] byteArray = new byte[]{t[i+11],t[i+12],t[i+13],t[i+14]};
            ByteBuffer bb = ByteBuffer.wrap(byteArray);
            bb.order(ByteOrder.LITTLE_ENDIAN);
            rate = bb.getInt(0);
        }

    }
    stream.close();

    double duration = (double) (length*1000) / (double) rate;
    return duration;
}

Beware, finding the rate this way will work only for vorbis OGG!

Feel free to edit my answer, it may not be perfect.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top