Just moving on to the next stage.
This code takes an ogg file and gets all page offsets and splits the three vorbis headers into three files. 'vorbis 1.tmp' etc.
After this it reassembles the file by first repacking the three headers then adding the audio data from the original.
Then resequencing the whole file putting in the correct page sequence number and recalculating the checksum for every page.
The output is placed in 'OGGOutput.tmp' rename this to 'OGGOutput.ogg' to test.
Just need to do a comment parser with images. (image processing missed off here for clarity)
Code: Select all
DeclareModule OGGvorbis
;Load ogg file and split headers
Declare LoadOGGFile(FileName.s)
;Load The Comments
Declare GetComments()
;Saves A Loaded Ogg File
Declare SaveOGGFile()
;The SongTags() List Is Available After Using getComments()
Structure tag
Length.i
Title.s
Value.s
ImageID.i
EndStructure
Global NewList SongTags.tag()
EndDeclareModule
Module OGGvorbis
Structure OGGPage
CapturePattern.l
Version.a
HeaderType.a
GranulePosition.q
BitstreamSerialNumber.l
PageSequenceNumber.l
Checksum.l
PageSegments.a
SegmentTable.a[255]
EndStructure
Global OggFile.i,OutputFile.i
Global OggHeader.OGGPage
;Only jpeg and png Supported In Comments
UseJPEGImageDecoder()
UsePNGImageDecoder()
Structure VorbisPictureInfo
Type.l
MIMEType.s
Description.s
EndStructure
Structure Offset
PageNumber.i
Start.i
Finish.i
EndStructure
Structure Page
Start.i
Finish.i
EndStructure
Global Dim Pages.Page(0) ;Holds Start and end page offsets
Global BitstreamSerialNumber.i
Global AudioStartPage.i = 0 ;Page After end of vorbis header 3
Global OGGPage.oggPage
Global OriginalFileName.s
Procedure.i GetOggOffsets(File.s)
Protected handle.i
Protected *buffer
Protected buffer_size.i
Protected *offset.Long
Protected result.i
Protected error.i
Protected PageCount.i = 0
Pages(PageCount)\Start = 0 ;Firstpage Start at 0
If File
OggFile = ReadFile(#PB_Any,File.s)
If IsFile(OggFile)
buffer_size = Lof(OggFile)
If buffer_size > 4
*buffer = AllocateMemory(buffer_size)
If ReadData(OggFile,*buffer,buffer_size) = buffer_size
For *offset = *buffer To (*buffer + buffer_size - 5)
If *offset\l = 1399285583
If *offset - *buffer > 0
Pages(PageCount)\Finish = *offset - *buffer
PageCount + 1
ReDim Pages(PageCount)
Pages(PageCount)\Start = *offset - *buffer
EndIf
EndIf
Next
result = Bool(error = #False)
EndIf
FreeMemory(*buffer)
EndIf
EndIf
EndIf
ProcedureReturn result
EndProcedure
Procedure GetHeaders()
Protected Start.i, Finish.i,iLoop.i,Page.OGGPage
Protected Buffersize.i, *Header,HeaderSize.i
Define HeaderCount.i = 1 ;Look For First Header
For iLoop = 0 To 5
If HeaderCount < 4 ;vorbis has three headers
FileSeek(OggFile,Pages(iLoop)\Start)
ReadData(OGGFile, @Page, 27)
Buffersize = 27 + ReadData(OGGFile, @Page + 27, Page\PageSegments)
BitstreamSerialNumber = Page\BitstreamSerialNumber
BytesToRead = 0
For i = 0 To Page\PageSegments - 1
If Page\SegmentTable[i] = 255
*Mem = ReAllocateMemory(*Header, HeaderSize + 255, #PB_Memory_NoClear)
ReadData(OGGFile,*Mem + HeaderSize,255)
HeaderSize + 255
If *Mem
*Header = *Mem
EndIf
Else
*Mem = ReAllocateMemory(*Header, HeaderSize + Page\SegmentTable[i], #PB_Memory_NoClear)
ReadData(OGGFile,*Mem + HeaderSize,Page\SegmentTable[i])
HeaderSize + Page\SegmentTable[i]
If *Mem
*Header = *Mem
EndIf
;End Of This Header
Select HeaderCount
Case 1
CreateFile(0,"vorbis 1.tmp")
WriteData(0,*Header,HeaderSize)
CloseFile(0)
HeaderCount + 1
HeaderSize = 0
Case 2
CreateFile(0,"vorbis 2.tmp")
WriteData(0,*Header,HeaderSize)
CloseFile(0)
HeaderCount + 1
HeaderSize = 0
Case 3
CreateFile(0,"vorbis 3.tmp")
WriteData(0,*Header,HeaderSize)
CloseFile(0)
AudioStartPage = iLoop + 1 ;First Page After vorbis header 3
HeaderCount + 1
EndSelect
EndIf
Next
EndIf
Next
EndProcedure
Procedure GetComments()
Protected CommentFile.i, *Comments, BufferSize.i
Define VendorLength.i,NumberOfComments.i,CommentLength.i,Picture.i,PictureWidth.i,PictureHeight.i,imgWin.i
Define ThisComment.s
Define Info.VorbisPictureInfo
Protected CurrentPosition.i
CommentFile = ReadFile(#PB_Any,"vorbis 2.tmp")
If IsFile(CommentFile)
BufferSize = Lof(CommentFile)
*Comments = AllocateMemory(BufferSize)
ReadData(CommentFile,*Comments,BufferSize)
;Get Ready For list
ClearList(SongTags())
AddElement(SongTags())
CurrentPosition = 7
;Length Of Vendor String
VendorLength = PeekL(*Comments + CurrentPosition)
CurrentPosition + 4
SongTags()\Title = "VENDOR" ;Never Set In file
SongTags()\Value = PeekS(*Comments + CurrentPosition,VendorLength,#PB_UTF8)
;Add Vendor string length
CurrentPosition + VendorLength
;Get Number Of Comments
NumberOfComments = PeekL(*Comments + CurrentPosition)
CurrentPosition + 4
;Fetch Each comment
For iLoop = 1 To NumberOfComments
CommentLength = PeekL(*Comments + CurrentPosition)
CurrentPosition + 4
ThisComment = PeekS(*Comments + CurrentPosition,CommentLength,#PB_UTF8)
CurrentPosition + CommentLength
;Break Up Comment Into Title and Value
AddElement(SongTags())
SongTags()\Title = StringField(ThisComment,1,"=")
SongTags()\Value = StringField(ThisComment,2,"=")
Next
EndIf
CloseFile(CommentFile)
EndProcedure
Procedure PackHeader(FileNumber.i)
Define BufferSize.i,Segments.i,LastSegment.i,PageNumber.i
Define *PageBody,BytesRead.i,ThisPage.i
Buffersize = Lof(FileNumber)
Segments = 0
NewPage = #True
PageNumber = 0
Repeat
If NewPage = #True
OggHeader\CapturePattern = $5367674F ;Allways "OggS"
OggHeader\Version = 0 ;Allways 0
If PageNumber > 0
OggHeader\HeaderType = 1 ;1 if more than one page used
Else
OggHeader\HeaderType = 0
EndIf
OggHeader\GranulePosition = 0 ;Not Part of Audio Stream
OggHeader\BitstreamSerialNumber = BitstreamSerialNumber ;Set From original File
OggHeader\PageSequenceNumber = PageNumber ;Set After Main Header
OggHeader\Checksum = 0 ;Set After adding all segments to page
OggHeader\PageSegments = 0 ;Set After Adding segments
PageNumber + 1
Segments = 0
NewPage = #False
LastSegment = #False
ThisPage = 0
EndIf
If BufferSize > 255
BufferSize - 255
Segments + 1
OggHeader\PageSegments = Segments
OggHeader\SegmentTable[Segments - 1] = $FF
*Mem = ReAllocateMemory(*PageBody,BytesRead + 255)
*PageBody = *Mem
FileSeek(FileNumber,BytesRead)
ReadData(FileNumber,*PageBody + ThisPage , 255)
ThisPage + 255
BytesRead + 255
Else
Segments + 1
OggHeader\PageSegments = Segments
OggHeader\SegmentTable[Segments - 1] = BufferSize
*Mem = ReAllocateMemory(*PageBody,BytesRead + BufferSize)
*PageBody = *Mem
FileSeek(FileNumber,BytesRead)
ReadData(FileNumber,*PageBody + ThisPage,255)
BytesRead + BufferSize
ThisPage + BufferSize
BufferSize - BufferSize
LastSegment = #True
EndIf
If Segments = 255 And Not LastSegment
NewPage = #True
;Write Page To File
WriteData(OutputFile,@OggHeader,27 + Segments)
WriteData(OutputFile,*PageBody,ThisPage)
ElseIf LastSegment = #True
;Write Page To File
WriteData(OutputFile,@OggHeader,27 + Segments)
WriteData(OutputFile,*PageBody,ThisPage)
EndIf
Until buffersize = 0
CloseFile(FileNumber)
EndProcedure
Procedure LoadOGGFile(FileName.s)
OriginalFileName = Filename
GetOggOffsets(FileName) ;Used With GetHeaders and later for writing File
Getheaders() ;Split Headers Into temp files
CloseFile(OggFile)
EndProcedure
Procedure.i checksumbuffer(*Buffer,BufferLength.i)
Define iLoop.i
Protected crc_reg.l, b.a
For iLoop = 0 To BufferLength - 1
b = PeekA(*Buffer + iLoop)
crc_reg = (crc_reg << 8) ! PeekL(?crc_lookup + (((crc_reg >> 24) & $ff) ! b) * 4)
Next iLoop
ProcedureReturn crc_reg
EndProcedure
Procedure SequenceFile()
Protected NewFile.i, PageSequence.l = 0, Page.OGGPage
Protected PageStart.i,PageEnd.i,*Page
NewFile = OpenFile(#PB_Any, "OGGOutput.tmp",#PB_File_NoBuffering)
If NewFile
PageStart = 0 ;Just First Page
PageSequence = 0
; >> Process pages <<
While ReadData(NewFile, @Page, 27) = 27 And
Page\CapturePattern = $5367674F And
ReadData(NewFile, @Page + 27, Page\PageSegments) = Page\PageSegments
; >> Calculate file position of next page <<
LastSegment = Page\PageSegments - 1
NextPageLoc = Loc(NewFile)
For i = 0 To LastSegment
NextPageLoc + Page\SegmentTable[i]
Next
PageEnd = NextPageLoc
;Read In Whole Page
*Mem = ReAllocateMemory(*Page,PageEnd - PageStart)
*Page = *Mem
FileSeek(NewFile,PageStart)
ReadData(NewFile,*Page,PageEnd - PageStart)
If PageSequence = 0
;HeaderType $02 on first ogg page
PokeA(*Page + 5,$02)
EndIf
;Page Sequence Number
PokeL(*Page + 18,PageSequence)
;Add Checksum
;Zero Checksum Bytes
PokeL(*Page + 22,0)
PokeL(*Page + 22,checksumbuffer(*Page,PageEnd - PageStart))
FileSeek(NewFile,PageStart)
WriteData(NewFile,*Page,PageEnd - PageStart)
PageSequence + 1
; Set the file pointer to the next page
FileSeek(NewFile, NextPageLoc)
PageStart = NextPageLoc
Wend
EndIf
EndProcedure
Procedure SaveOGGFile()
Protected NewOGGFile.i,OriginalFile.i,*AudioBuffer
;Start Building Output
OutputFile = CreateFile(#PB_Any,"OGGOutput.tmp")
NewOGGFile = OpenFile(#PB_Any,"C:\PB Projects\Ogg Vorbis\vorbis 1.tmp")
PackHeader(NewOGGFile)
NewOGGFile = OpenFile(#PB_Any,"C:\PB Projects\Ogg Vorbis\vorbis 2.tmp")
PackHeader(NewOGGFile)
NewOGGFile = OpenFile(#PB_Any,"C:\PB Projects\Ogg Vorbis\vorbis 3.tmp")
PackHeader(NewOGGFile)
;Add The Audio Bits
OriginalFile = OpenFile(#PB_Any,OriginalFileName)
FileSeek(OriginalFile,Pages(AudioStartPage)\Start)
*AudioBuffer = AllocateMemory(Lof(OriginalFile) - Pages(AudioStartPage)\Start)
ReadData(OriginalFile,*AudioBuffer,Lof(OriginalFile) - Pages(AudioStartPage)\Start)
WriteData(OutputFile,*AudioBuffer,Lof(OriginalFile) - Pages(AudioStartPage)\Start)
CloseFile(OriginalFile)
CloseFile(OutputFile)
SequenceFile()
EndProcedure
DataSection
crc_lookup:
Data.l $00000000,$04c11db7,$09823b6e,$0d4326d9
Data.l $130476dc,$17c56b6b,$1a864db2,$1e475005
Data.l $2608edb8,$22c9f00f,$2f8ad6d6,$2b4bcb61
Data.l $350c9b64,$31cd86d3,$3c8ea00a,$384fbdbd
Data.l $4c11db70,$48d0c6c7,$4593e01e,$4152fda9
Data.l $5f15adac,$5bd4b01b,$569796c2,$52568b75
Data.l $6a1936c8,$6ed82b7f,$639b0da6,$675a1011
Data.l $791d4014,$7ddc5da3,$709f7b7a,$745e66cd
Data.l $9823b6e0,$9ce2ab57,$91a18d8e,$95609039
Data.l $8b27c03c,$8fe6dd8b,$82a5fb52,$8664e6e5
Data.l $be2b5b58,$baea46ef,$b7a96036,$b3687d81
Data.l $ad2f2d84,$a9ee3033,$a4ad16ea,$a06c0b5d
Data.l $d4326d90,$d0f37027,$ddb056fe,$d9714b49
Data.l $c7361b4c,$c3f706fb,$ceb42022,$ca753d95
Data.l $f23a8028,$f6fb9d9f,$fbb8bb46,$ff79a6f1
Data.l $e13ef6f4,$e5ffeb43,$e8bccd9a,$ec7dd02d
Data.l $34867077,$30476dc0,$3d044b19,$39c556ae
Data.l $278206ab,$23431b1c,$2e003dc5,$2ac12072
Data.l $128e9dcf,$164f8078,$1b0ca6a1,$1fcdbb16
Data.l $018aeb13,$054bf6a4,$0808d07d,$0cc9cdca
Data.l $7897ab07,$7c56b6b0,$71159069,$75d48dde
Data.l $6b93dddb,$6f52c06c,$6211e6b5,$66d0fb02
Data.l $5e9f46bf,$5a5e5b08,$571d7dd1,$53dc6066
Data.l $4d9b3063,$495a2dd4,$44190b0d,$40d816ba
Data.l $aca5c697,$a864db20,$a527fdf9,$a1e6e04e
Data.l $bfa1b04b,$bb60adfc,$b6238b25,$b2e29692
Data.l $8aad2b2f,$8e6c3698,$832f1041,$87ee0df6
Data.l $99a95df3,$9d684044,$902b669d,$94ea7b2a
Data.l $e0b41de7,$e4750050,$e9362689,$edf73b3e
Data.l $f3b06b3b,$f771768c,$fa325055,$fef34de2
Data.l $c6bcf05f,$c27dede8,$cf3ecb31,$cbffd686
Data.l $d5b88683,$d1799b34,$dc3abded,$d8fba05a
Data.l $690ce0ee,$6dcdfd59,$608edb80,$644fc637
Data.l $7a089632,$7ec98b85,$738aad5c,$774bb0eb
Data.l $4f040d56,$4bc510e1,$46863638,$42472b8f
Data.l $5c007b8a,$58c1663d,$558240e4,$51435d53
Data.l $251d3b9e,$21dc2629,$2c9f00f0,$285e1d47
Data.l $36194d42,$32d850f5,$3f9b762c,$3b5a6b9b
Data.l $0315d626,$07d4cb91,$0a97ed48,$0e56f0ff
Data.l $1011a0fa,$14d0bd4d,$19939b94,$1d528623
Data.l $f12f560e,$f5ee4bb9,$f8ad6d60,$fc6c70d7
Data.l $e22b20d2,$e6ea3d65,$eba91bbc,$ef68060b
Data.l $d727bbb6,$d3e6a601,$dea580d8,$da649d6f
Data.l $c423cd6a,$c0e2d0dd,$cda1f604,$c960ebb3
Data.l $bd3e8d7e,$b9ff90c9,$b4bcb610,$b07daba7
Data.l $ae3afba2,$aafbe615,$a7b8c0cc,$a379dd7b
Data.l $9b3660c6,$9ff77d71,$92b45ba8,$9675461f
Data.l $8832161a,$8cf30bad,$81b02d74,$857130c3
Data.l $5d8a9099,$594b8d2e,$5408abf7,$50c9b640
Data.l $4e8ee645,$4a4ffbf2,$470cdd2b,$43cdc09c
Data.l $7b827d21,$7f436096,$7200464f,$76c15bf8
Data.l $68860bfd,$6c47164a,$61043093,$65c52d24
Data.l $119b4be9,$155a565e,$18197087,$1cd86d30
Data.l $029f3d35,$065e2082,$0b1d065b,$0fdc1bec
Data.l $3793a651,$3352bbe6,$3e119d3f,$3ad08088
Data.l $2497d08d,$2056cd3a,$2d15ebe3,$29d4f654
Data.l $c5a92679,$c1683bce,$cc2b1d17,$c8ea00a0
Data.l $d6ad50a5,$d26c4d12,$df2f6bcb,$dbee767c
Data.l $e3a1cbc1,$e760d676,$ea23f0af,$eee2ed18
Data.l $f0a5bd1d,$f464a0aa,$f9278673,$fde69bc4
Data.l $89b8fd09,$8d79e0be,$803ac667,$84fbdbd0
Data.l $9abc8bd5,$9e7d9662,$933eb0bb,$97ffad0c
Data.l $afb010b1,$ab710d06,$a6322bdf,$a2f33668
Data.l $bcb4666d,$b8757bda,$b5365d03,$b1f740b4
EndDataSection
EndModule
;Replace this with path and filename of your own ogg file backup first
OGGvorbis::LoadOGGFile("C:\PB Projects\Ogg Vorbis\1st Lady - Never Be Replaced.ogg")
OGGvorbis::GetComments()
;ForEach OGGvorbis::SongTags()
; Debug OGGvorbis::SongTags()\Title
; Debug OGGvorbis::SongTags()\Value
;Next
;when finished editing Comment parser still to do
OGGvorbis::SaveOGGFile()
End
Please let me know if there are any problems or if the process can be speeded up.