Page 1 of 1

ACM Encoder Sample Code (mp3 encoding)

Posted: Wed Sep 21, 2005 2:02 am
by Rescator
I ported the "ACM Encoder Sample Code (mp3 encoding)"
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 :P

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.
Enjoy!

Posted: Wed Sep 21, 2005 9:59 am
by Tranquil
Great one! thanks for shareing!!

Posted: Sun Sep 06, 2009 10:06 pm
by Rescator
Updated the url to the original source, there is also a ACM Decoder and more at the officials site: http://www.all4mp3.com/Software4.aspx

Posted: Mon Sep 07, 2009 11:33 am
by dige
Works fine with PB 4.40B2

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, #PB_Byte) + "." + StrU(*acmver\rev, #PB_Byte))
If *acmver\build = 0
  PrintN(" Retail")
Else
  PrintN(" build " + StrU(*acmver\build, #PB_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, #PB_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, #PB_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, #PB_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, #PB_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
FileSeek(#infile, 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
    nb_read = ReadData(#infile, *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, #PB_Long))
    ;do something here about the failure
    Goto quit
  EndIf
  
  PrintN("compressed total: " + StrU(total_bytes, #PB_Long) + " mp3: " + StrU(ash\cbDstLengthUsed, #PB_Long) + " pcm: " + StrU(ash\cbSrcLengthUsed, #PB_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)
  WriteData(#outfile, *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
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, #PB_Long))
    ;do something here about the failure
    Goto quit
  EndIf
  If ash\cbDstLengthUsed>0
    PrintN("compressed " + StrU(ash\cbDstLengthUsed, #PB_Long) + " bytes from " + StrU(ash\cbSrcLengthUsed, #PB_Long))
    WriteData(#outfile, *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, #PB_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, #PB_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.

Re: ACM Encoder Sample Code (mp3 encoding)

Posted: Fri Sep 18, 2009 1:39 am
by oryaaaaa
Hello dige.

I try this code, but it has error. Error 11 means
"MMSYSERR_INVALPARAM invalid parameter passed"

My weak point is reading this acm decorde vc code. Could you tell me correct code?, please.

Code: Select all

FillMemory(@ash, SizeOf(ACMSTREAMHEADER), 0)
ash\cbStruct = SizeOf(ACMSTREAMHEADER)
ash\fdwStatus=0
ash\dwUser=0
ash\pbSrc = *pSrcBuf
ash\cbSrcLength = cbSrcBuf
ash\pbDst = *pDstBuf
ash\cbDstLength = cbDstBuf
res = acmStreamPrepareHeader_(has, @ash, 0)

Re: ACM Encoder Sample Code (mp3 encoding)

Posted: Sat Sep 19, 2009 5:58 am
by oryaaaaa
ACM decoder tips, until covert only

Code: Select all

#BUFSIZE = 131072 ; 128KB
#WAVE_FORMAT_MPEGLAYER3 = $0055
#MPEGLAYER3_WFX_EXTRA_BYTES = 12
#MPEGLAYER3_ID_MPEG = 1
#MPEGLAYER3_FLAG_PADDING_ON=1
#MPEGLAYER3_FLAG_PADDING_OFF=2
#ACM_FORMATSUGGESTF_WFORMATTAG = $00010000
#ACM_STREAMOPENF_NONREALTIME = $00000004
#ACM_STREAMSIZEF_SOURCE = $00000000

InitSound()

OpenConsole()

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

Global *pBuf, nFirstSize.l
*pBuf = AllocateMemory(#BUFSIZE)

Global Dim tbFreq.l(2) 
tbFreq(0) = 44100
tbFreq(1) = 48000
tbFreq(2) = 32000
 
Global Dim tbBitrate(1, 2, 14)
Restore Bitrate
For n1=0 To 1
  For n2=0 To 2
    For n3=0 To 14
      Read.w tbBitrate(n1,n2,n3)
    Next
  Next
Next

DataSection
  Bitrate:
  Data.w 0,32,64,96,128,160,192,224,256,288,320,352,384,416,448
  Data.w 0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384
  Data.w 0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320
  Data.w 0,32,48,56,64,80,96,112,128,144,160,176,192,224,256
  Data.w 0,8,16,24,32,40,48,56,64,80,96,112,128,144,160
  Data.w 0,8,16,24,32,40,48,56,64,80,96,112,128,144,160
EndDataSection

Global Dim tbSmpPerFrame(2,2)
tbSmpPerFrame(0,0) = 384
tbSmpPerFrame(0,1) = 1152
tbSmpPerFrame(0,2) = 1152
tbSmpPerFrame(1,0) = 384
tbSmpPerFrame(1,1) = 1152
tbSmpPerFrame(1,2) = 576
tbSmpPerFrame(2,0) = 384
tbSmpPerFrame(2,1) = 1152
tbSmpPerFrame(2,2) = 576

Procedure GetFrameHeader(*pwf.MPEGLAYER3WAVEFORMAT)
  Protected Dim head.a(4)
  Protected ver.l, Layer.l, Bitrate.l, samp.l, padding.l, empha.l, fsize.l, File.l, quit.b
   
  File = ReadFile(#PB_Any, "source.mp3")
  head(1) = ReadByte(File)  :  head(2) = ReadByte(File) 
  
  Repeat
    While Eof(File)=0
      If head(1)=$FF And head(2)=>$E0
        Break
      EndIf
      head(1) = head(2)
      head(2) = ReadByte(File)
    Wend
    
    head(3) = ReadByte(File) : head(4) = ReadByte(File)
    
    ver = (head(2) >> 3) & 3
    Layer = (head(2) >>1 ) & 3;
    Bitrate = (head(3) >> 4 ) & $F
    samp = (head(3) >> 2 ) & 3
    empha = head(4) & 3;
    If Layer=<3
      If Bitrate=<15
        If samp=<3
          quit = #True
        EndIf
      EndIf
    EndIf
    
  Until quit=#True
  Debug Hex(Loc(File))
  
  If ver=0
    ver=2
  ElseIf ver=2
    ver=1
  Else
    ver=0
  EndIf
  
  Layer = 3 - Layer
  
  If ver=0
    Bitrate = tbBitrate(0, Layer, Bitrate) 
  Else
    Bitrate = tbBitrate(ver, Layer, Bitrate) 
  EndIf
  
  samp = tbFreq(samp) >> ver
  
  padding = ( head(3) >> 1 ) &1
  
  fsize =  tbSmpPerFrame(ver, Layer) /8 * Bitrate * 1000 / samp + padding
  
  *pwf\wfx\wFormatTag   = #WAVE_FORMAT_MPEGLAYER3
  *pwf\wfx\nChannels      = 2
  *pwf\wfx\nSamplesPerSec = samp
  *pwf\wfx\nAvgBytesPerSec = Bitrate * 1000 /8
  *pwf\wfx\nBlockAlign     = 1
  *pwf\wfx\wBitsPerSample = 0
  *pwf\wfx\cbSize = #MPEGLAYER3_WFX_EXTRA_BYTES
  
  *pwf\wID = #MPEGLAYER3_ID_MPEG
  *pwf\nBlockSize = fsize
  *pwf\nFramesPerBlock = 1
  *pwf\nCodecDelay = 1393
  If padding
    *pwf\fdwFlags = #MPEGLAYER3_FLAG_PADDING_ON
  Else
    *pwf\fdwFlags = #MPEGLAYER3_FLAG_PADDING_OFF 
  EndIf
  FileSeek(File, Loc(File)-4)
  
EndProcedure

Procedure ConvWav()
  Protected wfmp3.MPEGLAYER3WAVEFORMAT
  Protected wfwav.WAVEFORMATEX
  Protected ash.ACMSTREAMHEADER
  Protected *pOutBuf
  Protected hStream.l, decsize.l, rec.l
  
  Debug @wfmp3
  GetFrameHeader(@wfmp3)
  
  FillMemory(wfwav, SizeOf(WAVEFORMATEX), 0)
  wfwav\wFormatTag = #WAVE_FORMAT_PCM
  res = acmFormatSuggest_(#Null, @wfmp3, @wfwav, SizeOf(WAVEFORMATEX), #ACM_FORMATSUGGESTF_WFORMATTAG)

  If res<>#S_OK
    PrintN("error1 " + StrU(res, #PB_Long))
  EndIf  
  
  res = acmStreamOpen_(@hStream, #Null, @wfmp3, @wfwav, #Null, 0, 0, #ACM_STREAMOPENF_NONREALTIME)
  
  If res<>#S_OK
    PrintN("error2 " + StrU(res, #PB_Long))
  EndIf  

  res = acmStreamSize_(hStream, wfmp3\nBlockSize +1, @decsize, # ACM_STREAMSIZEF_SOURCE)

  If res<>#S_OK
    PrintN("error3 " + StrU(res, #PB_Long))
  EndIf  
  
  *pOutBuf = AllocateMemory(decsize)
  
  FillMemory(@ash, SizeOf(ash), 0)
  ash\cbStruct = SizeOf(ash)
  ash\pbSrc = *pBuf
  ash\cbSrcLength = wfmp3\nBlockSize +1
  ash\pbDst = *pOutBuf
  ash\cbDstLength = decsize
  res = acmStreamPrepareHeader_(hStream, @ash, 0)
  
  If res<>#S_OK
    PrintN("error4 " + StrU(res, #PB_Long))
  EndIf  
  
  res = acmStreamConvert_(hStream, @ash, 0)
  
  If res<>#S_OK
    PrintN("error5 " + StrU(res, #PB_Long))
  EndIf  
  
  CallDebugger
EndProcedure

ConvWav()

CloseConsole()

ACM decode and WAV up sampling Tips

Posted: Tue Sep 22, 2009 11:03 am
by oryaaaaa
I successded MP3 decode, and Streaming by DirectSound.

Tips1
*pwf\nBlockSize = fsize (MAX 32KB)
Please change structure .w to .u then = fsize * n (MAX 64KB)

Tips2
*men is wave data

Code: Select all

  While Eof(File)=0
    resbyte.l = ReadData(File, *pBuf, wfmp3¥nBlockSize +1) 
    ash¥cbSrcLength = resbyte
    res = acmStreamConvert_(hStream, @ash, 0)
    SetGadgetState(1, Loc(File))
    WindowEvent()

    If ash¥cbDstLengthUsed
      t_totalsize = totalsize
      totalsize + ash¥cbDstLengthUsed
      If MemorySize(*mem)<totalsize
        *mem = ReAllocateMemory(*mem, totalsize+cbDstLengthUsed*1000) 
      EndIf
      CopyMemory(*pOutBuf, *mem+t_totalsize, ash¥cbDstLengthUsed)
    EndIf
    
  Wend
Tips3
UpSampling : *BufferAudio (*mem x 2) buflen (*men x 2)
Simple up Sampling. Direct Sound Output 96kHz Mode
need external DAC amp or 96kHz over can playing card

DirectSound Topic
viewtopic.php?f=12&t=20841

Code: Select all

Procedure upFreqWav(*BufferAudio, buflen.l)
  Protected I.l, *clearmem, flag.l, *memory_buf
  ; Skip noise block
  *memory_buf = *BufferAudio
  buflen = buflen/2
  
  ; Skip Noise Block
  *clearmem = AllocateMemory(256)
  Repeat
    If CompareMemory(*BufferAudio, *clearmem, 255)
      Break
    Else
      *BufferAudio+255
      buflen - 255
    EndIf 
  ForEver
  FreeMemory(*clearmem)
  
  ; Adjust Data position
  Repeat
    *BufferAudio +1
  Until PeekB(*BufferAudio)<>0
  *BufferAudio -1
  
  ; Insert zero data
  *clearmem = AllocateMemory(4)
  For I =buflen To 1 Step -4
    CopyMemory(*BufferAudio+I, *memory_buf+I*2 , 4)
    CopyMemory(*clearmem, *memory_buf+I*2 -4, 4)
  Next
  FreeMemory(*clearmem)
EndProcedure

Code: Select all

      ;WAVE FORMAT (If use ACM stream, this block remove)
      *pWfx¥wFormatTag = #WAVE_FORMAT_PCM
      *pWfx¥nChannels = 2
      *pWfx¥nSamplesPerSec = 44100 * 2
      *pWfx¥nAvgBytesPerSec = 176400 * 2
      *pWfx¥nBlockAlign = 4
      *pWfx¥wBitsPerSample = 16
      *pWfx¥cbSize = 0