winamp/Src/Plugins/Input/in_mp3/LAMEinfo.cpp
2024-09-24 14:54:57 +02:00

398 lines
9.0 KiB
C++

#include "LAMEinfo.h"
#include <windows.h>
#include <memory.h>
#include <math.h>
#include "api__in_mp3.h"
#include "resource.h"
#include "in2.h"
#pragma intrinsic(memcmp)
extern In_Module mod;
// Xing header -
// 4 Xing
// 4 flags
// 4 frames
// 4 bytes
// 100 toc
// 4 bytes VBR quality
// Lame tag
// 9 bytes - release name
// 11
// Lame extended info tag
// http://gabriel.mp3-tech.org/mp3infotag.html
/*-------------------------------------------------------------*/
static int32_t ExtractI4(unsigned char *buf)
{
int x;
// big endian extract
x = buf[0];
x <<= 8;
x |= buf[1];
x <<= 8;
x |= buf[2];
x <<= 8;
x |= buf[3];
return x;
}
static int16_t ExtractI2(unsigned char *buf)
{
int x;
// big endian extract
x = buf[0];
x <<= 8;
x |= buf[1];
return x;
}
const static int bitrateV1L3[] = { 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0};
const static int bitrateV1L1[] = { 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000, 0};
const static int bitrateV1L2[] = { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000, 0};
const static int bitrateV2L1[] = { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000, 0};
const static int bitrateV2L2L3[] = { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 0};
const static int sampleRateV1[] = {44100, 48000, 32000, 0};
const static int sampleRateV2[] = {22050, 24000, 16000, 0};
const static int sampleRateV2_5[] = {11025, 12000, 8000, 0};
// [mpeg_version][layer]
static const int samples_per_frame[4][4] =
{
// Layer 3, Layer 2, Layer 1
{ 0, 576, 1152, 384}, // MPEG2.5
{ 0, },
{ 0, 576, 1152, 384}, // MPEG2
{ 0, 1152, 1152, 384}, // MPEG1
};
void MPEGFrame::ReadBuffer(const unsigned char *buffer)
{
sync = ((unsigned short)buffer[0] << 3) | (buffer[1] >> 5);
mpegVersion = (buffer[1] >> 3) & 3;
layer = (buffer[1] >> 1) & 3;
protection = (buffer[1]) & 1;
bitrateIndex = (buffer[2] >> 4) & 0xF;
sampleRateIndex = (buffer[2] >> 2) & 3;
paddingBit = (buffer[2] >> 1) & 1;
privateBit = buffer[2] & 1;
channelMode = (buffer[3] >> 6) & 3;
modeExtension = (buffer[3] >> 4) & 3;
copyright = (buffer[3] >> 3) & 1;
original = (buffer[3] >> 2) & 1;
emphasis = (buffer[3]) & 3;
}
bool MPEGFrame::IsSync()
{
return sync == 0x07FF
&& layer != LayerError
&& mpegVersion != MPEG_Error
&& bitrateIndex != 15
&& bitrateIndex != 0
&& sampleRateIndex != 3
&& !(mpegVersion == MPEG2 && layer != Layer3)
&& !(mpegVersion == MPEG2_5 && layer != Layer3);
}
int MPEGFrame::GetBitrate()
{
switch (mpegVersion)
{
case MPEG1:
switch (layer)
{
case Layer1:
return bitrateV1L1[bitrateIndex];
case Layer2:
return bitrateV1L2[bitrateIndex];
case Layer3:
return bitrateV1L3[bitrateIndex];
}
break;
case MPEG2:
case MPEG2_5:
switch (layer)
{
case Layer1:
return bitrateV2L1[bitrateIndex];
case Layer2:
case Layer3:
return bitrateV2L2L3[bitrateIndex];
}
break;
}
return 0; // shouldn't get here
}
int MPEGFrame::GetPadding()
{
if (paddingBit == NotPadded)
return 0;
if (layer == Layer1)
return 4;
else
return 1;
}
int MPEGFrame::HeaderSize()
{
if (protection == CRC)
return 4 + 2; // 32bits frame header, 16bits CRC
else
return 4; // 32bits frame ehader
}
int MPEGFrame::GetSampleRate() const
{
switch(mpegVersion)
{
case MPEG1: return sampleRateV1[sampleRateIndex];
case MPEG2:return sampleRateV2[sampleRateIndex];
case MPEG2_5:return sampleRateV2_5[sampleRateIndex];
default: return 99999999; // return something that will hopefully cause the framesize to be 0
}
}
int MPEGFrame::GetSamplesPerFrame() const
{
return samples_per_frame[mpegVersion][layer];
}
bool MPEGFrame::IsCopyright()
{
return copyright == 1;
}
bool MPEGFrame::IsCRC()
{
return protection == CRC;
}
bool MPEGFrame::IsOriginal()
{
return original == 1;
}
const char *MPEGFrame::GetEmphasisString()
{
static char tempGE[32];
switch (emphasis)
{
case Emphasis_None:
return WASABI_API_LNGSTRING_BUF(IDS_NONE,tempGE,32);
case Emphasis_50_15_ms:
return WASABI_API_LNGSTRING_BUF(IDS_50_15_MICROSEC,tempGE,32);
case Emphasis_reserved:
return WASABI_API_LNGSTRING_BUF(IDS_INVALID,tempGE,32);
case Emphasis_CCIT_J_17:
return "CITT j.17";
default:
return WASABI_API_LNGSTRING_BUF(IDS_ERROR,tempGE,32);
}
}
int MPEGFrame::FrameSize()
{
if (layer == Layer1)
{
return (int)floor((48.0f*(float)GetBitrate())/GetSampleRate()) + GetPadding();
}
else if (layer == Layer2 || layer == Layer3)
{
if (mpegVersion == MPEG1)
return (int)floor((144.0f*(float)GetBitrate())/GetSampleRate()) + GetPadding();
else
return (int)floor((72.0f*(float)GetBitrate())/GetSampleRate()) + GetPadding();
}
return 0;
}
const char *MPEGFrame::GetMPEGVersionString()
{
switch(mpegVersion)
{
case MPEG1:
return "MPEG-1";
case MPEG2:
return "MPEG-2";
case MPEG2_5:
return "MPEG-2.5";
default:
static char tempMF[16];
return WASABI_API_LNGSTRING_BUF(IDS_ERROR,tempMF,16);
}
}
const char *MPEGFrame::GetChannelModeString()
{
static char tempGC[32];
switch(channelMode)
{
case Stereo:
return WASABI_API_LNGSTRING_BUF(IDS_STEREO,tempGC,32);
case JointStereo:
return WASABI_API_LNGSTRING_BUF(IDS_JOINT_STEREO,tempGC,32);
case DualChannel:
return WASABI_API_LNGSTRING_BUF(IDS_2_CHANNEL,tempGC,32);
case Mono:
return WASABI_API_LNGSTRING_BUF(IDS_MONO,tempGC,32);
default:
return WASABI_API_LNGSTRING_BUF(IDS_ERROR,tempGC,32);
}
}
int MPEGFrame::GetLayer()
{
switch(layer)
{
case Layer1:
return 1;
case Layer2:
return 2;
case Layer3:
return 3;
default:
return 0;
}
}
int MPEGFrame::GetNumChannels()
{
switch(channelMode)
{
case Stereo:
return 2;
case JointStereo:
return 2;
case DualChannel:
return 2;
case Mono:
return 1;
default:
return 0;
}
}
int ReadLAMEinfo(unsigned char *buffer, LAMEinfo *lameInfo)
{
int flags;
MPEGFrame frame;
frame.ReadBuffer(buffer);
if (!frame.IsSync())
return 0;
lameInfo->h_id = frame.mpegVersion & 1;
lameInfo->samprate = frame.GetSampleRate();
// determine offset of header
if (frame.mpegVersion == MPEGFrame::MPEG1) // MPEG 1
{
if (frame.channelMode == MPEGFrame::Mono)
buffer += (17 + 4);//frame.HeaderSize());
else
buffer += (32 + 4);//frame.HeaderSize());
}
else if (frame.mpegVersion == MPEGFrame::MPEG2) // MPEG 2
{
if (frame.channelMode == MPEGFrame::Mono)
buffer += (9 + 4);//frame.HeaderSize());
else
buffer += (17 + 4);//frame.HeaderSize());
}
else if (frame.mpegVersion == MPEGFrame::MPEG2_5) // MPEG 2
{
if (frame.channelMode == MPEGFrame::Mono)
buffer += (9 + 4);//frame.HeaderSize());
else
buffer += (17 + 4);//frame.HeaderSize());
}
if (!memcmp(buffer, "Info", 4))
lameInfo->cbr=1;
else if (memcmp(buffer, "Xing", 4) && memcmp(buffer, "Lame", 4))
return 0;
buffer += 4; // skip Xing tag
flags = lameInfo->flags = ExtractI4(buffer);
buffer += 4; // skip flags
if (flags & FRAMES_FLAG)
{
lameInfo->frames = ExtractI4(buffer);
buffer += 4; // skip frames
}
if (flags & BYTES_FLAG)
{
lameInfo->bytes = ExtractI4(buffer);
buffer += 4;
}
if (flags & TOC_FLAG)
{
if (lameInfo->toc)
{
for (int i = 0;i < 100;i++)
lameInfo->toc[i] = buffer[i];
}
buffer += 100;
}
lameInfo->vbr_scale = -1;
if (flags & VBR_SCALE_FLAG)
{
lameInfo->vbr_scale = ExtractI4(buffer);
buffer += 4;
}
if (!memcmp(buffer, "LAME", 4))
{
for (int i=0;i<9;i++)
lameInfo->lameTag[i]=*buffer++;
lameInfo->lameTag[9]=0; // null terminate in case tag used all 20 characters
lameInfo->encodingMethod = (*buffer++)&0xF; // we'll grab the VBR method
lameInfo->lowpass = (*buffer++)*100; // lowpass value
lameInfo->peak=*((float *)buffer); // read peak value
buffer+=4; // skip peak value
// read track gain
int16_t gain_word = ExtractI2(buffer);
if ((gain_word & 0xFC00) == 0x2C00)
{
lameInfo->replaygain_track_gain = (float)(gain_word & 0x01FF);
lameInfo->replaygain_track_gain /= 10;
if (gain_word & 0x0200)
lameInfo->replaygain_track_gain = -lameInfo->replaygain_track_gain;
}
buffer+=2;
// read album gain
gain_word = ExtractI2(buffer);
if ((gain_word & 0xFC00) == 0x4C00)
{
lameInfo->replaygain_album_gain = (float)(gain_word & 0x01FF);
lameInfo->replaygain_album_gain /= 10;
if (gain_word & 0x0200)
lameInfo->replaygain_album_gain = -lameInfo->replaygain_album_gain;
}
buffer+=2;
buffer+=1; // skip encoding flags + ATH type
buffer+=1; // skip bitrate
// get the encoder delay and padding, annoyingly as 12 bit values packed into 3 bytes
lameInfo->encoderDelay = ((unsigned short)buffer[0] << 4) | (buffer[1] >> 4);
lameInfo->padding = ((unsigned short)(buffer[1]&0x0F) << 8) | (buffer[2]);
}
return frame.FrameSize();
}