Skizmata Posted September 28, 2008 Share Posted September 28, 2008 (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 expandcollapse popup#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 September 29, 2008 by Skizmata AutoIt changed my life. Link to comment Share on other sites More sharing options...
RazerM Posted September 29, 2008 Share Posted September 29, 2008 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. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted September 29, 2008 Moderators Share Posted September 29, 2008 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 Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
RazerM Posted September 29, 2008 Share Posted September 29, 2008 (edited) @Melba23That'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.au3To 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 September 29, 2008 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. Link to comment Share on other sites More sharing options...
Skizmata Posted September 29, 2008 Author Share Posted September 29, 2008 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. Link to comment Share on other sites More sharing options...
RazerM Posted September 30, 2008 Share Posted September 30, 2008 (edited) The sound.au3 beta is at post #32 in the following thread:http://www.autoitscript.com/forum/index.php?showtopic=30123Thanks 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 September 30, 2008 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. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted October 1, 2008 Moderators Share Posted October 1, 2008 (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: expandcollapse popup;=============================================================================== ; 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 October 1, 2008 by Melba23  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted October 1, 2008 Moderators Share Posted October 1, 2008 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 Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
Skizmata Posted October 2, 2008 Author Share Posted October 2, 2008 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. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted October 2, 2008 Moderators Share Posted October 2, 2008 (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!) expandcollapse popupBits .... .... .... . .. . .... .. . . .. .. . . .. 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 October 2, 2008 by Melba23  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
RazerM Posted October 2, 2008 Share Posted October 2, 2008 @SkizmataI 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. Link to comment Share on other sites More sharing options...
Skizmata Posted October 2, 2008 Author Share Posted October 2, 2008 Thank you both very much. An amazing amount of work doing for us all from both of you. Thank you! AutoIt changed my life. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted October 4, 2008 Moderators Share Posted October 4, 2008 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 Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
RazerM Posted October 4, 2008 Share Posted October 4, 2008 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. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted October 4, 2008 Moderators Share Posted October 4, 2008 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:expandcollapse popup; 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 Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
RazerM Posted October 5, 2008 Share Posted October 5, 2008 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 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. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted October 5, 2008 Moderators Share Posted October 5, 2008 (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 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.expandcollapse popupFunc 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 October 5, 2008 by Melba23  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now