winamp/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.c
2024-09-24 14:54:57 +02:00

1029 lines
32 KiB
C

/*
* Blade DLL Interface for LAME.
*
* Copyright (c) 1999 - 2002 A.L. Faber
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <windows.h>
#include <Windef.h>
#include "BladeMP3EncDLL.h"
#include <assert.h>
#include <stdio.h>
#include <lame/lame.h>
#ifdef __cplusplus
extern "C" {
#endif
#define Min(A, B) ((A) < (B) ? (A) : (B))
#define Max(A, B) ((A) > (B) ? (A) : (B))
#define _RELEASEDEBUG 0
// lame_enc DLL version number
const BYTE MAJORVERSION = 1;
const BYTE MINORVERSION = 32;
// Local variables
static DWORD dwSampleBufferSize = 0;
static HMODULE gs_hModule = NULL;
static BOOL gs_bLogFile = FALSE;
static lame_global_flags* gfp_save = NULL;
// Local function prototypes
static void dump_config(lame_global_flags* gfp);
static void DebugPrintf(const char* pzFormat, ...);
static void DispErr(wchar_t const* strErr);
static void PresetOptions(lame_global_flags* gfp, LONG myPreset);
static void DebugPrintf(const char* pzFormat, ...)
{
char szBuffer[1024] = { '\0', };
char szFileName[MAX_PATH + 1] = { '\0', };
va_list ap;
// Get the full module (DLL) file name
GetModuleFileNameA(gs_hModule,
szFileName,
sizeof(szFileName));
// change file name extention
szFileName[strlen(szFileName) - 3] = 't';
szFileName[strlen(szFileName) - 2] = 'x';
szFileName[strlen(szFileName) - 1] = 't';
// start at beginning of the list
va_start(ap, pzFormat);
// copy it to the string buffer
_vsnprintf(szBuffer, sizeof(szBuffer), pzFormat, ap);
// log it to the file?
if (gs_bLogFile)
{
FILE* fp = NULL;
// try to open the log file
fp = fopen(szFileName, "a+");
// check file open result
if (fp)
{
// write string to the file
fputs(szBuffer, fp);
// close the file
fclose(fp);
}
}
#if defined _DEBUG || defined _RELEASEDEBUG
OutputDebugStringA(szBuffer);
#endif
va_end(ap);
}
static void PresetOptions(lame_global_flags* gfp, LONG myPreset)
{
switch (myPreset)
{
/*-1*/case LQP_NOPRESET:
break;
/*0*/case LQP_NORMAL_QUALITY:
/* lame_set_quality( gfp, 5 );*/
break;
/*1*/case LQP_LOW_QUALITY:
lame_set_quality(gfp, 9);
break;
/*2*/case LQP_HIGH_QUALITY:
lame_set_quality(gfp, 2);
break;
/*3*/case LQP_VOICE_QUALITY: // --voice flag for experimental voice mode
lame_set_mode(gfp, MONO);
lame_set_preset(gfp, 56);
break;
/*4*/case LQP_R3MIX: // --R3MIX
lame_set_preset(gfp, R3MIX);
break;
/*5*/case LQP_VERYHIGH_QUALITY:
lame_set_quality(gfp, 0);
break;
/*6*/case LQP_STANDARD: // --PRESET STANDARD
lame_set_preset(gfp, STANDARD);
break;
/*7*/case LQP_FAST_STANDARD: // --PRESET FAST STANDARD
lame_set_preset(gfp, STANDARD_FAST);
break;
/*8*/case LQP_EXTREME: // --PRESET EXTREME
lame_set_preset(gfp, EXTREME);
break;
/*9*/case LQP_FAST_EXTREME: // --PRESET FAST EXTREME:
lame_set_preset(gfp, EXTREME_FAST);
break;
/*10*/case LQP_INSANE: // --PRESET INSANE
lame_set_preset(gfp, 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(gfp, MEDIUM);
break;
/*14*/case LQP_FAST_MEDIUM: // --PRESET FAST MEDIUM
lame_set_preset(gfp, MEDIUM_FAST);
break;
/*1000*/case LQP_PHONE:
lame_set_mode(gfp, MONO);
lame_set_preset(gfp, 16);
break;
/*2000*/case LQP_SW:
lame_set_mode(gfp, MONO);
lame_set_preset(gfp, 24);
break;
/*3000*/case LQP_AM:
lame_set_mode(gfp, MONO);
lame_set_preset(gfp, 40);
break;
/*4000*/case LQP_FM:
lame_set_preset(gfp, 112);
break;
/*5000*/case LQP_VOICE:
lame_set_mode(gfp, MONO);
lame_set_preset(gfp, 56);
break;
/*6000*/case LQP_RADIO:
lame_set_preset(gfp, 112);
break;
/*7000*/case LQP_TAPE:
lame_set_preset(gfp, 112);
break;
/*8000*/case LQP_HIFI:
lame_set_preset(gfp, 160);
break;
/*9000*/case LQP_CD:
lame_set_preset(gfp, 192);
break;
/*10000*/case LQP_STUDIO:
lame_set_preset(gfp, 256);
break;
}
}
__declspec(dllexport) BE_ERR beInitStream(PBE_CONFIG pbeConfig, PDWORD dwSamples, PDWORD dwBufferSize, PHBE_STREAM phbeStream)
{
int actual_bitrate;
//2001-12-18
BE_CONFIG lameConfig = { 0, };
int nInitReturn = 0;
lame_global_flags* gfp = NULL;
// Init the global flags structure
gfp = lame_init();
*phbeStream = (HBE_STREAM)gfp;
// clear out structure
memset(&lameConfig, 0x00, CURRENT_STRUCT_SIZE);
// 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 = CURRENT_STRUCT_SIZE;
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
lame_set_in_samplerate(gfp, 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.
lame_set_bWriteVbrTag(gfp, 0);
//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;
}
lame_set_preset(gfp, 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;
lame_set_preset(gfp, actual_bitrate);
lame_set_VBR(gfp, 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:
lame_set_mode(gfp, STEREO);
lame_set_num_channels(gfp, 2);
break;
case BE_MP3_MODE_JSTEREO:
lame_set_mode(gfp, JOINT_STEREO);
//lame_set_force_ms( gfp, bForceMS ); // no check box to force this?
lame_set_num_channels(gfp, 2);
break;
case BE_MP3_MODE_MONO:
lame_set_mode(gfp, MONO);
lame_set_num_channels(gfp, 1);
break;
case BE_MP3_MODE_DUALCHANNEL:
lame_set_mode(gfp, DUAL_CHANNEL);
lame_set_num_channels(gfp, 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 */
lame_set_VBR_q(gfp, lameConfig.format.LHV1.nVBRQuality);
/* select proper VBR method */
switch (lameConfig.format.LHV1.nVbrMethod)
{
case VBR_METHOD_NONE:
lame_set_VBR(gfp, vbr_off);
break;
case VBR_METHOD_DEFAULT:
lame_set_VBR(gfp, vbr_default);
break;
case VBR_METHOD_OLD:
lame_set_VBR(gfp, 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
*/
lame_set_VBR(gfp, vbr_mtrh);
break;
case VBR_METHOD_ABR:
lame_set_VBR(gfp, vbr_abr);
break;
default:
/* unsupported VBR method */
assert(FALSE);
}
}
else
{
/* use CBR encoding method, so turn off VBR */
lame_set_VBR(gfp, vbr_off);
}
/* Set bitrate. (CDex users always specify bitrate=Min bitrate when using VBR) */
lame_set_brate(gfp, 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 */
lame_set_VBR(gfp, vbr_abr);
/* calculate to kbps, round to nearest kbps */
lame_set_VBR_mean_bitrate_kbps(gfp, (lameConfig.format.LHV1.dwVbrAbr_bps + 500) / 1000);
/* limit range */
if (lame_get_VBR_mean_bitrate_kbps(gfp) > 320)
{
lame_set_VBR_mean_bitrate_kbps(gfp, 320);
}
if (lame_get_VBR_mean_bitrate_kbps(gfp) < 8)
{
lame_set_VBR_mean_bitrate_kbps(gfp, 8);
}
}
}
// First set all the preset options
if (LQP_NOPRESET != lameConfig.format.LHV1.nPreset)
{
PresetOptions(gfp, lameConfig.format.LHV1.nPreset);
}
// Set frequency resampling rate, if specified
if (lameConfig.format.LHV1.dwReSampleRate > 0)
{
lame_set_out_samplerate(gfp, lameConfig.format.LHV1.dwReSampleRate);
}
switch (lameConfig.format.LHV1.nMode)
{
case BE_MP3_MODE_MONO:
lame_set_mode(gfp, MONO);
lame_set_num_channels(gfp, 1);
break;
default:
break;
}
// Use strict ISO encoding?
lame_set_strict_ISO(gfp, (lameConfig.format.LHV1.bStrictIso) ? 1 : 0);
// Set copyright flag?
if (lameConfig.format.LHV1.bCopyright)
{
lame_set_copyright(gfp, 1);
}
// Do we have to tag it as non original
if (!lameConfig.format.LHV1.bOriginal)
{
lame_set_original(gfp, 0);
}
else
{
lame_set_original(gfp, 1);
}
// Add CRC?
if (lameConfig.format.LHV1.bCRC)
{
lame_set_error_protection(gfp, 1);
}
else
{
lame_set_error_protection(gfp, 0);
}
// Set private bit?
if (lameConfig.format.LHV1.bPrivate)
{
lame_set_extension(gfp, 1);
}
else
{
lame_set_extension(gfp, 0);
}
// Set VBR min bitrate, if specified
if (lameConfig.format.LHV1.dwBitrate > 0)
{
lame_set_VBR_min_bitrate_kbps(gfp, lameConfig.format.LHV1.dwBitrate);
}
// Set Maxbitrate, if specified
if (lameConfig.format.LHV1.dwMaxBitrate > 0)
{
lame_set_VBR_max_bitrate_kbps(gfp, lameConfig.format.LHV1.dwMaxBitrate);
}
// Set bit resovoir option
if (lameConfig.format.LHV1.bNoRes)
{
lame_set_disable_reservoir(gfp, 1);
}
// check if the VBR tag is required
if (lameConfig.format.LHV1.bWriteVBRHeader)
{
lame_set_bWriteVbrTag(gfp, 1);
}
else
{
lame_set_bWriteVbrTag(gfp, 0);
}
// Override Quality setting, use HIGHBYTE = NOT LOWBYTE to be backwards compatible
if ((lameConfig.format.LHV1.nQuality & 0xFF) ==
((~(lameConfig.format.LHV1.nQuality >> 8)) & 0xFF))
{
lame_set_quality(gfp, lameConfig.format.LHV1.nQuality & 0xFF);
}
if (0 != (nInitReturn = lame_init_params(gfp)))
{
return nInitReturn;
}
//LAME encoding call will accept any number of samples.
if (0 == lame_get_version(gfp))
{
// For MPEG-II, only 576 samples per frame per channel
*dwSamples = 576 * lame_get_num_channels(gfp);
}
else
{
// For MPEG-I, 1152 samples per frame per channel
*dwSamples = 1152 * lame_get_num_channels(gfp);
}
// 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 / lame_get_num_channels(gfp)) + 7200);
// For debugging purposes
dump_config(gfp);
// Everything went OK, thus return SUCCESSFUL
return BE_ERR_SUCCESSFUL;
}
__declspec(dllexport) 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 = lame_encode_flush_nogap(gfp, pOutput, LAME_MAXMP3BUFFER);
if (nOutputSamples < 0)
{
*pdwOutput = 0;
return BE_ERR_BUFFER_TOO_SMALL;
}
else
{
*pdwOutput = nOutputSamples;
}
return BE_ERR_SUCCESSFUL;
}
__declspec(dllexport) BE_ERR beDeinitStream(HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput)
{
int nOutputSamples = 0;
lame_global_flags* gfp = (lame_global_flags*)hbeStream;
nOutputSamples = lame_encode_flush(gfp, pOutput, 0);
if (nOutputSamples < 0)
{
*pdwOutput = 0;
return BE_ERR_BUFFER_TOO_SMALL;
}
else
{
*pdwOutput = nOutputSamples;
}
return BE_ERR_SUCCESSFUL;
}
__declspec(dllexport) BE_ERR beCloseStream(HBE_STREAM hbeStream)
{
lame_global_flags* gfp = (lame_global_flags*)hbeStream;
// lame will be close in VbrWriteTag function
if (!lame_get_bWriteVbrTag(gfp))
{
// clean up of allocated memory
lame_close(gfp);
gfp_save = NULL;
}
else
{
gfp_save = (lame_global_flags*)hbeStream;
}
// DeInit encoder
return BE_ERR_SUCCESSFUL;
}
__declspec(dllexport) VOID beVersion(PBE_VERSION pbeVersion)
{
// DLL Release date
char lpszDate[20] = { '\0', };
char lpszTemp[5] = { '\0', };
lame_version_t lv = { 0, };
// Set DLL interface version
pbeVersion->byDLLMajorVersion = MAJORVERSION;
pbeVersion->byDLLMinorVersion = MINORVERSION;
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));
// Get compilation date
strcpy(lpszDate, __DATE__);
// Get the first three character, which is the month
strncpy(lpszTemp, lpszDate, 3);
lpszTemp[3] = '\0';
pbeVersion->byMonth = 1;
// Set month
if (strcmp(lpszTemp, "Jan") == 0) pbeVersion->byMonth = 1;
if (strcmp(lpszTemp, "Feb") == 0) pbeVersion->byMonth = 2;
if (strcmp(lpszTemp, "Mar") == 0) pbeVersion->byMonth = 3;
if (strcmp(lpszTemp, "Apr") == 0) pbeVersion->byMonth = 4;
if (strcmp(lpszTemp, "May") == 0) pbeVersion->byMonth = 5;
if (strcmp(lpszTemp, "Jun") == 0) pbeVersion->byMonth = 6;
if (strcmp(lpszTemp, "Jul") == 0) pbeVersion->byMonth = 7;
if (strcmp(lpszTemp, "Aug") == 0) pbeVersion->byMonth = 8;
if (strcmp(lpszTemp, "Sep") == 0) pbeVersion->byMonth = 9;
if (strcmp(lpszTemp, "Oct") == 0) pbeVersion->byMonth = 10;
if (strcmp(lpszTemp, "Nov") == 0) pbeVersion->byMonth = 11;
if (strcmp(lpszTemp, "Dec") == 0) pbeVersion->byMonth = 12;
// Get day of month string (char [4..5])
pbeVersion->byDay = (BYTE)atoi(lpszDate + 4);
// Get year of compilation date (char [7..10])
pbeVersion->wYear = (WORD)atoi(lpszDate + 7);
memset(pbeVersion->zHomepage, 0x00, BE_MAX_HOMEPAGE);
strcpy(pbeVersion->zHomepage, "http://www.mp3dev.org/");
}
__declspec(dllexport) 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 / lame_get_num_channels(gfp);
// old versions of lame_enc.dll required exactly 1152 samples
// and worked even if nSamples accidently set to 2304
// simulate this behavoir:
if (1 == lame_get_num_channels(gfp) && nSamples == 2304)
{
dwSamples /= 2;
}
if (1 == lame_get_num_channels(gfp))
{
nOutputSamples = lame_encode_buffer(gfp, pSamples, pSamples, dwSamples, pOutput, 0);
}
else
{
nOutputSamples = lame_encode_buffer_interleaved(gfp, pSamples, dwSamples, pOutput, 0);
}
if (nOutputSamples < 0)
{
*pdwOutput = 0;
return BE_ERR_BUFFER_TOO_SMALL;
}
else
{
*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
__declspec(dllexport) 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 = lame_encode_buffer_float(gfp, 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* fpStream)
{
unsigned char mp3_frame_header[4];
size_t nbytes = fread(mp3_frame_header, 1, sizeof(mp3_frame_header), fpStream);
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* fpStream, 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(fpStream, 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), fpStream);
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(fpStream, id3v2TagSize, SEEK_SET) != 0) {
return -2;
}
if (maybeSyncWord(fpStream) != 0) {
return -1;
}
if (fseek(fpStream, id3v2TagSize + lametag_frame_size, SEEK_SET) != 0) {
return -2;
}
if (maybeSyncWord(fpStream) != 0) {
return -1;
}
/* OK, it seems we found our LAME-Tag/Xing frame again */
/* Seek to the beginning of the audio stream */
if (fseek(fpStream, id3v2TagSize, SEEK_SET) != 0) {
return -2;
}
return 0;
}
static BE_ERR
updateLameTagFrame(lame_global_flags* gfp, FILE* fpStream)
{
size_t n = lame_get_lametag_frame(gfp, 0, 0); /* ask for bufer size */
if (n > 0)
{
unsigned char* buffer = 0;
size_t m = 1;
if (0 != skipId3v2(fpStream, n))
{
DispErr("Error updating LAME-tag frame:\n\n"
"can't locate old frame\n");
return BE_ERR_INVALID_FORMAT_PARAMETERS;
}
buffer = (unsigned char*)malloc(n);
if (buffer == 0)
{
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 = lame_get_lametag_frame(gfp, buffer, n);
if (n > 0)
{
m = fwrite(buffer, n, 1, fpStream);
}
free(buffer);
if (m != 1)
{
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;
}
__declspec(dllexport) BE_ERR beWriteInfoTag(HBE_STREAM hbeStream,
LPCSTR lpszFileName)
{
FILE* fpStream = NULL;
BE_ERR beResult = BE_ERR_SUCCESSFUL;
lame_global_flags* gfp = (lame_global_flags*)hbeStream;
if (NULL != gfp)
{
// Do we have to write the VBR tag?
if (lame_get_bWriteVbrTag(gfp))
{
// Try to open the file
fpStream = fopen(lpszFileName, "rb+");
// Check file open result
if (NULL == fpStream)
{
beResult = BE_ERR_INVALID_FORMAT_PARAMETERS;
DispErr("Error updating LAME-tag frame:\n\n"
"can't open file for reading and writing\n");
}
else
{
beResult = updateLameTagFrame(gfp, fpStream);
// Close the file stream
fclose(fpStream);
}
}
// clean up of allocated memory
lame_close(gfp);
}
else
{
beResult = BE_ERR_INVALID_FORMAT_PARAMETERS;
}
// return result
return beResult;
}
// for backwards compatiblity
__declspec(dllexport) BE_ERR beWriteVBRHeader(LPCSTR lpszFileName)
{
return beWriteInfoTag((HBE_STREAM)gfp_save, lpszFileName);
}
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
(void)lpReserved;
gs_hModule = (HMODULE)hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// Enable debug/logging?
gs_bLogFile = GetPrivateProfileIntA("Debug", "WriteLogFile", gs_bLogFile, "lame_enc.ini");
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
static void dump_config(lame_global_flags* gfp)
{
DebugPrintf("\n\nLame_enc configuration options:\n");
DebugPrintf("==========================================================\n");
DebugPrintf("version =%d\n", lame_get_version(gfp));
DebugPrintf("Layer =3\n");
DebugPrintf("mode =");
switch (lame_get_mode(gfp))
{
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", lame_get_in_samplerate(gfp) / 1000.0);
DebugPrintf("Output sample rate =%.1f kHz\n", lame_get_out_samplerate(gfp) / 1000.0);
DebugPrintf("bitrate =%d kbps\n", lame_get_brate(gfp));
DebugPrintf("Quality Setting =%d\n", lame_get_quality(gfp));
DebugPrintf("Low pass frequency =%d\n", lame_get_lowpassfreq(gfp));
DebugPrintf("Low pass width =%d\n", lame_get_lowpasswidth(gfp));
DebugPrintf("High pass frequency =%d\n", lame_get_highpassfreq(gfp));
DebugPrintf("High pass width =%d\n", lame_get_highpasswidth(gfp));
DebugPrintf("No short blocks =%d\n", lame_get_no_short_blocks(gfp));
DebugPrintf("Force short blocks =%d\n", lame_get_force_short_blocks(gfp));
DebugPrintf("de-emphasis =%d\n", lame_get_emphasis(gfp));
DebugPrintf("private flag =%d\n", lame_get_extension(gfp));
DebugPrintf("copyright flag =%d\n", lame_get_copyright(gfp));
DebugPrintf("original flag =%d\n", lame_get_original(gfp));
DebugPrintf("CRC =%s\n", lame_get_error_protection(gfp) ? "on" : "off");
DebugPrintf("Fast mode =%s\n", (lame_get_quality(gfp)) ? "enabled" : "disabled");
DebugPrintf("Force mid/side stereo =%s\n", (lame_get_force_ms(gfp)) ? "enabled" : "disabled");
DebugPrintf("Disable Reservoir =%d\n", lame_get_disable_reservoir(gfp));
DebugPrintf("Allow diff-short =%d\n", lame_get_allow_diff_short(gfp));
DebugPrintf("Interchannel masking =%f\n", lame_get_interChRatio(gfp));
DebugPrintf("Strict ISO Encoding =%s\n", (lame_get_strict_ISO(gfp)) ? "Yes" : "No");
DebugPrintf("Scale =%5.2f\n", lame_get_scale(gfp));
DebugPrintf("VBR =%s, VBR_q =%d, VBR method =",
(lame_get_VBR(gfp) != vbr_off) ? "enabled" : "disabled",
lame_get_VBR_q(gfp));
switch (lame_get_VBR(gfp))
{
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(gfp));
break;
default:
DebugPrintf("error, unknown VBR setting\n");
break;
}
DebugPrintf("Vbr Min bitrate =%d kbps\n", lame_get_VBR_min_bitrate_kbps(gfp));
DebugPrintf("Vbr Max bitrate =%d kbps\n", lame_get_VBR_max_bitrate_kbps(gfp));
DebugPrintf("Write VBR Header =%s\n", (lame_get_bWriteVbrTag(gfp)) ? "Yes" : "No");
DebugPrintf("VBR Hard min =%d\n", lame_get_VBR_hard_min(gfp));
DebugPrintf("ATH Only =%d\n", lame_get_ATHonly(gfp));
DebugPrintf("ATH short =%d\n", lame_get_ATHshort(gfp));
DebugPrintf("ATH no =%d\n", lame_get_noATH(gfp));
DebugPrintf("ATH type =%d\n", lame_get_ATHtype(gfp));
DebugPrintf("ATH lower =%f\n", lame_get_ATHlower(gfp));
DebugPrintf("ATH aa =%d\n", lame_get_athaa_type(gfp));
//DebugPrintf("ATH aa loudapprox =%d\n", lame_get_athaa_loudapprox( gfp ) );
DebugPrintf("ATH aa sensitivity =%f\n", lame_get_athaa_sensitivity(gfp));
DebugPrintf("Experimental nspsytune =%d\n", lame_get_exp_nspsytune(gfp));
DebugPrintf("Experimental X =%d\n", lame_get_experimentalX(gfp));
DebugPrintf("Experimental Y =%d\n", lame_get_experimentalY(gfp));
DebugPrintf("Experimental Z =%d\n", lame_get_experimentalZ(gfp));
}
static void DispErr(wchar_t const* strErr)
{
MessageBox(NULL, strErr, L"LAME_ENC.DLL", MB_OK | MB_ICONHAND);
}
#ifdef __cplusplus
}
#endif