/*
* Blade DLL Interface for LAME.
*
* Copyright (c) 1999 - 2002 A.L. Faber
*/
#include "lame.h"
#include <windows.h>
#include <Windef.h>
#include "lame_enc.h"
#include "lame_global_flags.h"
#include <stdio.h> // atoi
// lame_enc DLL version number
const BYTE MAJORVERSION = 1;
const BYTE MINORVERSION = 32;
// Local variables
static lame_global_flags*gfp_save;
static void DispErr(char const* strErr)
{
MessageBoxA(NULL,strErr,"LAME_ENC.DLL",MB_OK|MB_ICONHAND);
}
static void _cdecl DebugPrintf(const char* pzFormat, ...) {
#ifdef _DEBUG
char szBuffer[1024]={'\0',};
va_list ap;
va_start(ap, pzFormat);
_vsnprintf(szBuffer, sizeof(szBuffer), pzFormat, ap);
va_end(ap);
OutputDebugStringA( szBuffer );
#endif
}
void lame_global_flags::dump_config() {
DebugPrintf("\n\nLame_enc configuration options:\n");
DebugPrintf("==========================================================\n");
DebugPrintf("version =%d\n",lame_get_version() );
DebugPrintf("Layer =3\n");
DebugPrintf("mode =");
switch ( lame_get_mode() )
{
case STEREO: DebugPrintf( "Stereo\n" ); break;
case JOINT_STEREO: DebugPrintf( "Joint-Stereo\n" ); break;
case DUAL_CHANNEL: DebugPrintf( "Forced Stereo\n" ); break;
case MONO: DebugPrintf( "Mono\n" ); break;
case NOT_SET: /* FALLTROUGH */
default: DebugPrintf( "Error (unknown)\n" ); break;
}
DebugPrintf("Input sample rate =%.1f kHz\n", get_in_samplerate() /1000.0 );
DebugPrintf("Output sample rate =%.1f kHz\n", get_out_samplerate() /1000.0 );
DebugPrintf("bitrate =%d kbps\n", lame_get_brate() );
DebugPrintf("Quality Setting =%d\n", lame_get_quality() );
DebugPrintf("Low pass frequency =%d\n", lame_get_lowpassfreq() );
DebugPrintf("Low pass width =%d\n", lame_get_lowpasswidth() );
DebugPrintf("High pass frequency =%d\n", lame_get_highpassfreq() );
DebugPrintf("High pass width =%d\n", lame_get_highpasswidth() );
DebugPrintf("No short blocks =%d\n", lame_get_no_short_blocks() );
DebugPrintf("Force short blocks =%d\n", lame_get_force_short_blocks() );
DebugPrintf("de-emphasis =%d\n", lame_get_emphasis() );
DebugPrintf("private flag =%d\n", lame_get_extension() );
DebugPrintf("copyright flag =%d\n", lame_get_copyright() );
DebugPrintf("original flag =%d\n", lame_get_original() );
DebugPrintf("CRC =%s\n", lame_get_error_protection() ? "on" : "off" );
DebugPrintf("Fast mode =%s\n", ( lame_get_quality() )? "enabled" : "disabled" );
DebugPrintf("Force mid/side stereo =%s\n", ( lame_get_force_ms() )?"enabled":"disabled" );
DebugPrintf("Disable Reservoir =%d\n", lame_get_disable_reservoir() );
DebugPrintf("Allow diff-short =%d\n", lame_get_allow_diff_short() );
DebugPrintf("Interchannel masking =%f\n", lame_get_interChRatio() );
DebugPrintf("Strict ISO Encoding =%s\n", ( lame_get_strict_ISO() ) ?"Yes":"No");
DebugPrintf("Scale =%5.2f\n", get_scale() );
DebugPrintf("VBR =%s, VBR_q =%d, VBR method =",
( lame_get_VBR() !=vbr_off ) ? "enabled": "disabled",
lame_get_VBR_q() );
switch ( lame_get_VBR() )
{
case vbr_off: DebugPrintf( "vbr_off\n" ); break;
case vbr_mt : DebugPrintf( "vbr_mt \n" ); break;
case vbr_rh : DebugPrintf( "vbr_rh \n" ); break;
case vbr_mtrh: DebugPrintf( "vbr_mtrh \n" ); break;
case vbr_abr:
DebugPrintf( "vbr_abr (average bitrate %d kbps)\n", lame_get_VBR_mean_bitrate_kbps() );
break;
default:
DebugPrintf("error, unknown VBR setting\n");
break;
}
DebugPrintf("Vbr Min bitrate =%d kbps\n", lame_get_VBR_min_bitrate_kbps() );
DebugPrintf("Vbr Max bitrate =%d kbps\n", lame_get_VBR_max_bitrate_kbps() );
DebugPrintf("Write VBR Header =%s\n", ( lame_get_bWriteVbrTag() ) ?"Yes":"No");
DebugPrintf("VBR Hard min =%d\n", lame_get_VBR_hard_min() );
DebugPrintf("ATH Only =%d\n", lame_get_ATHonly() );
DebugPrintf("ATH short =%d\n", lame_get_ATHshort() );
DebugPrintf("ATH no =%d\n", lame_get_noATH() );
DebugPrintf("ATH type =%d\n", lame_get_ATHtype() );
DebugPrintf("ATH lower =%f\n", lame_get_ATHlower() );
DebugPrintf("ATH aa =%d\n", lame_get_athaa_type() );
//DebugPrintf("ATH aa loudapprox =%d\n", lame_get_athaa_loudapprox( ) );
DebugPrintf("ATH aa sensitivity =%f\n", lame_get_athaa_sensitivity() );
DebugPrintf("Experimental nspsytune =%d\n", lame_get_exp_nspsytune() );
DebugPrintf("Experimental X =%d\n", lame_get_experimentalX() );
DebugPrintf("Experimental Y =%d\n", lame_get_experimentalY() );
DebugPrintf("Experimental Z =%d\n", lame_get_experimentalZ() );
}
void lame_global_flags::PresetOptions(LONG myPreset) {
switch (myPreset) {
/*-1*/case LQP_NOPRESET:
break;
/*0*/case LQP_NORMAL_QUALITY:
/* lame_set_quality( , 5 );*/
break;
/*1*/case LQP_LOW_QUALITY:
lame_set_quality( 9 );
break;
/*2*/case LQP_HIGH_QUALITY:
lame_set_quality( 2 );
break;
/*3*/case LQP_VOICE_QUALITY: // --voice flag for experimental voice mode
lame_set_mode( MONO );
lame_set_preset( 56);
break;
/*4*/case LQP_R3MIX: // --R3MIX
lame_set_preset( R3MIX);
break;
/*5*/case LQP_VERYHIGH_QUALITY:
lame_set_quality( 0 );
break;
/*6*/case LQP_STANDARD: // --PRESET STANDARD
lame_set_preset( STANDARD);
break;
/*7*/case LQP_FAST_STANDARD: // --PRESET FAST STANDARD
lame_set_preset( STANDARD_FAST);
break;
/*8*/case LQP_EXTREME: // --PRESET EXTREME
lame_set_preset( EXTREME);
break;
/*9*/case LQP_FAST_EXTREME: // --PRESET FAST EXTREME:
lame_set_preset( EXTREME_FAST);
break;
/*10*/case LQP_INSANE: // --PRESET INSANE
lame_set_preset( INSANE);
break;
/*11*/case LQP_ABR: // --PRESET ABR
// handled in beInitStream
break;
/*12*/case LQP_CBR: // --PRESET CBR
// handled in beInitStream
break;
/*13*/case LQP_MEDIUM: // --PRESET MEDIUM
lame_set_preset( MEDIUM);
break;
/*14*/case LQP_FAST_MEDIUM: // --PRESET FAST MEDIUM
lame_set_preset( MEDIUM_FAST);
break;
/*1000*/case LQP_PHONE:
lame_set_mode( MONO );
lame_set_preset( 16);
break;
/*2000*/case LQP_SW:
lame_set_mode( MONO );
lame_set_preset( 24);
break;
/*3000*/case LQP_AM:
lame_set_mode( MONO );
lame_set_preset( 40);
break;
/*4000*/case LQP_FM:
lame_set_preset( 112);
break;
/*5000*/case LQP_VOICE:
lame_set_mode( MONO );
lame_set_preset( 56);
break;
/*6000*/case LQP_RADIO:
lame_set_preset( 112);
break;
/*7000*/case LQP_TAPE:
lame_set_preset( 112);
break;
/*8000*/case LQP_HIFI:
lame_set_preset( 160);
break;
/*9000*/case LQP_CD:
lame_set_preset( 192);
break;
/*10000*/case LQP_STUDIO:
lame_set_preset( 256);
break;
}
}
FUNC(BE_ERR,beInitStream) (BE_CONFIG*pbeConfig, PDWORD dwSamples, PDWORD dwBufferSize, HBE_STREAM*phbeStream) {
int actual_bitrate;
//2001-12-18
BE_CONFIG lameConfig;
int nInitReturn = 0;
// Init the global flags structure
lame_global_flags &gfp = *lame_init();
*phbeStream = (HBE_STREAM)&gfp;
// clear out structure
memset(&lameConfig,0,sizeof lameConfig);
// Check if this is a regular BLADE_ENCODER header
if (pbeConfig->dwConfig!=BE_CONFIG_LAME) {
int nCRC=pbeConfig->format.mp3.bCRC;
int nVBR=(nCRC>>12)&0x0F;
// Copy parameter from old Blade structure
lameConfig.format.LHV1.dwSampleRate =pbeConfig->format.mp3.dwSampleRate;
//for low bitrates, LAME will automatically downsample for better
//sound quality. Forcing output samplerate = input samplerate is not a good idea
//unless the user specifically requests it:
//lameConfig.format.LHV1.dwReSampleRate=pbeConfig->format.mp3.dwSampleRate;
lameConfig.format.LHV1.nMode =(pbeConfig->format.mp3.byMode&0x0F);
lameConfig.format.LHV1.dwBitrate =pbeConfig->format.mp3.wBitrate;
lameConfig.format.LHV1.bPrivate =pbeConfig->format.mp3.bPrivate;
lameConfig.format.LHV1.bOriginal =pbeConfig->format.mp3.bOriginal;
lameConfig.format.LHV1.bCRC =nCRC&0x01;
lameConfig.format.LHV1.bCopyright =pbeConfig->format.mp3.bCopyright;
// Fill out the unknowns
lameConfig.format.LHV1.dwStructSize=sizeof lameConfig;
lameConfig.format.LHV1.dwStructVersion=CURRENT_STRUCT_VERSION;
// Get VBR setting from fourth nibble
if ( nVBR>0 ) {
lameConfig.format.LHV1.bWriteVBRHeader = TRUE;
lameConfig.format.LHV1.bEnableVBR = TRUE;
lameConfig.format.LHV1.nVBRQuality = nVBR-1;
}
// Get Quality from third nibble
lameConfig.format.LHV1.nPreset=((nCRC>>8)&0x0F);
}else{
// Copy the parameters
memcpy(&lameConfig,pbeConfig,pbeConfig->format.LHV1.dwStructSize);
}
// --------------- Set arguments to LAME encoder -------------------------
// Set input sample frequency
gfp.set_in_samplerate(lameConfig.format.LHV1.dwSampleRate);
// disable INFO/VBR tag by default.
// if this tag is used, the calling program must call beWriteVBRTag()
// after encoding. But the original DLL documentation does not
// require the
// app to call beWriteVBRTag() unless they have specifically
// set LHV1.bWriteVBRHeader=TRUE. Thus the default setting should
// be disabled.
gfp.lame_set_bWriteVbrTag(false);
//2001-12-18 Dibrom's ABR preset stuff
if (lameConfig.format.LHV1.nPreset == LQP_ABR) { // --ALT-PRESET ABR
actual_bitrate = lameConfig.format.LHV1.dwVbrAbr_bps / 1000;
// limit range
if (actual_bitrate > 320) actual_bitrate = 320;
if( actual_bitrate < 8) actual_bitrate = 8;
gfp.lame_set_preset(actual_bitrate );
}
// end Dibrom's ABR preset 2001-12-18 ****** START OF CBR
if (lameConfig.format.LHV1.nPreset == LQP_CBR) { // --ALT-PRESET CBR
actual_bitrate = lameConfig.format.LHV1.dwBitrate;
gfp.lame_set_preset(actual_bitrate);
gfp.lame_set_VBR(vbr_off);
}
// end Dibrom's CBR preset 2001-12-18
// The following settings only used when preset is not one of the LAME QUALITY Presets
if ((int)lameConfig.format.LHV1.nPreset < (int) LQP_STANDARD ) {
switch (lameConfig.format.LHV1.nMode) {
case BE_MP3_MODE_STEREO:
gfp.lame_set_mode(STEREO );
gfp.set_num_channels(2);
break;
case BE_MP3_MODE_JSTEREO:
gfp.lame_set_mode(JOINT_STEREO );
//lame_set_force_ms( gfp, bForceMS ); // no check box to force this?
gfp.set_num_channels(2);
break;
case BE_MP3_MODE_MONO:
gfp.lame_set_mode(MONO);
gfp.set_num_channels(1);
break;
case BE_MP3_MODE_DUALCHANNEL:
gfp.lame_set_mode(DUAL_CHANNEL );
gfp.set_num_channels(2);
break;
default: {
DebugPrintf("Invalid lameConfig.format.LHV1.nMode, value is %d\n",lameConfig.format.LHV1.nMode);
return BE_ERR_INVALID_FORMAT_PARAMETERS;
}
}
if (lameConfig.format.LHV1.bEnableVBR) {
/* set VBR quality */
gfp.lame_set_VBR_q(lameConfig.format.LHV1.nVBRQuality);
/* select proper VBR method */
switch (lameConfig.format.LHV1.nVbrMethod) {
case VBR_METHOD_NONE:
gfp.lame_set_VBR(vbr_off );
break;
case VBR_METHOD_DEFAULT:
gfp.lame_set_VBR(vbr_default );
break;
case VBR_METHOD_OLD:
gfp.lame_set_VBR(vbr_rh );
break;
case VBR_METHOD_MTRH:
case VBR_METHOD_NEW:
/*
* the --vbr-mtrh commandline switch is obsolete.
* now --vbr-mtrh is known as --vbr-new
*/
gfp.lame_set_VBR(vbr_mtrh );
break;
case VBR_METHOD_ABR:
gfp.lame_set_VBR(vbr_abr );
break;
default:
/* unsupported VBR method */
DebugPrintf("unsupported VBR method %u\n",lameConfig.format.LHV1.nVbrMethod);
}
}else{
/* use CBR encoding method, so turn off VBR */
gfp.lame_set_VBR(vbr_off );
}
/* Set bitrate. (CDex users always specify bitrate=Min bitrate when using VBR) */
gfp.lame_set_brate(lameConfig.format.LHV1.dwBitrate);
/* check if we have to use ABR, in order to backwards compatible, this
* condition should still be checked indepedent of the nVbrMethod method
*/
if (lameConfig.format.LHV1.dwVbrAbr_bps > 0) {
/* set VBR method to ABR */
gfp.lame_set_VBR(vbr_abr );
/* calculate to kbps, round to nearest kbps */
gfp.lame_set_VBR_mean_bitrate_kbps(( lameConfig.format.LHV1.dwVbrAbr_bps + 500 ) / 1000 );
/* limit range */
if (gfp.lame_get_VBR_mean_bitrate_kbps() > 320) {
gfp.lame_set_VBR_mean_bitrate_kbps(320 );
}
if (gfp.lame_get_VBR_mean_bitrate_kbps() < 8) {
gfp.lame_set_VBR_mean_bitrate_kbps(8 );
}
}
}
// First set all the preset options
if (LQP_NOPRESET != lameConfig.format.LHV1.nPreset) {
gfp.PresetOptions(lameConfig.format.LHV1.nPreset );
}
// Set frequency resampling rate, if specified
if (lameConfig.format.LHV1.dwReSampleRate > 0) {
gfp.set_out_samplerate(lameConfig.format.LHV1.dwReSampleRate );
}
switch (lameConfig.format.LHV1.nMode) {
case BE_MP3_MODE_MONO:
gfp.lame_set_mode(MONO );
gfp.set_num_channels(1);
break;
}
// Use strict ISO encoding?
gfp.lame_set_strict_ISO(buffer_constraint(lameConfig.format.LHV1.bStrictIso));
// Set copyright flag?
if (lameConfig.format.LHV1.bCopyright) gfp.lame_set_copyright(true);
// Do we have to tag it as non original
gfp.lame_set_original(!!lameConfig.format.LHV1.bOriginal);
// Add CRC?
gfp.lame_set_error_protection(!!lameConfig.format.LHV1.bCRC);
// Set private bit?
gfp.lame_set_extension(!!lameConfig.format.LHV1.bPrivate);
// Set VBR min bitrate, if specified
if (lameConfig.format.LHV1.dwBitrate > 0) {
gfp.lame_set_VBR_min_bitrate_kbps(lameConfig.format.LHV1.dwBitrate );
}
// Set Maxbitrate, if specified
if (lameConfig.format.LHV1.dwMaxBitrate > 0) {
gfp.lame_set_VBR_max_bitrate_kbps(lameConfig.format.LHV1.dwMaxBitrate );
}
// Set bit resovoir option
if (lameConfig.format.LHV1.bNoRes) {
gfp.lame_set_disable_reservoir(true);
}
// check if the VBR tag is required
gfp.lame_set_bWriteVbrTag(!!lameConfig.format.LHV1.bWriteVBRHeader);
// Override Quality setting, use HIGHBYTE = NOT LOWBYTE to be backwards compatible
if ((lameConfig.format.LHV1.nQuality & 0xFF ) ==
((~( lameConfig.format.LHV1.nQuality >> 8 )) & 0xFF) ) {
gfp.lame_set_quality(lameConfig.format.LHV1.nQuality & 0xFF );
}
if (0 != ( nInitReturn = gfp.lame_init_params() )) {
return nInitReturn;
}
//LAME encoding call will accept any number of samples.
if (!gfp.lame_get_version()) {
// For MPEG-II, only 576 samples per frame per channel
*dwSamples= 576 * gfp.get_num_channels();
}else{
// For MPEG-I, 1152 samples per frame per channel
*dwSamples= 1152 * gfp.get_num_channels();
}
// Set the input sample buffer size, so we know what we can expect
// dwSampleBufferSize = *dwSamples;
// Set MP3 buffer size, conservative estimate
*dwBufferSize=(DWORD)( 1.25 * ( *dwSamples / gfp.get_num_channels()) + 7200 );
// For debugging purposes
gfp.dump_config();
// Everything went OK, thus return SUCCESSFUL
return BE_ERR_SUCCESSFUL;
}
FUNC(BE_ERR,beFlushNoGap) (HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput) {
int nOutputSamples = 0;
lame_global_flags & gfp = *(lame_global_flags*)hbeStream;
// Init the global flags structure
nOutputSamples = gfp.lame_encode_flush_nogap(pOutput, LAME_MAXMP3BUFFER );
if (nOutputSamples < 0) {
*pdwOutput = 0;
return BE_ERR_BUFFER_TOO_SMALL;
}
*pdwOutput = nOutputSamples;
return BE_ERR_SUCCESSFUL;
}
FUNC(BE_ERR,beDeinitStream) (HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput) {
int nOutputSamples = 0;
lame_global_flags & gfp = *(lame_global_flags*)hbeStream;
nOutputSamples = gfp.lame_encode_flush(pOutput, 0 );
if (nOutputSamples < 0) {
*pdwOutput = 0;
return BE_ERR_BUFFER_TOO_SMALL;
}
*pdwOutput = nOutputSamples;
return BE_ERR_SUCCESSFUL;
}
FUNC(BE_ERR,beCloseStream) (HBE_STREAM hbeStream) {
lame_global_flags & gfp = *(lame_global_flags*)hbeStream;
// lame will be close in VbrWriteTag function
if (!gfp.lame_get_bWriteVbrTag()) {
gfp.lame_close(); // clean up of allocated memory
gfp_save = 0;
}else{
gfp_save = (lame_global_flags*)hbeStream;
}
// DeInit encoder
return BE_ERR_SUCCESSFUL;
}
FUNC(VOID,beVersion) (BE_VERSION*pbeVersion) {
// Set DLL interface version
pbeVersion->byDLLMajorVersion=MAJORVERSION;
pbeVersion->byDLLMinorVersion=MINORVERSION;
lame_version_t lv; // DLL Release date
memset(&lv,0,sizeof lv);
get_lame_version_numerical ( &lv );
// Set Engine version number (Same as Lame version)
pbeVersion->byMajorVersion = (BYTE)lv.major;
pbeVersion->byMinorVersion = (BYTE)lv.minor;
pbeVersion->byAlphaLevel = (BYTE)lv.alpha;
pbeVersion->byBetaLevel = (BYTE)lv.beta;
#ifdef MMX_choose_table
pbeVersion->byMMXEnabled=1;
#else
pbeVersion->byMMXEnabled=0;
#endif
memset(pbeVersion->btReserved,0,sizeof( pbeVersion->btReserved));
// compilation date
switch (*(DWORD*)(__DATE__)&0xFFFFFF) {
case 'Jan': pbeVersion->byMonth = 1; break;
case 'Feb': pbeVersion->byMonth = 2; break;
case 'Mar': pbeVersion->byMonth = 3; break;
case 'Apr': pbeVersion->byMonth = 4; break;
case 'May': pbeVersion->byMonth = 5; break;
case 'Jun': pbeVersion->byMonth = 6; break;
case 'Jul': pbeVersion->byMonth = 7; break;
case 'Aug': pbeVersion->byMonth = 8; break;
case 'Sep': pbeVersion->byMonth = 9; break;
case 'Oct': pbeVersion->byMonth = 10; break;
case 'Nov': pbeVersion->byMonth = 11; break;
case 'Dec': pbeVersion->byMonth = 12; break;
}
// day of month (char [4..5])
pbeVersion->byDay = (BYTE)atoi(__DATE__ + 4 );
// year (char [7..10])
pbeVersion->wYear = (WORD)atoi(__DATE__ + 7 );
strcpy(pbeVersion->zHomepage,"http://www.mp3dev.org/");
}
FUNC(BE_ERR,beEncodeChunk) (HBE_STREAM hbeStream, DWORD nSamples,
PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput) {
// Encode it
int dwSamples;
int nOutputSamples = 0;
lame_global_flags &gfp = *(lame_global_flags*)hbeStream;
dwSamples = nSamples / gfp.get_num_channels();
// old versions of lame_enc.dll required exactly 1152 samples
// and worked even if nSamples accidently set to 2304
// simulate this behavoir:
if ( 1 == gfp.get_num_channels() && nSamples == 2304) {
dwSamples/= 2;
}
if ( 1 == gfp.get_num_channels() ) {
nOutputSamples = gfp.lame_encode_buffer(pSamples,pSamples,dwSamples,pOutput,0);
}else{
nOutputSamples = gfp.lame_encode_buffer_interleaved(pSamples,dwSamples,pOutput,0);
}
if ( nOutputSamples < 0 ) {
*pdwOutput=0;
return BE_ERR_BUFFER_TOO_SMALL;
}
*pdwOutput = (DWORD)nOutputSamples;
return BE_ERR_SUCCESSFUL;
}
// accept floating point audio samples, scaled to the range of a signed 16-bit
// integer (within +/- 32768), in non-interleaved channels -- DSPguru, jd
FUNC(BE_ERR,beEncodeChunkFloatS16NI) (HBE_STREAM hbeStream, DWORD nSamples,
PFLOAT buffer_l, PFLOAT buffer_r, PBYTE pOutput, PDWORD pdwOutput) {
int nOutputSamples;
lame_global_flags & gfp = *(lame_global_flags*)hbeStream;
nOutputSamples = gfp.lame_encode_buffer_float(buffer_l,buffer_r,nSamples,pOutput,0);
if ( nOutputSamples >= 0 ) {
*pdwOutput = (DWORD) nOutputSamples;
}else{
*pdwOutput=0;
return BE_ERR_BUFFER_TOO_SMALL;
}
return BE_ERR_SUCCESSFUL;
}
static int maybeSyncWord(FILE*f) {
unsigned char mp3_frame_header[4];
size_t nbytes = fread(mp3_frame_header,1,sizeof(mp3_frame_header),f);
if (nbytes != sizeof(mp3_frame_header)) return -1;
if (mp3_frame_header[0] != 0xffu) return -1; /* doesn't look like a sync word */
if ((mp3_frame_header[1] & 0xE0u) != 0xE0u) return -1; /* doesn't look like a sync word */
return 0;
}
static int skipId3v2(FILE*f,size_t lametag_frame_size) {
size_t nbytes;
size_t id3v2TagSize = 0;
unsigned char id3v2Header[10];
/* seek to the beginning of the stream */
if (fseek(f,0,SEEK_SET) != 0) return -2; /* not seekable, abort */
/* read 10 bytes in case there's an ID3 version 2 header here */
nbytes = fread(id3v2Header,1,sizeof(id3v2Header),f);
if (nbytes != sizeof(id3v2Header)) return -3; /* not readable, maybe opened Write-Only */
/* does the stream begin with the ID3 version 2 file identifier? */
if (!strncmp((char *) id3v2Header, "ID3", 3)) {
/* the tag size (minus the 10-byte header) is encoded into four
* bytes where the most significant bit is clear in each byte
*/
id3v2TagSize = (((id3v2Header[6] & 0x7f) << 21)
| ((id3v2Header[7] & 0x7f) << 14)
| ((id3v2Header[8] & 0x7f) << 7)
| (id3v2Header[9] & 0x7f))
+ sizeof id3v2Header;
}
/* Seek to the beginning of the audio stream */
if (fseek(f,(long)id3v2TagSize,SEEK_SET)) return -2;
if (maybeSyncWord(f)) return -1;
if (fseek(f,(long)(id3v2TagSize+lametag_frame_size),SEEK_SET)) return -2;
if (maybeSyncWord(f)) return -1;
/* OK, it seems we found our LAME-Tag/Xing frame again */
/* Seek to the beginning of the audio stream */
if (fseek(f,(long)id3v2TagSize,SEEK_SET)) return -2;
return 0;
}
static BE_ERR updateLameTagFrame(lame_global_flags&gfp, FILE*f) {
size_t n = gfp.lame_get_lametag_frame(0,0); /* ask for bufer size */
if (n>0) {
if (skipId3v2(f,n)) {
DispErr("Error updating LAME-tag frame: "
"Can't locate old frame\n");
return BE_ERR_INVALID_FORMAT_PARAMETERS;
}
unsigned char*buffer=new unsigned char[n];
if (!buffer) {
DispErr( "Error updating LAME-tag frame:\n\n"
"can't allocate frame buffer\n" );
return BE_ERR_INVALID_FORMAT_PARAMETERS;
}
/* Put it all to disk again */
n = gfp.lame_get_lametag_frame(buffer,n);
size_t m = n;
if (n) m = fwrite(buffer,1,n,f);
delete[] buffer;
if (m!=n) {
DispErr( "Error updating LAME-tag frame:\n\n"
"couldn't write frame into file\n" );
return BE_ERR_INVALID_FORMAT_PARAMETERS;
}
}
return BE_ERR_SUCCESSFUL;
}
FUNC(BE_ERR,beWriteInfoTag) (HBE_STREAM hbeStream, const char*lpszFileName) {
lame_global_flags&gfp = *(lame_global_flags*)hbeStream;
if (!&gfp) return BE_ERR_INVALID_FORMAT_PARAMETERS;
// Do we have to write the VBR tag?
BE_ERR beResult = BE_ERR_SUCCESSFUL;
if (gfp.lame_get_bWriteVbrTag()) {
#ifdef UNICODE
UINT cp=CP_UTF8;
int l=MultiByteToWideChar(cp,MB_ERR_INVALID_CHARS,lpszFileName,-1,0,0);
if (!l) {
cp=CP_ACP;
l=MultiByteToWideChar(cp,0,lpszFileName,-1,0,0);
if (!l) return 6;
}
wchar_t*s=new wchar_t[l];
if (!s) return 7;
MultiByteToWideChar(cp,0,lpszFileName,-1,s,l);
FILE*f=_wfopen(s,L"rb+");
delete s;
#else // Try to open the file
FILE*f=fopen(lpszFileName,"rb+");
#endif
// Check file open result
if (f) {
beResult = updateLameTagFrame(gfp,f);
fclose(f); // Close the file stream
}else{
beResult = BE_ERR_INVALID_FORMAT_PARAMETERS;
DispErr("Error updating LAME-tag frame: "
"Can't open file for reading and writing\n" );
}
} // clean up of allocated memory
gfp.lame_close();
return beResult;
}
// for backwards compatiblity
FUNC(BE_ERR,beWriteVBRHeader) (const char*lpszFileName) {
return beWriteInfoTag((HBE_STREAM)gfp_save, lpszFileName );
}
/*
BOOL CALLBACK _DllMainCRTStartup(HINSTANCE hModule,DWORD,LPVOID) {
DisableThreadLibraryCalls(hModule);
return TRUE;
}
*/
Detected encoding: ASCII (7 bit) | 2
|