ogg vorbis comment (tag)

Just starting out? Need help? Post your questions and find answers here.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ogg vorbis comment (tag)

Post by wilbert »

collectordave wrote:I am moving on to firstly attempt to split the first three vorbis headers onto an ogg page each instead of scrunching them up. So that ogg page 1 is vorbis header 1 (no problem) section 2 comments on to page or pages, can be multiple, then vorbis header 3 on the next page after comments. Only 1 vorbis section to pack in the ogg file then comments. All the audio data is after vorbis header 3 so the most I will have to do is rewrite the page sequence number calculate the CRC for each page and add to the new file.
Sounds like a wise decision.
collectordave wrote:My big worry for later is encodeing images ready to be placed in the comment pages.
To be honest, that's the easy part ...

Code: Select all

; >> EncodeVorbisPicture procedure <<

UseJPEGImageEncoder()

Procedure.s EncodeVorbisPicture(Image, PictureType = 3, JPEGQuality = 7)
  
  Protected *Image = EncodeImage(Image, #PB_ImagePlugin_JPEG, JPEGQuality)
  Protected ImageDataSize = MemorySize(*Image)
  Protected BufferSize = ImageDataSize + 42
  
  ; Allocate Buffer array and copy the encoded image to it
  Protected Dim Buffer.a(BufferSize - 1)
  CopyMemory(*Image, @Buffer(42), ImageDataSize)
  FreeMemory(*Image)
  
  ; Set the additional information
  Buffer(3) = PictureType
  Buffer(7) = 10
  PokeS(@Buffer(8), "image/jpeg", 10, #PB_Ascii)
  Buffer(38) = ImageDataSize >> 24
  Buffer(39) = ImageDataSize >> 16
  Buffer(40) = ImageDataSize >> 8
  Buffer(41) = ImageDataSize
  
  ; Return Base64 encoded Buffer without CRLF
  ProcedureReturn ReplaceString(Base64Encoder(@Buffer(0), BufferSize), #CRLF$, "")
  
EndProcedure



; Test

CreateImage(0, 8, 8)
CreateImage(1, 64, 64)
Debug EncodeVorbisPicture(0)
Debug EncodeVorbisPicture(1)
If you also need PNG encoding, that would still need to be added.

Valid values for PictureType are

Code: Select all

0 - Other
1 - 32x32 pixels 'file icon' (PNG only)
2 - Other file icon
3 - Cover (front)
4 - Cover (back)
5 - Leaflet page
6 - Media (e.g. label side of CD)
7 - Lead artist/lead performer/soloist
8 - Artist/performer
9 - Conductor
10 - Band/Orchestra
11 - Composer
12 - Lyricist/text writer
13 - Recording Location
14 - During recording
15 - During performance
16 - Movie/video screen capture
17 - A bright coloured fish
18 - Illustration
19 - Band/artist logotype
20 - Publisher/Studio logotype
Last edited by wilbert on Tue Aug 13, 2019 3:04 pm, edited 4 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: ogg vorbis comment (tag)

Post by kenmo »

In 2012 kenmo already demonstrated in this example how to determine the length of an ogg file. I tested it with this ogg file with a length of 6:40 minutes (using MacOS 10.6.8 Snow Leopard with PB 5.46 x86 in ASCII mode, Unicode mode didn't work!) and kenmo's example displayed the length correctly. So kenmo's code doesn't have the problem described in collectordave's linked PlaySound() bug.
Thanks for mentioning this! I've updated the procedure to work with Unicode mode / PB 5.70.
viewtopic.php?f=13&t=50333&start=9

It's still a quick and dirty solution, with no guarantees just like 7 years ago :)
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

Hi

Thanks wilbert only jpeg and png images are specified so maybe later for png.

Hi kenmo the bug was a windows bug I tried the code and it was not quite accurate with an 18 minute ogg file.

The bug was not just the length being reported incorrectly but also PlaySound() stopped playing after the 6 minutes as well.

All now fixed in release 5.7 Beta 3 length correct and Playsound() plays it all.

Thanks for all the help

Regards

CD
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

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.

Regards

CD
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ogg vorbis comment (tag)

Post by wilbert »

collectordave wrote:Please let me know if there are any problems or if the process can be speeded up.
From what I've read, if only a few bytes are changing (like page number in this case) it should be possible to update a crc without recalculating everything.
If you Google for "Combining and splitting CRCs" you will find some information. Unfortunately I find it hard to understand how it works and how fast/slow it is. :?
Windows (x64)
Raspberry Pi OS (Arm64)
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

Hi wilbert

Unfortunately I do not have a clue.

I now have a scrappy version that actually allows me to edit text tags and I have compared the speed against a couple of other tag editors and it seems to hold it's own. The crc calculation is I think optimised, the datasection is a list of pre calculated vectors so that doesn't have to be done during the crc calculation (sounds good but I do not know what that means).

The only improvement I can think of is to use a single buffer to write the pagesequence and crc to each page. I am just a little nervous of buffers and pointers this is my first go at them. I do a lot of disc access when sequencing the file.

Regards

CD
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

Hi All

I now have it working.

However when adding images I get a specific fault.

The sequence is I select the image encode and add to the file.

I then reload the file to show the image.

If I do this immediatly after adding the image i get a memory acces error on line 64 of the following code.

If I close the application then reload the file there is the picture no problem.

I am obviously not clearing something before reloading but I have no idea what?

The following code simply works

Code: Select all

UseJPEGImageDecoder()
UseJPEGImageEncoder()

  Structure VorbisPictureInfo
    Type.l
    MIMEType.s
    Description.s
  EndStructure



  Procedure.s EncodeVorbisPicture(Image, PictureType = 3, JPEGQuality = 7)
 
  Protected *Image = EncodeImage(Image, #PB_ImagePlugin_JPEG, JPEGQuality)
  Protected ImageDataSize = MemorySize(*Image)
  Protected BufferSize = ImageDataSize + 42
 
  ; Allocate Buffer array and copy the encoded image to it
  Protected Dim Buffer.a(BufferSize - 1)
  CopyMemory(*Image, @Buffer(42), ImageDataSize)
  FreeMemory(*Image)
 
  ; Set the additional information
  Buffer(3) = PictureType
  Buffer(7) = 10
  PokeS(@Buffer(8), "image/jpeg", 10, #PB_Ascii)
  Buffer(38) = ImageDataSize >> 24
  Buffer(39) = ImageDataSize >> 16
  Buffer(40) = ImageDataSize >> 8
  Buffer(41) = ImageDataSize
 
  ; Return Base64 encoded Buffer without CRLF
  ProcedureReturn ReplaceString(Base64Encoder(@Buffer(0), BufferSize), #CRLF$, "")
 
EndProcedure
  
  Procedure.i DecodeVorbisPicture(Base64String.s, *Info.VorbisPictureInfo = #Null)
 
  Protected.i i, n = Len(Base64String)
  Protected Dim InputBuffer.a((n >> 6) * 66 + n & 63)
  Protected Dim P.a((3 * n) >> 2)
  Protected *c.Character = @Base64String
  Protected *a.Ascii = @InputBuffer()
 
  ; Decode Base64
  i = 0
  While *c\c
    *a\a = *c\c : i + 1
    *a + 1 : *c + SizeOf(Character)
    If i = 64
      *a\a = #CR : *a + 1
      *a\a = #LF : *a + 1
      i = 0 
    EndIf   
  Wend
  Base64DecoderBuffer(@InputBuffer(), ArraySize(InputBuffer()), @P(), ArraySize(P()))
  
  ; Type
  n = P(0)<<24 | P(1)<<16 | P(2)<<8 | P(3) : i = 4
  If *Info : *Info\Type = n : EndIf
 
  ; MIMEType
  n = P(i)<<24 | P(i+1)<<16 | P(i+2)<<8 | P(i+3) : i + 4
  If *Info : *Info\MIMEType = PeekS(@P(i), n, #PB_Ascii) : EndIf ; This is where I get the error!
  i + n
 
  ; Description 
  n = P(i)<<24 | P(i+1)<<16 | P(i+2)<<8 | P(i+3) : i + 4

  i + n + 16
 
  ; Return image
  n = P(i)<<24 | P(i+1)<<16 | P(i+2)<<8 | P(i+3) : i + 4
  If n
    ProcedureReturn CatchImage(#PB_Any, @P(i), n)
  Else
    ProcedureReturn #Null
  EndIf
 
EndProcedure

Global Window_0

Global Image_0



CurrentFile.s = OpenFileRequester("Please choose jpg file", "", "Picture Files (*.jpg)|*.jpg", 0)


Thisimage.i = LoadImage(#PB_Any,Currentfile)

ResizeImage(Thisimage,250,250)

If IsImage(ThisImage)
  
  encimg.s = EncodeVorbisPicture(Thisimage)
  
  
  decImage.i = DecodeVorbisPicture(encimg)
  
  Debug decImage
  
  Window_0 = OpenWindow(#PB_Any, 40, 40, 600, 400, "", #PB_Window_SystemMenu)
  Image_0 = ImageGadget(#PB_Any, 39, 10, 240, 240, 0)
  
  SetGadgetState(Image_0,ImageID(decImage))
  
  While WindowEvent() <> #PB_Event_CloseWindow : Wend
  
  
  
EndIf
Anyone any ideas?

collectordave

PS I think I have traced my error.

I was passing a pointer to a variable that did not exist at the time I was passing it (@Info)

Hopefully that is the way it is said.

Cd
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
potorieg
New User
New User
Posts: 1
Joined: Sun Aug 11, 2019 6:18 pm

Re: ogg vorbis comment (tag)

Post by potorieg »

Do you have an ogg file with embedded artwork ?
I tried to find one online to test with but can't find one.
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

I have quite a few.

Not worth testing with artwork as yet unless you would like to try the code as is then you can add your own artwork.

I am just adding the standard image types mentioned by wilbert in an earlier post though not all.

It will take me a few hours before it is testable but will post a link if anyone is interested no help file at all yet.

Regards

CD
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

Managed to get it working you can find a link to the code here. viewtopic.php?f=27&t=73426&p=540703#p540703
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ogg vorbis comment (tag)

Post by wilbert »

collectordave wrote:Managed to get it working you can find a link to the code here. viewtopic.php?f=27&t=73426&p=540703#p540703
I don't know if you are still interested in improving speed.
I've been looking into CRC calculations and posted both a module ( viewtopic.php?f=12&t=73462 ) and a single procedure ( viewtopic.php?f=12&t=73468 ).
To get the same results as the procedure you are currently using, the module needs to be used with #CRC32_OGG, the procedure needs to be configured with Width=32, Poly=$04c11db7, Init=0, RefIn=#False, RefOut=#False, XOrOut=0 .

On my computer, the single procedure is somewhere between two and three times faster compared to your current procedure and the module over ten times faster.
I don't know if it would have a significant impact on the total speed of your application but you can try it if you wish.
It's likely that you would only notice the difference with large ogg files or if you would do batch processing of a whole directory of ogg files at once.
If you are happy with the current speed, you can also keep it as it is of course since your current code also works fine. :)
Windows (x64)
Raspberry Pi OS (Arm64)
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

FastCRC module is such a great speed improvement!

Will integrate it ASAP. Opens up the possibility of batch tagging ogg files.

Tested with check ogg posted on your module topic.


Thanks wilbert

CD
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

Hi all

Added support for png images just one problem.

The image encodes ok as a png then decodes ok but when displayed the background is totally black.

Maybe missing a drawing mode?

I am drawing onto a canvas gadget.

If I swap to an image gadget it seems to work ok?

Anyone any ideas?
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ogg vorbis comment (tag)

Post by wilbert »

collectordave wrote:Added support for png images just one problem.

The image encodes ok as a png then decodes ok but when displayed the background is totally black.
Did you encode the png as a 32 bit image ?
Windows (x64)
Raspberry Pi OS (Arm64)
collectordave
Addict
Addict
Posts: 1309
Joined: Fri Aug 28, 2015 6:10 pm
Location: Portugal

Re: ogg vorbis comment (tag)

Post by collectordave »

I have modyfied the encode procedure to this

Code: Select all

Protected *Image = EncodeImage(Image, #PB_ImagePlugin_PNG,JPEGQuality,32)
Adding the 32 at the end. The original png image has a depth of 32 as well.

It seems to make no difference.

Regards

CD

PS

May have found my problem.

Need to use Drawalphaimage()

Adjust aspect ration just before seems to work. Still testing
Any intelligent fool can make things bigger and more complex. It takes a touch of genius — and a lot of courage to move in the opposite direction.
Post Reply