A LGPL MP3 encoder. Where: Project home : http://lame.sourceforge.net/ Linux binaries : http://users.rsise.anu.edu.au/~conrad/not_lame/ Windows binaries : http://www.rarewares.org/mp3.html ---- Applications based on LAME, or in related areas: * [A Radio Recorder] ---- [dzach]:'''At the time being, this is a request for help. Hopefully, if this problem gets resolved, this page will be usefull to more people.''' I have used the windows executable with a pipe, but on a slower win98 machine I get errors. It should be better if the dll could be called directly from tcl and be passed the sound data to encode, or even better, if a tcl wrapper for LAME existed ;-). Trying to do the first, I post here, ''temporarily'', the files documenting the dll's usage, from the project's source code: * BladeMP3EncDLL.c [http://users.hol.gr/~dzach/vox/lame/BladeMP3EncDLL.c] * BladeMP3EncDLL.h [http://users.hol.gr/~dzach/vox/lame/BladeMP3EncDLL.h] * Example.cpp [http://users.hol.gr/~dzach/vox/lame/Example.cpp] * LameDLLInterface.htm [http://users.hol.gr/~dzach/vox/lame/LameDLLInterface.htm] With dll_tcl and lame_enc.dll, my code looks like this: source ./dll_tcl.tcl ::dll::load {D:\sound\lame-3.96.1\lame_enc.dll} -> lame # beInitStream( PBE_CONFIG pbeConfig, PDWORD dwSamples, PDWORD dwBufferSize, PHBE_STREAM phbeStream ) ::lame::cmd "int beInitStream( void *, uint *, uint *, uint * )" # beEncodeChunk( HBE_STREAM hbeStream, DWORD nSamples, PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput ) ::lame::cmd "int beEncodeChunk( uint, uint, short *, char *, uint * )" # beDeinitStream( HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput ) ::lame::cmd "int beDeinitStream( uint, char *, uint * )" # beCloseStream( HBE_STREAM hbeStream ) ::lame::cmd "int beCloseStream( uint )" # beVersion( PBE_VERSION pbeVersion ) ::lame::cmd "int beVersion( void * )" # beWriteVBRHeader( LPCSTR pszMP3FileName ) ::lame::cmd "int beWriteVBRHeader( char * )" # #------------ set LHV1 structure values ---------------------- array set cfg { \ dwConfig 256 \ dwStructVersion 1 \ dwStructSize 331 \ dwSampleRate 16000 \ dwReSampleRate 0 \ nMode 3 \ dwBitrate 32 \ dwMaxBitrate 0 \ nPreset 0 \ dwMpegVersion 1 \ dwPsyModel 0 \ dwEmphasis 0 \ bPrivate 0 \ bCRC 0 \ bCopyright 0 \ bOriginal 1 \ bWriteVBRHeader 0 \ bEnableVBR 0 \ nVBRQuality 0 \ dwVbrAbr_bps 0 \ nVbrMethod 0 \ bNoRes 0 \ bStrictIso 0 \ nQuality 0 \ } set cfg(btReserved) [::dll::buffer 266] #----------- assign values to structure--------------------- set pbeConfig [binary format iiiiiiiiiiiibbbbbbiiibbia266 \ $cfg(dwConfig) \ $cfg(dwStructVersion) \ $cfg(dwStructSize) \ $cfg(dwSampleRate) \ $cfg(dwReSampleRate) \ $cfg(nMode) \ $cfg(dwBitrate) \ $cfg(dwMaxBitrate) \ $cfg(nPreset) \ $cfg(dwMpegVersion) \ $cfg(dwPsyModel) \ $cfg(dwEmphasis) \ $cfg(bPrivate) \ $cfg(bCRC) \ $cfg(bCopyright) \ $cfg(bOriginal) \ $cfg(bWriteVBRHeader) \ $cfg(bEnableVBR) \ $cfg(nVBRQuality) \ $cfg(dwVbrAbr_bps) \ $cfg(nVbrMethod) \ $cfg(bNoRes) \ $cfg(bStrictIso) \ $cfg(nQuality) \ $cfg(btReserved) \ ] set btVersion [::dll::buffer 265] # set dwSamples 0 set dwBuffersize 0 set phbeStream 0 if {[::lame::beInitStream pbeConfig dwSamples dwBuffersize phbeStream] != 0} { error "could not initialize lame" } set pOutput [::dll::buffer $dwBuffersize] set pdwOutput 0 # # Just put 10 samples into the pSamples buffer for the test. # set nSamples 10 set pSamples "0123456789" if {[::lame::beEncodeChunk $phbeStream $nSamples pSamples pOutput pdwOutput] != 0} { error "encoding failed" } if {[::lame::beDeinitStream $phbeStream pOutput pdwOutput] != 0} { error "Deinit failed" } if {[::lame::beCloseStream $phbeStream] != 0} { error "CloseStream failed" } [dzach] 5 June 2005: I changed the last portion of code, from # Just put 10 samples into the pSamples buffer for the test. to the end with: # here we create a sinusoidal waveform of about 444 Hz, +/- 32000 amplitude, for the test, # ... convert it to short with the use of "binary format", # ... and populate pSamples, ready for use for {set i 0} {$i<16000} {incr i} { append pSamples [binary format s [expr {int(32000*sin(10*$i*3.14/180))}]] } # calculate sound length ( devide by 2 since 1 short = 2 bytes ) set slength [expr {[string length $pSamples]/2}] # LAME wants chunks of $dwSamples length or less set nSamples [expr {$slength>=$dwSamples ? $dwSamples:$slength}] if {[::lame::beEncodeChunk $phbeStream $nSamples pSamples pOutput pdwOutput] != 0} { error "encoding failed" } if {[::lame::beDeinitStream $phbeStream pOutput pdwOutput] != 0} { error "Deinit failed" } if {[::lame::beCloseStream $phbeStream] != 0} { error "CloseStream failed" } I get an "integer parameter error" right when executing "::lame::beEncodeChunk", and this is where I'm stuck. I hope I've not overlooked something obvious. ---- [Peter Newman] 4 June 2005: I'd check that ''pSamples'' value first. Does 10 digits fit into a short? And doesn't Tcl think that anything starting with a zero is Octal? I could be wrong - and I don't really understand how ''nSamples'' and ''pSamples'' work - but does it work with ''nSamples'' = 5 and ''pSamples'' = "12345", for example. ---- [dzach] I've tried many normal (and a lot lengthier) sound files before this, but failed. I just put these values there for testing. '''pSamples''' is passed to the ::lame:: functions as a pointer to short (actually I suspect the dll expects an array of shorts at that position) while '''nSamples''' is passed by value (the length of the array), as stated in the ::lame:: function declarations (I hope). So to the dll this pSamples should look like the start address of an array of 10 shorts. ---- [Peter Newman] 4 June 2005: I use ''::dll'' for my Win32 access - and I'm pretty sure I've had an "integer parameter error" from it too. So my gut feeling is that it can't translate the '''pSamples''' pointer you've supplied it into the '''short *''' you specified above. To me, '''short *''' specifies a pointer to a signed 32-bit integer. And I think the problem is that "::dll" can't translate the Tcl string "0123456789" to a signed 32-bit integer. Consider the following ''wish'' session:- % puts [expr 123456789 + 0] 123456789 () 2 % puts [expr 0123456789 + 0] expected integer but got "0123456789" (looks like invalid octal number) In other words, the Tcl parser doesn't like "0123456789" - and presumably, passes it's opinion onto "::dll" - which then aborts with the "integer parameter error". So (without testing it), it's your "0123456789" string that's the problem. My understanding (from the [Yet another dll caller] documentation,) is that you'll have to use '''binary format''', to create what the pointer points to. See the examples there. I also suspect that you'd be better off with '''void *''' (instead of '''short *'''). To me, '''short *''' is a pointer to one single signed int; not to an array of 10 of them. [dzach] 5 June 2005: Thanks a lot for the replies. I've changed the code to produce a better testing sinusoidal sound, converting it properly to int16 (short) with the use of "binary format". If I read [Yet another dll caller] correctly, '''short''' stands for ''int16'' and '''int''' for ''int32''. A pointer to a single signed 16 bit integer is all LAME needs for '''pSamples''', as I read it in the sources. The problem remains.