#include
#include
#include
#include
#include
#define MP3_BLOCK_SIZE 522
#define SOURCE_MP3 "C:\\audiograbber\\At The Club Last Night\\At_The_Club_Last_Night_-_Haven't_You_Heard.mp3"
#define OUTPUT_PCM_FILE "c:\\dump.pcm"
int g_mp3Drivers = 0;
BOOL CALLBACK acmDriverEnumCallback( HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport ){
if( fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC ) {
MMRESULT mmr;
ACMDRIVERDETAILS details;
details.cbStruct = sizeof(ACMDRIVERDETAILS);
mmr = acmDriverDetails( hadid, &details, 0 );
HACMDRIVER driver;
mmr = acmDriverOpen( &driver, hadid, 0 );
int i;
for(i = 0; i < details.cFormatTags; i++ ){
ACMFORMATTAGDETAILS fmtDetails;
ZeroMemory( &fmtDetails, sizeof(fmtDetails) );
fmtDetails.cbStruct = sizeof(ACMFORMATTAGDETAILS);
fmtDetails.dwFormatTagIndex = i;
mmr = acmFormatTagDetails( driver, &fmtDetails, ACM_FORMATTAGDETAILSF_INDEX );
if( fmtDetails.dwFormatTag == WAVE_FORMAT_MPEGLAYER3 ){
OutputDebugString( L"Found an MP3-capable ACM codec: " );
OutputDebugString( details.szLongName );
OutputDebugString( L"\n" );
g_mp3Drivers++;
}
}
mmr = acmDriverClose( driver, 0 );
}
return true;
}
HACMSTREAM g_mp3stream = NULL;
convertMP3(){
MMRESULT mmr;
// try to find an MP3 codec
acmDriverEnum( acmDriverEnumCallback, 0, 0 );
if(g_mp3Drivers == 0){
OutputDebugString( L"No MP3 decoders found!\n" );
return E_FAIL;
}
// find the biggest format size
DWORD maxFormatSize = 0;
mmr = acmMetrics( NULL, ACM_METRIC_MAX_SIZE_FORMAT, &maxFormatSize );
// define desired output format
LPWAVEFORMATEX waveFormat = (LPWAVEFORMATEX) LocalAlloc( LPTR, maxFormatSize );
waveFormat->wFormatTag = WAVE_FORMAT_PCM;
waveFormat->nChannels = 2; // stereo
waveFormat->nSamplesPerSec = 44100; // 44.1kHz
waveFormat->wBitsPerSample = 16; // 16 bits
waveFormat->nBlockAlign = 4; // 4 bytes of data at a time are useful (1 sample)
waveFormat->nAvgBytesPerSec = 4 * 44100; // byte-rate
waveFormat->cbSize = 0; // no more data to follow
// define MP3 input format
LPMPEGLAYER3WAVEFORMAT mp3format = (LPMPEGLAYER3WAVEFORMAT) LocalAlloc( LPTR, maxFormatSize );
mp3format->wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
mp3format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
mp3format->wfx.nChannels = 2;
mp3format->wfx.nAvgBytesPerSec = 128 * (1024 / 8); // not really used but must be one of 64, 96, 112, 128, 160kbps
mp3format->wfx.wBitsPerSample = 0; // MUST BE ZERO
mp3format->wfx.nBlockAlign = 1; // MUST BE ONE
mp3format->wfx.nSamplesPerSec = 44100; // 44.1kHz
mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;
mp3format->nBlockSize = MP3_BLOCK_SIZE; // voodoo value #1
mp3format->nFramesPerBlock = 1; // MUST BE ONE
mp3format->nCodecDelay = 1393; // voodoo value #2
mp3format->wID = MPEGLAYER3_ID_MPEG;
g_mp3stream = NULL;
mmr = acmStreamOpen( &g_mp3stream, // open an ACM conversion stream
NULL, // querying all ACM drivers
(LPWAVEFORMATEX) mp3format, // converting from MP3
waveFormat, // to PCM
NULL, // no filter
0, // no callback
0, // no instance data
ACM_STREAMOPENF_NONREALTIME ); // not realtime - query all ACM drivers
if( mmr ){
assert( !"failed to open ACM stream" );
return E_FAIL;
}
// allocate our I/O buffers
LPBYTE mp3buf, rawbuf;
mp3buf = (LPBYTE) LocalAlloc( LPTR, MP3_BLOCK_SIZE );
DWORD rawbufsize = 0;
mmr = acmStreamSize( g_mp3stream, MP3_BLOCK_SIZE, &rawbufsize, ACM_STREAMSIZEF_SOURCE );
assert( mmr == 0 );
rawbuf = (LPBYTE) LocalAlloc( LPTR, rawbufsize );
// prepare the decoder
ACMSTREAMHEADER mp3streamHead;
ZeroMemory( &mp3streamHead, sizeof(ACMSTREAMHEADER) );
mp3streamHead.cbStruct = sizeof(ACMSTREAMHEADER);
mp3streamHead.pbSrc = mp3buf;
mp3streamHead.cbSrcLength = MP3_BLOCK_SIZE;
mp3streamHead.pbDst = rawbuf;
mp3streamHead.cbDstLength = rawbufsize;
mmr = acmStreamPrepareHeader( g_mp3stream, &mp3streamHead, 0 );
assert( mmr == 0 );
// open the files for conversion
FILE *fpIn, *fpOut;
fpIn = fopen( SOURCE_MP3, "rb" );
if( !fpIn ){
assert( !"can't open input MP3!" );
return E_FAIL;
}
fpOut = fopen( OUTPUT_PCM_FILE, "wb" );
if( !fpOut ){
assert( !"can't output output PCM!" );
return E_FAIL;
}
while(1) {
// suck in some MP3 data
int count = fread( mp3buf, 1, MP3_BLOCK_SIZE, fpIn );
if( count != MP3_BLOCK_SIZE )
break;
// convert the data
mmr = acmStreamConvert( g_mp3stream, &mp3streamHead, ACM_STREAMCONVERTF_BLOCKALIGN );
assert( mmr == 0 );
// write the decoded PCM to disk
count = fwrite( rawbuf, 1, mp3streamHead.cbDstLengthUsed, fpOut );
assert( count == mp3streamHead.cbDstLengthUsed );
};
// clean up after yourself like a good little boy
fclose( fpIn );
fclose( fpOut );
mmr = acmStreamUnprepareHeader( g_mp3stream, &mp3streamHead, 0 );
assert( mmr == 0 );
LocalFree(rawbuf);
LocalFree(mp3buf);
mmr = acmStreamClose( g_mp3stream, 0 );
assert( mmr == 0 );
return S_OK;
}
This code demonstrates MP3 decoding using Microsoft's Audio Compression Manager (ACM) APIs, which were commonly used in Windows applications during the early 2000s for audio format conversion. The code shows the complex process of finding MP3 codecs, setting up the conversion stream, and performing block-by-block decoding from MP3 to PCM format.
Note the "voodoo values" mentioned in the comments - these were empirically determined constants needed to make the Microsoft APIs work correctly for MP3 decoding, highlighting the challenges developers faced when working with these proprietary audio APIs.
Technical Note: This code is preserved for historical and educational purposes. Modern applications would typically use more current audio libraries like DirectSound, Core Audio, or cross-platform solutions like FMOD or OpenAL.