Compressing WAV File to MP3
?? 1. IntroductionFirst, I don't meant to achieve you informations about how to understand The mp3 algorithm. My Goal Is To Explain How To Use an already existing encoder with bcb.
?? 2. Choosing the mp3 encoderThere are tons of mp3 encoders. Some of them are free others are not. Some are fast but produce an awful result. Others are slow but with excellent result and give a high audio quality. The ideal would be A freienably fast encoder giving a high audio Quality, All at The Same Time. Enjoy! This Pearl EXISTS. But WE Have to Look at It in the gnu world. There is a gnu project, called lame, for lame ain't A MP3 Encoder, Under The GPL License. The Official Web Site of the Lame Project Is Http://www.mp3dev.org/mp3/ Moreover, As IS A GNU Project, We Have Access To The Source and There IS a Version Compiled for Win32 in A DLL. Among All The Other Encoders, I want to quote Two of them. The first, fraunhofer, because it is a fast and excellent encoder: http://www.iis.fhg.de/ But it's not Free though. The second encoder but the audio result is awful. so don't ready for a fast encoder. it's the encoder from Xing tech: http://www.xingtech.com/ NOTE: The Lame Encoder Has A Limitation. The Sample Rate Must BE 32000, 44100 OR 48000.?
?? 3. Some informations about the WAV formatA wav file is just a collection of chunks. There is a format chunk wich contains all the informations about the samples. For instance, the bitrate, the number of channels, if it's stereo or mono. .. there is also a chunk containing the data. in other words, this chunk contains all the samples. in front of the file, there are 12 characters indicating that the file is a wav file. The two chunks given above must be present in The File. There Could Be Other CHUNK But We Just Ignore Them. They Are Not NEeded for Our Purpose. If You Want To Know More About WAV File, Take a Look At http://www.Wotsit.org/ for a Complete Description . The format chunk: struct FormatChunk {char chunkID [4]; long chunkSize; short wFormatTag; unsigned short wChannels; unsigned long dwSamplesPerSec; unsigned long dwAvgBytesPerSec; unsigned short wBlockAlign; unsigned short wBitsPerSample; // Note: there may be Additional Fields Here, Depending Upon WFORMATTAG.
Above, you can see the struct representing the format chunk. The chunkID is always "fmt" with an ending space (4 characters). It's the identification of the chunk. All other chunk have such an ID. The chunkSize parameter contains the number of .
The data chunk: struct Chunk {char chunkID [4]; long chunkSize;}; In the case of the data chunk, the chunkID contains "data" The chunkSize parameters contains the size of the raw data (samples) The data begins.. Just after chunksize.
In the case of the data chunk, the chunkID contains "data". The chunkSize parameters contains the size of the raw data (samples). The data begins just after chunkSize.Dans le cas du bloc de donn¨? Es, chunkID contient " Data ". Le Param ¨ ¨ TRE Chunksize Contient La Taille DU Bloc de DONN ¨? ES ProPremen. Celles-Ci Commenct Juste Apr ¨ ¨s chunksize.so, When We Read A WAV File, All We Have to do is: - read the first 12 characters to check if it's a real wav file -. read the format chunk in a struct similar to the formatChunk struct -. skip the extra parameters in the format chunk, if any -. find the data chunk, read the raw Data and carry out with the encoding. -skip All Other chunks.donc, CE Que Nous Devons Faire EST: - LIRE LES 12 premier, Terminer Si ON EST BIEN EN PR ¨? Sence d'unfichier WAV. - LIRE Le Bloc de format Dans Une Structure Similaire ¤ ¤ LA STRUCTURE FORMATCHUNK. - IGNORER LES CARACT ¨RES SUPPL ¨? MentaiRes Dans Le Bloc de format, S'il y en a. - Ignorer Tous Les Blocs Qui Ne Sont Pas Le Bloc de DONN ¨? ES, TROUVER LE BLOC DE DONN ¨ ES, LIRE CES DONN ¨? ES ET LANCER L'ENCODAGE. ?? 4. Importing The DLLTHE DLL Used for the Encoding IS called lame_enc.dll. Unfortunately, this DLL was build with VC 6 from Microsoft. If we just create a lib file from the DLL and try to import the library in BCB, we'll get an 'Unresolved external error' at link time for each function we'll try to use. Due to the declaration type, BCB is expecting a function name with a leading underscore and the function names does not have such a leading underscore. to resolve this issue, we must, first, create a DEF File from Our DLL. Open a console windows and type:
IMPDEF lame_enc.def lame_enc.dllOpen the lame_enc.def file with an editor (Notepad for instance) and modify it like this This will create aliases for the functions:. LIBRARY LAME_ENC.DLL EXPORTS _beCloseStream = beCloseStream _beDeinitStream = beDeinitStream _beEncodeChunk = beEncodeChunk _beInitStream = BeinitStream _beversion = Beversion _bewritevbrheader = BEWRITEVBRHEADER BECLOSSTREAM @ 4 bedeinitstream @ 3 beencodechunk @ 2 BeinitStream @ 1 Beversion @ 5 BEWRITEVBRHEADER @ 6
Now, we can create the lib file from our def file. We'll Import That Lib file in out, type: implib lame_enc.lib lame_enc.def
?? 5. The codeFirst, you have to import the libary in your project. Next, include the header file of the DLL into your unit. In the DLL header file, you have to add extern "C" in front of all exported function Here is the header with the moffications (lame_enc.h): / * Bladedll.H Blade's Encoder DLL
-------------------------------------------------- ---- - Version 1.00 (7 November 1998) - Jukka Poikolainen - ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------- * / # ifndef ___ blackdll_h_included ___ # define ___bladedll_h_included___
#pragma pack (push) #pragma Pack (1)
/ * encoding formats * / # define be_config_mp3 0 # define be_config_lame 256
/ * Type definitions * /
TypedEf unsigned long hbe_stream; typef hbe_stream * phbe_stream; typedef unsigned long be_err;
/ * Error Codes * /
#define BE_ERR_SUCCESSFUL 0x00000000 # define BE_ERR_INVALID_FORMAT 0x00000001 # define BE_ERR_INVALID_FORMAT_PARAMETERS 0x00000002 # define BE_ERR_NO_MORE_HANDLES 0x00000003 # define BE_ERR_INVALID_HANDLE 0x00000004 # define BE_ERR_BUFFER_TOO_SMALL 0x00000005
/ * Other constants * /
#define be_max_homepage 256
/ * Format Specific Variables * /
#DEFINE BE_MP3_MODE_STEREO 0 # define be_mp3_mode_jstereo 1 # define be_mp3_mode_dualchannel 2 # define be_mp3_mode_mono 3
#define mpeg1 1 # Define MPEG2 0
#ifdef _Bladedll # undef float #include
Enum mpeg_quality {normal_quality = 0, low_quality, high_quality, voice_quality};
typedef struct {DWORD dwConfig; // BE_CONFIG_XXXXX // Currently only BE_CONFIG_MP3 is supported union {struct {DWORD dwSampleRate; // 48000, 44100 and 32000 allowed BYTE byMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL // BE_MP3_MODE_MONO WORD wBitrate; // 32, 40 , 48, 56, 64, 80, 96, 112, 128, // 160, 192, 224, 256 and 320 allowed bool bPrivate; Bool Bocrc; Bool BcoPyright; bool boriginal;} mp3; // be_config_mp3 struct {// structure Information DWORD DWSTRUCTVERSION; DWORD DWSTRUCTSIZE; // Basic Encoder Settings DWORD DWSAMPLERATE; / / ALLOWED SAMPLERATE VALUES Depends // on dwmpegversion DWORD DWRES ampleRate; // DOWNSAMPLERATE, 0 = ENCODER DECIDES INT nMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL // BE_MP3_MODE_MONO DWORD dwBitrate; // CBR bitrate, VBR min bitrate DWORD dwMaxBitrate; // CBR ignored, VBR Max bitrate MPEG_QUALITY nQuality; // Quality setting (Normal, High, Low, Voice) DWORD DWMPEGVERSION; // MPEG-1 or MPEG-2 DWORD DWPSYMODEL; // Future Use, Set To 0 DWORD DWEMPHAS; / / FUTURE USE, SET TO 0
// bit stream settings bool bprivate; // set private bit (true / false) BOOL BCRC; // INSERT CRC (TRUE / FALSE) BOOL BCOPYRIGHT; // SET COPYRIGHT BIT (TRUE / FALSE) BOOL BORIGINAL; // set Original Bit (True / false) // vbr stuff bool bwritevbrheader; // Write Xing VBR Header (True / false) BOOL BOOL BENABLVBR; / / USE VBR Encoding (True / false) int nvbrquality; // vbr Quality 0..9 byte btreserved [255]; // Future Use, set to 0} LHV1; // Lame Header Version 1Struct {DWORD DWSAMPLERATE; BYTE BYMODE; Word WbitRate; BYTE BYENCODINGMETHOD;} aac;} format;} aac;
struct BE_VERSION {// BladeEnc DLL Version number BYTE byDLLMajorVersion; BYTE byDLLMinorVersion; // BladeEnc Engine Version Number BYTE byMajorVersion; BYTE byMinorVersion; // DLL Release date BYTE byDay; BYTE byMonth; WORD wYear; // BladeEnc Homepage URL CHAR zHomepage [BE_MAX_HOMEPAGE 1];
#ifndef _bladedll
typedef BE_ERR (* BEINITSTREAM) (BE_CONFIG *, PDWORD, PDWORD, PHBE_STREAM); typedef BE_ERR (* BEENCODECHUNK) (HBE_STREAM, DWORD, PSHORT, PBYTE, PDWORD); typedef BE_ERR (* BEDEINITSTREAM) (HBE_STREAM, PBYTE, PDWORD); typedef BE_ERR (* becloseStream) (hbe_stream); typedef void (* be_version *);
#define TEXT_BEINITSTREAM "beInitStream" #define TEXT_BEENCODECHUNK "beEncodeChunk" #define TEXT_BEDEINITSTREAM "beDeinitStream" #define TEXT_BECLOSESTREAM "beCloseStream" #define TEXT_BEVERSION "beVersion" / * BE_ERR beInitStream (BE_CONFIG * beConfig, PDWORD dwSamples, PDWORD dwBufferSize, PHBE_STREAM phbeStream); BE_ERR beEncodeChunk (HBE_STREAM hbeStream, DWORD nSamples, PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput); BE_ERR beDeinitStream (HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput); BE_ERR beCloseStream (HBE_STREAM hbeStream); VOID beVersion (BE_VERSION * beVersion); * /
#ELSE
extern "C" __declspec (dllexport) BE_ERR beInitStream (BE_CONFIG * beConfig, PDWORD dwSamples, PDWORD dwBufferSize, PHBE_STREAM phbeStream); extern "C" __declspec (dllexport) BE_ERR beEncodeChunk (HBE_STREAM hbeStream, DWORD nSamples, PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput ); extern "C" __declspec (dllexport) BE_ERR beDeinitStream (HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput); extern "C" __declspec (dllexport) BE_ERR beCloseStream (HBE_STREAM hbeStream); extern "C" __declspec (dllexport) VOID beVersion (BE_VERSION * BEVERSION;
# ENDIF # Pragma Pack (POP) #ENDIF
As you can see in the header above, you have to add #define _bladedll into your .cpp file before including the header.
Below, you'll find the code of a little application which takes a wav file in input and encode the file to mp3. I do not give more explanations because the code is very straightforward and commented. It is not very elegant but it's just To show how to use the dll.
File format.h // -------------------------------------------- ------------------------------- # i indef format_h # define format_h // ----------- -------------------------------------------------- -------------- struct FormatChunk {char chunkID [4]; long chunkSize; short wFormatTag; unsigned short wChannels; unsigned long dwSamplesPerSec; unsigned long dwAvgBytesPerSec; unsigned short wBlockAlign; unsigned short wBitsPerSample; / / NOTE: There May Be Additional Fields Here, Depending Upon WFORMATTAG.}; // this is the start id of a Wave file //rust contains' riff 'and' Wave'char StartId [12];
// Contains the chunk ID ('Data', 'Cue' ...) and the chunk sizestruct chunk {char chunkid [4]; long chunksize;};
// a Pointer to the samples in the data chunkunsigned char * WAVEFORMDATA DITER;
/ / -------------------------------------------------------------------------------------------- --------------------------- # Endif
File unit1_h.h // -------------------------------------------- ------------------------------- # i indef unit1h # define unit1H // ----------- -------------------------------------------------- -------------- # include
#include
#define _Bladedll // Don't forget it # include "lame_enc.h" #include "format.h" // ------------------------ -------------------------------------------------- - # Pragma package (smart_init) #pragma resource "* .dfm" TFORM1 * FORM1; // --------------------------- ---------------------------------------------__ fastcall tform1 :: TFORM1 (TComponent * Owner: TFORM (OWNER) {} // ----------------------------------- ---------------------------------------- void __fastcall tform1 :: broseclick (Tobject * Sender ) {OpenDialog1-> InitialDir = ExtractFileDir (Application-> ExeName); if (OpenDialog1-> Execute ()) {FileEdit-> Text = OpenDialog1-> FileName; OutputFileName = ChangeFileExt (OpenDialog1-> FileName, ".mp3"); }} // ---------------------------------------------- ----------------------------- void __fastcall tform1 :: encodeclick (Tobject * sender) {if (fileEdit-> text == " ") Return; std :: ifstream fin (fileEdit-> text.c_str (), std :: os :: binary); if (! Fin) return; // r Ead The 12 Character in Front of The File Fin.Read ((Char *) & StartId, Sizeof (StartID));
// Get The Format Chunk Formatchunk Fc; Fin.Read ((Char *) & FC, Sizeof (Formatchunk)); // The First Chunk Must Be The Format Chunkiff (Strncmp (Fc.chunkid, "FMT", 4)! = 0) {Application-> MessageBox ("this is not a value", "wav2mp3 error", mb_ok); return;} f (fc.wformattag! = 1) {Application-> MessageBox ("Cannot Handle Compressed Wave file "," Wav2Mp3 ERROR ", MB_OK); return;} // initialization of Mp3 encoder BE_CONFIG bc; bc.dwConfig = BE_CONFIG_MP3; // 32000, 44100 and 48000 are the only sample rate authorized // due to encoding limitations if ( fc.dwSamplesPerSec == 32000 || fc.dwSamplesPerSec == 44100 || fc.dwSamplesPerSec == 48000) bc.format.mp3.dwSampleRate = fc.dwSamplesPerSec; else {Application-> MessageBox ( "Unsuported sample rate", "Wav2Mp3 Error ", MB_OK); return;}}} (fc.wchannels = = 1) bc.format.mp3.byMode = BE_MP3_MODE_MONO; else bc.format.mp3.byMode = BE_MP3_MODE_STEREO; // the resulting file length depends on this parameter // higher the bitrate, better the result bc.format.mp3.wBitrate = 192; bc.format.mp3.bcopyright = false; bc.format.mp3.bcrc = false; bc.format.mp3.boriginal = false; bc.format.mp3.bprivate = false; // Skip extra formter, IF ANY IF (SIZEOF (Formatchunk)) {char C; for (INT i = 0; i Fin.get (c);} // get next chunk chunk chunk; fin.read ((char *) & chunk, sizeof (chunk)); // Check if it's the data chunk while (strncmp (chunk.chunkid, " Data ", 4)! = 0) {char C; for (int i = 0; i