123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- #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;
- }
|