Click here to Skip to main content
15,894,540 members
Articles / Desktop Programming / MFC

Sound recording and encoding in MP3 format.

Rate me:
Please Sign up or sign in to vote.
4.89/5 (64 votes)
16 Nov 2006CPOL6 min read 930.3K   19.2K   287  
An article describing the technique of recording sound from waveform-audio input devices and encoding it in MP3 format.
#ifndef ___MP3_SIMPLE_H_INCLUDED___
#define ___MP3_SIMPLE_H_INCLUDED___

#include <windows.h>
#include "INCLUDE/BladeMP3EncDLL.h"
#include "INCLUDE/sync_simple.h"

// Pointers to LAME API functions
BEINITSTREAM		beInitStream	=NULL;
BEENCODECHUNK		beEncodeChunk	=NULL;
BEDEINITSTREAM		beDeinitStream	=NULL;
BECLOSESTREAM		beCloseStream	=NULL;
BEVERSION		beVersion	=NULL;
BEWRITEVBRHEADER	beWriteVBRHeader=NULL;
BEWRITEINFOTAG		beWriteInfoTag	=NULL;

//---------------------------- CLASS -------------------------------------------------------------

// Class that implements basic MP3 encoding, by wrapping LAME API.
// This class can be extended to add extra functionality and customization features.
class CMP3Simple {
private:
	static QMutex m_qMutex;
	static bool m_isLibLoaded;

	BE_CONFIG	beConfig;
	HBE_STREAM	hbeStream;
	DWORD		dwMP3Buffer;
	DWORD		dwPCMBuffer;

public:
	// This static method performs LAME API initialization
	static void LoadLIBS();

	// Constructer of the class accepts only three parameters.
	// Feel free to add more constructors with different parameters, if a better
	// customization is necessary.
	//
	// nBitRate - says at what bitrate to encode the raw (PCM) sound
	// (e.g. 16, 32, 40, 48, ... 64, ... 96, ... 128, etc), see official LAME documentation
	// for accepted values.
	//
	// nInputSampleRate - expected input frequency of the raw (PCM) sound
	// (e.g. 44100, 32000, 22050, etc), see official LAME documentation
	// for accepted values.
	//
	// nOutSampleRate - requested frequency for the encoded/output (MP3) sound.
	// If equal with zero, then sound is not
	// re-sampled (nOutSampleRate = nInputSampleRate).
	CMP3Simple(unsigned int nBitRate, unsigned int nInputSampleRate = 44100,
		unsigned int nOutSampleRate = 0);


	~CMP3Simple();

	// This method performs encoding.
	//
	// pSamples - pointer to the buffer containing raw (PCM) sound to be encoded.
	// Mind that buffer must be an array of SHORT (16 bits PCM stereo sound, for
	// mono 8 bits PCM sound better to double every byte to obtain 16 bits).
	//
	// nSamples - number of elements in "pSamples" (SHORT). Not to be confused with
	// buffer size which represents (usually) volume in bytes. See also
	// "MaxInBufferSize" method.
	//
	// pOutput - pointer to the buffer that will receive encoded (MP3) sound, here
	// we have bytes already. LAME says that if pOutput is not cleaned before call,
	// data in pOutput will be mixed with incoming data from pSamples.
	//
	// pdwOutput - pointer to a variable that will receive the number of bytes written
	// to "pOutput". See also "MaxOutBufferSize" method.
	BE_ERR Encode(PSHORT pSamples, DWORD nSamples, PBYTE pOutput, PDWORD pdwOutput);

	// Returns maximum suggested number of elements (SHORT) to send to "Encode" method.
	// e.g. PSHORT pSamples = (PSHORT) malloc(sizeof(SHORT) * MaxInBufferSize())
	// or PSHORT pSamples = new SHORT[MaxInBufferSize()]
	DWORD MaxInBufferSize() const { return this->dwPCMBuffer; }

	// Returns minimum suggested buffer size (in bytes) for the buffer that will
	// receive encoded (MP3) sound, see "Encode" method for more details.
	DWORD MinOutBufferSize() const { return this->dwMP3Buffer; }

	// Returns requested bitrate for the MP3 sound.
	DWORD BitRate() const { return this->beConfig.format.LHV1.dwBitrate; }

	// Returns expected input frequency of the raw (PCM) sound
	DWORD InSampleRate() const { return this->beConfig.format.LHV1.dwSampleRate; }

	// Returns requested frequency for the encoded/output (MP3) sound.
	DWORD OutSampleRate() const {
		if (this->beConfig.format.LHV1.dwReSampleRate == 0) {
			// If equal with zero, then sound is not
			// re-sampled (nOutSampleRate = nInputSampleRate).
			return this->beConfig.format.LHV1.dwSampleRate;
		}

		return this->beConfig.format.LHV1.dwReSampleRate;
	}
};

//---------------------------- IMPLEMENTATION ----------------------------------------------------
bool CMP3Simple::m_isLibLoaded = false;
QMutex CMP3Simple::m_qMutex;

BE_ERR CMP3Simple::Encode(PSHORT pSamples, DWORD nSamples, PBYTE pOutput, PDWORD pdwOutput) {
	return beEncodeChunk(this->hbeStream, nSamples, pSamples, pOutput, pdwOutput);
}

CMP3Simple::CMP3Simple(unsigned int nBitRate, unsigned int nInputSampleRate,
						   unsigned int nOutSampleRate) {
	BE_ERR		err = 0;

	CMP3Simple::LoadLIBS();

	this->dwMP3Buffer = 0;
	this->dwPCMBuffer = 0;
	this->hbeStream = 0;
	memset(&this->beConfig, 0, sizeof(BE_CONFIG));

	this->beConfig.dwConfig = BE_CONFIG_LAME;
	this->beConfig.format.LHV1.dwStructVersion = 1;
	this->beConfig.format.LHV1.dwStructSize = sizeof(BE_CONFIG);

	// OUTPUT IN STREO
	this->beConfig.format.LHV1.nMode = BE_MP3_MODE_JSTEREO;	

	// QUALITY PRESET SETTING, CBR = Constant Bit Rate
	this->beConfig.format.LHV1.nPreset = LQP_CBR;

	// USE DEFAULT PSYCHOACOUSTIC MODEL
	this->beConfig.format.LHV1.dwPsyModel = 0;

	// NO EMPHASIS TURNED ON
	this->beConfig.format.LHV1.dwEmphasis = 0;

	// SET ORIGINAL FLAG
	this->beConfig.format.LHV1.bOriginal = TRUE;

	// Write INFO tag
	this->beConfig.format.LHV1.bWriteVBRHeader = FALSE;

	// No Bit resorvoir
	this->beConfig.format.LHV1.bNoRes = TRUE;		

	this->beConfig.format.LHV1.bPrivate = TRUE;
	this->beConfig.format.LHV1.bCopyright = TRUE;

	// MINIMUM BIT RATE
	this->beConfig.format.LHV1.dwBitrate = nBitRate;

	// MPEG VERSION (I or II)
	this->beConfig.format.LHV1.dwMpegVersion = (nBitRate > 32)?MPEG1:MPEG2;

	// INPUT FREQUENCY
	this->beConfig.format.LHV1.dwSampleRate	= nInputSampleRate;

	// OUTPUT FREQUENCY, IF == 0 THEN DON'T RESAMPLE
	this->beConfig.format.LHV1.dwReSampleRate = nOutSampleRate;

  	err = beInitStream(&this->beConfig, &this->dwPCMBuffer, &this->dwMP3Buffer, &this->hbeStream);
	if(err != BE_ERR_SUCCESSFUL) throw "ERRORR in beInitStream.";
}

CMP3Simple::~CMP3Simple() {

	beCloseStream(this->hbeStream);
}

void CMP3Simple::LoadLIBS() {
	HINSTANCE  hDLLlame = NULL;

	m_qMutex.Lock();
	if (!m_isLibLoaded) {
		// LAME API wasn't loaded yet, so load it

		hDLLlame = ::LoadLibrary("lame_enc.dll");

		if( hDLLlame == NULL ) {
			m_qMutex.Unlock();
			throw "Error loading lame_enc.DLL";
		}

		beInitStream	= (BEINITSTREAM) GetProcAddress(hDLLlame, TEXT_BEINITSTREAM);
		beEncodeChunk	= (BEENCODECHUNK) GetProcAddress(hDLLlame, TEXT_BEENCODECHUNK);
		beDeinitStream	= (BEDEINITSTREAM) GetProcAddress(hDLLlame, TEXT_BEDEINITSTREAM);
		beCloseStream	= (BECLOSESTREAM) GetProcAddress(hDLLlame, TEXT_BECLOSESTREAM);
		beVersion      	= (BEVERSION) GetProcAddress(hDLLlame, TEXT_BEVERSION);
		beWriteVBRHeader= (BEWRITEVBRHEADER) GetProcAddress(hDLLlame, TEXT_BEWRITEVBRHEADER);
		beWriteInfoTag  = (BEWRITEINFOTAG) GetProcAddress(hDLLlame, TEXT_BEWRITEINFOTAG);

		if(!beInitStream || !beEncodeChunk || !beDeinitStream ||
			!beCloseStream || !beVersion || !beWriteVBRHeader) {

			::FreeLibrary(hDLLlame);
			m_qMutex.Unlock();
			throw "Unable to get LAME interfaces";
		}

		m_isLibLoaded = true;
	}

	m_qMutex.Unlock();
}

#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) BlackRock
United Kingdom United Kingdom
My name is Ruslan Ciurca. Currently, I am a Software Engineer at BlackRock.

Comments and Discussions