aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_mp3/LAMEinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_mp3/LAMEinfo.cpp')
-rw-r--r--Src/Plugins/Input/in_mp3/LAMEinfo.cpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_mp3/LAMEinfo.cpp b/Src/Plugins/Input/in_mp3/LAMEinfo.cpp
new file mode 100644
index 00000000..febfec98
--- /dev/null
+++ b/Src/Plugins/Input/in_mp3/LAMEinfo.cpp
@@ -0,0 +1,398 @@
+#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();
+} \ No newline at end of file