Source file: /~heha/hs/puh.zip/lame_enc.cpp

/*
*	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