Exemplo n.º 1
0
long MPEG_File::previousFrameOffset(long position)
{
	// TODO: This will miss syncs spanning buffer read boundaries.

	while(int(position - BufferSize()) > int(BufferSize()))
	{
		position -= BufferSize();
		Seek(position);
		SjByteVector buffer = ReadBlock(BufferSize());

		// If the amount of data is smaller than an MPEG header (4 bytes) there's no
		// chance of this being valid.

		if(buffer.size() < 4)
		{
			return -1;
		}

		for(int i = buffer.size() - 2; i >= 0; i--)
		{
			if((unsigned char)(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
			{
				return position + i;
			}
		}
	}

	return -1;
}
Exemplo n.º 2
0
void ID3v2_PopularimeterFrame::parseFields(const SjByteVector& data)
{
	m_email.Empty();
	m_rating255 = 0;
	m_counter = 0;

	int offset, pos = 0, size = (int)data.size();
	offset = data.find(textDelimiter(SJ_LATIN1), pos);
	if( offset < pos ) {
		return;
	}

	m_email = data.mid(pos, offset - pos).toString(SJ_LATIN1);
	pos = offset + 1;

	if(pos < size)
	{
		m_rating255 = (int)(data[pos]);
		pos++;

		if(pos < size)
		{
			m_counter = data.mid(pos, 4).toUInt();
		}
	}
}
Exemplo n.º 3
0
SjByteVector ID3v2_Frame::render() const
{
	SjByteVector fieldData = renderFields();
	m_header->setFrameSize(fieldData.size());
	SjByteVector headerData = m_header->render();

	return headerData + fieldData;
}
Exemplo n.º 4
0
SjByteVector ID3v2_Tag::render()
{
	// We need to render the "tag data" first so that we have to correct size to
	// render in the tag's header.  The "tag data" -- everything that is included
	// in ID3v2::Header::tagSize() -- includes the extended header, frames and
	// padding, but does not include the tag's header or footer.

	SjByteVector tagData;

	// TODO: Render the extended header.

	// Loop through the frames rendering them and adding them to the tagData.

	//for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++)
	for ( ID3v2_FrameList::Node *node = m_frameList.GetFirst(); node; node = node->GetNext() )
	{
		ID3v2_Frame* it = node->GetData();
		if(!it->header()->tagAlterPreservation())
			tagData.append(it->render());
	}

	// Compute the amount of padding, and append that to tagData.

	SjUint paddingSize = 0;
	SjUint originalSize = m_header.tagSize();

	if(tagData.size() < originalSize)
		paddingSize = originalSize - tagData.size();
	else
		paddingSize = 1024;

	tagData.append(SjByteVector(paddingSize, char(0)));

	// Set the tag size.
	m_header.setTagSize(tagData.size());

	// TODO: This should eventually include d->footer->render().
	return m_header.render() + tagData;
}
Exemplo n.º 5
0
long MPEG_File::nextFrameOffset(long position)
{
	// TODO: This will miss syncs spanning buffer read boundaries.

	SjByteVector buffer = ReadBlock(BufferSize());

	while(buffer.size() > 0)
	{
		Seek(position);
		SjByteVector buffer = ReadBlock(BufferSize());

		for(SjUint i = 0; i < buffer.size(); i++)
		{
			if((unsigned char)(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
			{
				return position + i;
			}
		}
		position += BufferSize();
	}

	return -1;
}
Exemplo n.º 6
0
void APE_Tag::parse(const SjByteVector &data)
{
	SjUint pos = 0;

	// 11 bytes is the minimum size for an APE item
	for(SjUint i = 0; i < m_footer.itemCount() && pos <= data.size() - 11; i++)
	{
		APE_Item item;
		item.parse(data.mid(pos));

		setItem(item.key().Upper(), item);

		pos += item.size();
	}
}
Exemplo n.º 7
0
void ID3v2_AttachedPictureFrame::parseFields(const SjByteVector &data)
{
	if(data.size() < 5) {
		wxLogDebug(wxT("A picture frame must contain at least 5 bytes."));
		return;
	}

	int pos = 0, offset;

	// read text encoding
	m_textEncoding = (SjStringType)(data[pos]);
	pos += 1;

	if( header()->version() <= 2 )
	{
		// read image format (3 characters), valid for ID3V2_2_1 or older
		m_mimeType = data.mid(pos, 3).toString(SJ_LATIN1);
		pos += 3;
	}
	else
	{
		// read mime type (null-terminated), valid for newer specs
		offset = data.find(textDelimiter(SJ_LATIN1), pos);

		if(offset < pos)
			return;

		m_mimeType = data.mid(pos, offset - pos).toString(SJ_LATIN1);
		pos = offset + 1;
	}

	// read type
	m_type = (ID3v2_AttachedPictureType)(data[pos]);
	pos += 1;

	// read description
	offset = data.find(textDelimiter(m_textEncoding), pos);

	if(offset < pos)
		return;

	m_description = data.mid(pos, offset - pos).toString(m_textEncoding);
	pos = offset + 1;

	// read image data
	m_data = data.mid(pos);
}
Exemplo n.º 8
0
SjByteVector APE_Tag::render() const
{
	SjByteVector data;
	SjUint itemCount = 0;

	{
		SjHashIterator iterator;
		APE_Item* item;
		wxString key;
		while( (item=(APE_Item*)m_itemListMap.Iterate(iterator, key)) )
		{
			data.append(item->render());
			itemCount++;
		}
	}

	m_footer.setItemCount(itemCount);
	m_footer.setTagSize(data.size()+APE_FOOTER_SIZE);
	m_footer.setHeaderPresent(true);

	return m_footer.renderHeader() + data + m_footer.renderFooter();
}
Exemplo n.º 9
0
void APE_Item::parse(const SjByteVector &data)
{
	// 11 bytes is the minimum size for an APE item
	if(data.size() < 11)
	{
		wxLogDebug(wxT("APE::Item::parse() -- no data in item"));
		return;
	}

	SjUint valueLength  = data.mid(0, 4).toUInt(false);
	SjUint flags        = data.mid(4, 4).toUInt(false);

	m_key = data.mid(8).toString(SJ_UTF8); // data.mid(8) contains more than just the string -- but SjBytevector only converts up to the first null-byte at (***)
	m_binary = data.mid(8 + m_key.size() + 1, valueLength);

	setReadOnly(flags & 1);
	setType((APE_ItemType)((flags >> 1) & 3));

	if(int(m_type) < 2)
	{
		m_stringList = m_binary.splitToStrings((unsigned char)'\0', SJ_UTF8);
	}
}
Exemplo n.º 10
0
void ID3v2_CommentsFrame::parseFields(const SjByteVector &data)
{
	if(data.size() < 5) {
		wxLogDebug(wxT("A comment frame must contain at least 5 bytes."));
		return;
	}

	m_textEncoding = (SjStringType)(data[0]);
	m_language = data.mid(1, 3);

	int byteAlign = (m_textEncoding == SJ_LATIN1 || m_textEncoding == SJ_UTF8) ? 1 : 2;

	//ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
	SjArrayByteVector l = data.mid(4).splitToArray(textDelimiter(m_textEncoding), byteAlign, 2);

	if(l.GetCount() == 2) {
		/*d->description = String(l.front(), d->textEncoding);
		d->text = String(l.back(), d->textEncoding);
		*/
		m_description = l.Item(0).toString(m_textEncoding);
		m_text = l.Item(1).toString(m_textEncoding);
	}
}
Exemplo n.º 11
0
SjByteVector APE_Item::render() const
{
	SjByteVector data;
	SjUint flags = ((m_readOnly) ? 1 : 0) | (m_type << 1);
	SjByteVector value;

	if(isEmpty())
		return data;

	if(m_type != APE_ItemBinary)
	{
		int i, iCount = m_stringList.GetCount();
		if( iCount>0 )
		{
			value.appendString(m_stringList.Item(0), SJ_UTF8);
			for( i = 1; i < iCount; i++ )
			{
				value.append((unsigned char)'\0');
				value.appendString(m_stringList.Item(i), SJ_UTF8);
			}
		}

		// there should be no need to set back m_binary
	}
	else
	{
		value.append(m_binary);
	}

	data.append(SjByteVector::fromUint(value.size(), false));
	data.append(SjByteVector::fromUint(flags, false));
	data.appendString(m_key, SJ_UTF8);
	data.append(SjByteVector((unsigned char)'\0'));
	data.append(value);

	return data;
}
Exemplo n.º 12
0
void APE_Footer::parse(const SjByteVector &data)
{
	if( data.size() < APE_FOOTER_SIZE )
	{
		return;
	}

	// The first eight bytes, data[0..7], are the File Identifier, "APETAGEX".

	// Read the version number
	m_version = data.mid(8, 4).toUInt(false);

	// Read the tag size
	m_tagSize = data.mid(12, 4).toUInt(false);

	// Read the item count
	m_itemCount = data.mid(16, 4).toUInt(false);

	// Read the flags
	unsigned long flags = (unsigned long)data.mid(20, 4).toUInt(false);
	m_headerPresent =   (flags & (1<<31))!=0;
	m_footerPresent = !((flags & (1<<30))!=0);
	m_isHeader      =   (flags & (1<<29))!=0;
}
Exemplo n.º 13
0
long MPEG_File::findID3v2()
{
	// This method is based on the contents of Tagger_File::find(), but because
	// of some subtlteies -- specifically the need to look for the bit pattern of
	// an MPEG sync, it has been modified for use here.

	if( IsValid()
	        && ID3v2_Header::fileIdentifier().size() <= BufferSize() )
	{
		// The position in the file that the current buffer starts at.

		long bufferOffset = 0;
		SjByteVector buffer;

		// These variables are used to keep track of a partial match that happens at
		// the end of a buffer.

		int previousPartialMatch = -1;
		bool previousPartialSynchMatch = false;

		// Save the location of the current read pointer.  We will restore the
		// position using seek() before all returns.

		long originalPosition = Tell();

		// Start the search at the beginning of the file.

		Seek(0);

		// This loop is the crux of the find method.  There are three cases that we
		// want to account for:
		// (1) The previously searched buffer contained a partial match of the search
		// pattern and we want to see if the next one starts with the remainder of
		// that pattern.
		//
		// (2) The search pattern is wholly contained within the current buffer.
		//
		// (3) The current buffer ends with a partial match of the pattern.  We will
		// note this for use in the next itteration, where we will check for the rest
		// of the pattern.

		for(buffer = ReadBlock(BufferSize()); buffer.size() > 0; buffer = ReadBlock(BufferSize()))
		{

			// (1) previous partial match

			if(previousPartialSynchMatch && secondSynchByte(buffer[0]))
			{
				return -1;
			}

			if(previousPartialMatch >= 0 && int(BufferSize()) > previousPartialMatch)
			{
				const int patternOffset = (BufferSize() - previousPartialMatch);
				if(buffer.containsAt(ID3v2_Header::fileIdentifier(), 0, patternOffset))
				{
					Seek(originalPosition);
					return bufferOffset - BufferSize() + previousPartialMatch;
				}
			}

			// (2) pattern contained in current buffer

			long location = buffer.find(ID3v2_Header::fileIdentifier());
			if(location >= 0)
			{
				Seek(originalPosition);
				return bufferOffset + location;
			}

			int firstSynchByte = buffer.find(/*(char)*/((unsigned char)(255)));

			// Here we have to loop because there could be several of the first
			// (11111111) byte, and we want to check all such instances until we find
			// a full match (11111111 111) or hit the end of the buffer.

			while(firstSynchByte >= 0)
			{

				// if this *is not* at the end of the buffer

				if(firstSynchByte < int(buffer.size()) - 1)
				{
					if(secondSynchByte(buffer[firstSynchByte + 1]))
					{
						// We've found the frame synch pattern.
						Seek(originalPosition);
						return -1;
					}
					else
					{

						// We found 11111111 at the end of the current buffer indicating a
						// partial match of the synch pattern.  The find() below should
						// return -1 and break out of the loop.

						previousPartialSynchMatch = true;
					}
				}

				// Check in the rest of the buffer.

				firstSynchByte = buffer.find(/*char*/((unsigned char)(255)), firstSynchByte + 1);
			}

			// (3) partial match

			previousPartialMatch = buffer.endsWithPartialMatch(ID3v2_Header::fileIdentifier());

			bufferOffset += BufferSize();

		} // for()

		// Since we hit the end of the file, reset the status before continuing.

		Clear();

		Seek(originalPosition);
	}

	return -1;
}
Exemplo n.º 14
0
void ID3v2_Tag::parse(const SjByteVector &data)
{
	SjUint frameDataPosition = 0;
	SjUint frameDataLength = data.size();

	// check for extended header

	if(m_header.extendedHeader()) {
		if(!m_extendedHeader)
			m_extendedHeader = new ID3v2_ExtendedHeader;
		m_extendedHeader->setData(data);
		if(m_extendedHeader->size() <= data.size()) {
			frameDataPosition += m_extendedHeader->size();
			frameDataLength -= m_extendedHeader->size();
		}
	}

	// check for footer -- we don't actually need to parse it, as it *must*
	// contain the same data as the header, but we do need to account for its
	// size.

	if(m_header.footerPresent() && ID3v2_Footer::size() <= frameDataLength)
		frameDataLength -= ID3v2_Footer::size();

	// parse frames

	// Make sure that there is at least enough room in the remaining frame data for
	// a frame header.

	while(frameDataPosition < frameDataLength - ID3v2_Frame::headerSize(m_header.majorVersion())) {

		// If the next data is position is 0, assume that we've hit the padding
		// portion of the frame data.

		if(data.at(frameDataPosition) == 0) {
			if(m_header.footerPresent())
				wxLogDebug(wxT("Padding *and* a footer found.  This is not allowed by the spec."));

			m_paddingSize = frameDataLength - frameDataPosition;
			return;
		}

		ID3v2_Frame *frame = m_factory->createFrame(data.mid(frameDataPosition),
		                     m_header.majorVersion());

		if(!frame)
			return;

		// get the next frame position

		frameDataPosition += frame->size() + ID3v2_Frame::headerSize(m_header.majorVersion());

		// add the frame if it has a size of at least 1 byte (smaller frames are not allowed
		// by the specification, but they're returned from createFrame() to allow seeking to the
		// next frame).
		// modification by me

		if(frame->size() <= 0) {
			delete frame;
		}
		else {
			addFrame(frame);
		}
	}
}
Exemplo n.º 15
0
void ID3v2_FrameHeader::setData(const SjByteVector &data, SjUint version)
{
	// this was ID3v2_FrameHeaderPrivate
	m_isOkay = FALSE;
	m_frameSize = 0;
	m_version = 4;
	m_tagAlterPreservation = false;
	m_fileAlterPreservation = false;
	m_readOnly = false;
	m_groupingIdentity = false;
	m_compression = false;
	m_encryption = false;
	m_unsyncronisation = false;
	m_dataLengthIndicator = false;
	// /ID3v2_FrameHeaderPrivate


	m_version = version;

	switch(version) {
		case 0:
		case 1:
		case 2:
		{
			// ID3v2.2

			if(data.size() < 3) {
				wxLogDebug(wxT("You must at least specify a frame ID."));
				return;
			}

			// Set the frame ID -- the first three bytes

			m_frameID = data.mid(0, 3);

			// If the full header information was not passed in, do not continue to the
			// steps to parse the frame size and flags.

			if(data.size() < 6) {
				return;
			}

			m_frameSize = data.mid(3, 3).toUInt();

			break;
		}
		case 3:
		{
			// ID3v2.3 - see http://www.id3.org/id3v2.3.0.html#sec3.3

			if(data.size() < 4) {
				wxLogDebug(wxT("You must at least specify a frame ID."));
				return;
			}

			// Set the frame ID -- the first four bytes

			m_frameID = data.mid(0, 4);

			// If the full header information was not passed in, do not continue to the
			// steps to parse the frame size and flags.

			if(data.size() < 10) {
				return;
			}

			// Set the size -- the frame size is the four bytes starting at byte four in
			// the frame header (structure 4)

			m_frameSize = data.mid(4, 4).toUInt();

			{	// read the first byte of flags
				/*std::bitset<8> flags(data[8]);
				d->tagAlterPreservation  = flags[7]; // (structure 3.3.1.a)
				d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b)
				d->readOnly              = flags[5]; // (structure 3.3.1.c)*/
				unsigned char flags = data[8];
				m_tagAlterPreservation    = (flags & (1<<7)) != 0;
				m_fileAlterPreservation   = (flags & (1<<6)) != 0;
				m_readOnly                = (flags & (1<<5)) != 0;
			}

			{	// read the second byte of flags
				/*std::bitset<8> flags(data[9]);
				d->compression         = flags[7]; // (structure 3.3.1.i)
				d->encryption          = flags[6]; // (structure 3.3.1.j)
				d->groupingIdentity    = flags[5]; // (structure 3.3.1.k)*/
				unsigned char flags = data[9];
				m_compression     = (flags & (1<<7)) != 0;
				m_encryption          = (flags & (1<<6)) != 0;
				m_groupingIdentity    = (flags & (1<<5)) != 0;
			}
			break;
		}
		case 4:
		default:
		{
			// ID3v2.4

			if(data.size() < 4) {
				wxLogDebug(wxT("You must at least specify a frame ID."));
				return;
			}

			// Set the frame ID -- the first four bytes

			m_frameID = data.mid(0, 4);

			// If the full header information was not passed in, do not continue to the
			// steps to parse the frame size and flags.

			if(data.size() < 10) {
				return;
			}

			// Set the size -- the frame size is the four bytes starting at byte four in
			// the frame header (structure 4)

			m_frameSize = ID3v2_SynchDataToUInt(data.mid(4, 4));

			{	// read the first byte of flags
				/*std::bitset<8> flags(data[8]);
				d->tagAlterPreservation  = flags[6]; // (structure 4.1.1.a)
				d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b)
				d->readOnly              = flags[4]; // (structure 4.1.1.c)*/
				unsigned char flags = data[8];
				m_tagAlterPreservation    = (flags & (1<<6)) != 0;
				m_fileAlterPreservation   = (flags & (1<<5)) != 0;
				m_readOnly                = (flags & (1<<4)) != 0;
			}

			{	// read the second byte of flags
				/*std::bitset<8> flags(data[9]);
				d->groupingIdentity    = flags[6]; // (structure 4.1.2.h)
				d->compression         = flags[3]; // (structure 4.1.2.k)
				d->encryption          = flags[2]; // (structure 4.1.2.m)
				d->unsyncronisation    = flags[1]; // (structure 4.1.2.n)
				d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)*/
				unsigned char flags = data[9];
				m_groupingIdentity    = (flags & (1<<6)) != 0;
				m_compression         = (flags & (1<<3)) != 0;
				m_encryption          = (flags & (1<<2)) != 0;
				m_unsyncronisation    = (flags & (1<<1)) != 0;
				m_dataLengthIndicator = (flags & (1<<0)) != 0;
			}
			break;
		}
	}

	m_isOkay = TRUE;
}
Exemplo n.º 16
0
void MPEG_Header::parse(const SjByteVector &data)
{
	// see http://www.mp3-tech.org/programmer/frame_header.html

	m_isValid           = false;
	m_version           = MPEG_Version1;
	m_layer             = 0;
	m_protectionEnabled = false;
	m_sampleRate        = 0;
	m_isPadded          = false;
	m_channelMode       = MPEG_Stereo;
	m_isCopyrighted     = false;
	m_isOriginal        = false;
	m_emphasis          = 0;
	m_frameLength       = 0;

	// check for the size and for the first synch byte

	if(data.size() < 4 || (unsigned char)(data[0]) != 0xff)
	{
		wxLogDebug(wxT("MPEG::Header::parse() -- First byte did not mactch MPEG synch."));
		return;
	}

	unsigned long flags = data.toUInt();

	// Check for the second byte's part of the MPEG synch

	if( !(flags&(1<<23)) || !(flags&(1<<22)) || !(flags&(1<<21)) )
	{
		wxLogDebug(wxT("MPEG::Header::parse() -- Second byte did not mactch MPEG synch."));
		return;
	}

	// Set the MPEG version

	if( !(flags&(1<<20)) && !(flags&(1<<19)) )
	{
		m_version = MPEG_Version2_5;
	}
	else if( (flags&(1<<20)) && !(flags&(1<<19)) )
	{
		m_version = MPEG_Version2;
	}
	else if( (flags&(1<<20)) && (flags&(1<<19)) )
	{
		m_version = MPEG_Version1;
	}

	// Set the MPEG layer

	if( !(flags&(1<<18)) && (flags&(1<<17)) )
	{
		m_layer = 3;
	}
	else if( (flags&(1<<18)) && !(flags&(1<<17)) )
	{
		m_layer = 2;
	}
	else if( (flags&(1<<18)) && (flags&(1<<17)) )
	{
		m_layer = 1;
	}


	// set protection flags

	m_protectionEnabled = !(flags&(1<<16));

	// Set the bitrate

	static const int bitrates[2][3][16] =
	{
		{	// Version 1
			{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1
			{ 0, 32, 48, 56, 64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2
			{ 0, 32, 40, 48, 56,  64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 0 }  // layer 3
		},
		{	// Version 2 or 2.5
			{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1
			{ 0, 8,  16, 24, 32, 40, 48, 56,  64,  80,  96,  112, 128, 144, 160, 0 }, // layer 2
			{ 0, 8,  16, 24, 32, 40, 48, 56,  64,  80,  96,  112, 128, 144, 160, 0 }  // layer 3
		}
	};

	const int versionIndex = m_version == MPEG_Version1 ? 0 : 1;
	const int layerIndex = m_layer > 0 ? m_layer - 1 : 0;

	// The bitrate index is encoded as the first 4 bits of the 3rd byte,
	// i.e. 1111xxxx

	int i = (unsigned char)(data[2]) >> 4;

	m_bitrate = bitrates[versionIndex][layerIndex][i];

	// Set the sample rate

	static const int sampleRates[3][4] =
	{
		{ 44100, 48000, 32000, 0 }, // Version 1
		{ 22050, 24000, 16000, 0 }, // Version 2
		{ 11025, 12000, 8000,  0 }  // Version 2.5
	};

	// The sample rate index is encoded as two bits in the 3nd byte, i.e. xxxx11xx

	i = (unsigned char)(data[2]) >> 2 & 0x03;

	m_sampleRate = sampleRates[m_version][i];

	if(m_sampleRate == 0)
	{
		wxLogDebug(wxT("MPEG::Header::parse() -- Invalid sample rate."));
		return;
	}

	// The channel mode is encoded as a 2 bit value at the end of the 3nd byte,
	// i.e. xxxxxx11 - stimmt nicht! s. http://www.mp3-tech.org/programmer/frame_header.html

	m_channelMode = (MPEG_HeaderChannelMode)((flags&0xC0) >> 6);

	// TODO: Add mode extension for completeness

	m_isCopyrighted = (flags&(1<<3)) != 0;
	m_isOriginal = (flags&(1<<2)) != 0;
	m_emphasis = (unsigned char)flags&3;

	// Calculate the frame length

	if( m_layer == 1 )
	{
		m_frameLength = 24000 * 2 * m_bitrate / m_sampleRate + int(m_isPadded);
	}
	else
	{
		m_frameLength = 72000 * m_bitrate / m_sampleRate + int(m_isPadded);
	}

	// Now that we're done parsing, set this to be a valid header

	m_isValid = true;
}