WIN/MAC - [MODULE] MP3-Info

Share your advanced PureBasic knowledge/code with the community.
User avatar
Bisonte
Addict
Addict
Posts: 1226
Joined: Tue Oct 09, 2007 2:15 am

WIN/MAC - [MODULE] MP3-Info

Post by Bisonte »

Hi there....

jak64 ask for a way to get the duration of a mp3 file... and it is more than just read a value ... So I dig in my sources and .... voila.
This module based on "MP3 Header auslesen" from the german forum (Thorsten1867)

This module get a lot of infos about a mp3 file. Also the ID3Tags. The Duration is sometimes not 100% exact, but who is perfect ;)

Code: Select all

DeclareModule MP3Info
  
;{ ===========================================================================================================[Header ]
;: 
;: Name ......... : Name
;: Type ......... : Module
;: Author ....... : George Bisonte
;: Compiler ..... : PureBasic V5.60
;: Flags ........ : Unicode/XP-Skin/UserMode/ThreadSafe
;: Subsystem .... : none
;: TargetOS ..... : Windows / MacOs / Linux ?
;: Link ......... : none
;: Description .. : Read MP3 Data. Based on Thorsten1867 : https://www.purebasic.fr/german/viewtopic.php?p=356500
;: License ...... : MIT License 
;: 
;: Permission is hereby granted, free of charge, to any person obtaining a copy
;: of this software and associated documentation files (the "Software"), to deal
;: in the Software without restriction, including without limitation the rights
;: to use, copy, modify, merge, publish, distribute, sublicense, And/Or sell
;: copies of the Software, and to permit persons to whom the Software is
;: furnished to do so, subject to the following conditions:
;:  
;: The above copyright notice and this permission notice shall be included in all
;: copies or substantial portions of the Software.
;: 
;: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;: SOFTWARE. 
;:
;}
  
  EnableExplicit
  
  Structure mp3_data
    ID.i
    Layer.i
    Protection.i
    Bitrate.i
    Samplingrate.i
    Padding.i
    Private.i
    Channel.i
    Extension.i
    Copyright.i
    Original.i
    Emphasis.i
    FrameLength.i
    Frames.i
    Duration.i
    ID3v10.i
    ID3v11.i
    ID3v2.i
    Map ID3Tag.s()
  EndStructure
  
  Declare.i GetMP3Info(FileName.s, *mp3_data.mp3_data)
  
EndDeclareModule
Module        MP3Info
  
  Macro uInt8(Value)
    Value & $FF  
  EndMacro
  Macro uInt16(Value)
    (Value>>8&$FF) | (Value<<8&$FF00)
  EndMacro
  
  #MPEG25   = 0 ; MPEG Version 2.5 (later extension of MPEG 2)
  #MPEG2    = 2 ; MPEG Version 2 (ISO/IEC 13818-3)
  #MPEG1    = 3 ; MPEG Version 1 (ISO/IEC 11172-3)
  #Layer1   = 3
  #Layer2   = 2
  #Layer3   = 1
  #Stereo   = 0
  #Joint    = 1 ; Joint stereo (Stereo)
  #DualMono = 2 ; Dual channel (2 mono channels)
  #Mono     = 3 ; Single channel (Mono)
  #None     = 0
  #MS50_15  = 1 ; 50/15 ms
  #CCIT_J17 = 3 ; CCIT J.17 
  
  Structure ID3TAGV1
    TAG.a[3]
    Title.a[30]
    Artist.a[30]
    Album.a[30]
    Year.a[4]
    Comment.b[30]
    Genre.a
  EndStructure
  Structure ID3TAGV2_HEADER
    Identifier.a[3]
    Version.a[2]
    flags.a
    size.b[4]
  EndStructure
  Structure ID3TAGV2_FRAME
    FrameID.a[4]
    Size.l
    Flags.a[2]
    Encoding.a
    *Data
  EndStructure
  
  Global Dim SamplesPerFrame(3, 3)
  Global Dim Genre.s(125)
  
  SamplesPerFrame(#Layer1, #MPEG1)  = 384
  SamplesPerFrame(#Layer1, #MPEG2)  = 384
  SamplesPerFrame(#Layer1, #MPEG25) = 384
  
  SamplesPerFrame(#Layer2, #MPEG1)  = 1152
  SamplesPerFrame(#Layer2, #MPEG2)  = 1152
  SamplesPerFrame(#Layer2, #MPEG25) = 1152
  
  SamplesPerFrame(#Layer3, #MPEG1)  = 1152
  SamplesPerFrame(#Layer3, #MPEG2)  = 576
  SamplesPerFrame(#Layer3, #MPEG25) = 576
  
  ;{ From Celtic88 : https://www.purebasic.fr/english/viewtopic.php?p=506795#p506795
  Macro SwapDataType(iValue,iReturn)
    Protected vSize.b = SizeOf(iReturn)-1,ii.b:For ii= 0 To vSize:PokeB(@iReturn+ii,PeekB(@iValue+(vSize-ii))):Next
  EndMacro
  
  Procedure.w SwapWord(iValue.w)
    Protected iReturn.w
    SwapDataType(iValue,iReturn)
    ProcedureReturn iReturn
  EndProcedure
  
  Procedure.l SwapLong(iValue.l)
    Protected iReturn.l
    SwapDataType(iValue,iReturn)
    ProcedureReturn iReturn
  EndProcedure
  ;}
  
  ; Windows Only
  ;   Procedure.l SwapLong(Value.l)
  ;     !MOV Eax, [p.v_Value]
  ;     !BSWAP Eax
  ;     ProcedureReturn
  ;   EndProcedure
  
  Procedure.i GetBitrate(ID.i, Layer.i, Value.i)
    
    If ID = #MPEG1 ;{ MPEG 1
      
      Select Value
        Case 1
          ProcedureReturn 32
        Case 2
          Select Layer
            Case #Layer1   
              ProcedureReturn 64
            Case #Layer2 
              ProcedureReturn 48
            Case #Layer3
              ProcedureReturn 40
          EndSelect   
        Case 3
          Select Layer
            Case #Layer1   
              ProcedureReturn 96
            Case #Layer2 
              ProcedureReturn 56
            Case #Layer3
              ProcedureReturn 48
          EndSelect
        Case 4
          Select Layer
            Case #Layer1   
              ProcedureReturn 128
            Case #Layer2 
              ProcedureReturn 64
            Case #Layer3
              ProcedureReturn 56
          EndSelect
        Case 5
          Select Layer
            Case #Layer1   
              ProcedureReturn 160
            Case #Layer2 
              ProcedureReturn 80
            Case #Layer3
              ProcedureReturn 64
          EndSelect
        Case 6
          Select Layer
            Case #Layer1   
              ProcedureReturn 192
            Case #Layer2 
              ProcedureReturn 96
            Case #Layer3
              ProcedureReturn 80
          EndSelect
        Case 7
          Select Layer
            Case #Layer1   
              ProcedureReturn 224
            Case #Layer2 
              ProcedureReturn 112
            Case #Layer3
              ProcedureReturn 96
          EndSelect
        Case 8
          Select Layer
            Case #Layer1   
              ProcedureReturn 256
            Case #Layer2 
              ProcedureReturn 128
            Case #Layer3
              ProcedureReturn 112
          EndSelect
        Case 9
          Select Layer
            Case #Layer1   
              ProcedureReturn 288
            Case #Layer2 
              ProcedureReturn 160
            Case #Layer3
              ProcedureReturn 128
          EndSelect
        Case 10
          Select Layer
            Case #Layer1   
              ProcedureReturn 320
            Case #Layer2 
              ProcedureReturn 192
            Case #Layer3
              ProcedureReturn 160
          EndSelect
        Case 11
          Select Layer
            Case #Layer1   
              ProcedureReturn 352
            Case #Layer2 
              ProcedureReturn 224
            Case #Layer3
              ProcedureReturn 192
          EndSelect
        Case 12 
          Select Layer
            Case #Layer1   
              ProcedureReturn 384
            Case #Layer2 
              ProcedureReturn 256
            Case #Layer3
              ProcedureReturn 224
          EndSelect
        Case 13
          Select Layer
            Case #Layer1   
              ProcedureReturn 416
            Case #Layer2 
              ProcedureReturn 320
            Case #Layer3
              ProcedureReturn 256
          EndSelect
        Case 14 
          Select Layer
            Case #Layer1   
              ProcedureReturn 448
            Case #Layer2 
              ProcedureReturn 384
            Case #Layer3
              ProcedureReturn 320
          EndSelect
      EndSelect   
      ;}
    Else
      
      Select Value
        Case 1
          If Layer = #Layer1
            ProcedureReturn 32
          Else
            ProcedureReturn 8
          EndIf 
        Case 2
          If Layer = #Layer1
            ProcedureReturn 48
          Else
            ProcedureReturn 16
          EndIf   
        Case 3
          If Layer = #Layer1
            ProcedureReturn 56
          Else
            ProcedureReturn 24
          EndIf 
        Case 4
          If Layer = #Layer1
            ProcedureReturn 64
          Else
            ProcedureReturn 32
          EndIf   
        Case 5
          If Layer = #Layer1
            ProcedureReturn 80
          Else
            ProcedureReturn 40
          EndIf 
        Case 6
          If Layer = #Layer1
            ProcedureReturn 96
          Else
            ProcedureReturn 48
          EndIf
        Case 7
          If Layer = #Layer1
            ProcedureReturn 112
          Else
            ProcedureReturn 56
          EndIf 
        Case 8
          If Layer = #Layer1
            ProcedureReturn 128
          Else
            ProcedureReturn 64
          EndIf 
        Case 9
          If Layer = #Layer1
            ProcedureReturn 144
          Else
            ProcedureReturn 80
          EndIf 
        Case 10
          If Layer = #Layer1
            ProcedureReturn 160
          Else
            ProcedureReturn 96
          EndIf 
        Case 11
          If Layer = #Layer1
            ProcedureReturn 176
          Else
            ProcedureReturn 112
          EndIf 
        Case 12 
          If Layer = #Layer1
            ProcedureReturn 192
          Else
            ProcedureReturn
          EndIf 
        Case 13
          If Layer = #Layer1
            ProcedureReturn 224
          Else
            ProcedureReturn 144
          EndIf 
        Case 14 
          If Layer = #Layer1
            ProcedureReturn 256
          Else
            ProcedureReturn 160
          EndIf 
      EndSelect
      
    EndIf
    
  EndProcedure 
  Procedure.i GetSamplingRate(ID.i, Value.i)
    
    Select ID
      Case #MPEG1
        Select Value
          Case 0
            ProcedureReturn 44100
          Case 1
            ProcedureReturn 48000
          Case 2
            ProcedureReturn 32000
        EndSelect
      Case #MPEG2
        Select Value
          Case 0
            ProcedureReturn 22050
          Case 1
            ProcedureReturn 24000
          Case 2
            ProcedureReturn 16000
        EndSelect
      Case #MPEG25
        Select Value
          Case 0
            ProcedureReturn 11025
          Case 1
            ProcedureReturn 12000
          Case 2
            ProcedureReturn 8000
        EndSelect
    EndSelect   
    
  EndProcedure
  Procedure.i LoadBinaryFile(FileName.s)
    
    Protected ID, Size.q, Bytes.q, *Memory = #Null
    
    ID = ReadFile(#PB_Any, FileName, #PB_File_SharedRead)
    
    If ID
      
      Size = Lof(ID)
      
      If Size
        
        *Memory = AllocateMemory(Size)
        
        If *Memory
          
          Bytes = ReadData(ID, *Memory, Size)
          
          If Bytes = 0 Or Bytes <> Size
            
            FreeMemory(*Memory)
            *Memory = #Null
            
          EndIf
          
        EndIf
        
        CloseFile(ID)
        
      EndIf
      
    EndIf
    
    ProcedureReturn *Memory
    
  EndProcedure
     
  Procedure.i GetMP3Info(FileName.s, *mp3_data.mp3_data)
    
    Protected *mp3
    Protected *ID3V1_Frame.ID3TAGV1
    Protected *ID3V2_Header.ID3TAGV2_HEADER
    Protected *ID3V2_Frame.ID3TAGV2_FRAME
    Protected *mp3_start
    Protected *mp3_stop
    Protected *Memory
    Protected *EndMem
    
    Protected Bytes.w, Byte.b, Size, i, PosNow, EndPosition
    Protected FrameID.s, FrameText.s, FrameSize, Encoding, Add
    Protected PaddingBytes
    
    *mp3 = LoadBinaryFile(FileName)
    
    If Not *mp3       : ProcedureReturn #False : EndIf
    If Not *mp3_data  : ProcedureReturn #False : EndIf
    
    If Genre(0) = ""
      Restore GENRES
      For i = 0 To 125
        Read.s Genre(i)
      Next i
    EndIf
    
    ClearStructure(*mp3_data, mp3_data)
    
    NewMap *mp3_data\ID3Tag()
    
    ; Get ID3V2 Tag if present
    
    *ID3V2_Header = *mp3

    If PeekS(*ID3V2_Header + OffsetOf(ID3TAGV2_HEADER\Identifier), SizeOf(ID3TAGV2_HEADER\Identifier), #PB_Ascii) = "ID3" And *ID3V2_Header\Version[0] = 3
      Size = 0
      For i = 3 To 0 Step - 1
        Size + ((*ID3V2_Header\Size[3 - i] & $7F) << (7 * i))
      Next i
      PosNow = SizeOf(ID3TAGV2_HEADER)
      *mp3_data\ID3v2 = #True
    EndIf
    
    Repeat
      
      *ID3V2_Frame = *mp3 + PosNow      
      
      FrameID = PeekS(*ID3V2_Frame + OffsetOf(ID3TAGV2_FRAME\FrameID), SizeOf(ID3TAGV2_FRAME\FrameID), #PB_Ascii)
      
      If FrameID = "" : Break : EndIf
      FrameSize = *ID3V2_Frame\Size & $FFFFFFFF
      FrameSize = SwapLong(FrameSize)
      EndPosition + FrameSize
      
      Encoding = #PB_Ascii
      Add      = 1
      If *ID3V2_Frame\Encoding = 1
        Encoding = #PB_UTF16
        Add      = 2
      EndIf
      
      FrameSize - 1
      If Left(LCase(FrameID), 1) = "t"
        If PeekW(*ID3V2_Frame + OffsetOf(ID3TAGV2_FRAME\Data)) & $FFFF = $FFFE Or PeekW(*ID3V2_Frame + OffsetOf(ID3TAGV2_FRAME\Data)) & $FFFF = $FEFF
          FrameText = PeekS(*ID3V2_Frame + OffsetOf(ID3TAGV2_FRAME\Data) + 2, (FrameSize / Add) - 1, #PB_UTF16)
        Else
          FrameText = PeekS(*ID3V2_Frame + OffsetOf(ID3TAGV2_FRAME\Data), FrameSize / Add, Encoding)
        EndIf
      ElseIf LCase(FrameID) = "comm"
        If PeekW(*ID3V2_Frame + 3 + OffsetOf(ID3TAGV2_FRAME\Data)) & $FFFF = $FFFE Or PeekW(*ID3V2_Frame + 3 + OffsetOf(ID3TAGV2_FRAME\Data)) & $FFFF = $FEFF
          FrameText = PeekS(*ID3V2_Frame + OffsetOf(ID3TAGV2_FRAME\Data) + 5, (FrameSize / Add) - 4, #PB_UTF16)
        Else
          FrameText = PeekS(*ID3V2_Frame + 3 + OffsetOf(ID3TAGV2_FRAME\Data), FrameSize / Add, Encoding)
        EndIf
      EndIf    
      
      *mp3_data\ID3Tag(UCase(FrameID)) = FrameText
      
      PosNow + FrameSize + OffsetOf(ID3TAGV2_FRAME\Data)
      
    Until EndPosition >= Size
    
    *mp3_start = *mp3 + Size
    
    ; Get ID3V1 Tag if present
    *ID3V1_Frame = *mp3 + MemorySize(*mp3) - 128
    
    If PeekS(*ID3V1_Frame + OffsetOf(ID3TAGV1\TAG), 3, #PB_Ascii) = "TAG"
      *mp3_stop = *ID3V1_Frame - 1
      
      If Not FindMapElement(*mp3_data\ID3Tag(), "TIT2")
        *mp3_data\ID3Tag("TIT2") = PeekS(*ID3V1_Frame + OffsetOf(ID3TAGV1\Title), SizeOf(ID3TAGV1\Title), #PB_Ascii)
      EndIf
      
      If Not FindMapElement(*mp3_data\ID3Tag(), "TPE1")
        *mp3_data\ID3Tag("TPE1") = PeekS(*ID3V1_Frame + OffsetOf(ID3TAGV1\Artist), SizeOf(ID3TAGV1\Artist), #PB_Ascii)
      EndIf
      
      If Not FindMapElement(*mp3_data\ID3Tag(), "TYER")
        *mp3_data\ID3Tag("TYER") = PeekS(*ID3V1_Frame + OffsetOf(ID3TAGV1\Year), SizeOf(ID3TAGV1\Year), #PB_Ascii)
      EndIf
      
      If Not FindMapElement(*mp3_data\ID3Tag(), "GENRE")
        If *ID3V1_Frame\Genre >= 0 And *ID3V1_Frame\Genre <=125
          *mp3_data\ID3Tag("TCON") = Genre(*ID3V1_Frame\Genre)
        EndIf
      EndIf
      
      
      If *ID3V1_Frame\Comment[28] = 0
        *mp3_data\ID3v11 = #True
        If Not FindMapElement(*mp3_data\ID3Tag(), "TRCK")
          *mp3_data\ID3Tag("TRCK") = Str(*ID3V1_Frame\Comment[29])
        EndIf
        If Not FindMapElement(*mp3_data\ID3Tag(), "COMM")
          *mp3_data\ID3Tag("COMM") = PeekS(*ID3V1_Frame + OffsetOf(ID3TAGV1\Comment), 28, #PB_Ascii)
        EndIf        
      Else
        *mp3_data\ID3v10 = #True
        If Not FindMapElement(*mp3_data\ID3Tag(), "TRCK")
          *mp3_data\ID3Tag("TRCK") = Str(0)
        EndIf
        If Not FindMapElement(*mp3_data\ID3Tag(), "COMM")
          *mp3_data\ID3Tag("COMM") = PeekS(*ID3V1_Frame + OffsetOf(ID3TAGV1\Comment), SizeOf(ID3TAGV1\Comment), #PB_Ascii)
        EndIf        
      EndIf
      
    Else
      *mp3_stop = *mp3 + MemorySize(*mp3)
    EndIf
    
    *Memory = *mp3_start
    *EndMem = *mp3_stop
    
    Repeat
      Bytes = uint16(PeekW(*Memory))
      If (Bytes >> 5) & %11111111111 = 2047 : Break : EndIf ; Sync
      *Memory + 1
    Until *Memory >= *EndMem
    
    If (Bytes >> 5) & %11111111111 = 2047
      
      *mp3_data\ID         = (Bytes >> 3) & %11
      *mp3_data\Layer      = (Bytes >> 1) & %11
      *mp3_data\Protection = Bytes & %1
      
      Byte = uint8(PeekB(*Memory + 2))
      
      *mp3_data\Bitrate      = GetBitrate(*mp3_data\ID, *mp3_data\Layer, (Byte >> 4) & %1111)
      *mp3_data\Samplingrate = GetSamplingRate(*mp3_data\ID, (Byte >> 2) & %11)
      *mp3_data\Padding      = (Byte >> 1) & %1
      *mp3_data\Private      = Byte & %1
      
      Byte = uint8(PeekB(*Memory + 3))
      
      *mp3_data\Channel   = (Byte >> 6) & %11
      *mp3_data\Extension = (Byte >> 4) & %11
      *mp3_data\Copyright = (Byte >> 3) & %1
      *mp3_data\Original  = (Byte >> 2) & %1
      *mp3_data\Emphasis  = Byte & %11

    EndIf
    
    If *mp3_data\Protection
      *Memory + 2  
    EndIf
    
    If *mp3_data\Padding
      If *mp3_data\Layer = #Layer1
        PaddingBytes = 4
      Else
        PaddingBytes = 1  
      EndIf
    Else
      PaddingBytes = 0
    EndIf
    
    *mp3_data\FrameLength = (144 * (*mp3_data\Bitrate * 1000) / *mp3_data\Samplingrate) + PaddingBytes
    *mp3_data\Frames      = (*mp3_stop - *mp3_start) / *mp3_data\FrameLength
    *mp3_data\Duration    = (*mp3_data\Frames * SamplesPerFrame(*mp3_data\Layer, *mp3_data\ID)) / 44100
    
    FreeMemory(*mp3)
    
    ProcedureReturn #True
    
  EndProcedure
  
  DataSection
    GENRES:
    Data.s "Blues",           "Classic Rock", "Country",        "Dance",             "Disco",            "Funk",             "Grunge",           "Hip-Hop"
    Data.s "Jazz",            "Metal",        "New Age",        "Oldies",            "Other",            "Pop",              "R&B",              "Rap"
    Data.s "Reggae",          "Rock",         "Techno",         "Industrial",        "Alternative",      "Ska",              "Death Metal",      "Pranks"
    Data.s "Soundtrack",      "Euro-Techno",  "Ambient",        "Trip-Hop",          "Vocal",            "Jazz+Funk",        "Fusion",           "Trance"
    Data.s "Classical",       "Instrumental", "Acid",           "House",             "Game",             "Sound Clip",       "Gospel",           "Noise"
    Data.s "AlternRock",      "Bass",         "Soul",           "Punk",              "Space",            "Meditative",       "Instrumental Pop", "Instrumental Rock"
    Data.s "Ethnic",          "Gothic",       "Darkwave",       "Techno-Industrial", "Electronic",       "Pop-Folk",         "Eurodance",        "Dream"
    Data.s "Southern Rock",   "Comedy",       "Cult",           "Gangsta",           "Top 40",           "Christian Rap",    "PopFunk",          "Jungle"
    Data.s "Native American", "Cabaret",      "New Wave",       "Psychadelic",       "Rave",             "Showtunes",        "Trailer",          "Lo-Fi"
    Data.s "Tribal",          "Acid Punk",    "Acid Jazz",      "Polka",             "Retro",            "Musical",          "Rock & Roll",      "Hard Rock"
    Data.s "Folk",            "Folk-Rock",    "National Folk",  "Swing",             "Fast Fusion",      "Bebob",            "Latin",            "Revival"
    Data.s "Celtic",          "Bluegrass",    "Avantgarde",     "Gothic Rock",       "Progressive Rock", "Psychedelic Rock", "Symphonic Rock",   "Slow Rock"
    Data.s "Big Band",        "Chorus",       "Easy Listening", "Acoustic",          "Humour",           "Speech",           "Chanson",          "Opera"
    Data.s "Chamber Music",   "Sonata",       "Symphony",       "Booty Bass",        "Primus",           "Porn Groove",      "Satire",           "Slow Jam"
    Data.s "Club",            "Tango",        "Samba",          "Folklore",          "Ballad",           "Power Ballad",     "Rhythmic Soul",    "Freestyle"
    Data.s "Duet",            "Punk Rock",    "Drum Solo",      "A capella",         "Euro-House",       "Dance Hall"
    Data.s ""
  EndDataSection
  
EndModule

UseModule     MP3Info

Procedure.s FormatSeconds(lSeconds.q, Days = 0) ; by Hroudtwolf
  
  Protected lHours.q, lMinutes, lDays.q, String.s, First.s
  
  lMinutes = lSeconds / 60
  lSeconds = lSeconds % 60
  lHours   = lMinutes / 60
  lMinutes = lMinutes % 60
  
  If Days
    If lHours =>24
      lDays = lHours / 24
      lHours = lHours % 24
    EndIf
    First.s = Str(lDays) + ":" + RSet (Str(lHours) , 2 , "0") + ":"
  Else
    If lHours > 0
      First.s = Str(lHours) + ":"
    Else
      First.s = ""
    EndIf
  EndIf
  
  String.s = First + RSet (Str(lMinutes) , 2 , "0") + ":" + RSet (Str(lSeconds) , 2 , "0")
  
  ProcedureReturn String
  
EndProcedure

Define m.mp3_data

GetMP3Info("Your MP3 File.mp3", @m)

Debug FormatSeconds(m\Duration)

ForEach m\ID3Tag()
  Debug MapKey(m\ID3Tag()) + ": " + m\ID3Tag()
Next

Last edited by Bisonte on Wed Mar 15, 2023 5:58 pm, edited 2 times in total.
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
English is not my native language... (I often use DeepL to translate my texts.)
WilliamL
Addict
Addict
Posts: 1215
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: [MODULE] MP3-Info

Post by WilliamL »

Hey, this looks interesting.

It might work on a Mac without the !MOV and !BSWAP. Is there any alternative to using assembly?

Code: Select all

Procedure.l SwapLong(Value.l)
    !MOV Eax, [p.v_Value]
    !BSWAP Eax
    ProcedureReturn
EndProcedure
If I REM out those lines I get:
/Users/vering2/Desktop/Downpour.mp3 (I added this line)
04:40 (should be 3:19)
TCON: SoundtrackTENC (Soundtrack is correct!)
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
User avatar
Bisonte
Addict
Addict
Posts: 1226
Joined: Tue Oct 09, 2007 2:15 am

Re: [MODULE] MP3-Info

Post by Bisonte »

Celtic88 wrote some procedures that might help : viewtopic.php?p=506795#p506795
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
English is not my native language... (I often use DeepL to translate my texts.)
jak64
Enthusiast
Enthusiast
Posts: 502
Joined: Sat Aug 15, 2020 5:02 pm
Location: Ciboure (France)

Re: [MODULE] MP3-Info

Post by jak64 »

Hello Bison,
I saw Celtic88's post but haven't tested it yet.
Yesterday I tested the code of ZX80.

See you
WilliamL
Addict
Addict
Posts: 1215
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: [MODULE] MP3-Info

Post by WilliamL »

Thanks Bisonte,

That worked.

Now I get: (which is correct)
Users/vering2/Desktop/Downpour.mp3
04:40
COMM:
TPE1: Atom Music Audio
APIC: 2019
TCON: Soundtrack
TIT2: Downpour
TALB: A New Decade
TYER: 2019
TENC: Fission
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
User avatar
Bisonte
Addict
Addict
Posts: 1226
Joined: Tue Oct 09, 2007 2:15 am

Re: WIN/MAC - [MODULE] MP3-Info

Post by Bisonte »

Thanks for testing.... I changed it in the first post !
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
English is not my native language... (I often use DeepL to translate my texts.)
jak64
Enthusiast
Enthusiast
Posts: 502
Joined: Sat Aug 15, 2020 5:02 pm
Location: Ciboure (France)

Re: WIN/MAC - [MODULE] MP3-Info

Post by jak64 »

Hello Bisonte,
I tested with an mp3 (I took the code which is in the first post of "Thu Mar 09, 2023 6:30 pm"

I recover the duration of the mp3 well but also a second line with strange characters!

03:41
ŸÛ°@:

See you soon
User avatar
Caronte3D
Addict
Addict
Posts: 1027
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: WIN/MAC - [MODULE] MP3-Info

Post by Caronte3D »

Use only: FormatSeconds(m\Duration) If you can get only the duration.
jak64
Enthusiast
Enthusiast
Posts: 502
Joined: Sat Aug 15, 2020 5:02 pm
Location: Ciboure (France)

Re: WIN/MAC - [MODULE] MP3-Info

Post by jak64 »

OK Caronte3D

Thank you
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: [MODULE] MP3-Info

Post by mk-soft »

WilliamL wrote: Mon Mar 13, 2023 9:25 pm Hey, this looks interesting.

It might work on a Mac without the !MOV and !BSWAP. Is there any alternative to using assembly?

Code: Select all

Procedure.l SwapLong(Value.l)
    !MOV Eax, [p.v_Value]
    !BSWAP Eax
    ProcedureReturn
EndProcedure
If I REM out those lines I get:
/Users/vering2/Desktop/Downpour.mp3 (I added this line)
04:40 (should be 3:19)
TCON: SoundtrackTENC (Soundtrack is correct!)
Should already be used in ASM as it is very optimised and for C-backend (M1 and ARM) here is the optimised alternative.
Link: ASM and C-backend bswap32
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
jak64
Enthusiast
Enthusiast
Posts: 502
Joined: Sat Aug 15, 2020 5:02 pm
Location: Ciboure (France)

Re: WIN/MAC - [MODULE] MP3-Info

Post by jak64 »

Thank you WilliamL.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: WIN/MAC - [MODULE] MP3-Info

Post by Kwai chang caine »

Works nice here
Thanks a lot for this very useful code 8)
ImageThe happiness is a road...
Not a destination
Post Reply