diff options
Diffstat (limited to 'Src/replicant/nsmp3/LAMEInfo.cpp')
-rw-r--r-- | Src/replicant/nsmp3/LAMEInfo.cpp | 293 |
1 files changed, 293 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; +} |