diff options
Diffstat (limited to 'Src/replicant/nsmp3')
-rw-r--r-- | Src/replicant/nsmp3/LAMEInfo.cpp | 293 | ||||
-rw-r--r-- | Src/replicant/nsmp3/LAMEInfo.h | 66 | ||||
-rw-r--r-- | Src/replicant/nsmp3/MPEGHeader.cpp | 170 | ||||
-rw-r--r-- | Src/replicant/nsmp3/MPEGHeader.h | 47 | ||||
-rw-r--r-- | Src/replicant/nsmp3/OFL.cpp | 165 | ||||
-rw-r--r-- | Src/replicant/nsmp3/OFL.h | 20 | ||||
-rw-r--r-- | Src/replicant/nsmp3/nsmp3.sln | 31 | ||||
-rw-r--r-- | Src/replicant/nsmp3/nsmp3.vcxproj | 159 |
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 |