Initial community commit
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
#include "LAMEInfo.h"
|
||||
#include "MPEGHeader.h"
|
||||
#include "foundation/error.h"
|
||||
#include <string.h>
|
||||
#include "nu/ByteReader.h"
|
||||
#include "nu/BitReader.h"
|
||||
// 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
|
||||
|
||||
|
||||
|
||||
LAMEInfo::LAMEInfo()
|
||||
{
|
||||
memset(this, 0, sizeof(LAMEInfo));
|
||||
}
|
||||
|
||||
bool LAMEInfo::Flag(int flag) const
|
||||
{
|
||||
return flags & flag;
|
||||
}
|
||||
|
||||
int LAMEInfo::GetGaps(size_t *pregap, size_t *postgap)
|
||||
{
|
||||
if (!encoder_delay)
|
||||
return NErr_Empty;
|
||||
|
||||
*pregap = encoder_delay;
|
||||
*postgap = padding;
|
||||
return NErr_Success;
|
||||
}
|
||||
|
||||
uint64_t LAMEInfo::GetSeekPoint(double percent) const
|
||||
{
|
||||
// interpolate in TOC to get file seek point in bytes
|
||||
int a;
|
||||
uint64_t seekpoint;
|
||||
double fa, fb, fx;
|
||||
|
||||
percent*=100.0;
|
||||
if (percent < 0.0)
|
||||
percent = 0.0;
|
||||
if (percent > 100.0)
|
||||
percent = 100.0;
|
||||
|
||||
a = (int)(percent);
|
||||
if (a > 99) a = 99;
|
||||
fa = toc[a];
|
||||
if (a < 99)
|
||||
{
|
||||
fb = toc[a + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
fb = 256.0;
|
||||
}
|
||||
|
||||
fx = fa + (fb - fa) * (percent - a);
|
||||
seekpoint = (uint64_t) ((1.0 / 256.0) * fx * bytes);
|
||||
return seekpoint;
|
||||
}
|
||||
|
||||
uint64_t LAMEInfo::GetSamples() const
|
||||
{
|
||||
if (flags&FRAMES_FLAG)
|
||||
{
|
||||
uint64_t samples = frames * samples_per_frame;
|
||||
samples -= (encoder_delay + padding);
|
||||
return samples;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t LAMEInfo::GetFrames() const
|
||||
{
|
||||
if (flags&FRAMES_FLAG)
|
||||
return frames;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
double LAMEInfo::GetLengthSeconds() const
|
||||
{
|
||||
if (flags&FRAMES_FLAG)
|
||||
{
|
||||
return (double)GetSamples() / (double)sample_rate;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LAMEInfo::Read(const MPEGHeader &frame, const uint8_t *buffer, size_t buffer_length)
|
||||
{
|
||||
int flags;
|
||||
bool crc_hack_applied=false;
|
||||
bytereader_value_t byte_reader;
|
||||
|
||||
/* maybe toolame writes these things also, I dunno. we'll just abort for now */
|
||||
if (frame.layer != MPEGHeader::Layer3)
|
||||
return 0;
|
||||
|
||||
|
||||
bytereader_init(&byte_reader, buffer, buffer_length);
|
||||
|
||||
sample_rate = frame.GetSampleRate();
|
||||
version = frame.mpeg_version;
|
||||
samples_per_frame = frame.GetSamplesPerFrame();
|
||||
|
||||
// skip sideinfo
|
||||
if (frame.mpeg_version == MPEGHeader::MPEG1) // MPEG 1
|
||||
{
|
||||
if (frame.channel_mode == MPEGHeader::Mono)
|
||||
bytereader_advance(&byte_reader, 17);
|
||||
else
|
||||
bytereader_advance(&byte_reader, 32);
|
||||
}
|
||||
else if (frame.mpeg_version == MPEGHeader::MPEG2) // MPEG 2
|
||||
{
|
||||
if (frame.channel_mode == MPEGHeader::Mono)
|
||||
bytereader_advance(&byte_reader, 9);
|
||||
else
|
||||
bytereader_advance(&byte_reader, 17);
|
||||
}
|
||||
else if (frame.mpeg_version == MPEGHeader::MPEG2_5) // MPEG 2
|
||||
{
|
||||
if (frame.channel_mode == MPEGHeader::Mono)
|
||||
bytereader_advance(&byte_reader, 9);
|
||||
else
|
||||
bytereader_advance(&byte_reader, 17);
|
||||
}
|
||||
|
||||
if (bytereader_size(&byte_reader) > buffer_length /* check for wraparound */
|
||||
|| bytereader_size(&byte_reader) < 8)
|
||||
return NErr_Insufficient;
|
||||
|
||||
again:
|
||||
if (bytereader_show_u32_be(&byte_reader) == 'Info')
|
||||
cbr=1;
|
||||
else if (bytereader_show_u32_be(&byte_reader) != 'Xing' && bytereader_show_u32_be(&byte_reader) != 'Lame')
|
||||
{
|
||||
// if there's CRC data, LAME sometimes writes to the wrong position
|
||||
if (frame.IsCRC() && !crc_hack_applied)
|
||||
{
|
||||
crc_hack_applied=true;
|
||||
bytereader_advance(&byte_reader, 2);
|
||||
goto again;
|
||||
}
|
||||
return NErr_False;
|
||||
}
|
||||
|
||||
bytereader_advance(&byte_reader, 4); // skip Xing tag
|
||||
flags = this->flags = bytereader_read_u32_be(&byte_reader);
|
||||
|
||||
if (flags & FRAMES_FLAG)
|
||||
{
|
||||
if (bytereader_size(&byte_reader) < 4)
|
||||
return NErr_Insufficient;
|
||||
|
||||
frames = bytereader_read_u32_be(&byte_reader);
|
||||
}
|
||||
if (flags & BYTES_FLAG)
|
||||
{
|
||||
if (bytereader_size(&byte_reader) < 4)
|
||||
return NErr_Insufficient;
|
||||
bytes = bytereader_read_u32_be(&byte_reader);
|
||||
}
|
||||
if (flags & TOC_FLAG)
|
||||
{
|
||||
if (bytereader_size(&byte_reader) < 100)
|
||||
return NErr_Insufficient;
|
||||
|
||||
int i;
|
||||
memcpy(toc, bytereader_pointer(&byte_reader), 100);
|
||||
|
||||
// verify that TOC isn't empty
|
||||
for (i = 0; i < 100; i++)
|
||||
if (toc[i]) break;
|
||||
if (i == 100)
|
||||
flags &= ~TOC_FLAG;
|
||||
|
||||
bytereader_advance(&byte_reader, 100);
|
||||
}
|
||||
|
||||
vbr_scale = -1;
|
||||
if (flags & VBR_SCALE_FLAG)
|
||||
{
|
||||
if (bytereader_size(&byte_reader) < 4)
|
||||
return NErr_Insufficient;
|
||||
vbr_scale = bytereader_read_u32_be(&byte_reader);
|
||||
}
|
||||
|
||||
if (bytereader_size(&byte_reader) < 27)
|
||||
return NErr_Success; // stop here if we have to, we have at least some data
|
||||
|
||||
if (bytereader_show_u32_be(&byte_reader) == 'LAME')
|
||||
{
|
||||
for (int i=0;i<9;i++)
|
||||
encoder[i]=bytereader_read_u8(&byte_reader);
|
||||
encoder[9]=0; // null terminate in case tag used all 9 characters
|
||||
|
||||
if (bytereader_show_u8(&byte_reader) == '(')
|
||||
{
|
||||
// read 11 more characters
|
||||
for (int i=9;i<20;i++)
|
||||
encoder[i]=bytereader_read_u8(&byte_reader);
|
||||
encoder[20]=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tag_revision = bytereader_show_u8(&byte_reader)>>4;
|
||||
if (tag_revision == 0)
|
||||
{
|
||||
encoding_method = bytereader_read_u8(&byte_reader)&0xF; // VBR method
|
||||
lowpass = bytereader_read_u8(&byte_reader)*100; // lowpass value
|
||||
peak=bytereader_read_f32_be(&byte_reader); // read peak value
|
||||
|
||||
// read track gain
|
||||
int16_t gain_word = bytereader_read_s16_be(&byte_reader);
|
||||
if ((gain_word & 0xFC00) == 0x2C00)
|
||||
{
|
||||
replaygain_track_gain = (float)(gain_word & 0x01FF);
|
||||
replaygain_track_gain /= 10;
|
||||
if (gain_word & 0x0200)
|
||||
replaygain_track_gain = -replaygain_track_gain;
|
||||
}
|
||||
|
||||
// read album gain
|
||||
gain_word = bytereader_read_s16_be(&byte_reader);
|
||||
if ((gain_word & 0xFC00) == 0x4C00)
|
||||
{
|
||||
replaygain_album_gain = (float)(gain_word & 0x01FF);
|
||||
replaygain_album_gain /= 10;
|
||||
if (gain_word & 0x0200)
|
||||
replaygain_album_gain = -replaygain_album_gain;
|
||||
}
|
||||
|
||||
bytereader_advance(&byte_reader, 1); // skip encoding flags + ATH type
|
||||
abr_bitrate = bytereader_read_u8(&byte_reader); // bitrate
|
||||
|
||||
// get the encoder delay and padding, annoyingly as 12 bit values packed into 3 bytes
|
||||
BitReader bit_reader;
|
||||
bit_reader.data = (const uint8_t *)bytereader_pointer(&byte_reader);
|
||||
bit_reader.numBits = 24;
|
||||
const uint8_t *temp = (const uint8_t *)bytereader_pointer(&byte_reader);
|
||||
encoder_delay = bit_reader.getbits(12);
|
||||
padding = bit_reader.getbits(12);
|
||||
bytereader_advance(&byte_reader, 3);
|
||||
|
||||
bytereader_advance(&byte_reader, 4);
|
||||
// skip misc
|
||||
// skip MP3Gain reconstruction info
|
||||
// skip surround info and preset info
|
||||
|
||||
music_length = bytereader_read_u32_be(&byte_reader);
|
||||
music_crc = bytereader_read_u16_be(&byte_reader);
|
||||
tag_crc = bytereader_read_u16_be(&byte_reader);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!memcmp(bytereader_pointer(&byte_reader), "iTunes", 6))
|
||||
{
|
||||
int i=0;
|
||||
while (bytereader_size(&byte_reader) && i < 31)
|
||||
{
|
||||
encoder[i] = bytereader_read_u8(&byte_reader);
|
||||
if (!encoder[i])
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
encoder[31]=0;
|
||||
}
|
||||
else if (!memcmp(bytereader_pointer(&byte_reader), "\0\0\0\0mp3HD", 9))
|
||||
{
|
||||
bytereader_advance(&byte_reader, 4);
|
||||
for (int i=0;i<5;i++)
|
||||
encoder[i] = bytereader_read_u8(&byte_reader);
|
||||
|
||||
encoder[5]=0;
|
||||
}
|
||||
return NErr_Success;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
#include "MPEGHeader.h"
|
||||
#define FRAMES_FLAG 0x0001
|
||||
#define BYTES_FLAG 0x0002
|
||||
#define TOC_FLAG 0x0004
|
||||
#define VBR_SCALE_FLAG 0x0008
|
||||
|
||||
#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
|
||||
|
||||
struct LAMEInfo
|
||||
{
|
||||
LAMEInfo();
|
||||
uint64_t GetSeekPoint(double percent) const; /* 0 <= percent <= 1.0 */
|
||||
int Read(const MPEGHeader &frame, const uint8_t *buffer, size_t bufferlen);
|
||||
double GetLengthSeconds() const;
|
||||
uint64_t GetSamples() const;
|
||||
uint32_t GetFrames() const;
|
||||
|
||||
bool Flag(int flag) const;
|
||||
int GetGaps(size_t *pregap, size_t *postgap);
|
||||
protected:
|
||||
int version;
|
||||
int sample_rate;
|
||||
int samples_per_frame;
|
||||
|
||||
int cbr; // set to 1 if the file is actually just CBR
|
||||
// Xing
|
||||
int flags; // from Xing header data
|
||||
uint32_t frames; // total bit stream frames from Xing header data
|
||||
uint64_t bytes; // total bit stream bytes from Xing header data
|
||||
int vbr_scale; // encoded vbr scale from Xing header data
|
||||
uint8_t toc[100]; // pointer to unsigned char toc_buffer[100]
|
||||
// may be NULL if toc not desired
|
||||
|
||||
// LAME
|
||||
char encoder[32]; // 9 characters, but we'll add an extra NULL just in case
|
||||
float peak;
|
||||
float replaygain_album_gain;
|
||||
float replaygain_track_gain;
|
||||
unsigned short lowpass;
|
||||
unsigned short encoder_delay;
|
||||
unsigned short padding;
|
||||
uint8_t encoding_method;
|
||||
uint8_t tag_revision;
|
||||
uint8_t abr_bitrate;
|
||||
uint32_t music_length;
|
||||
uint16_t music_crc;
|
||||
uint16_t tag_crc;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ENCODING_METHOD_LAME = 0,
|
||||
ENCODING_METHOD_CBR = 1,
|
||||
ENCODING_METHOD_ABR = 2,
|
||||
ENCODING_METHOD_VBR1 = 3,
|
||||
ENCODING_METHOD_VBR2 = 4,
|
||||
ENCODING_METHOD_VBR3 = 5,
|
||||
ENCODING_METHOD_VBR4 = 6,
|
||||
ENCODING_METHOD_CBR_2PASS = 8,
|
||||
ENCODING_METHOD_ABR_2PASS = 9,
|
||||
};
|
||||
|
||||
int ReadLAMEInfo(const MPEGHeader &frame, const uint8_t *buffer, LAMEInfo *lameInfo);
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "MPEGHeader.h"
|
||||
#include <math.h>
|
||||
|
||||
// [mpeg_version][layer][index]
|
||||
static const int bitrates[4][4][15] =
|
||||
{
|
||||
{
|
||||
// MPEG-2.5
|
||||
{ 0,},
|
||||
{ 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 3
|
||||
{ 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 2
|
||||
{ 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000}, // Layer 1
|
||||
},
|
||||
|
||||
{
|
||||
// invalid
|
||||
{ 0, },
|
||||
{ 0, },
|
||||
{ 0, },
|
||||
{ 0, },
|
||||
},
|
||||
|
||||
{
|
||||
// MPEG-2
|
||||
{ 0,},
|
||||
{ 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 3
|
||||
{ 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000}, // Layer 2
|
||||
{ 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000}, // Layer 1
|
||||
},
|
||||
|
||||
{
|
||||
// MPEG-1
|
||||
{ 0,},
|
||||
{ 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000}, // Layer 3
|
||||
{ 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000}, // Layer 2
|
||||
{ 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000}, // Layer 1
|
||||
},
|
||||
};
|
||||
|
||||
// [mpeg_version][index]
|
||||
static const int sample_rates[4][4] =
|
||||
{
|
||||
{11025, 12000, 8000, 0}, // MPEG-2.5
|
||||
{0, },
|
||||
{22050, 24000, 16000, 0}, // MPEG-2
|
||||
{44100, 48000, 32000, 0}, // MPEG-1
|
||||
};
|
||||
|
||||
// [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
|
||||
};
|
||||
|
||||
// [layer]
|
||||
static const int bits_per_slot[4] = { 0, 8, 8, 32 };
|
||||
|
||||
void MPEGHeader::ReadBuffer(const uint8_t *buffer)
|
||||
{
|
||||
sync = ((uint16_t)buffer[0] << 3) | (buffer[1] >> 5);
|
||||
mpeg_version = (buffer[1] >> 3) & 3;
|
||||
layer = (buffer[1] >> 1) & 3;
|
||||
protection = (buffer[1]) & 1;
|
||||
bitrate_index = (buffer[2] >> 4) & 0xF;
|
||||
sample_rate_index = (buffer[2] >> 2) & 3;
|
||||
padding_bit = (buffer[2] >> 1) & 1;
|
||||
private_bit = buffer[2] & 1;
|
||||
channel_mode = (buffer[3] >> 6) & 3;
|
||||
mode_extension = (buffer[3] >> 4) & 3;
|
||||
copyright = (buffer[3] >> 3) & 1;
|
||||
original = (buffer[3] >> 2) & 1;
|
||||
emphasis = (buffer[3]) & 3;
|
||||
}
|
||||
|
||||
bool MPEGHeader::IsSync() const
|
||||
{
|
||||
return sync == 0x07FF
|
||||
&& layer != LayerError
|
||||
&& mpeg_version != MPEG_Error
|
||||
&& bitrate_index != 15
|
||||
&& bitrate_index != 0
|
||||
&& sample_rate_index != 3
|
||||
&& !(mpeg_version == MPEG2 && layer != Layer3)
|
||||
&& !(mpeg_version == MPEG2_5 && layer != Layer3);
|
||||
}
|
||||
|
||||
int MPEGHeader::GetBitrate() const
|
||||
{
|
||||
return bitrates[mpeg_version][layer][bitrate_index];
|
||||
}
|
||||
|
||||
int MPEGHeader::HeaderSize() const
|
||||
{
|
||||
if (protection == CRC)
|
||||
return 4 + 2; // 32bits frame header, 16bits CRC
|
||||
else
|
||||
return 4; // 32bits frame ehader
|
||||
}
|
||||
|
||||
int MPEGHeader::GetSampleRate() const
|
||||
{
|
||||
return sample_rates[mpeg_version][sample_rate_index];
|
||||
}
|
||||
|
||||
bool MPEGHeader::IsCopyright() const
|
||||
{
|
||||
return copyright == 1;
|
||||
}
|
||||
bool MPEGHeader::IsCRC() const
|
||||
{
|
||||
return protection == CRC;
|
||||
}
|
||||
|
||||
bool MPEGHeader::IsOriginal() const
|
||||
{
|
||||
return original == 1;
|
||||
}
|
||||
|
||||
int MPEGHeader::GetSamplesPerFrame() const
|
||||
{
|
||||
return samples_per_frame[mpeg_version][layer];
|
||||
}
|
||||
|
||||
int MPEGHeader::FrameSize() const
|
||||
{
|
||||
int nBitsPerSlot;
|
||||
int nAvgSlotsPerFrame;
|
||||
|
||||
nBitsPerSlot = bits_per_slot[layer];
|
||||
|
||||
nAvgSlotsPerFrame = (GetSamplesPerFrame() * (bitrates[mpeg_version][layer][bitrate_index] / nBitsPerSlot)) / sample_rates[mpeg_version][sample_rate_index];
|
||||
|
||||
return (nAvgSlotsPerFrame + padding_bit) * nBitsPerSlot / 8;
|
||||
}
|
||||
|
||||
int MPEGHeader::GetLayer() const
|
||||
{
|
||||
switch(layer)
|
||||
{
|
||||
case Layer1:
|
||||
return 1;
|
||||
case Layer2:
|
||||
return 2;
|
||||
case Layer3:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int MPEGHeader::GetNumChannels() const
|
||||
{
|
||||
switch(channel_mode)
|
||||
{
|
||||
case Stereo:
|
||||
return 2;
|
||||
case JointStereo:
|
||||
return 2;
|
||||
case DualChannel:
|
||||
return 2;
|
||||
case Mono:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "foundation/types.h"
|
||||
|
||||
class MPEGHeader
|
||||
{
|
||||
public:
|
||||
void ReadBuffer(const uint8_t *buffer);
|
||||
int GetNumChannels() const;
|
||||
bool IsSync() const;
|
||||
int GetBitrate() const;
|
||||
int HeaderSize() const;
|
||||
int GetSampleRate() const;
|
||||
int FrameSize() const;
|
||||
int GetLayer() const;
|
||||
bool IsCRC() const;
|
||||
bool IsCopyright() const;
|
||||
bool IsOriginal() const;
|
||||
int GetSamplesPerFrame() const;
|
||||
enum
|
||||
{
|
||||
NotPadded=0,
|
||||
Padded=1,
|
||||
CRC = 0,
|
||||
NoProtection = 1,
|
||||
Stereo = 0,
|
||||
JointStereo = 1,
|
||||
DualChannel = 2,
|
||||
Mono = 3,
|
||||
MPEG1 = 3,
|
||||
MPEG2 = 2,
|
||||
MPEG_Error = 1,
|
||||
MPEG2_5 = 0,
|
||||
Layer1 = 3,
|
||||
Layer2 = 2,
|
||||
Layer3 = 1,
|
||||
LayerError = 0,
|
||||
Emphasis_None = 0,
|
||||
Emphasis_50_15_ms = 1,
|
||||
Emphasis_reserved = 2,
|
||||
Emphasis_CCIT_J_17 = 3,
|
||||
};
|
||||
|
||||
uint16_t sync;
|
||||
uint8_t mpeg_version, layer, protection, bitrate_index;
|
||||
uint8_t padding_bit, private_bit, channel_mode, mode_extension;
|
||||
uint8_t sample_rate_index, copyright, original, emphasis;
|
||||
};
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "OFL.h"
|
||||
#include "foundation/error.h"
|
||||
|
||||
static void crcofl(unsigned short crcPoly, unsigned short crcMask, unsigned long *crc, unsigned char byte)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
unsigned short flag = (*crc) & crcMask ? 1:0;
|
||||
flag ^= (byte & 0x80 ? 1 : 0);
|
||||
(*crc)<<=1;
|
||||
byte <<= 1;
|
||||
if(flag)
|
||||
(*crc) ^= crcPoly;
|
||||
}
|
||||
}
|
||||
|
||||
int OFL::GetGaps(size_t *pregap, size_t *postgap)
|
||||
{
|
||||
/* TODO: verify the postgap calculation */
|
||||
if (codec_delay >= 529)
|
||||
{
|
||||
*pregap = codec_delay;
|
||||
size_t endcut;
|
||||
endcut = samples_per_frame - ((total_length + codec_delay) % samples_per_frame); // how many 0 samples had to be added?
|
||||
*postgap = endcut;
|
||||
return NErr_Success;
|
||||
}
|
||||
return NErr_Empty;
|
||||
}
|
||||
|
||||
|
||||
double OFL::GetLengthSeconds() const
|
||||
{
|
||||
return (double)GetSamples() / (double)sample_rate;
|
||||
}
|
||||
|
||||
uint64_t OFL::GetSamples() const
|
||||
{
|
||||
return total_length;
|
||||
}
|
||||
|
||||
uint32_t OFL::GetFrames() const
|
||||
{
|
||||
uint64_t real_samples = (total_length+codec_delay)*samples_per_frame;
|
||||
return (uint32_t) (real_samples/samples_per_frame);
|
||||
}
|
||||
|
||||
int OFL::Read(const MPEGHeader &header, const uint8_t *buffer, size_t buffer_len)
|
||||
{
|
||||
if (header.layer != MPEGHeader::Layer3)
|
||||
return NErr_False;
|
||||
|
||||
sample_rate = header.GetSampleRate();
|
||||
samples_per_frame = header.GetSamplesPerFrame();
|
||||
|
||||
if (header.channel_mode == MPEGHeader::Mono)
|
||||
{
|
||||
if (header.mpeg_version == MPEGHeader::MPEG1)
|
||||
{
|
||||
// 0-9 : main_data_end
|
||||
int16_t main_data_end = (buffer[0] << 1) | (buffer[1] >> 7);
|
||||
|
||||
// read the 2 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[2] & 0x3F) << 6) | (buffer[3]>>2); // bits 18-30
|
||||
part2_3_length += ((buffer[9] & 0x7) << 9) | (buffer[10] << 1) | (buffer[11] >> 7) ; // bits 77-89
|
||||
|
||||
size_t offset = 17 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // MPEG2 and 2.5
|
||||
// 0-8 : main_data_end
|
||||
uint16_t main_data_end = buffer[0];
|
||||
|
||||
// read the 2 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[1] & 0x7F) << 5) | (buffer[2]>>3); // bits 9-21
|
||||
|
||||
size_t offset = 9 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (header.mpeg_version == MPEGHeader::MPEG1)
|
||||
{
|
||||
// 0-9 : main_data_end
|
||||
uint16_t main_data_end = (buffer[0] << 1) | (buffer[1] >> 7);
|
||||
|
||||
// read the 4 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[2] & 0xF) << 8) | buffer[3]; // bits 20-32
|
||||
part2_3_length += ((buffer[9] & 0x1) << 11) | (buffer[10] << 3) | (buffer[11] >> 5) ; // bits 79-91
|
||||
part2_3_length += ((buffer[17] & 0x3F) << 6) | (buffer[18] >> 2); // bits 138-150
|
||||
part2_3_length += ((buffer[24] & 0x7) << 9) | (buffer[25] << 1) | (buffer[26] >> 7); // bits 197-209
|
||||
|
||||
size_t offset = 32 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // MPEG2 and 2.5
|
||||
// 0-8 : main_data_end
|
||||
uint16_t main_data_end = buffer[0];
|
||||
|
||||
// read the 4 part2_3_lengths out so we know how big the main data section is
|
||||
uint16_t part2_3_length = ((buffer[1] & 0x3F) << 6) | (buffer[2] >> 2); // bits 10-22
|
||||
part2_3_length += ((buffer[8] & 0x7) << 9) | (buffer[9] << 1) | (buffer[10] >> 7) ; // bits 69-81
|
||||
|
||||
size_t offset = 17 + (part2_3_length+7)/8;
|
||||
if (offset+9 < buffer_len && buffer[offset] == 0xb4)
|
||||
{
|
||||
unsigned long crc=255;
|
||||
for (int i=0;i<9;i++)
|
||||
crcofl(0x0045, 0x0080, &crc, buffer[offset+i]);
|
||||
|
||||
if ((crc & 0xFF) == buffer[offset+9])
|
||||
{
|
||||
total_length = (buffer[offset+3] << 24) | (buffer[offset+4] << 16) | (buffer[offset+5] << 8) | (buffer[offset+6]);
|
||||
codec_delay = (buffer[offset+1] << 8) | (buffer[offset+2]);
|
||||
additional_delay= (buffer[offset+7] << 8) | (buffer[offset+8]);
|
||||
return NErr_Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NErr_False;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "MPEGHeader.h"
|
||||
class OFL
|
||||
{
|
||||
public:
|
||||
int Read(const MPEGHeader &header, const uint8_t *buffer, size_t buffer_len);
|
||||
double GetLengthSeconds() const;
|
||||
uint64_t GetSamples() const;
|
||||
uint32_t GetFrames() const;
|
||||
int GetGaps(size_t *pregap, size_t *postgap);
|
||||
|
||||
private:
|
||||
int samples_per_frame;
|
||||
uint32_t total_length;
|
||||
uint16_t codec_delay;
|
||||
uint16_t additional_delay;
|
||||
|
||||
unsigned int sample_rate;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29609.76
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsmp3", "nsmp3.vcxproj", "{422DBC5C-A877-4023-8918-4CDF8068DDA1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Debug|x64.Build.0 = Debug|x64
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|Win32.Build.0 = Release|Win32
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|x64.ActiveCfg = Release|x64
|
||||
{422DBC5C-A877-4023-8918-4CDF8068DDA1}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1E476B03-144A-407A-91B6-F40A1A24357F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,159 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{422DBC5C-A877-4023-8918-4CDF8068DDA1}</ProjectGuid>
|
||||
<RootNamespace>nsmp3</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>x86_Debug\</OutDir>
|
||||
<IntDir>x86_Debug\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>x64_Debug\</OutDir>
|
||||
<IntDir>x64_Debug\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>x86_Release\</OutDir>
|
||||
<IntDir>x86_Release\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>x64_Release\</OutDir>
|
||||
<IntDir>x64_Release\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(ProjectDir)x86_Debug\$(ProjectName).lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(ProjectDir)x64_Debug\$(ProjectName).lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(ProjectDir)x86_Release\$(ProjectName).lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(ProjectDir)x64_Release\$(ProjectName).lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="LAMEInfo.cpp" />
|
||||
<ClCompile Include="MPEGHeader.cpp" />
|
||||
<ClCompile Include="OFL.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="LAMEInfo.h" />
|
||||
<ClInclude Include="MPEGHeader.h" />
|
||||
<ClInclude Include="OFL.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user