Sign in to follow this  
Followers 0
Skizmata

Unpredictable Results From _SoundLength

17 posts in this topic

#1 ·  Posted (edited)

Been gone from the forums for a long time glad to see all is still well.

I wanted to write a quick and simple mp3 player to solve two problems.

First portablity I wanted something that could ride along on my thumb drive at work and at home . Secondly I listen to allot of very long podcasts and audio books. So I wanted to be able to resume a mp3 at the exact place I had left off. (also I really like AutoIt hotkeys and plan to use them should I get this fixed)

This code works fine as a start for what I'm looking for except the output from _SoundLength does not agree with the length of the file in other mp3 players. Sometimes its off by just a few seconds sometimes much more. The length mp3 don't seem to give a uniform deviation so I'm at a loss as to how to derive a curve (not that I should have to).

Any help would be greatly appreciated. This mishap is keeping my simple little utill from doing its simple but very helpful job!

Some examples...

28-Hiiinks-Ensue-Podcast.mp3 3:28:39 according to _SoundLength 3:04:30 according to media player and VLC.

04. Psychosocial.mp3 4:46 according to _SoundLength 4:44 otherwise

1.02 Chapter 2 - The Nature Of Belief.mp3 00:30:41 according to _SoundLength 1:12:45 by all other accounts

#include <Sound.au3>
#include <Misc.au3>


Opt("TrayAutoPause", 0)
Opt("TrayMenuMode", 1)

Dim $mp3File = FileOpenDialog("",@ScriptDir,"MP3 (*.mp3)")

$trayItemPausePlay = TrayCreateItem("Pause")
$trayItemExit = TrayCreateItem("Exit")

$mp3 = _SoundOpen($mp3File)

_SoundPlay($mp3)
Dim $isPlaying = True

$iniPlayPos = IniRead("mp3.ini", "Main", $mp3File, "NotFound")
If $iniPlayPos <> "NotFound" Then
    $splitTime = StringSplit ( $iniPlayPos, ":")
    _SoundSeek($mp3,$splitTime[1],$splitTime[2],$splitTime[3])
    _SoundPlay($mp3)
EndIf

While 1
    $msg = TrayGetMsg()
    Select
        Case $msg = 0
            $playPos = _SoundPos($mp3,1)
            ToolTip($playPos & " of " & _SoundLength($mp3),-1,-1,$mp3File,0,4)
            IniWrite(@ScriptDir & "\mp3.ini", "Main",$mp3File,$playPos)
            sleep(100)
        Case $msg = $trayItemPausePlay
            if $isPlaying Then
                _SoundPause($mp3)
                TrayItemSetText ( $trayItemPausePlay, "Play")
                $isPlaying = Not $isPlaying
            Else
                _SoundResume($mp3)
                TrayItemSetText ( $trayItemPausePlay, "Pause")
                $isPlaying = Not $isPlaying
            EndIf
        Case $msg = $trayItemExit
            Exit
    EndSelect
WEnd
Edited by Skizmata

AutoIt changed my life.

Share this post


Link to post
Share on other sites



The problem here is with the (multi?)media Command interface, or mci.

As this is built in with Windows, I suggest you find a way of reading the length of mp3s separately.

Unfortunately this same bug probably affects _SoundSeek too.


My Programs:AInstall - Create a standalone installer for your programUnit Converter - Converts Length, Area, Volume, Weight, Temperature and Pressure to different unitsBinary Clock - Hours, minutes and seconds have 10 columns each to display timeAutoIt Editor - Code Editor with Syntax Highlighting.Laserix Editor & Player - Create, Edit and Play Laserix LevelsLyric Syncer - Create and use Synchronised Lyrics.Connect 4 - 2 Player Connect 4 Game (Local or Online!, Formatted Chat!!)MD5, SHA-1, SHA-256, Tiger and Whirlpool Hash Finder - Dictionary and Brute Force FindCool Text Client - Create Rendered ImageMy UDF's:GUI Enhance - Enhance your GUIs visually.IDEA File Encryption - Encrypt and decrypt files easily! File Rename - Rename files easilyRC4 Text Encryption - Encrypt text using the RC4 AlgorithmPrime Number - Check if a number is primeString Remove - remove lots of strings at onceProgress Bar - made easySound UDF - Play, Pause, Resume, Seek and Stop.

Share this post


Link to post
Share on other sites

Hi,

I had the same problem when I was writing a background MP3 player. The problem is that the Windows MCI DLL used by Sound.au3 assumes that the file is Constant Bit Rate (CBR) and uses the bitrate of the first frame as the bitrate for the entire track. So a track recorded at a Variable Bit rate (VBR) will not move to the correct place with _SoundSeek and, if corrected, will then will not show the correct time with _SoundLen. There is nothing you can do about this using the MCI DLL, so I used the following code to get over the problem (I've left out a lot of errorchecking code to hopefully increase clarity):

Firstly, get the "true" length from file properties:

$DOS_Dir = FileGetShortName($Dir_Name, 1)

$ShellApp = ObjCreate("shell.application")
If IsObj($ShellApp) Then
    $Dir = $ShellApp.NameSpace ($DOS_Dir)
    If IsObj($Dir) Then
        $File = $Dir.Parsename ($File_Name)
        If IsObj($File) Then
            ; Property 27 is true for Vista - I believe XP uses 21
            $Track_Length = $Dir.GetDetailsOf ($File, 27)
        EndIf
    EndIf
EndIf

; Convert Track_Length to mSec
$Time = StringSplit($Track_Length, ":")

$Track_Ticks = _TimeToTicks($Time[1], $Time[2], $Time[3])

Now you have $Track_Ticks as the "true" length in ms.

Next see if the file is VBR or CBR by comparing the time given by the method above and that returned by _SoundLen:

; Get estimated length
$Sound_Ticks = _SoundLength($Music_handle, 2)

; Compare to actual length (difference < 1 sec = CBR)
If Abs($Sound_Ticks - $Track_Ticks) < 1000 Then

    ; Assume CBR
    $VBR_Factor = 0

Else

    ; Set correction ratio for VBR operations
    $VBR_Factor = $Sound_Ticks/$Track_Ticks

EndIf

; Set VBR initial seek error to 0
$VBR_Tick_Diff = 0

You are now in a position to _SoundSeek to a "corrected" value. I used a slider to give me $Seek_Pos as a percentage of the length:

; Convert required seek position into time
$Seek_Ticks = $Seek_Pos / 100 * $Track_Ticks

If $VBR_Factor <> 0 Then

    ; It is a VBR track so make adjustment to seek point
    $Corr_Ticks = $Seek_Ticks * $VBR_Factor
    ; Set correction factor for timer
    $VBR_Tick_Diff = $Corr_Ticks - $Seek_Ticks
    $Seek_Ticks = $Corr_Ticks

EndIf

; Set the music to the "corrected" time
_SoundStop($Music_handle)

; Convert to H:M:S and move to that point
_TicksToTime($Seek_Ticks, $Time[1], $Time[2], $Time[3])
_SoundSeek($Music_handle, $Time[1], $Time[2], $Time[3])

; Start playing from the "correct" point
_SoundPlay($Music_handle)

Final point. If you have used _SoundSeek as above, _SoundPos will give incorrect readings because you have deliberately used an "incorrect" time to restart the track. You must adjust the response from _SoundPos as follows:

$Played_Ticks = _SoundPos($Music_handle, 2)

If $VBR_Factor <> 0 Then
    ; Adjust for VBR seek if needed
    $Display_Ticks = $Played_Ticks - $VBR_Tick_Diff
Else
    ; No need to adjust for CBR
    $Display_Ticks = $Played_Ticks
EndIf

And $Display_Ticks now gives the correct time - or to within a second if my experience is anything to go by. This is not a one-shot solution - it will work for multiple changes in position.

I also wanted to resume a track in the same place on restarting, so I used the same principle. On exiting, save the necessary values:

IniWrite($Ini_File, "Reload", "Track",      $Music_File)
IniWrite($Ini_File, "Reload", "Position",    $Display_Ticks / $Track_Ticks * 100)
IniWrite($Ini_File, "Reload", "VRF",          $VBR_Factor)
IniWrite($Ini_File, "Reload", "VRC",          $VBR_Tick_Diff)

Then read them back in on start:

$Music_File =   IniRead($Ini_File, "Reload", "Track", "")
$Reload_Pos =   IniRead($Ini_File, "Reload", "Position", "")
$VBR_Factor =   IniRead($Ini_File, "Reload", "VRF", "")
$VBR_Tick_Diff = IniRead($Ini_File, "Reload", "VRC", "")

And adjust the values to resume the track from the "correct" place:

; Determine place to restart track
$Seek_Ticks = $Reload_Pos / 100 * $Track_Ticks

If $VBR_Factor <> 0 Then 
    ; Make adjustment to seek point
    $Corr_Ticks = $Seek_Ticks * $VBR_Factor
    ; Correct for new time from _SoundPos
    $VBR_Tick_Diff = $Corr_Ticks - $Seek_Ticks
    $Seek_Ticks = $Corr_Ticks

EndIf

; Convert to H:M:S and move to that point
_TicksToTime($Seek_Ticks, $Time[1], $Time[2], $Time[3])
_SoundSeek($Music_handle, $Time[1], $Time[2], $Time[3])

; Play track
 _SoundPlay($Music_handle)

I hope this is useful.

M23

P.S. Thanks to the authors of Sound.au3 - I wouldn't want then to think that I didn't appreciate their work in developing the UDFs in the first place!


Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

@Melba23

That's a very helpful answer to this long-standing bug. If we can ensure the shell.application object exists in all versions of windows (the 21 or 27 constant can be changed depending on @OSVersion), then we can hopefully add the correction into Sound.au3

To get a hold of me:- I will subscribe to this post as I don't visit this forum very often, and sending a PM will show up in my email inbox.

Edit:

Can you send me code with error checking, I've worked out a (hopefully) universal solution using $Dir.GetDetailsOf($File, -1) and using a regular expression to parse the duration.

I'm going to create a sort of beta Sound.au3 with this new stuff built in, or maybe a test script so we can ask users to post back results.

Thanks again

Edited by RazerM

My Programs:AInstall - Create a standalone installer for your programUnit Converter - Converts Length, Area, Volume, Weight, Temperature and Pressure to different unitsBinary Clock - Hours, minutes and seconds have 10 columns each to display timeAutoIt Editor - Code Editor with Syntax Highlighting.Laserix Editor & Player - Create, Edit and Play Laserix LevelsLyric Syncer - Create and use Synchronised Lyrics.Connect 4 - 2 Player Connect 4 Game (Local or Online!, Formatted Chat!!)MD5, SHA-1, SHA-256, Tiger and Whirlpool Hash Finder - Dictionary and Brute Force FindCool Text Client - Create Rendered ImageMy UDF's:GUI Enhance - Enhance your GUIs visually.IDEA File Encryption - Encrypt and decrypt files easily! File Rename - Rename files easilyRC4 Text Encryption - Encrypt text using the RC4 AlgorithmPrime Number - Check if a number is primeString Remove - remove lots of strings at onceProgress Bar - made easySound UDF - Play, Pause, Resume, Seek and Stop.

Share this post


Link to post
Share on other sites

I look forward to playing with the new sound beta RazerM (please post a link to it here). Thanks for all your work Melba23 and RazerM!


AutoIt changed my life.

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

The sound.au3 beta is at post #32 in the following thread:

http://www.autoitscript.com/forum/index.php?showtopic=30123

Thanks a lot to Melba23 for the contribution.

If no problems are found, the new sound.au3 will be sent to jpm for the next beta.

Edited by RazerM

My Programs:AInstall - Create a standalone installer for your programUnit Converter - Converts Length, Area, Volume, Weight, Temperature and Pressure to different unitsBinary Clock - Hours, minutes and seconds have 10 columns each to display timeAutoIt Editor - Code Editor with Syntax Highlighting.Laserix Editor & Player - Create, Edit and Play Laserix LevelsLyric Syncer - Create and use Synchronised Lyrics.Connect 4 - 2 Player Connect 4 Game (Local or Online!, Formatted Chat!!)MD5, SHA-1, SHA-256, Tiger and Whirlpool Hash Finder - Dictionary and Brute Force FindCool Text Client - Create Rendered ImageMy UDF's:GUI Enhance - Enhance your GUIs visually.IDEA File Encryption - Encrypt and decrypt files easily! File Rename - Rename files easilyRC4 Text Encryption - Encrypt text using the RC4 AlgorithmPrime Number - Check if a number is primeString Remove - remove lots of strings at onceProgress Bar - made easySound UDF - Play, Pause, Resume, Seek and Stop.

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

RazerM and Skizmata,

Thanks for your kind comments, but I am not clever enough to write the "file properties" code snippet on my own. I found a solution on the forums and modified it to do what I wanted. Although I did determine the correct Vista property coefficients! So if you want to credit someone for that bit, could I suggest the original authors - Simucal & PsaltyDS.

Their code:

;===============================================================================
; Function Name:    _FileGetExtProp($sPath,$iProp)
; Description:    Returns extended properties of a given file.
; Parameter(s):  $sPath = The file from which to retrieve an extended property.
;            Does not have to be fully pathed out, and can use realative path
;              from @WorkingDir; i.e. "File.txt", or "..\File.txt".
;            $iProp = The integer value for the property to return.
;            $iProp = -1 (the default) returns all properties in a 0-based array.
;            The properties are as follows: 
;       XP (?)                  Vista mp3 files
;              Name = 0             <---
;              Size = 1             <---
;              Type = 2             <---
;              DateModified = 3         <---
;              DateCreated = 4              <---
;              DateAccessed = 5         <---
;              Attributes = 6               <---
;              Status = 7
;              Owner = 8
;              Author = 9               "Audio"
;              Title = 10               "Everyone"
;              Subject = 11             "Music"
;              Category = 12
;              Pages = 13               Artist
;              Comments = 14                Album
;              Copyright = 15
;              Artist = 16              Genre
;              AlbumTitle = 17
;              Year = 18
;              TrackNumber = 19         Unrated
;              Genre = 20               Artist
;              Duration = 21                Track Title
;              BitRate = 22
;              Protected = 23
;              CameraModel = 24
;              DatePictureTaken = 25
;              Dimensions = 26              Track Number
;              Width = 27               Duration
;              Height = 28              Bitrate
;              Company = 30
;              Description = 31
;              FileVersion = 32
;              ProductName = 33
;              ProductVersion = 34
; Requirement(s):   File specified in $spath must exist.
; Return Value(s):  On Success returns 0-based array of properties, or string specified by $iProp.
;                   On Failure sets @error and @extended (see code below for details)
;           @error = 1 for bad parameter, 2 for object reference failure
;           e: Will return "" for an unset property (no error).
; Author(s):        Simucal (Simucal@gmail.com)
;          Modified version by PsaltyDS at www.autoitscript.com/forum
; Note(s):
;   2007-04-27  Modified by PsaltyDS from Simucal's original function:
;    Changed function name from _GetExtProperty to _FileGetExtProp, to group it by
;      naming convention with the other _File* UDFs.
;    Made optional $iProp = -1 default.
;    Changed to allow $sPath without full path.
;    Changed to not report empty fields as errors, returns "".
;    Added more detailed @error and @extended on failure.
;===============================================================================
Func _FileGetExtProp($sPath, $iProp = -1)
    Local $iExist, $sFile, $sDir, $oShellApp, $oDir, $oFile, $aProperty, $sProperty
    If FileExists($sPath) Then
        $sPath = FileGetShortName($sPath, 1)
        If IsInt($iProp) And $iProp >= -1 And $iProp <= 34 Then
            $sFile = StringTrimLeft($sPath, StringInStr($sPath, "\", 0, -1))
            $sDir = StringTrimRight($sPath, (StringLen($sPath) - StringInStr($sPath, "\", 0, -1)))
            $oShellApp = ObjCreate("shell.application")
            If IsObj($oShellApp) Then
                $oDir = $oShellApp.NameSpace ($sDir)
                If IsObj($oDir) Then
                    $oFile = $oDir.Parsename ($sFile)
                    If IsObj($oFile) Then
                        If $iProp = -1 Then
                            Local $aProperty[35]
                            For $i = 0 To 34
                                $aProperty[$i] = $oDir.GetDetailsOf ($oFile, $i)
                            Next
                            Return $aProperty
                        Else
                            $sProperty = $oDir.GetDetailsOf ($oFile, $iProp)
                            Return $sProperty
                        EndIf
                    Else
                        Return SetError(2, 2, 0); Failed to get file object
                    EndIf
                Else
                    Return SetError(2, 1, 0); Failed to get directory object
                EndIf
            Else
                Return SetError(2, 0, 0); Failed to create shell app
            EndIf
        Else
            Return SetError(1, 1, 0); $iProp is not integer, or out of range
        EndIf
    Else
        Return SetError(1, 0, 0); file does not exist
    EndIf
EndFunc  ;==>_FileGetExtProp

;===============================================================================
Edited by Melba23

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

RazerM,

Please check your PM's.

M23


Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

It now seems to work in most ways unfortunatly allot of my audio book mp3's still don't work properly. _SoundPos progresses to quickly 2x or more times normal time speed. Since it works perfectly with most mp3's attached is an audio book excerpt (short enough to be considered fair use if anyone wants to wine about copy right). Use the attached mp3 with the code in my first post and you will see the time problem.

Thank you both very much!

test.mp3


AutoIt changed my life.

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Skizmata,

I have been playing with your audiobook file and trying to see why it behaves as it does with the Sound.au3 UDF. Delving into the MP3 file structure has brought up a lot of info, much of which was new to me. So hold on tight, here goes (you can skip straight to the end to get the short version if you want to!):

I am sure we all know that an MP3 file is made up of multiple MP3 frames. These frames consist of an MP3 header and the MP3 data, which is the actual audio payload. The diagram below shows that an MP3 header consists of a sync word, used to identify the beginning of a valid frame, followed by a bit indicating that the file uses the MPEG standard and two bits that indicate the layer used; MPEG-1 Audio Layer 3 = MP3. After this, the values will differ, depending on the MP3 file. ISO/IEC 11172-3 defines the range of values for each section of the header along with the specification of the header. Most MP3 files today contain ID3 metadata, which precedes or follows the MP3 frames.

The fact that frames are used is why we started all of this. The MCI DLL assumes the rate for the first frame is used for them all and so miscalculates the bit rate for VBR files.

Example MP3 Header structure: (I know it is not code, but I cannot find any other way of keeping the formatting!)

Bits    .... .... ....  .        ..   .    .... ..   .    .  ..   ..    .     .     ..
Bin  1111 1111 1111  1       01   1    1010 00   0    0  01   00    0     0     00
Hex : F  : F  : F   :         B           : A    :     0        : 4        :       0          :

What?   MP3 Sync Word   Version   Layer   Error   Bit    Freq   Pad ?  Mode Mode  Copy   Original Emphasis
                                          Protect Rate                           Ext   right

Example              1 =       1 =   1 = No  0x0101  00 =   0 = ?  01 =       0 =   0 = Copy 0 = None
                        MPEG-1  III          = 160   44100  No Pad  Stereo      No


This is what I dug up concerning the values of the various elements:

Version:  0 = MPEG-2, 1 = MPEG-1

Layer: (in a most illogical manner!)

00  Not defined
01  Layer III   (which is what we want for audio files)
10  Layer II 
11  Layer I 

Bitrate:  MPEG-1      MPEG-2
          layer III   layer III
                      (very weird order!)
0000        -          -
0001       32          8
0010       40         16
0011       48         24
0100       56         32
0101       64         64
0110       80         80
0111       96         56
1000      112         64
1001      128        128
1010      160        160
1011      192        112
1100      224        128
1101      256        256
1110      320        320
1111        -          -

Sampling freq:

         MPEG-1  MPEG-2
00     44100 Hz   22050 Hz
01     48000 Hz   24000 Hz
10     32000 Hz   16000 Hz
11

I also found out that the (unofficial) MPEG-2.5 standard added some more:
Bit rate: 144 kbit/s
Sampling frequencies: 8, 11.025, 12 kHz.

The copyright, original and private bits are not needed for the decoding process.
The copyright bit is the same as the copyright bit on CDs and DAT tapes, preventing copying if the bit is set.
The original bit indicates, if set, that the frame is located on its original media.
No one seems to know what the private bit is for.

Mode

00  Stereo 
01  Joint stereo 
10  Dual channel 
11  Mono 

I cannot find decodes for the mode extension field values.

If the protection bit is NOT set then the frame header is followed by a 16 bit checksum, inserted before the audio data.
If the padding bit is set then the frame is padded with an extra byte.
The emphasis field indicates the noise supression model used (I do not know what they mean!):

Emphasis     Emphasis
Value       method 
00         none 
01         50/15ms 
10         -
11         CCITT j.17

Looking at the headers of some MP3 files we find the following:

A random music file:

FF )
FB )= MPEG-1 layer III (MP3), No Error
90  = 128 kbits/s, 44100 hz sampling (ripped from CD so normal), No Pad
40  = Joint Stereo, No copyright, Not original, No emphasis

Your Audio book file:

FF )
F3 )= MPEG-2 Layer III, No Error
84  = 64 kbit/s (24 kbit/s from file properties), 24000 hz (as shown by player), No Pad
C4  = Mono, , No copyright, Original, No emphasis

So, it looks as if the file is a non-standard MPEG-2 Layer III because of the bit rate setting.

This file plays and times perfectly in 1by1 and Random Mp3 Player, 2 software MP3 players I use.

It plays OK but times fast using the beta Sound.au3. The difference in the 64/24 kbit/s = 2.66 which is very close to the 2. 57~2.65 difference I noted when manually measuring the speed of the accelerated timer - so I think that is explained!

Using the old Sound.au3, however, it plays OK, but the timer behaves very strangely:

It times OK until 1:30 when it stops - although the file continues playing.

If you seek to elsewhere in the file, the timer restarts and then soon stops once more - again with the file still playing.

I converted your Audio book to .wav and then reconverted to MP3 @ 128 kbit/s:

FF )
FB )= MPEG-1 layer III (MP3), No Error
90  = 128 kbits/s, 44100 hz sampling (converted from .wav so quite normal), No Pad
00  = Stereo, No copyright, Not original, No emphasis

And this plays and times perfectly on my software MP3 players and with Sound.au3 (both versions).

I then tried using Audacity (a freeware audio editor) to convert it directly from whatever it is to normal 24 kbit/s MPEG:

FF )
F2 )= MPEG-2 layer III, Error checked
38  = 24 kbits/s, 16 kHz sampling, , No Pad
C0  = Mono, No copyright, Not original, No emphasis

A perfectly standard MPEG-2 file (I presume the sampling rate was changed by Audacity)

This also plays and times perfectly on my software MP3 players and with Sound.au3. Result!!!!!!

It looks as if both the old and new Sound.au3 UDFs are unhappy with the non-standard format of your audiobook files. As to why there is a difference in the behaviour of the old and beta Sound.au3s....I think I'll leave that up to RazerM as I can see no essential difference in way the 2 scripts call the DLL in the _SoundPos function.

So, the short answer is that if you want to use the Sound.au3 UDFs to play these audiobooks, you need to convert the mis-behaving files into standard MP3 format - that should keep you busy on a rainy day!

As I mentioned, I used Audacity 1.2.6 to transform the file - it is very good and absolutely free (http://audacity.sourceforge.net/ is the freeware one). All I did was import the file into Audacity and then save it as an MP3 - after showing Audacity where the LAME encoder was and setting the required bit rate. I know that you should not really re-encode MP3 files, but to my ears there was not a great loss of quality when re-saved at 24 kbit/s. Audacity will insist on adding an ID3 tag to the file, but leaving the fields blank only increased the file size by 26 bytes!

I hope this solves your problems. It was fun trying, and I learnt more about MP3 files than I ever wanted to!

M23

Edited by Melba23

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

@Skizmata

I managed to fix 2 bugs whilst working out what was wrong in your case, I noticed mci not reporting the sound position after 01:30 but Melba23 has worked that out above.

Beta is at post #40: http://www.autoitscript.com/forum/index.php?showtopic=30123


My Programs:AInstall - Create a standalone installer for your programUnit Converter - Converts Length, Area, Volume, Weight, Temperature and Pressure to different unitsBinary Clock - Hours, minutes and seconds have 10 columns each to display timeAutoIt Editor - Code Editor with Syntax Highlighting.Laserix Editor & Player - Create, Edit and Play Laserix LevelsLyric Syncer - Create and use Synchronised Lyrics.Connect 4 - 2 Player Connect 4 Game (Local or Online!, Formatted Chat!!)MD5, SHA-1, SHA-256, Tiger and Whirlpool Hash Finder - Dictionary and Brute Force FindCool Text Client - Create Rendered ImageMy UDF's:GUI Enhance - Enhance your GUIs visually.IDEA File Encryption - Encrypt and decrypt files easily! File Rename - Rename files easilyRC4 Text Encryption - Encrypt text using the RC4 AlgorithmPrime Number - Check if a number is primeString Remove - remove lots of strings at onceProgress Bar - made easySound UDF - Play, Pause, Resume, Seek and Stop.

Share this post


Link to post
Share on other sites

Thank you both very much. An amazing amount of work doing for us all from both of you. Thank you!


AutoIt changed my life.

Share this post


Link to post
Share on other sites

RazerM,

It occurred to me that another way of determining the correct length of a VBR mp3 is to look in the ID3 tag (if it exists) and see if the TLEN field is set.

I appreciate that it is not a guaranteed way to get the answer, and that it is only valid for files with a completed tag, but this might be a solution for those earlier versions of Windows where the "file properties" method is not available.

Trying it out on my mp3 library, for example, gave a valid time for only 1500 of the 5500 files I tested. Interestingly on some albums, ripped in one operation, some tracks have the TLEN field correctly completed, some have the field but no time, while others are missing the field entirely! Ah well, a new problem to play with.

I did check - the TLEN figure is the "true" length and matches the "file properties" method. If converted to hh:mm:ss t is often a second shorter - looks like Windows sets the file property to the nearest second.

The following is the code I developed to read the TLEN field in an ID3v2 tag:

Func Read_TLEN($Passed_File_Name)

; Read info from file as binary
$file_handle = FileOpen($Passed_File_Name, 4)
$Tag = FileRead($file_handle, 1024)
FileClose($file_handle)

; Check that an ID3v2.3 tag is present
If StringLeft($Tag, 10) <> "0x49443303" Then Return -1

; Search for "TLEN" within the tag
; $TLEN_Found = StringInStr($Tag, "544C454E") 
; Strip the tag to the start of the recorded time (11 bytes on from TLEN)
; $TLEN_Read = StringTrimLeft($Tag, $TLEN_Found + 21)
; Convert it to a string
; $Hex_TLEN = _HexToString($TLEN_Read)
; And then to a number
; $TLEN = Number($Hex_TLEN)

; Which combines to give:
$TLEN = Number(_HexToString(StringTrimLeft($Tag, StringInStr($Tag, "544C454E") + 21)))

; return result in ms (an error will return 0 or -1)
Return $TLEN

EndFunc

Feel free to play with it if you feel it might be useful addition to Sound.au3

Other than parsing the entire file to read and compute the length of each frame, I cannot think of any other way to get a correct length for VBR files. Judging by the results of Google searches, we are not alone in having this problem. If I come up with any other bright ideas, I will let you know.

M23


Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

I was thinking about this, thanks for the code. I'll have a a chance to try it out later.


My Programs:AInstall - Create a standalone installer for your programUnit Converter - Converts Length, Area, Volume, Weight, Temperature and Pressure to different unitsBinary Clock - Hours, minutes and seconds have 10 columns each to display timeAutoIt Editor - Code Editor with Syntax Highlighting.Laserix Editor & Player - Create, Edit and Play Laserix LevelsLyric Syncer - Create and use Synchronised Lyrics.Connect 4 - 2 Player Connect 4 Game (Local or Online!, Formatted Chat!!)MD5, SHA-1, SHA-256, Tiger and Whirlpool Hash Finder - Dictionary and Brute Force FindCool Text Client - Create Rendered ImageMy UDF's:GUI Enhance - Enhance your GUIs visually.IDEA File Encryption - Encrypt and decrypt files easily! File Rename - Rename files easilyRC4 Text Encryption - Encrypt text using the RC4 AlgorithmPrime Number - Check if a number is primeString Remove - remove lots of strings at onceProgress Bar - made easySound UDF - Play, Pause, Resume, Seek and Stop.

Share this post


Link to post
Share on other sites

RazerM,

A rainy Saturday may have led me to what appears to be the ultimate solution for mp3 files. Forget the TLEN field in the ID3 tag, we need to work with the "Xing" header which mp3 encoders should put into VBR files - the LAME encoder certainly does.

This "Xing" header (named after the first encoder to use VBR, apparently) contains the number of frames in the file, which when combined with the "samples per frame" and the "sampling rate" as found in the mp3 header I dissected yesterday, provides the duration of the file as follows:

Duration in secs = No of frames * Samples per frame / Sampling rate

Sounds good, doesn't it?

Here is the code I cobbled together to read the "Xing" header and the mp3 header data and so calculate the duration:

; Read from file
$file_handle = FileOpen($Passed_File_Name, 4)
$Tag = FileRead($file_handle, 5156)  
FileClose($file_handle)

; The furthest I ever needed to go to find the Xing header was 4168 bytes
; A suitable multiple of 512 is 4608
; but I added a further 1024 for safety making 5156

; Find the "Xing" string which denotes the data area

$Xing_Pos = StringInStr($Tag, "58696e67")

; If not found return

If $Xing_Pos = 0 Then Return 0

; Read a flag byte to make sure the "frames" field exists
; I have yet to find a file where it does not, which as its absence
; would defeat the whole object of the "Xing" header is not surprising

$Flags = Number("0x" & StringMid($Tag, $Xing_Pos + 14, 2))

; If bit 0 of the flag is set we can read the "frames" value
; it is a "Big-Endian" DWord

If BitAND($Flags, 1) = 1 Then
     $Frames = Number("0x" & StringMid($Tag, $Xing_Pos + 16, 8))
Else
     Return 0; No frames field so error return
EndIf

; We have the frames value
; Now to find Samples per frame & Sampling rate
; Go back to the start of the frame which holds the "Xing" header

$Mp3_Header = StringMid($Tag, $Xing_Pos - 72, 8)

; Read the relevant bytes
$MPEG_byte = Number("0x" & StringMid($Mp3_Header, 4, 1))
$Freq_byte = Number("0x" & StringMid($Mp3_Header, 6, 1))

; And decode them using the values as defined in my post above

; Determine MPEG version
; 8 = MPEG-1, 0 = MPEG-2
$MPEG_Ver =  BitAnd($MPEG_byte, 8)

; Determine Layer type
; 2 = Layer III, 4 = Layer II, 6 = Layer I
$Layer_Num = BitAnd($MPEG_byte, 6)

; Look up sampling rate
Switch $Layer_Num
    Case 6
        $Samples = 384
    Case 4
        $Samples = 1152
    Case 2
        Switch $MPEG_Ver
            Case 8 
                $Samples = 1152
            Case 0
                $Samples = 576
            Case Else
                $Samples = 0
        EndSwitch
    Case Else
        $Samples = 0
EndSwitch

; If not valid return
If $Samples = 0 Then Return 0

; Determine sampling frequency code
; 0 = bit 00, 4 = Bit 01, 8 = Bit 10
$Freq_Num = BitAnd($Freq_byte, 12)

; Look up sampling frequency
Switch $Freq_Num
    Case 0
        $Frequency = 44100
    Case 4
        $Frequency = 48000
    Case 8
        $Frequency = 32000
    Case Else
        $Frequency = 0
EndSwitch

; If not valid return
If $Frequency = 0 Then Return 0

; MPEG-2 halves the value
If $MPEG_Ver = 0 Then $Frequency = $frequency / 2

; Now ready to calculate duration
; Duration in millisecs = No of frames * Samples per frame / Sampling freq * 1000
$Length = $Frames * $Samples / $Frequency * 1000

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

That is brilliant Melba23, I will be adding it shortly so that we have mci, file properties, TLEN and Xing as way of getting the track length.

I will add all four into the udf with precedence given in this order:

Xing (highest)

TLEN

File Properties

MCI (lowest)

That's sure to give the correct length :P


My Programs:AInstall - Create a standalone installer for your programUnit Converter - Converts Length, Area, Volume, Weight, Temperature and Pressure to different unitsBinary Clock - Hours, minutes and seconds have 10 columns each to display timeAutoIt Editor - Code Editor with Syntax Highlighting.Laserix Editor & Player - Create, Edit and Play Laserix LevelsLyric Syncer - Create and use Synchronised Lyrics.Connect 4 - 2 Player Connect 4 Game (Local or Online!, Formatted Chat!!)MD5, SHA-1, SHA-256, Tiger and Whirlpool Hash Finder - Dictionary and Brute Force FindCool Text Client - Create Rendered ImageMy UDF's:GUI Enhance - Enhance your GUIs visually.IDEA File Encryption - Encrypt and decrypt files easily! File Rename - Rename files easilyRC4 Text Encryption - Encrypt text using the RC4 AlgorithmPrime Number - Check if a number is primeString Remove - remove lots of strings at onceProgress Bar - made easySound UDF - Play, Pause, Resume, Seek and Stop.

Share this post


Link to post
Share on other sites

#17 ·  Posted (edited)

I will add all four into the udf with precedence given in this order:

Xing (highest)

TLEN

File Properties

MCI (lowest)

That's sure to give the correct length :P

RazerM,

When the torrential rain woke me at 5 this morning, I spent a little time thinking about the order to try and if I might make a suggestion, the following might be a better order - at least for mp3s:

File properties (This will work in XP & Vista which should cover the majority of cases today - unless I am much mistaken - and avoids the need to read in the file)

TLEN (We have to open the file for both TLEN and Xing and TLEN offers an immediate reading without any calculation)

Xing (Will give VBR duration - seemingly guaranteed - but requires a bit if calculation)

MCI (Needed to do the VBR/CBR check in all cases. So could be either first or last)

However, I am more than happy with whatever order you choose. Given that the UDF will have to deal with only 1 file at a time, the overhead of the file read and Xing calculation is probably negligible.

I have just been trying out the code on a Win98 machine to see how it works there - did you know that the MCI method does not work? Or is it just my elderly spare PC? The TLEN and Xing methods worked fine (as long as the fields existed to be read!) and, of course, the file properties method failed. But I cannot imagine that there are very many Win98 PCs are still out there with owners who have large mp3 libraries!

M23

Added this afternoon.

It is 3PM, the rain is stopping and I am going out for a walk. But before I do, here is a last offering.

This code will find the first mp3 header (jumping over the ID3 tag if necessary) and calculate the duration by dividing the file size by the bitrate. Interestingly this gives exactly (to the ms!) result as the MCI call - so it must be how it does it. As the calculation is only valid for CBR files, it would explain why MCI cannot cope with VBR. And if MCI does do it this way, it does not make any allowance for ID3 tags - but as the tags tend to be less than 2k in size (about 100 ms at 160 kbit/s) it does not make a great deal of difference to the overall duration.

There are a couple of peculiar points. The ID3 tag length is encoded in 7-bit bytes with the high bit always 0 (because the FF bytes are used as synch bytes for frame headers). Hence the testing and bit-shifting to get the correct size. And I have used a rather unwieldy look-up table to store the bitrates - seems a bit fiddly but I could not think of a better way.

Func Calc_CBR($Passed_File_Name)

; Read tag info from file
$file_handle = FileOpen($Passed_File_Name, 4)
$Tag = FileRead($file_handle, 5156)
FileClose($file_handle)

$Start_bytes = StringMid($Tag, 3, 6)

Select 
    Case StringLeft($Start_bytes, 3) = "FFF"; It is an MP3 header
        $Header_pos = 3
    Case $Start_bytes = "494433"; It is an ID3 tag and we need to calculate tag length
        
        $Header_Length_bytes = StringMid($Tag, 19, 4)
        $Byte_1 = StringLeft ($Header_Length_bytes, 2)
        $Byte_2 = StringRight($Header_Length_bytes, 2)

        $test = BitAnd(Number("0x" & $Byte_1), 1)

        $Byte_1 = BitShift(Number("0x" & $Byte_1), 1)

        If $test Then 
            $Byte_2 = Number("0x" & $Byte_2) + 128
        Else
            $Byte_2 = Number("0x" & $Byte_2)
        EndIf

        $Tag_len = $Byte_1 * 256 + $Byte_2 + 10
        
        $Header_pos = $Tag_len * 2 + 3
        
    Case Else
        Return 0; Not a file we recognise
EndSelect

$Mp3_Header = StringMid($Tag, $Header_pos, 8)

$MPEG_byte =      Number("0x" & StringMid($Mp3_Header, 4, 1))
$Bit_Rate_byte =  Number("0x" & StringMid($Mp3_Header, 5, 1))

; 8 = MPEG-1, 0 = MPEG-2
$MPEG_Ver = BitAND($MPEG_byte, 8)
Switch $MPEG_Ver
    Case 8
        $Dim_1 = 0
    Case 0
        $Dim_1 = 3
EndSwitch

; 2 = Layer III, 4 = Layer II, 6 = Layer I
$Layer_Num = BitAND($MPEG_byte, 6)
Switch $Layer_Num
    Case 6
        $Dim_1 += 0
    Case 4
        $Dim_1 += 1
    Case 2
        $Dim_1 += 2
EndSwitch

; Now $Dim_1 = 0-5 and fits into the look-up table
; Make the bit rate byte 0-13 rather than 1-14
$Dim_2 = $Bit_Rate_byte - 1

; Create look up table (this is only filled for MPEG-1 layer III)
Local $Bit_table[6][14] = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0],[32,40,48,56,64,80,96,112,128,160,192,224,256,320],[0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0]]

; Look up in table
$Bitrate = $Bit_table[$Dim_1][$Dim_2]

; Calculate in ms
$File_duration = Int(FileGetSize($Passed_File_Name) * 8 / $Bitrate)

Return $File_Duration

EndFunc
Edited by Melba23

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0