aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/nsmp3
diff options
context:
space:
mode:
Diffstat (limited to 'Src/replicant/nsmp3')
-rw-r--r--Src/replicant/nsmp3/LAMEInfo.cpp293
-rw-r--r--Src/replicant/nsmp3/LAMEInfo.h66
-rw-r--r--Src/replicant/nsmp3/MPEGHeader.cpp170
-rw-r--r--Src/replicant/nsmp3/MPEGHeader.h47
-rw-r--r--Src/replicant/nsmp3/OFL.cpp165
-rw-r--r--Src/replicant/nsmp3/OFL.h20
-rw-r--r--Src/replicant/nsmp3/nsmp3.sln31
-rw-r--r--Src/replicant/nsmp3/nsmp3.vcxproj159
8 files changed, 951 insertions, 0 deletions
diff --git a/Src/replicant/nsmp3/LAMEInfo.cpp b/Src/replicant/nsmp3/LAMEInfo.cpp
new file mode 100644
index 00000000..0f791532
--- /dev/null
+++ b/Src/replicant/nsmp3/LAMEInfo.cpp
@@ -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;
+}
diff --git a/Src/replicant/nsmp3/LAMEInfo.h b/Src/replicant/nsmp3/LAMEInfo.h
new file mode 100644
index 00000000..4ed8d666
--- /dev/null
+++ b/Src/replicant/nsmp3/LAMEInfo.h
@@ -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);
+
diff --git a/Src/replicant/nsmp3/MPEGHeader.cpp b/Src/replicant/nsmp3/MPEGHeader.cpp
new file mode 100644
index 00000000..1971577e
--- /dev/null
+++ b/Src/replicant/nsmp3/MPEGHeader.cpp
@@ -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;
+ }
+}
diff --git a/Src/replicant/nsmp3/MPEGHeader.h b/Src/replicant/nsmp3/MPEGHeader.h
new file mode 100644
index 00000000..d69271e1
--- /dev/null
+++ b/Src/replicant/nsmp3/MPEGHeader.h
@@ -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;
+};
diff --git a/Src/replicant/nsmp3/OFL.cpp b/Src/replicant/nsmp3/OFL.cpp
new file mode 100644
index 00000000..247992aa
--- /dev/null
+++ b/Src/replicant/nsmp3/OFL.cpp
@@ -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;
+}
diff --git a/Src/replicant/nsmp3/OFL.h b/Src/replicant/nsmp3/OFL.h
new file mode 100644
index 00000000..4d3e08c2
--- /dev/null
+++ b/Src/replicant/nsmp3/OFL.h
@@ -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;
+};
+
diff --git a/Src/replicant/nsmp3/nsmp3.sln b/Src/replicant/nsmp3/nsmp3.sln
new file mode 100644
index 00000000..ab85989c
--- /dev/null
+++ b/Src/replicant/nsmp3/nsmp3.sln
@@ -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
diff --git a/Src/replicant/nsmp3/nsmp3.vcxproj b/Src/replicant/nsmp3/nsmp3.vcxproj
new file mode 100644
index 00000000..246e2114
--- /dev/null
+++ b/Src/replicant/nsmp3/nsmp3.vcxproj
@@ -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> \ No newline at end of file