Music.Length

View: New views
16 Messages — Rating Filter:   Alert me  

Music.Length

by Patrik Karlsson-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello, I'm about to port an old Delphiapp to a nicer environment and
I'm looking at Gambas to do the work, Gambas is realy nice!

One function I used in Delphi was to get the length of the sound
played in TMediaPlayer, since I would like things done after 80%,
90%...
Music.Pos tells me where I am, but I can not found a function that
tells me how far I have to go.

Is there a solution to this?

--
Patrik Karlsson

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Benoit Minisini :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On vendredi 20 juin 2008, Patrik Karlsson wrote:

> Hello, I'm about to port an old Delphiapp to a nicer environment and
> I'm looking at Gambas to do the work, Gambas is realy nice!
>
> One function I used in Delphi was to get the length of the sound
> played in TMediaPlayer, since I would like things done after 80%,
> 90%...
> Music.Pos tells me where I am, but I can not found a function that
> tells me how far I have to go.
>
> Is there a solution to this?

Alas, if I remember correctly, the SDL library has no method to know the
length of the music! I don't know if it has changed recently, I will check...

--
Benoit Minisini

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Benoit Minisini :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On vendredi 20 juin 2008, Benoit Minisini wrote:

> On vendredi 20 juin 2008, Patrik Karlsson wrote:
> > Hello, I'm about to port an old Delphiapp to a nicer environment and
> > I'm looking at Gambas to do the work, Gambas is realy nice!
> >
> > One function I used in Delphi was to get the length of the sound
> > played in TMediaPlayer, since I would like things done after 80%,
> > 90%...
> > Music.Pos tells me where I am, but I can not found a function that
> > tells me how far I have to go.
> >
> > Is there a solution to this?
>
> Alas, if I remember correctly, the SDL library has no method to know the
> length of the music! I don't know if it has changed recently, I will
> check...

Apparently, SDL cannot extract the audio length from some formats, like MP3
with a variable bitrate stream.

I don't know why apparently the length of the audio is not encoded in the file
header...

--
Benoit Minisini

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Jaap Cramer :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

In the GambasDoc is an example on how to get information of a *.wav
file, including duration. See: http://gambasdoc.org/help/lang/read?show

Maybe useful?


On Fri, 2008-06-20 at 12:41 +0200, Benoit Minisini wrote:

> On vendredi 20 juin 2008, Benoit Minisini wrote:
> > On vendredi 20 juin 2008, Patrik Karlsson wrote:
> > > Hello, I'm about to port an old Delphiapp to a nicer environment and
> > > I'm looking at Gambas to do the work, Gambas is realy nice!
> > >
> > > One function I used in Delphi was to get the length of the sound
> > > played in TMediaPlayer, since I would like things done after 80%,
> > > 90%...
> > > Music.Pos tells me where I am, but I can not found a function that
> > > tells me how far I have to go.
> > >
> > > Is there a solution to this?
> >
> > Alas, if I remember correctly, the SDL library has no method to know the
> > length of the music! I don't know if it has changed recently, I will
> > check...
>
> Apparently, SDL cannot extract the audio length from some formats, like MP3
> with a variable bitrate stream.
>
> I don't know why apparently the length of the audio is not encoded in the file
> header...
>



-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Patrik Karlsson-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2008/6/20 Benoit Minisini <gambas@...>:
> I don't know why apparently the length of the audio is not encoded in the file
> header...

Ok, I see, at http://www.mp3-tech.org/programmer/frame_header.html I found

"There is no main file header in an MPEG audio file. An MPEG audio
file is built up from a succession of smaller parts called frames. A
frame is a datablock with its own header and audio information.
    In the case of Layer I or Layer II, frames are some totally
independent items, so you can cut any part of MPEG file and play it
correctly. The player will then play the music starting to the first
plain valid frame founded. However, in the case of Layer III, frames
are not always independant. Due to the possible use of the "byte
reservoir", wich is a kind of buffer, frames are often dependent of
each other. In the worst case, 9 frames may be needed before beeing
able to decode one frame."

--
Patrik Karlsson

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Parent Message unknown Re: Music.Length

by Patrik Karlsson-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2008/6/20 Jaap Cramer <jaap_cramer@...>:
> In the GambasDoc is an example on how to get information of a *.wav
> file, including duration. See: http://gambasdoc.org/help/lang/read?show
>
> Maybe useful?

Yes it might, thank you!
Maybe I just convert the mp3s to wav since it's only "internal" files
for SoundFX. Or, I define the length of each file manually, or
calculate the time, they have CBR.

--
Patrik Karlsson

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Benoit Minisini :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On vendredi 20 juin 2008, Patrik Karlsson wrote:

> 2008/6/20 Benoit Minisini <gambas@...>:
> > I don't know why apparently the length of the audio is not encoded in the
> > file header...
>
> Ok, I see, at http://www.mp3-tech.org/programmer/frame_header.html I found
>
> "There is no main file header in an MPEG audio file. An MPEG audio
> file is built up from a succession of smaller parts called frames. A
> frame is a datablock with its own header and audio information.
>     In the case of Layer I or Layer II, frames are some totally
> independent items, so you can cut any part of MPEG file and play it
> correctly. The player will then play the music starting to the first
> plain valid frame founded. However, in the case of Layer III, frames
> are not always independant. Due to the possible use of the "byte
> reservoir", wich is a kind of buffer, frames are often dependent of
> each other. In the worst case, 9 frames may be needed before beeing
> able to decode one frame."

Hopefully, there is http://developer.kde.org/~wheeler/taglib.html

Maybe a component could be made from this library to get meta information
about audio files.

--
Benoit Minisini

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Stefano Palmeri :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Il venerdì 20 giugno 2008 13:09:34 Patrik Karlsson ha scritto:

> 2008/6/20 Benoit Minisini <gambas@...>:
> > I don't know why apparently the length of the audio is not encoded in the
> > file header...
>
> Ok, I see, at http://www.mp3-tech.org/programmer/frame_header.html I found
>
> "There is no main file header in an MPEG audio file. An MPEG audio
> file is built up from a succession of smaller parts called frames. A
> frame is a datablock with its own header and audio information.
>     In the case of Layer I or Layer II, frames are some totally
> independent items, so you can cut any part of MPEG file and play it
> correctly. The player will then play the music starting to the first
> plain valid frame founded. However, in the case of Layer III, frames
> are not always independant. Due to the possible use of the "byte
> reservoir", wich is a kind of buffer, frames are often dependent of
> each other. In the worst case, 9 frames may be needed before beeing
> able to decode one frame."

If you have "mp3info" installed in your system you can get the length:

SHELL "mp3info -x " & "pathtofile.mp3" & " | grep 'Length:' | awk '{print
$2}'" TO sString

Stefano

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Stefano Palmeri :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Il venerdì 20 giugno 2008 13:25:03 Stefano Palmeri ha scritto:
>
> If you have "mp3info" installed in your system you can get the length:
>
> SHELL "mp3info -x " & "pathtofile.mp3" & " | grep 'Length:' | awk '{print
> $2}'" TO sString
>
> Stefano
>
Little fix to avoid problems if a mp3 file name or tag contains the
word "length".

SHELL "mp3info -x " & "pathtofile.mp3" & " | grep '^Length:' | awk '{print
$2}'" TO sString

-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by leemcpherson :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I've been working on this for mp3 files.  It is really easy to just use
an external app to get the length of normal bitrate mp3 files as Stefano
suggested.  For variable bitrate mp3s, it's a little harder.  There are
visual basic scripts on the web that can do this... I have them
somewhere in my files.  If I can find them, I'll share them.

However, it's not perfect.  From what I remember, it goes through each
frame of the mp3 and calculates an average length for each frame, to
give you an approximate time length for the whole mp3.  So seeking in
that track will not work correctly, but it doesn't work perfectly in any
player as far as I know.  I never finished coding this into my own app
though (which plays mp3 files from MySQL somewhat like Amarok does, but
using VLC player).

-Lee

Benoit Minisini wrote:

> On vendredi 20 juin 2008, Benoit Minisini wrote:
>  
>> On vendredi 20 juin 2008, Patrik Karlsson wrote:
>>    
>>> Hello, I'm about to port an old Delphiapp to a nicer environment and
>>> I'm looking at Gambas to do the work, Gambas is realy nice!
>>>
>>> One function I used in Delphi was to get the length of the sound
>>> played in TMediaPlayer, since I would like things done after 80%,
>>> 90%...
>>> Music.Pos tells me where I am, but I can not found a function that
>>> tells me how far I have to go.
>>>
>>> Is there a solution to this?
>>>      
>> Alas, if I remember correctly, the SDL library has no method to know the
>> length of the music! I don't know if it has changed recently, I will
>> check...
>>    
>
> Apparently, SDL cannot extract the audio length from some formats, like MP3
> with a variable bitrate stream.
>
> I don't know why apparently the length of the audio is not encoded in the file
> header...
>
>  


-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Re: Music.Length

by Ron_1st :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Friday 20 June 2008, Benoit Minisini wrote:
> I don't know why apparently the length of the audio is not encoded in the file
> header...
>

It is, but not in the way you think.

In fact is the mp3 as we know nowadays a incomplete standard.
We need to split the parts out of it.

  Mp3 is the encoding method only used as _data_.

  mpeg the layout of a block containing _data_

  The file containing blocks of _data_

Audio is encoded, mp3 in this case, in blocks and each block
has a header telling the rate used and block length.
The block information is following a mpeg standard.
Divide the bytes by rate and the time per block is known.
Add the time per block for each block and you have the
total play time.

Storage on disk in a file was simple put all blocks
after each other and without a file header descibing the
content of the file.


It was the inovation of mp3 encoding that triggers a group
of enthousiast to write encoders/decoders to store there
music that way and made it as a pseudo standard for the
audiophiles, the 1/10 of the *.wav size that time in use.

The early players read the rate from first block and used it
for the whole file play and position, not looking every block.
Just to win decoding time on the slow computers those days.
They where not able to use variable rates at all.
In the early 90's using 90% cpu time with win3.11
 
Later the mp3 encoding programs were changed to use variable
rates and it was posible by using those block headers.

To know the total time now you need to read the whole file and
add the time per block. For file on local disk not a problem
but what if it is on local network share or via internet?
And how about live streams that use mp3 _encoding_?
The total time is only known when the stream is ready.

If the file had also container layout and could describe the
content you can write the time into this container header
and that does not exist for the populair mp3 encoded files.

It is missing the file(container) that the mp3 encoding
it is a good available encoding for streaming.
Just jump somewhere in the stream and in short time it plays.
 

side note 1)
The real name of a file should be myaudio.mp3.mpeg just as
myfileachive.tar.gz for files in a tar and late gzipped.
I's just MS DOS (DiskOperatingSystem) did not allow more as
3 characters for the extension that made the *.mp3 naming scheme.
 
side note 2)
when you were able to write the total length in the header.
Aborting a recording and you do not have a length to
write and the mp3 duration is 0 or endless.
In Result is you can't trust this value.

Same problem why a *.avi file is not a good container for
streaming with the seek index on the end of the file
and the start of the file contains the amount of segments
in the file to be able to find the start of the index.
Aborting the write of a avi file with encoded (mp3, divx, cvid)
data block makes the avi file corrupt for play.
Remember the avi repair tools for this problem.
The index is need while the encoded data blocks do not have(always)
fixed lengths and decoding _must_ begin on a block start.

Ron the 1'st


-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Gambas-user mailing list
Gambas-user@...
https://lists.sourceforge.net/lists/listinfo/gambas-user

Parent Message unknown Re: Music.Length

by werner 007 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi

Once i got a routine (don't remember from, sorry) for mp3-files witch
can extract many properties.
Maybe this can help you somehow.

Regards, Werner


' Gambas class file
'
'***
'*** Begin Declaration section
'***
PUBLIC OK AS Boolean

PUBLIC Duration        AS String
PUBLIC Frequency       AS String
PUBLIC Mode            AS String
PUBLIC IntensitySterio AS String
PUBLIC MSSterio        AS String
PUBLIC MpegLayer       AS String
PUBLIC MpegVersion     AS String
PUBLIC BitRate         AS String
PUBLIC Songname        AS String
PUBLIC Artist          AS String
PUBLIC Album           AS String
PUBLIC SongYear        AS String
PUBLIC Comment         AS String
PUBLIC Genre           AS String
PUBLIC Track           AS String
PUBLIC Protection      AS String
PUBLIC Copyright       AS String
PUBLIC Original        AS String
PUBLIC Emphasis        AS String
PUBLIC VBR             AS Boolean
PUBLIC ModeExtension   AS Boolean
PUBLIC Minutes         AS Integer
PUBLIC Seconds         AS Integer
PUBLIC FrameLength     AS Integer

PRIVATE GenreArray AS NEW String[]
'***
'*** End Declaration section
'***
'*** Begin Constructor section
'***
PUBLIC SUB _new()
   BitRate = ""
   Frequency = ""
   Duration = ""
   Songname = ""
   Artist = ""
   Album = ""
   SongYear = ""
   Comment = ""
   Genre = ""
   Track = ""
   MpegVersion = ""
   MpegLayer = ""
   Protection = ""
   Mode = ""
   VBR = FALSE
   Minutes = 0
   Seconds = 0
   FrameLength = 0

   LoadGenre()
END
'***
'*** End Constructor section
'***
'*** Begin Subroutine section
'***
PRIVATE SUB LoadGenre()
   GenreArray.Resize(126)

   GenreArray[0] = "Blues"
   GenreArray[1] = "Classic Rock"
   GenreArray[2] = "Country"
   GenreArray[3] = "Dance"
   GenreArray[4] = "Disco"
   GenreArray[5] = "Funk"
   GenreArray[6] = "Grunge"
   GenreArray[7] = "Hip-Hop"
   GenreArray[8] = "Jazz"
   GenreArray[9] = "Metal"
   GenreArray[10] = "New Age"
   GenreArray[11] = "Oldies"
   GenreArray[12] = "Other"
   GenreArray[13] = "Pop"
   GenreArray[14] = "R&B"
   GenreArray[15] = "Rap"
   GenreArray[16] = "Reggae"
   GenreArray[17] = "Rock"
   GenreArray[18] = "Techno"
   GenreArray[19] = "Industrial"
   GenreArray[20] = "Alternative"
   GenreArray[21] = "Ska"
   GenreArray[22] = "Death Metal"
   GenreArray[23] = "Pranks"
   GenreArray[24] = "Soundtrack"
   GenreArray[25] = "Euro-Techno"
   GenreArray[26] = "Ambient"
   GenreArray[27] = "Trip-Hop"
   GenreArray[28] = "Vocal"
   GenreArray[29] = "Jazz+Funk"
   GenreArray[30] = "Fusion"
   GenreArray[31] = "Trance"
   GenreArray[32] = "Classical"
   GenreArray[33] = "Instrumental"
   GenreArray[34] = "Acid"
   GenreArray[35] = "House"
   GenreArray[36] = "Game"
   GenreArray[37] = "Sound Clip"
   GenreArray[38] = "Gospel"
   GenreArray[39] = "Noise"
   GenreArray[40] = "AlternRock"
   GenreArray[41] = "Bass"
   GenreArray[42] = "Soul"
   GenreArray[43] = "Punk"
   GenreArray[44] = "Space"
   GenreArray[45] = "Meditative"
   GenreArray[46] = "Instrumental Pop"
   GenreArray[47] = "Instrumental Rock"
   GenreArray[48] = "Ethnic"
   GenreArray[49] = "Gothic"
   GenreArray[50] = "Darkwave"
   GenreArray[51] = "Techno-Indistrial"
   GenreArray[52] = "Electronic"
   GenreArray[53] = "Pop-Folk"
   GenreArray[54] = "Eurodance"
   GenreArray[55] = "Dream"
   GenreArray[56] = "Southern Rock"
   GenreArray[57] = "Comedy"
   GenreArray[58] = "Cult"
   GenreArray[59] = "Gansta"
   GenreArray[60] = "Top 40"
   GenreArray[61] = "Christian Rap"
   GenreArray[62] = "Pop/Funk"
   GenreArray[63] = "Jungle"
   GenreArray[64] = "Native American"
   GenreArray[65] = "Cabaret"
   GenreArray[66] = "New Wave"
   GenreArray[67] = "Psychaledic"
   GenreArray[68] = "Rave"
   GenreArray[69] = "Showtunes"
   GenreArray[70] = "Trailer"
   GenreArray[71] = "Lo-Fi"
   GenreArray[72] = "Tribal"
   GenreArray[73] = "Acid Punk"
   GenreArray[74] = "Acid Jazz"
   GenreArray[75] = "Polka"
   GenreArray[76] = "Retro"
   GenreArray[77] = "Musical"
   GenreArray[78] = "Rock & Roll"
   GenreArray[79] = "Hard Rock"
   GenreArray[80] = "Folk"
   GenreArray[81] = "Folk-Rock"
   GenreArray[82] = "National Folk"
   GenreArray[83] = "Swing"
   GenreArray[84] = "Fast Fusion"
   GenreArray[85] = "Bebop"
   GenreArray[86] = "Latin"
   GenreArray[87] = "Revival"
   GenreArray[88] = "Celtic"
   GenreArray[89] = "Bluegrass"
   GenreArray[90] = "Avantgarde"
   GenreArray[91] = "Gothic Rock"
   GenreArray[92] = "Progressive Rock"
   GenreArray[93] = "Psychadelic Rock"
   GenreArray[94] = "Symphonic Rock"
   GenreArray[95] = "Slow Rock"
   GenreArray[96] = "Big Band"
   GenreArray[97] = "Chorus"
   GenreArray[98] = "Easy Listening"
   GenreArray[99] = "Acoustic"
   GenreArray[100] = "Humour"
   GenreArray[101] = "Speech"
   GenreArray[102] = "Chanson"
   GenreArray[103] = "Opera"
   GenreArray[104] = "Chamber Music"
   GenreArray[105] = "Sonata"
   GenreArray[106] = "Symphony"
   GenreArray[107] = "Booty Bass"
   GenreArray[108] = "Primus"
   GenreArray[109] = "Porn Groove"
   GenreArray[110] = "Satire"
   GenreArray[111] = "Slow Jam"
   GenreArray[112] = "Club"
   GenreArray[113] = "Tango"
   GenreArray[114] = "Samba"
   GenreArray[115] = "Folklore"
   GenreArray[116] = "Ballad"
   GenreArray[117] = "Power Ballad"
   GenreArray[118] = "Rhythmic Soul"
   GenreArray[119] = "Freestyle"
   GenreArray[120] = "Duet"
   GenreArray[121] = "Punk Rock"
   GenreArray[122] = "Drum Solo"
   GenreArray[123] = "A Capella"
   GenreArray[124] = "Euro-House"
   GenreArray[125] = "Dance Hall"
END
'***
'*** End Subroutine section
'***
'*** Begin Function section
'***
PRIVATE FUNCTION BinToDec(BinValue AS String) AS Integer
   DIM i    AS Integer
   DIM iDec AS Integer

   FOR i = 1 TO Len(BinValue)
      IF Mid(BinValue, i, 1) = 1 THEN
         iDec = iDec + 2 ^ (Len(BinValue) - i)
      ENDIF
   NEXT

   RETURN iDec
END

PRIVATE FUNCTION ByteToBit(ByteArray AS Byte[]) AS String
   DIM sBit  AS String
   DIM z     AS Integer
   DIM i     AS Integer
   DIM Bytes AS Byte

   'convert 4*1 byte array to 4*8 bits'''''
   sBit = ""
   FOR z = 0 TO 3
      Bytes = ByteArray[z]
      FOR i = 7 TO 0 STEP -1
         IF Int(Bytes / (2 ^ i)) = 1 THEN
            sBit = sBit & "1"
            Bytes = Bytes - (2 ^ i)
         ELSE IF sBit <> "" THEN
            sBit = sBit & "0"
         ENDIF
      NEXT
   NEXT

   RETURN sBit
END

PRIVATE FUNCTION GetGenreFromID(iListGenre AS Integer) AS String
   DIM GenreFromID AS String

   IF iListGenre > -1 AND iListGenre < 126 THEN
      GenreFromID = GenreArray[iListGenre]
   ELSE
      GenreFromID = "Unknown"
   ENDIF

   RETURN GenreFromID
END

PRIVATE FUNCTION ReadHeader(sFileName AS String) AS Boolean
   DIM VersionLayer AS NEW String[]
   DIM SMode        AS NEW String[]
   DIM MpVersion    AS NEW String[]
   DIM MpLayer      AS NEW String[]
   DIM MpProtection AS NEW String[]
   DIM MpOriginal   AS NEW String[]
   DIM MpCopyright  AS NEW String[]
   DIM MpEmphasis   AS NEW String[]
   DIM ByteArray    AS NEW Byte[]

   DIM hFile        AS File
   DIM sBin         AS String
   DIM XingH        AS String
   DIM x            AS Byte
   DIM Bytes        AS Byte
   DIM i            AS Integer
   DIM z            AS Integer
   DIM HeadStart    AS Integer
   DIM Frames       AS Integer
   DIM Calc         AS Integer
   DIM LenFile      AS Integer
   DIM Padding      AS Integer
   DIM ModeExt      AS Integer
   DIM vTemp        AS Variant
   DIM Brate        AS Variant
   DIM Freq         AS Variant

   SMode.Resize(4)
   MpLayer.Resize(4)
   ByteArray.Resize(4)
   MpVersion.Resize(4)
   MpProtection.Resize(2)
   VersionLayer.Resize(4)
   MpOriginal.Resize(2)
   MpCopyright.Resize(2)
   MpEmphasis.Resize(4)

   'tables
   VersionLayer[0] = "0"
   VersionLayer[1] = "3"
   VersionLayer[2] = "2"
   VersionLayer[3] = "1"

   SMode[0] = "Stereo"
   SMode[1] = "Joint stereo"
   SMode[2] = "Dual channel"
   SMode[3] = "Single channel"

   MpVersion[0] = "MPEG Version 2.5"
   MpVersion[1] = "None"
   MpVersion[2] = "MPEG Version 2"
   MpVersion[3] = "MPEG Version 1"

   MpLayer[0] = "None"
   MpLayer[1] = "Layer III"
   MpLayer[2] = "Layer II"
   MpLayer[3] = "Layer I"

   MpProtection[0] = "Protected by CRC (16bit CRC)"
   MpProtection[1] = "Not protected"

   MpOriginal[0] = "Copy of original media"
   MpOriginal[1] = "Original media"

   MpCopyright[0] = "Audio is not copyryghted"
   MpCopyright[1] = "Audio is copyrighted"

   MpEmphasis[0] = "None"
   MpEmphasis[1] = "50/15 ms"
   MpEmphasis[2] = "None"
   MpEmphasis[3] = "CCIT J.17"

   'read the header
   OPEN sFileName FOR READ AS #hFile
      IF Lof(hFile) < 256 THEN
         CLOSE #hFile
         RETURN FALSE
      ENDIF

      '''''start check startposition for header''''''''''''
      '''''if start position <>1 then id3v2 tag exists'''''
      FOR i = 0 TO Lof(hFile)           'check the whole file for the header
         READ #hFile, x
         IF x = 255 THEN              'header always start with 255
followed by 250 or 251
            READ #hFile, x
            IF x > 249 AND x < 252 THEN
               HeadStart = i          'set header start position
               BREAK
            ENDIF
         ENDIF
      NEXT

      'no header start position was found
      IF HeadStart = 0 THEN
         RETURN FALSE
      ENDIF
      '''end check start position for header'''''''''''''

      ''start check for XingHeader'''
      SEEK #hFile, HeadStart + 36
      READ #hFile, XingH, 4
      IF XingH = "Xing" THEN
         VBR = TRUE
         SEEK #hFile, HeadStart + 43
         FOR z = 0 TO 3
            READ #hFile, Bytes                   'get framelength to array
            ByteArray[z] = Bytes
         NEXT
         Frames = BinToDec(ByteToBit(ByteArray)) 'calculate # of frames
      ELSE
         VBR = FALSE
      ENDIF
      '''end check for XingHeader

      '''start extract the first 4 bytes (32 bits) to an array
      SEEK #hFile, HeadStart
      FOR z = 0 TO 3
         READ #hFile, Bytes
         ByteArray[z] = Bytes
      NEXT
      '''stop extract the first 4 bytes (32 bits) to an array
      LenFile = Lof(hFile)
   CLOSE #hFile

   'header string
   sBin = ByteToBit(ByteArray)

   'get mpegversion from table
   MpegVersion = VersionLayer[BinToDec(Mid(sBin, 12, 2))]

   'get layer from table
   MpegLayer = VersionLayer[BinToDec(Mid(sBin, 14, 2))]

   'get mode from table
   Mode = SMode[BinToDec(Mid(sBin, 25, 2))]

   'get mode extension from table
   IF BinToDec(Mid(sBin, 25, 2)) = 1 THEN
      ModeExt = BinToDec(Mid(sBin, 27, 2))
      SELECT CASE ModeExt
         CASE 0
            IntensitySterio = "Off"
            MSSterio = "Off"
         CASE 1
            IntensitySterio = "On"
            MSSterio = "Off"
         CASE 2
            IntensitySterio = "Off"
            MSSterio = "On"
         CASE 3
            IntensitySterio = "On"
            MSSterio = "On"
      END SELECT
      ModeExtension = TRUE
   ELSE
      ModeExtension = FALSE
      IntensitySterio = "None"
      MSSterio = "None"
   ENDIF

   'look for version to create right table
   'ok = TRUE
   SELECT CASE Val(MpegVersion)
      CASE 1
         'for version 1
         Freq = Array(44100, 48000, 32000)
      CASE 2 OR 25
         'for version 2 or 2.5
         Freq = Array(22050, 24000, 16000)
      CASE ELSE
         'Frequency = 0
         'ok = FALSE  'RETURN TRUE 'FALSE
          Array(0, 0, 0)
   END SELECT

   'look for frequency in table
   'IF ok THEN
   Frequency = Freq[BinToDec(Mid(sBin, 21, 2))]

   IF VBR = TRUE THEN
      'define to calculate correct bitrate
      vTemp = Array(0, 12, 144, 144)
      BitRate = (Lof(sFileName) * Frequency) / (Int(Frames)) / 1000 /
vTemp[MpegLayer]
   ELSE
      'look for the right bitrate table
    '  ok = TRUE
      SELECT CASE Val(MpegVersion & MpegLayer)
         CASE 11
            'Version 1, Layer 1
            Brate = Array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
320, 352, 384, 416, 448)
         CASE 12
            'V1 L1
            Brate = Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192,
224, 256, 320, 384)
         CASE 13
            'V1 L3
            Brate = Array(0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
192, 224, 256, 320)
         CASE 21 OR 251
            'V2 L1 and 'V2.5 L1
            Brate = Array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160,
176, 192, 224, 256)
         CASE 22 OR 252 OR 23 OR 253
            'V2 L2 and 'V2.5 L2 etc...
            Brate = Array(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112,
128, 144, 160)
         CASE ELSE
            'if variable bitrate
           ' BitRate = Str(1)
            'ok = FALSE  'RETURN TRUE 'FALSE
            Brate = Array(0, 1, 2, 3, 4, 5, 6, 7, 8,9 , 10, 11, 12, 13, 14)
      END SELECT
      'IF ok THEN
      BitRate = Str(Brate[BinToDec(Mid(sBin, 17, 4))])
   ENDIF

   'if there is a decimal place, then parse it off
   IF Instr(BitRate, ".") THEN
      BitRate = Left(BitRate, Instr(BitRate, ".") - 1)
   ENDIF

   'calculate duration
   Calc = Int((LenFile * 8) / Val(BitRate) / 1000)
   Minutes = Calc \ 60
   IF Minutes < 10 THEN
      Duration = "0" & Str(Minutes) & "m "
   ELSE
      Duration = Str(Minutes) & "m "
   ENDIF

   Seconds = Calc - (Calc \ 60) * 60
   IF Seconds < 10 THEN
      Duration = Duration & "0" & Str(Seconds) & "s"
   ELSE
      Duration = Duration & Str(Seconds) & "s"
   ENDIF

   'get padding bit from table
   Padding = BinToDec(Mid(sBin, 23, 1))

   'get discription from table
   MpegVersion = MpVersion[BinToDec(Mid(sBin, 12, 2))]
   MpegLayer = MpLayer[BinToDec(Mid(sBin, 14, 2))]

   'get protection from table
   Protection = MpProtection[BinToDec(Mid(sBin, 16, 1))]

   'calc frame length
   FrameLength = ((144 * (Val(BitRate) * 1000)) / (Val(Frequency) +
Padding))

   'get copyright from table
   Copyright = MpCopyright[BinToDec(Mid(sBin, 29, 1))]

   'get original from table
   Original = MpOriginal[BinToDec(Mid(sBin, 30, 1))]

   'get emphasis from table
   Emphasis = MpEmphasis[BinToDec(Mid(sBin, 31, 2))]

   Frequency = Frequency & "  Hz"
   BitRate = BitRate & "  Kbit/s"
FINALLY
   RETURN TRUE
CATCH
   CLOSE #hFile
END

PRIVATE FUNCTION ReadID3(sFileName AS String) AS Boolean
   DIM hFile       AS File
   DIM TagString   AS String    'holds the string read from the mp3
   DIM TempString  AS String
   DIM strStrip    AS String
   DIM s           AS String    'holds temp strings
   DIM r           AS Integer
   DIM x           AS Integer
   DIM i           AS Integer   'holds temp int bytes
   DIM FieldSize   AS Integer
   DIM SizeOffset  AS Integer
   DIM FieldOffset AS Integer
   DIM b           AS Byte      'holds temp byte values
   DIM Version     AS Byte

   'fixed length vars for reading v1ID3v1 tags
   DIM v1ID        AS String
   DIM v1Title     AS String
   DIM v1Artist    AS String
   DIM v1Album     AS String
   DIM v1Year      AS String
   DIM v1Comment   AS String
   DIM v1Genre     AS Byte

   'vars that hold v1ID3v2 attibutes
   DIM v2Title     AS String
   DIM v2Artist    AS String
   DIM v2Album     AS String
   DIM v2Year      AS String
   DIM v2Genre     AS String
   DIM v2Track     AS String

   'Open the file so we can read it
   OPEN sFileName FOR READ AS #hFile
   ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
   ' Check for an ID3v1 tag
   ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
      SEEK #hFile, Lof(hFile) - 128
      READ #hFile, v1ID, 3
      IF v1ID = "TAG" THEN 'If "TAG" is present, then we have a valv1ID
v1ID3v1 tag and will extract all available v1ID3v1 info from the file
         READ #hFile, v1Title, 30    'Always limited to 30 characters
         READ #hFile, v1Artist, 30   'Always limited to 30 characters
         READ #hFile, v1Album, 30    'Always limited to 30 characters
         READ #hFile, v1Year, 4      'Always limited to 4 characters
         READ #hFile, v1Comment, 30  'Always limited to 30 characters
         READ #hFile, v1Genre, 1     'Always limited to 1 byte (?)

         Songname = v1Title
         Artist = v1Artist
         Album = v1Album
         SongYear = v1Year
         Comment = v1Comment
         Genre = v1Genre

         'get rv1ID of " " on the right sv1IDe of the string
         DO UNTIL Right(Songname, 1) <> " "
            Songname = Left(Songname, Len(Songname) - 1)
         LOOP

         DO UNTIL Right(Artist, 1) <> " "
            Artist = Left(Artist, Len(Artist) - 1)
         LOOP

         DO UNTIL Right(Album, 1) <> " "
            Album = Left(Album, Len(Album) - 1)
         LOOP

         DO UNTIL Right(SongYear, 1) <> " "
            SongYear = Left(SongYear, Len(SongYear) - 1)
         LOOP

         DO UNTIL Right(Comment, 1) <> " "
            Comment = Left(Comment, Len(Comment) - 1)
         LOOP

         DO UNTIL Right(Genre, 1) <> " "
            Genre = Left(Genre, Len(Genre) - 1)
         LOOP

         'get rv1ID of null characters
         Songname = Replace$(Songname, Chr$(0), "")
         Artist = Replace$(Artist, Chr$(0), "")
         Album = Replace$(Album, Chr$(0), "")
         SongYear = Replace$(SongYear, Chr$(0), "")
         Comment = Replace$(Comment, Chr$(0), "")
         Genre = Replace$(Genre, Chr$(0), "")

         'comments tend to have lots of unprintable chars, so remove them
         IF Len(Comment) > 0 THEN
            FOR x = 0 TO 47
               IF Instr(Comment, Chr$(x)) THEN
                  Comment = Replace$(Comment, Chr(x), "")
               ENDIF
            NEXT
         ENDIF
      ENDIF

      ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
      ' Check for a Header for ID3v2 tag
      ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
      SEEK #hFile, 1
      READ #hFile, b
      IF (b < 250 OR b > 251) THEN
         'We have an v1ID3v2 tag
         IF b = 68 THEN
            READ #hFile, b
            IF b = 51 THEN
               'get version
               SEEK #hFile, 3
               READ #hFile, Version

               'get how long the v2 tag is
               SEEK #hFile, 6
               READ #hFile, b
               r = Int(b) * 20917152

               READ #hFile, b
               r = r + Int(b) * 16384

               READ #hFile, b
               r = r + Int(b) * 128

               READ #hFile, b
               r = r + Int(b)

               'invalid length was calculated
               IF r > Lof(hFile) OR r > 2147483647 THEN
                  RETURN FALSE
               END IF

               'get the v2 tag according to the length calculated
               READ #hFile, TagString, r
            ENDIF
         ENDIF
      ENDIF


      IF TagString <> "" THEN     'there is an ID3v2 tag in TagString
         ' Determine if the v1ID3v2 tag is v1ID3v2.2 or v1ID3v2.3
         SELECT CASE Version
            CASE 2 'v1ID3v2.2
               'Set the fieldnames for version 2.0
               v2Title = "TT2"
               v2Artist = "TOA"
               v2Album = "TAL"
               v2Year = "TYE"
               v2Genre = "TCO"
               FieldOffset = 7
               SizeOffset = 5
               v2Track = "TRCK"
            CASE 3 'v1ID3v2.3
               'Set the fieldnames for version 3.0
               v2Title = "TIT2"
               v2Artist = "TPE1"
               v2Album = "TALB"
               v2Year = "TYER"
               v2Genre = "TCON"
               v2Track = "TRCK"
               FieldOffset = 11
               SizeOffset = 7
            CASE ELSE
               'We don't have a valv1ID v1ID3v2 tag, so bail
               RETURN FALSE
         END SELECT

         ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
         ' Extract track title
         ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
         i = Instr(TagString, v2Title)
         IF i > 0 THEN
            'read the title
            FieldSize = Asc(Mid$(TagString, i + SizeOffset)) - 1

            IF Version = 3 THEN
               'check for compressed or encrypted field
               b = Asc(Mid$(TagString, i + 9))
               IF (b AND 128) = TRUE OR (b AND 64) = TRUE THEN GOTO
ReadAlbum
            ENDIF

            TempString = Mid$(TagString, i + FieldOffset, FieldSize)

            IF TempString <> "" THEN
               Songname = TempString
            ENDIF
         ENDIF
         ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
         ' Extract album title
         ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ReadAlbum:
         i = Instr(TagString, v2Album)
         IF i > 0 THEN
            FieldSize = Asc(Mid$(TagString, i + SizeOffset)) - 1

            IF Version = 3 THEN
               'check for compressed or encrypted field
               b = Asc(Mid$(TagString, i + 9))
               IF (b AND 128) = 128 OR (b AND 64) = 64 THEN GOTO ReadArtist
            ENDIF

            TempString = Mid$(TagString, i + FieldOffset, FieldSize)

            IF TempString <> "" THEN
               Album = TempString
            ENDIF
         ENDIF

         ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
         ' Extract artist name
         ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ReadArtist:
         i = Instr(TagString, v2Artist)
         IF i > 0 THEN
            FieldSize = Asc(Mid$(TagString, i + SizeOffset)) - 1

            IF Version = 3 THEN
               'check for compressed or encrypted field
               b = Asc(Mid$(TagString, i + 9))
               IF (b AND 128) = 128 OR (b AND 64) = 64 THEN GOTO ReadYear
            ENDIF

            TempString = Mid$(TagString, i + FieldOffset, FieldSize)

            IF TempString <> "" THEN
               Artist = TempString