Guest Posted March 15, 2014 Share Posted March 15, 2014 (edited) Hello, Today I did a lot of research to know if it is possible to play mp3 file without using any DLL. Just use the Windows API.And I am happy to inform you in that it's possible! I managed to do it.First of all to understand why it works, you have to understand what is the wav format.According to the research I did, this format can contain mp3 compression.In simple words - you can create a wav file with mp3 compression. Reed more here: http://en.wikipedia.org/wiki/WAV (Read after "WAV file compression codecs compared" ) Windows can play mp3 compression but only if you will add the WAVE-Header (of the wav format). So my code do exactly this. my code add the WAVE-Header to the mp3 file and then send it to winmm.dll to play this. The problem is - the Header must to contain the information about the length of the audio and and other information that I I do not know about and how to edit. i didn't created that Header. so i don't know how to edit that Header. I created that Header with the free tool "WaveMP3". If i use that Header for another mp3 file with the same compression but not the same length and longer length then windows will play the file but the sound will ends before the time because the length is according to the header. So i need more brains to help find out how to change the correct parameters in the Header or create better header. I have attached my work. I hope that someone will continue from where I stopped. By the way,In the rar file in test.mp3 not i speaking.Do not worry .. Play mp3 file with win api.rar Edited March 16, 2014 by Guest Link to comment Share on other sites More sharing options...
wakillon Posted March 15, 2014 Share Posted March 15, 2014 why not use SoundPlay Function ? AutoIt 3.3.14.2 X86 - SciTE 3.6.0 - WIN 8.1 X64 - Other Example Scripts Link to comment Share on other sites More sharing options...
Guest Posted March 15, 2014 Share Posted March 15, 2014 (edited) why not use SoundPlay Function ? You must use sndPlaySound because you create virtual wav file with mp3 compression in the memory and the only way i know to play sound from memory is to send the binary data to sndPlaySound . I don;t know if SoundPlay can play directly from binary string EDIT: It is designed for the case that no mp3 codec installed on Windows. You playing mp3 file without any mp3 encoder And in this case you can play mp3 file directly from the exe (you only need make few changes in the code). Edited March 15, 2014 by Guest Link to comment Share on other sites More sharing options...
joakim Posted March 16, 2014 Share Posted March 16, 2014 Guess both functions will do as they both support SND_MEMORY. Apparently some calculation is needed in order to get at time length; http://www.datavoyage.com/mpgscript/mpeghdr.htm http://www.multiweb.cz/twoinches/mp3inside.htm Each frame has a constant time length. But in order to get at number of frames, you must parse the entire file. Frames can vary in size with vbr. And to complicate further, there may exist metadata containers, like id3.. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted March 16, 2014 Moderators Share Posted March 16, 2014 (edited) joakim, Each frame has a constant time length. But in order to get at number of frames, you must parse the entire file. Frames can vary in size with vbr. And to complicate further, there may exist metadata containers, like id3That is not entirely true - you might find this thread of interest - it was my first contribution to AutoIt. And you can see the final code in the _SoundOpen function of Sound.au3. M23 Edited March 16, 2014 by Melba23 Typo 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...
Guest Posted March 16, 2014 Share Posted March 16, 2014 The problem is - the Header must to contain the information about the length of the audio and and other information that I do not know about and how to edit. If i use that Header for another mp3 file with the same compression but not the same length and longer length then windows will play the file but the sound will ends before the time because the length is according to the header. If someone can solve that problem and make code that generate the correct header for the mp3 file then we can create a function which made a MAGIC - function that can play mp3 file without any codec or dll. this is magic. Guess both functions will do as they both support SND_MEMORY. Apparently some calculation is needed in order to get at time length; http://www.datavoyage.com/mpgscript/mpeghdr.htm http://www.multiweb.cz/twoinches/mp3inside.htm Each frame has a constant time length. But in order to get at number of frames, you must parse the entire file. Frames can vary in size with vbr. And to complicate further, there may exist metadata containers, like id3.. If you learning how to to calculate the time and how to generate the header then good luck.. after you know how to do this, you can write code that makes the header and that's exactly what is needed! Link to comment Share on other sites More sharing options...
joakim Posted March 17, 2014 Share Posted March 17, 2014 joakim, That is not entirely true - you might find this thread of interest - it was my first contribution to AutoIt. And you can see the final code in the _SoundOpen function of Sound.au3. M23 That's really nice! I did not notice it before now. One question, since I may not have understood all the code yet: Is it possible to use this code and calculate mp3 sound length for a resource or some memory chunk (without temporarily writing it to disk as .mp3)? My quick assumption is no, since it is based on reading file properties. Or yes if temporarily writing the data to disk as .mp3 is an option.. Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted March 17, 2014 Moderators Share Posted March 17, 2014 joakim,Although the default method used by the UDF is to look at the file properties (because it is the easiest to do ) most of the code uses secondary methods to look inside the mp3 frames. The Xing header is the real key for VBR files with MCI as a less accurate backup. Take a look at the code in detail and do not hesitate to come back with questions if necessary - although it was a long time ago I think I can just about remember what went on. 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...
joakim Posted March 17, 2014 Share Posted March 17, 2014 So I think I understand a bit more it now However, still I am not sure if MCI/mciSendString is supposed to support a memory pointer as lpszDevice (instead of filename as in sound.au3). Curently I get error code 263: "The specified device is not open or is not recognized by MCI." And still I am not sure exactly how mciSendString in the end resolves audio length with lpszCommand=status and lpszOpenFlags=length. Do you know Melba23? Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted March 17, 2014 Moderators Share Posted March 17, 2014 joakim,Re-reading the thread to which I linked, I see that you do not need to use MCI at all - the final post shows how to get a basic CBR time from the header. So I suggest that in the case of a resource file you first look at getting the CBR length (which will only be correct for a CBR encoded file) and then look for a Xing header - if you find one then it is a VBR file and you need to parse that header to get the length; if not then it is CBR and the initial timing will be correct. I shall look into this more deeply tomorrow when I have dug out all the mp3 documentation that I used when developing the Sound UDF - I think I know where it is on my hard drive. 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...
Guest Posted March 17, 2014 Share Posted March 17, 2014 (edited) joakim, If I understood correctly:Your goal is to create a code / use code which calculates the length of the mp3 file and then create / use code which generates the wave header with the length. I understood correctly? If so then how are you going to write the length of the mp3 file in wav header? It seems like you know how to do it ..I looked at the header and i do not see anything readable that you can easily edit. this is how the header that my script add look: i can't see(in the red area) Where the length is written. i know that the length is written in that area because I did some checks .. Edited March 17, 2014 by Guest Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted March 18, 2014 Moderators Share Posted March 18, 2014 gil900,You cannot save the timing in a wav header - there is nowhere to put it. What you will have to do is get the timing from the mp3 section of the resource before you play it. Anyway I have been having fun today with this! Here is my version of a script to convert an mp3 file to a hybrid wav fileL; Basic wav header blocks $sHdr_1 = "0x52494646" $sHdr_2 = "57415645666D74201E0000005500020044AC0000581B0000010000000C00010002000000B600010071056661637404000000640E060064617461" $sAlign_Buffer = "00" ; Select mp3 file $sFile = FileOpenDialog("Select mp3 file to convert", @ScriptDir, "mp3 (*.mp3)") If Not $sFile Then Exit ; Read mp3 file $sMp3 = StringTrimLeft(Binary(FileRead($sFile)), 2) $iMp3Size = StringLen($sMp3) ; Get file size $iFileSize = FileGetSize($sFile) ; Convert to required format $iMp3Size = StringRegExpReplace(Hex($iFileSize, 8), "(..)(..)(..)(..)", "$4$3$2$1") $iWavSize = StringRegExpReplace(Hex($iFileSize + 63, 8), "(..)(..)(..)(..)", "$4$3$2$1") ; Construct hybrid wav file $sHybridWav = $sHdr_1 & $iWavSize & $sHdr_2 & $iMp3Size & $sMp3 If Mod($iMp3Size, 2) Then $sHybridWav &= $sAlign_Buffer EndIf ; Save new file $sFile = FileSaveDialog("Select filename for new file", @ScriptDir, "wav (*.wav)") If Not $sFile Then Exit $hFile = FileOpen($sFile, 2 + 16) FileWrite($hFile, $sHybridWav) FileClose($hFile)It will take any mp3 file - even one with an ID3 tag - and convert it to a hybrid wav file by adding a valid wav header (and padding the file by one byte if required). A valid wav header contains sizing data and so needs to be calculated for each file - you cannot just add the same string to every mp3. And then I developed this script to play the hybrid wav file from the resource table - reading the timing from the file itself:expandcollapse popup#AutoIt3Wrapper_Res_File_Add=Sound.wav, RT_RCDATA, SOUND #include <Date.au3> #include <Resources.au3> _Play_Resource_Sound("SOUND") Func _Play_Resource_Sound($sName) ; Read resource into memory $pResPointer = _ResourceGet($sName) $iResSize = @extended $tResStruct = DllStructCreate("byte[" & $iResSize & "]", $pResPointer) $sSound = DllStructGetData($tResStruct, 1) ; Calculate CBR timing $iTrackLen_CBR = _CBR_Timing(StringLeft($sSound, 5120), $iResSize) $iBitrate = @extended ; Look for VBR timing $iTrackLen_VBR = _VBR_Timing(StringLeft($sSound, 5120)) ; Play sound Local $SND_NODEFAULT = 2 Local $iFlag = BitOR($SND_MEMORY, $SND_ASYNC, $SND_NODEFAULT) DllCall("winmm.dll", "int", "sndPlaySound", "ptr", $pResPointer, "UINT", $iFlag) ; Show results If $iTrackLen_VBR = 0 Then MsgBox(0, "CBR", _Convert_Timing($iTrackLen_CBR) & @CRLF & $iBitrate & " - " & Hex($iBitrate * 100, 8)) Else MsgBox(0, "VBR", _Convert_Timing($iTrackLen_VBR) & @CRLF & $iBitrate & " - " & Hex($iBitrate * 100, 8)) EndIf EndFunc ;==>_Play_Resource_Sound Func _Convert_Timing($iMs) Local $iHours, $iMins, $iSecs _TicksToTime($iMs, $iHours, $iMins, $iSecs) Return StringFormat("%02i:%02i:%02i", $iHours, $iMins, $iSecs) EndFunc ;==>_Convert_Timing Func _CBR_Timing($sSound, $iResSize) ; Look for start of MP3 header Local $iMP3_Start = StringInStr($sSound, "FFF") If Not $iMP3_Start Then Return SetError(1, 0, 0) EndIf Local $sSound_Header = StringMid($sSound, $iMP3_Start, 8) ; Create look up table (this is only filled for MPEG-1 layer III) Local $aBit_Table[14] = [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320] ; Look up bitrate $iBitrate = $aBit_Table[Number("0x" & StringMid($sSound_Header, 5, 1)) - 1] ; Length in ms Return SetExtended($iBitrate, Int($iResSize * 8 / $iBitrate)) EndFunc ;==>_CBR_Timing Func _VBR_Timing($sTag) Local $iXingPos = StringInStr($sTag, "58696E67") ; Xing If Not $iXingPos Then Return SetError(1, 0, 0) EndIf ; Read fields flag Local $iFrames, $iFlags = Number("0x" & StringMid($sTag, $iXingPos + 14, 2)) If BitAND($iFlags, 1) = 1 Then $iFrames = Number("0x" & StringMid($sTag, $iXingPos + 16, 8)) Else Return SetError(1, 0, 0); No frames field EndIf ; Now to find Samples per frame & Sampling rate ; Go back to the frame header start Local $sHeader = StringMid($sTag, $iXingPos - 72, 8) ; Read the relevant bytes Local $iMPEGByte = Number("0x" & StringMid($sHeader, 4, 1)) Local $iFreqByte = Number("0x" & StringMid($sHeader, 6, 1)) ; Decode them ; 8 = MPEG-1, 0 = MPEG-2 Local $iMPEGVer = BitAND($iMPEGByte, 8) ; 2 = Layer III, 4 = Layer II, 6 = Layer I Local $iLayerNum = BitAND($iMPEGByte, 6) Local $iSamples Switch $iLayerNum Case 6 $iSamples = 384 Case 4 $iSamples = 1152 Case 2 Switch $iMPEGVer Case 8 $iSamples = 1152 Case 0 $iSamples = 576 Case Else $iSamples = 0 EndSwitch Case Else $iSamples = 0 EndSwitch ; If not valid return If $iSamples = 0 Then Return SetError(1, 0, 0) ; 0 = bit 00, 4 = Bit 01, 8 = Bit 10 Local $iFrequency, $iFreqNum = BitAND($iFreqByte, 12) Switch $iFreqNum Case 0 $iFrequency = 44100 Case 4 $iFrequency = 48000 Case 8 $iFrequency = 32000 Case Else $iFrequency = 0 EndSwitch ; If not valid return If $iFrequency = 0 Then Return SetError(1, 0, 0) ; MPEG-2 halves the value If $iMPEGVer = 0 Then $iFrequency = $iFrequency / 2 ; Duration in ms = No of frames * Samples per frame / Sampling freq * 1000 Return Int(($iFrames * $iSamples / $iFrequency) * 1000) EndFunc ;==>_VBR_TimingDo not cancel the MsgBox if you want to verify the duration as the sound will stop immediately! Looking forward to some feedback. M23 UEZ, Mobius and mesale0077 3 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...
Guest Posted March 18, 2014 Share Posted March 18, 2014 (edited) Thank you Melba23! I'm glad someone actually did something useful that works and is based on/result of my discovery/start point.This is what I expected .. It will be very useful! Edited March 18, 2014 by Guest Link to comment Share on other sites More sharing options...
joakim Posted March 19, 2014 Share Posted March 19, 2014 Looks cool. Will be away for a few days, but will look further into it when back. 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