In case anybody else ever needs this, I'll post it here. It's NOT guaranteed to work with every Vorbis file!!!
I only used the bare minimum of the Vorbis specification that I needed... basically the 'OggS' and 'vorbis' headers.
2019-08-13 - Updated the code slightly to work with PB 5.70 / Unicode mode
Code: Select all
; Cross-platform procedure for determining length (in milliseconds)
; of an Ogg Vorbis audio file. It can easily be adapted to return
; seconds, or samples, or sample rate, etc.
; Absolutely NOT GUARANTEED to work with every .ogg file,
; but it seems to work with all the ones I'm using...
; 2019-08-13 : Updated to PureBasic 5.70 (Unicode fixed)
Procedure.i GetOggLength(File.s)
Protected BufferSize.i = 8192
Protected FN.i, *Buffer, FilePos.i, *P.LONG
Protected Result.i, Samples.q
Protected SampleRate.d
If (File)
FN = ReadFile(#PB_Any, File)
If (FN)
*Buffer = AllocateMemory(BufferSize)
If (*Buffer)
; Find sample rate (search forward from start)
FilePos = 0
Repeat
FileSeek(FN, FilePos)
If (ReadData(FN, *Buffer, BufferSize))
*P = *Buffer + 1
While (*P < *Buffer + BufferSize - 15)
If (*P\l = $62726F76) ; 'brov'
If ((PeekU(*P + 4) = $7369) And (PeekA(*P - 1) = 1)) ; 'si'
SampleRate = PeekL(*P + 11)
Break 2
EndIf
EndIf
*P + 1
Wend
Else
Break
EndIf
FilePos + BufferSize/2
Until (FilePos >= Lof(FN))
If (SampleRate > 0)
; Find number of samples (search backwards from end)
FilePos = Lof(FN) - BufferSize
If (FilePos < 0)
FilePos = 0
EndIf
Repeat
FileSeek(FN, FilePos)
If (ReadData(FN, *Buffer, BufferSize))
*P = *Buffer + BufferSize - 4
While (*P >= *Buffer)
If (*P\l = $5367674F) ; 'SggO'
Samples = PeekQ(*P + 6)
If (Samples > 0)
; Calculate audio length (milliseconds)
Result = Int(1000.0 * Samples / SampleRate)
Break 2
EndIf
EndIf
*P - 1
Wend
Else
Break
EndIf
If (FilePos > 0)
FilePos - BufferSize/2
Else
FilePos = -1
EndIf
Until (FilePos < 0)
EndIf
FreeMemory(*Buffer)
EndIf
CloseFile(FN)
EndIf
EndIf
ProcedureReturn (Result)
EndProcedure
; Test:
File.s = OpenFileRequester("OGG", "", "Ogg Vorbis|*.ogg", 0, #PB_Requester_MultiSelection)
While (File)
Length.i = GetOggLength(File)/1000
Debug Str(Length/60) + ":" + RSet(Str(Length % 60), 2, "0") + " " + GetFilePart(File)
File = NextSelectedFileName()
Wend