Hi all.
I want to load an MP3 file and save it as a WAV file.
Should be elementary.
As it turns out, it seems not to be.
The mmcontrol will only save with a sampling rate of 11kHz, rendering it useless for this purpose.
Any ideas?
David
Printable View
Hi all.
I want to load an MP3 file and save it as a WAV file.
Should be elementary.
As it turns out, it seems not to be.
The mmcontrol will only save with a sampling rate of 11kHz, rendering it useless for this purpose.
Any ideas?
David
Actually, it's quite easy. You can play a MP3 into a voice with the MCI (or externally in any MP3 player) and record it from another voice in the MCI as well. The MCI will support any sampling rate your audio card is able to support. I have been doing it for years. The MCI supports any sampling rate, but you are better stick with 44.1k. I often capture live audio with the MCI in wav format and use AudioGrabber to save it back to MP3. AudioGrabber is only comfortable with 44.1k, barring 48k to be processed.
Now the process is not actually a "conversion" but a capture rather, but I doubt any one can see (or hear) the difference.
I suppose the MCI can also save MP3 but I have never tried that.
Use ACM. Looking for the DirectSound8 type library on the CodeBank. There are my module which convert a mp3 file to wave PCM. Perharps you'll need to perform the conversion by two levels. Firstly convert mp3 to same PCM format, and further convert the obtained data to needed PCM format.
Navion,
Good to hear.
Have googled trying to find some code.
Could you post some code for this?
Would be much appreciated.
David
Well my code is part of a much larger framework involving lots of API support code about which extracting code would require way much work than writing what you are asking from scratch. It is also part of a commercial product about which I am under a non-disclosure contract.
Google "VB6 MCI multiple voices" and you should find examples to get you started. I suppose I could write a simple enough example that would not violate my contract. Lemme see.
Thanks for writing back.
A couple of lines of code is all that is needed.
MCISendstring(String to open an MP3 file)
MCISendstring(String to copy the MP3 to another voice)
MCISendstring(String to save the other voice as a WAV file)
Googling did not help :(
Have spent hours looking already.
Look forward to hearing from you.
David
Thanks for writing.
Apparently DirectSound8 will not run on anything laster than Vista (including Vista)
Any comments?
David
Thanks.
Do you have a link to the appropriate post in the codebank?
Here it is, down to bare bones, a little program to play an MP3 while recording it to WAV format. In command1_click, hard code your file name and the sample rate you want to use. File must exists and sample rate be valid, otherwise, the code is pretty robust.
A very simple program using the MCI, you have to play the entire song for the WAV to be written. Nothing as advanced as TheTrick's suggestion, but in a way, that's the beauty of it.
Thank you very much btw, TheTrick, for all your contributions to the Code Bank. They haven't gone unnoticed.
Attachment 130611
I found code at this link:
http://www.vbforums.com/showthread.p...-conversion%29
Is that the right link?
What I tried it out, it did not work.
MP3 Dec
Error:
"Wrong number of arguments or invalid property assignment"
On this line:
If 1 <> acm.ConvertData(0, realout, , , usedlen) Then
In the routine "Private Sub ConvertFile()"
Can you help?
David
acm.ConvertData(0, realout, , , usedlen)
takes 4 arguments where last 2 are optional - code shows 5
Maybe remove one or more of the commas???
http://www.vbforums.com/showthread.p...B6-DirectSound
Look function DSCreateSoundBufferFromMemory. There contain mp3 parser and decoder.
I downloaded the code and ran it.
Very impressive.
I don't have enough ability to build a "Save as wav" routine from your code though.
Could you throw something together?
Would be very much appreciated.
David
Dear The Trick.
I downloaded the code and ran it.
Very impressive.
I don't have enough ability to build a "Save as wav" routine from your code though.
Could you throw something together?
Would be very much appreciated.
David
As per this code :
I wonder how you got it to save your new .wav as xxxx.mp3.wav.Code:basename$ = App.Path & "\" & "With a little help from my friends"
playFilename$ = basename$ & ".mp3"
recordFilename$ = basename$ & ".wav"
You are on XP, are you not? I have not had a single miss on the Vista system on which the program was written. Works fine too on Win8. I'll investigate but I think I have clue what the problem might be.
Edit : Well of course, small overlook. All my systems are used to record, well not sound exclusively, but other devices that use "sound devices" as their transport. It's all controlled through software, but that part is not included in this little program. Easy fix : Control Panel, Sound... under the Recording tab... set "Stereo Mix" as the default recording device (may require right click to bring disabled devices if Stereo Mix has been disabled as recording device). Program will work.
Seems like the DX8-Typelib-solution will work out well for you in the end.
For comparison (and those who use the RichClient5)...
It contains an MP3 to PCM-Decoder Class for a few years already, wich is primarily
thought for live-decoding of MP3-ByteArray-Chunks to PCM-ByteArray-chunks
(to feed them into the new Vista+ CoreAudio-Classes which are contained as well)...
But it can be used without the CoreAudio-classes too (from XP onwards) ... here's some code
(open a normal StdExe-Project and put an RC5-reference into it)...:
Into a Class, named cMP3ToWav:
And here the Form-Code, which will deal with the three Events the above Class will raise during decodingCode:Option Explicit
Event NewMP3Info(ByVal Freq As Long, ByVal Channels As Long, ByVal ID3Offs As Long, ByVal DurationEstimate_msec As Long)
Event NewConvertedBuffer(WavBuf() As Byte, ByVal WavBytesInBuffer As Long, ByVal WavBytesTotal As Long)
Event ConversionFinished(ByVal Freq As Long, ByVal Channels As Long, ByVal Bits As Long, ByVal WavBytesTotal As Long)
Private Const MP3ChunkLen As Long = 262144, WafBufLen As Long = MP3ChunkLen * 20
Public Sub ConvertMP3(FileNameOrByteArray, Optional ByVal Bits As Long = 16)
'Initialization + MP3-Data-FileReading + MP3-Info-Retrieval
Dim MP3 As cMP3Resource, MP3Data() As Byte, Freq&, Channels&, ID3Offs&, MP3LenghtMS&
Set MP3 = New_c.MP3Resource
MP3Data = FileNameOrByteArray
If VarType(FileNameOrByteArray) = vbString Then MP3Data = New_c.FSO.ReadByteContent(CStr(FileNameOrByteArray))
MP3.GetMP3Info MP3Data, Freq, Channels, ID3Offs, MP3LenghtMS
RaiseEvent NewMP3Info(Freq, Channels, ID3Offs, MP3LenghtMS) 'signalize the Info we have found in the MP3-file
'prolong the MP3Data-Arr a little bit, to make Chunk-handling easier
ReDim Preserve MP3Data(((UBound(MP3Data) + 1 - ID3Offs) \ MP3ChunkLen + 2) * MP3ChunkLen + ID3Offs)
'now the Wav-conversion-part (in MP3-chunks, raising the so far converted WavBytes in an Event)
Dim WavBuf(0 To WafBufLen - 1) As Byte, MP3BIdx&, WavBytesConverted&, WavBytesTotal&
MP3.OpenConverter Freq, Channels, Bits
MP3.ConvertBuffer VarPtr(MP3Data(ID3Offs + MP3BIdx)), MP3ChunkLen, VarPtr(WavBuf(0)), WafBufLen, WavBytesConverted, True
Do While WavBytesConverted
WavBytesTotal = WavBytesTotal + WavBytesConverted
RaiseEvent NewConvertedBuffer(WavBuf, WavBytesConverted, WavBytesTotal) 'a fresh converted WavBuf has to be handled outside
MP3BIdx = MP3BIdx + MP3ChunkLen
MP3.ConvertBuffer VarPtr(MP3Data(ID3Offs + MP3BIdx)), MP3ChunkLen, VarPtr(WavBuf(0)), WafBufLen, WavBytesConverted
Loop
RaiseEvent ConversionFinished(Freq, Channels, Bits, WavBytesTotal) 'we're done with the conversion of all Chunks
End Sub
(stitching the decoded WaveBufs together by feeding them into a FileStream "as they come in"...)
Form needs a normal VB6-FileList-Box on it (named lstMP3s), which should be switched to filter
for *.mp3 in its Pattern-Property - otherwise it is only copy&paste:
HTHCode:Option Explicit
Private WithEvents MP3ToWav As cMP3ToWav, FSWav As cStream
Private mFreq&, mChannels&, mID3Offs&, mDurationEstimate_msec& 'for the Infos, read from the MP3-File
Private Sub Form_Load()
Caption = "Click the VB6-File-ListBox"
AutoRedraw = True
End Sub
Private Sub lstMP3s_Click() 'a VB6-FileListBox with its Pattern-Prop set to: *.mp3
New_c.Timing True
ConvertMP3 lstMP3s.Path & "\" & lstMP3s.FileName
Caption = "Wav-Data converted after:" & New_c.Timing
End Sub
Private Sub ConvertMP3(FileName As String)
Set FSWav = New_c.FSO.CreateFileStream(FileName & ".wav") 'for simplicity, we just append a '.wav' suffix to the *.mp3-FileName
FSWav.SetPosition 44 'skip the RIFF+WAVEfmt-Header (which is written later, in the MP3ToWav_ConversionFinished-Event)
Set MP3ToWav = New cMP3ToWav 'create a new Converter-instance
MP3ToWav.ConvertMP3 FileName 'and call the only method, currently contained in cMP3ToWav
Set MP3ToWav = Nothing 'cleanup the converter
Set FSWav = Nothing 'release the Wav-FileStream
End Sub
'Three Event-Handlers, to deal with the infos which come in from the cMP3ToWav-Converter-Class, due to calling its ConvertMP3-method
Private Sub MP3ToWav_NewMP3Info(ByVal Freq As Long, ByVal Channels As Long, ByVal ID3Offs As Long, ByVal DurationEstimate_msec As Long)
mFreq = Freq
mChannels = Channels
mID3Offs = ID3Offs
mDurationEstimate_msec = DurationEstimate_msec
End Sub
Private Sub MP3ToWav_NewConvertedBuffer(WavBuf() As Byte, ByVal WavBytesInBuffer As Long, ByVal WavBytesTotal As Long)
Cls
Print "MP3-Duration-Estimate:", CDate(mDurationEstimate_msec / 86400000)
Print "Current Wav-Length:", CDate(WavBytesTotal / (2 * mChannels * mFreq) / 86400)
FSWav.WriteFromPtr VarPtr(WavBuf(0)), WavBytesInBuffer 'just append the new incoming Wave-Buffer at the End of the Stream
Refresh
End Sub
Private Sub MP3ToWav_ConversionFinished(ByVal Freq As Long, ByVal Channels As Long, ByVal Bits As Long, ByVal WavBytesTotal As Long)
Const RIFF44$ = "RIFF WAVEfmt data " 'what remains, is writing out the missing Header at the start of the Stream
FSWav.SetPosition 0: FSWav.WriteFromByteArr StrConv(RIFF44, 128) 'write the 44Byte-Header to the File-Stream
FSWav.SetPosition 4: FSWav.WriteFromPtr VarPtr(WavBytesTotal + 36), 4 'FileSize (without the 8 Riff-Hdr-Bytes)
FSWav.SetPosition 16: FSWav.WriteFromPtr VarPtr(16&), 4 'Format-Data-Length
FSWav.WriteFromPtr VarPtr(1 + 65536 * Channels), 4 'PCM=1 + Channels
FSWav.WriteFromPtr VarPtr(Freq), 4 'Samples per second
FSWav.WriteFromPtr VarPtr(Bits * Channels * Freq \ 8), 4 'Bytes per second
FSWav.WriteFromPtr VarPtr(Bits * Channels \ 8 + 65536 * Bits), 4 'Bytes per Sample + Bits
FSWav.SetPosition 40: FSWav.WriteFromPtr VarPtr(WavBytesTotal), 4 'Wav-Data-length
End Sub
Olaf
Vielen Dank Olaf.
The only concern I have is the RC5 reference.
I googled and found out you are referring to the vbrichclient (I believe).
Is there a specific process for including this reference in VB6?
Could you perhaps upload a zip file with a working project including the RC5 reference?
MfG,
David
After downloading the Base-libraries from the Download-Section of vbRichClient.com, you will have
to run the small "register-in-place"-script (after unpacking the Zip into a folder of your choice).
Then you will be able, to include the vbRichClient5-reference into your project
(over the VB6-IDE-References dialogue - I usually hold down the 'V'-Key until the term
'vbRichClient5' scrolls by - since the entry is sitting quite some way down in the alphabetical list).
As for "regfree-deployment" (when you Zip and ship your App), I've explained that recently, here
in this thread: http://www.vbforums.com/showthread.p...iro-Rendering)
If you really only need the MP3ToWAV-functionality (as the last missing piece in your otherwise "finished" app),
then the RC5-dependency might be overkill (in this case the DX8-Typelib - which you will not have to ship with your App -
will save about 2.3MB in your final Zip).
If you can live with the 2.3 MB larger volume of your zipped solution - and maybe use a few
more things from the RC5 as well, then the Demo #3 in the above link should be helpful with
regards, how to manage regfree-deployment of the 3 RC5-Dlls (in a \Bin\-Subfolder of your Zip).
Olaf
Olaf,
My preference would be 'not' to add extra size to the app and instead to use the "DX8-Typelib".
Can you point me to a working example that uses the DX8-Typelib to convert an MP3 to a WAV?
Much appreciated,
David
I assume, that currently no such example ("complete with Wav-File-Writing") exists
(for the DX8-Typelib-approach, Trick has mentioned).
I'd leave it to him, to provide you with more infos, how to accomplish that.
As a suggestion (should you implement it yourself, in case the PCM-Buffers already come in
properly with Tricks example - and it is only the "File-Writing" which is missing...)
My little Code-Snippet further above already contains the needed parts for WavFileHeader-writing.
You could easily adapt this to VBs built-in FileFunctions (which work one-based with regards to FileOffsets):
HTHCode:Private Sub MP3ToWav_WriteFileHeader(ByVal VBFNr As Long, ByVal Freq As Long, ByVal Channels As Long, ByVal Bits As Long, ByVal WavBytesTotal As Long)
Const RIFF44$ = "RIFF WAVEfmt data "
Put VBFNr, 1, RIFF44 'write the 44Byte-Header to the File-Stream
Put VBFNr, 5, WavBytesTotal + 36 'FileSize (without the 8 Riff-Hdr-Bytes)
Put VBFNr, 17, CLng(16) 'Format-Data-Length
Put VBFNr, , CLng(1 + 65536 * Channels) 'PCM=1 + Channels
Put VBFNr, , CLng(Freq) 'Samples per second
Put VBFNr, , CLng(Bits * Channels * Freq \ 8) 'Bytes per second
Put VBFNr, , CLng(Bits * Channels \ 8 + 65536 * Bits) 'Bytes per Sample + Bits
Put VBFNr, 41, WavBytesTotal 'Wav-Data-length
End Sub
Olaf
Olaf,
I am indeed familiar with the WAV format and headers etc.
My problem is that I cannot figure out where the data is stored in TheTrick's code.
For example, I have code already to save a WAV file such as:
For n = 1 To NumberOfSamples
Put #FileNumber, , WaveData(n)
Next n
All I need really is to know where this "WaveData" is in TheTrick's code.
Could you help?
Thanks,
David
I made the example.
Much appreciated The Trick.
Огромное спасибо :)
I tried it out and it works.
Great.
Now a request...
The Lame encoder adds 1052 samples to the front of any MP3 file.
Why I don't know, but it's pretty well documented.
When going back from MP3 to WAV it would be good to start writing the WAV file only after the first 1052 samples.
That I could probably figure out on my own.
But the part I can not figure out is how to write the reduced WAV filesize in the WAV header (Bytes 5 to 8).
Any help would be appreciated.
David
Just skip its samples. You should begin to write since 1053 byte.
I tried this:
It didn't work.Code:Dim SampleCounter As Long
SampleCounter = SampleCounter + 1
If SampleCounter >= SampleStartPoint Then
If mmioWrite(hWave, buffer(0), acmHdr.cbDstLengthUsed) = -1 Then
acmStreamClose hStream, 0
mmioClose hWave
MsgBox "Error writing data"
Exit Function
End If
End If
Any suggestions?
David
I figured out how to skip X samples at the beginning to account for the MP3 encoding process.
I used TheTrick's code and added this routine to remove the front end bytes from the array:
Then in the conversion code from TheTrick I added this section:Code:Sub TrimInitial(ByRef Buffer() As Byte, ByVal BytesToTrim As Long)
Dim Counter As Long
Dim BufferTemp() As Byte
If BytesToTrim = 0 Then Exit Sub
ReDim BufferTemp(NumberOfElements - BytesToTrim)
For Counter = BytesToTrim + 1 To UBound(Buffer)
BufferTemp(Counter - BytesToTrim) = Buffer(Counter)
Next Counter
ReDim Buffer(0)
Buffer() = BufferTemp()
End Sub
...just before the "mmiowrite" line.Code:If IsFirstChunk Then
IsFirstChunk = False
TrimInitial Buffer(), SampleStartPoint * 2 'Samples are 2 bytes (16 bit)
acmHdr.cbDstLengthUsed = acmHdr.cbDstLengthUsed - SampleStartPoint * 2 'Samples are 2 bytes (16 bit)
End If
Thanks very much to you all for your help!
Thanks to TheTrick for his code.
David
I think there is an error in The Trick's code and I would like to offer a correction.
Code:.cbSize = Len(dstFormat) 'Incorrect
.cbSize = 16 'Correct
If mmioWrite(hWave, dstFormat, Len(dstFormat)) = -1 Then 'Incorrect
If mmioWrite(hWave, dstFormat, 16) = -1 Then 'Correct
David
Yes, but cbSize should be set to zero. However this field for WAVE_FORMAT_PCM ignored anyway.
Therefore it does not matterQuote:
Originally Posted by MSDN
Correct, you must pass 18:Quote:
If mmioWrite(hWave, dstFormat, Len(dstFormat)) = -1 Then
Attachment 130981
Hi, scallion.
Notice to that project. That vocoder has the clsTrickWavConverter class which converts any WAV-data to specified WAV.
Yes, it is a confirmed problem on my end, from .mp3 to .wav --- and I don't understand enough buffers/conversion/binary to fix it myself... it's way beyond my brain-grade...
Do you have a solution that does not add any data on the front of the .wav file?
It introduces latency, and therefore not acceptable in audio editing... Even the slightest ms.
iZotope conversion does not produce this anomaly.
Maybe you have overcome this issue with your plugin work since this post? Cheers
The original posting is quite old and ACM or DirectSound8 are also outdated. Today I would do the MP3 to WAV conversion using either MediaFoundation or WinRT.
yeah. DS8 works poorly in windows 10+ but perfectly in windows 7
while MF the reverse. use it if u are in windows 10+
another way is to used fmodex
I have now replaced both DS8/MF with that and now it works well in windows 7-11, its fast and it can play mp3 like a stream.
if u want to use that .dll, a tip is to be aware of FMOD_CREATESOUNDEXINFO that is in different structure depending on version.
(I did not know this and took me awhile to fix)
HOW DO WAVTOmp3 format?