example from http://www.all4mp3.com/Software4.aspx (was: http://www.all4mp3.com/dev/ )
There is several more examples there worth porting,
so get to work you lazy people

My port of this is minimalistic, apart from some minor variations
my port is very close to the original code.
Code: Select all
;this is a minimal set of source coded needed to make use of the l3codecp.acm ACM filter driver
;that is installed along with windows media player 10
;
;Copyright (C) 2005 Thomson, Inc.
;
;This library is free software; you can redistribute it and/or
;modify it under the terms of the GNU Lesser General Public
;License as published by the Free Software Foundation; either
;version 2.1 of the License, or (at your option) any later version.
;
;This library is distributed in the hope that it will be useful,
;but WITHOUT ANY WARRANTY; without even the implied warranty of
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;Lesser General Public License for more details.
;
;You should have received a copy of the GNU Lesser General Public
;License along with this library; if not, write to the Free Software
;Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
;here's a msdn page that has lots of good acm related info and code
;http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarmulmed/html/msdn_codec.asp
;If you find that you are unable to get this to work for bitrates higher than 56k, then it may be that there
;are some registry settings that are causing acm drivers to pick up the old restricted filter and not the new one
;installed along with windows media player 10
;
;this one should be l3codecp.acm, but if it is l3codeca.acm then it is the 56k limited driver
;[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32]
;"msacm.l3acm"="l3codecp.acm"
;
;this one may not be necessary, but may just be cosmetic
;[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\drivers.desc]
;"l3codecp.acm"="Fraunhofer IIS MPEG Layer-3 Codec (professional)"
;PureBasic port by Rescator
#MPEGLAYER3_ID_MPEG=1
#WAVE_FORMAT_MPEGLAYER3=$0055
#MPEGLAYER3_WFX_EXTRA_BYTES=12
#ACM_FORMATSUGGESTF_WFORMATTAG=$00010000
#ACM_STREAMSIZEF_SOURCE=$00000000
#ACM_STREAMCONVERTF_BLOCKALIGN=$00000004
#ACM_STREAMCONVERTF_END=$00000020
Structure WAVEFORMATEX
wFormatTag.w
nChannels.w
nSamplesPerSec.l
nAvgBytesPerSec.l
nBlockAlign.w
wBitsPerSample.w
cbSize.w
EndStructure
Structure MPEGLAYER3WAVEFORMAT
wfx.WAVEFORMATEX
wID.w
fdwFlags.l
nBlockSize.w
nFramesPerBlock.w
nCodecDelay.w
EndStructure
Structure ACMSTREAMHEADER
cbStruct.l
fdwStatus.l
dwUser.l
pbSrc.l
cbSrcLength.l
cbSrcLengthUsed.l
dwSrcUser.l
pbDst.l
cbDstLength.l
cbDstLengthUsed.l
dwDstUser.l
dwReservedDriver.l[10]
EndStructure
Structure ACMVERSION
build.w
rev.b
ver.b
EndStructure
OpenConsole()
Enumeration
#infile
#outfile
EndEnumeration
nb_read.l=0
leftover.l=0
wfxSrc.WAVEFORMATEX
MP3wfxDst.MPEGLAYER3WAVEFORMAT
dwACMVer.l=0
has.l=#Null
ash.ACMSTREAMHEADER
total_bytes.l=0
;these buffer sizes are hard coded here, you need to pick appropriate ones
cbSrcBuf.l=1152*2*2*4
cbDstBuf.l
*pSrcBuf=AllocateMemory(cbSrcBuf)
*pDstBuf=AllocateMemory(cbSrcBuf)
;Get and display the ACM version.
dwACMVer = acmGetVersion_();
*acmver.ACMVERSION=@dwACMVer
Print("ACM v"+StrU(*acmver\ver,#Byte)+"."+StrU(*acmver\rev,#Byte))
If *acmver\build=0
PrintN(" Retail")
Else
PrintN(" build "+StrU(*acmver\build,#Word))
EndIf
;set the source structure we need to know ahead of time what the sample rate,
;bits per sample, number of channels and bit rate numbers are
wfxSrc\wFormatTag=#WAVE_FORMAT_PCM
wfxSrc\nSamplesPerSec=44100
wfxSrc\nBlockAlign=4
wfxSrc\nAvgBytesPerSec=wfxSrc\nSamplesPerSec*wfxSrc\nBlockAlign
wfxSrc\nChannels=2
wfxSrc\wBitsPerSample=16
wfxSrc\cbSize=0
;set the destination structure
;in this example we'll say it is going to be an mp3 with 2 channels, 44100hz sample
;rate and 128k bitrate
MP3wfxDst\wID=#MPEGLAYER3_ID_MPEG
MP3wfxDst\wfx\wFormatTag=#WAVE_FORMAT_MPEGLAYER3
MP3wfxDst\wfx\nChannels=2
MP3wfxDst\wfx\nSamplesPerSec=44100
MP3wfxDst\wfx\wBitsPerSample=0
MP3wfxDst\wfx\nAvgBytesPerSec=320000/8
MP3wfxDst\wfx\cbSize=#MPEGLAYER3_WFX_EXTRA_BYTES
MP3wfxDst\wfx\nBlockAlign=1
;now let the driver try and find the best match for us
res=acmFormatSuggest_(#Null,@MP3wfxDst,@wfxSrc,SizeOf(WAVEFORMATEX),#ACM_FORMATSUGGESTF_WFORMATTAG)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
;now try to open the input and output streams
res=acmStreamOpen_(@has,#Null,@wfxSrc,@MP3wfxDst,#Null,0,0,ACM_STREAMOPENF_NONREALTIME)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
;now let the driver decide how big the destination buffer should be based
;on the parameters of both input and output wfx
res=acmStreamSize_(has,cbSrcBuf,@cbDstBuf,#ACM_STREAMSIZEF_SOURCE)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
;this would likely be a better place to allocate both the input and output buffers
;instead of the fixed size [0xffff] used at the top
;now set up the header to get ready to convert
ash\cbStruct=SizeOf(ACMSTREAMHEADER)
ash\pbSrc=*pSrcBuf
ash\cbSrcLength=cbSrcBuf
ash\pbDst=*pDstBuf
ash\cbDstLength=cbDstBuf
res=acmStreamPrepareHeader_(has,@ash,0)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
;open up the input pcm file
res=ReadFile(#infile,"source.wav")
If res=0
PrintN("cannot open input file")
;do something here about the failure
Goto quit
EndIf
;open up the output mp3 file
res=CreateFile(#outfile,"dest.mp3")
If res=0
PrintN("Cannot open outfile")
;do something here about the failure
Goto quit
EndIf
;here's a hack to skip over the usual 44 bytes in the wav header
UseFile(#infile)
FileSeek(44)
ash\cbDstLengthUsed=0
ash\cbSrcLengthUsed=0
leftover=0
total_bytes=0
;keep looping until we break or the eof is found on the input file
While Eof(#infile)=0
nb_read=0
If leftover<cbSrcBuf
;read some bytes from the file
UseFile(#infile)
nb_read=ReadData(*pSrcBuf+leftover,cbSrcBuf-leftover)
If nb_read=0
PrintN("cannot read bytes from input")
;do something here about the failure
Goto quit
EndIf
total_bytes+nb_read
EndIf
;adjust the size of the input buffer based on leftover amount and newly read data
ash\cbSrcLength=leftover+nb_read
;loop this section for a stream convert
;The acmStreamConvert function converts data in a source buffer into
;the destination format, writing the converted data into the destination buffer.
If nb_read>=0
res=acmStreamConvert_(has,@ash,#ACM_STREAMCONVERTF_BLOCKALIGN)
Else
res=acmStreamConvert_(has,@ash,#ACM_STREAMCONVERTF_BLOCKALIGN|#ACM_STREAMCONVERTF_END)
EndIf
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
PrintN("compressed total: "+StrU(total_bytes,#Long)+" mp3: "+StrU(ash\cbDstLengthUsed,#Long)+" pcm: "+StrU(ash\cbSrcLengthUsed,#Long))
leftover=ash\cbSrcLength-ash\cbSrcLengthUsed
;move to the start of the input buffer any of the unused pcm data
CopyMemory(*pSrcBuf+ash\cbSrcLengthUsed,*pSrcBuf,leftover)
UseFile(#outfile)
WriteData(*pDstBuf,ash\cbDstLengthUsed)
Wend
;now flush out any data from the queue
PrintN("")
PrintN("flushing decoder")
ash\cbDstLengthUsed=1
ash\cbSrcLength=0
;keep looping through the streamconverts until the input and output buffers are flushed
UseFile(#outfile)
While ash\cbDstLengthUsed>0
;The acmStreamConvert function converts data in a source buffer into
;the destination format, writing the converted data into the destination buffer.
res=acmStreamConvert_(has,@ash,#ACM_STREAMCONVERTF_BLOCKALIGN|#ACM_STREAMCONVERTF_END)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
If ash\cbDstLengthUsed>0
PrintN("compressed "+StrU(ash\cbDstLengthUsed,#Long)+" bytes from "+StrU(ash\cbSrcLengthUsed,#Long))
WriteData(*pDstBuf,ash\cbDstLengthUsed)
EndIf
Wend
;first make sure the input and output buffer sizes are
;the same as when the buffer was prepared
ash\cbSrcLength=cbSrcBuf
ash\cbDstLength=cbDstBuf
;unprepare the header before closing the stream
res=acmStreamUnprepareHeader_(has,@ash,0)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
;The acmStreamClose function closes a conversion stream.
res=acmStreamClose_(has,0)
If res<>#S_OK
PrintN("error "+StrU(res,#Long))
;do something here about the failure
Goto quit
EndIf
quit:
Input()
CloseConsole()
;these are some of the more common error codes that come back from acm
;ACMERR_BUSY The stream header specified in pash is currently in use and cannot be reused.
;ACMERR_UNPREPARED The stream header specified in pash is currently not prepared by the acmStreamPrepareHeader function.
;MMSYSERR_INVALFLAG At least one flag is invalid.
;MMSYSERR_INVALHANDLE The specified handle is invalid.
;MMSYSERR_INVALPARAM At least one parameter is invalid.