Beispiel #1
0
void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
  ByteVector formatData;
  uint streamLength = 0;
  for(uint i = 0; i < chunkCount(); i++) {
    String name = chunkName(i);
    if(name == "ID3 " || name == "id3 ") {
      d->tagChunkID = chunkName(i);
      d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i)));
    }
    else if(name == "fmt " && readProperties)
      formatData = chunkData(i);
    else if(name == "data" && readProperties)
      streamLength = chunkDataSize(i);
    else if(name == "LIST") {
      ByteVector data = chunkData(i);
      ByteVector type = data.mid(0, 4);

      if(type == "INFO")
        d->tag.set(InfoIndex, new RIFF::Info::Tag(data));
    }
  }

  if (!d->tag[ID3v2Index])
    d->tag.set(ID3v2Index, new ID3v2::Tag);

  if (!d->tag[InfoIndex])
    d->tag.set(InfoIndex, new RIFF::Info::Tag);

  if(!formatData.isEmpty())
    d->properties = new Properties(formatData, streamLength, propertiesStyle);
}
Beispiel #2
0
String::String(const ByteVector &v, Type t)
{
  d = new StringPrivate;

  if(v.isEmpty())
    return;

  if(t == Latin1 || t == UTF8) {

    int length = 0;
    d->data.resize(v.size());
    wstring::iterator targetIt = d->data.begin();
    for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
      *targetIt = uchar(*it);
      ++targetIt;
      ++length;
    }
    d->data.resize(length);
  }
  else  {
    d->data.resize(v.size() / 2);
    wstring::iterator targetIt = d->data.begin();

    for(ByteVector::ConstIterator it = v.begin();
        it != v.end() && it + 1 != v.end() && combine(*it, *(it + 1));
        it += 2)
    {
      *targetIt = combine(*it, *(it + 1));
      ++targetIt;
    }
  }
  prepare(t);
}
Beispiel #3
0
long MPEG::File::findID3v2()
{
  if(!isValid())
    return -1;

  // An ID3v2 tag or MPEG frame is most likely be at the beginning of the file.

  const ByteVector headerID = ID3v2::Header::fileIdentifier();

  seek(0);
  if(readBlock(headerID.size()) == headerID)
    return 0;

  const Header firstHeader(this, 0, true);
  if(firstHeader.isValid())
    return -1;

  // Look for an ID3v2 tag until reaching the first valid MPEG frame.

  ByteVector frameSyncBytes(2, '\0');
  ByteVector tagHeaderBytes(3, '\0');
  long position = 0;

  while(true) {
    seek(position);
    const ByteVector buffer = readBlock(bufferSize());
    if(buffer.isEmpty())
      return -1;

    for(unsigned int i = 0; i < buffer.size(); ++i) {
      frameSyncBytes[0] = frameSyncBytes[1];
      frameSyncBytes[1] = buffer[i];
      if(isFrameSync(frameSyncBytes)) {
        const Header header(this, position + i - 1, true);
        if(header.isValid())
          return -1;
      }

      tagHeaderBytes[0] = tagHeaderBytes[1];
      tagHeaderBytes[1] = tagHeaderBytes[2];
      tagHeaderBytes[2] = buffer[i];
      if(tagHeaderBytes == headerID)
        return position + i - 2;
    }

    position += bufferSize();
  }
}
Beispiel #4
0
void ID3v2::Tag::setAlbumArt(const ByteVector &v, ID3v2::AttachedPictureFrame::Type arttype, String &mimetype)
{
	if (v.isEmpty()) {
		removeFrames("APIC");
		return;
	} else {
		if (!d->frameListMap["APIC"].isEmpty()) {
			removeFrames("APIC");
		} else {
			// do nothing
		}
		AttachedPictureFrame *f = new AttachedPictureFrame("APIC");
		f->setMimeType(mimetype);
		f->setType(arttype);
		f->setPicture(v);
		addFrame(f);
	}
	return;
}
Beispiel #5
0
void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
  ByteVector formatData;
  uint streamLength = 0;
  for(uint i = 0; i < chunkCount(); i++) {
    if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
      d->tagChunkID = chunkName(i);
      d->tag = new ID3v2::Tag(this, chunkOffset(i));
    }
    else if(chunkName(i) == "fmt " && readProperties)
      formatData = chunkData(i);
    else if(chunkName(i) == "data" && readProperties)
      streamLength = chunkDataSize(i);
  }

  if(!formatData.isEmpty())
    d->properties = new Properties(formatData, streamLength, propertiesStyle);

  if(!d->tag)
    d->tag = new ID3v2::Tag;
}
Beispiel #6
0
long MPEG::File::nextFrameOffset(long position)
{
  ByteVector frameSyncBytes(2, '\0');

  while(true) {
    seek(position);
    const ByteVector buffer = readBlock(bufferSize());
    if(buffer.isEmpty())
      return -1;

    for(unsigned int i = 0; i < buffer.size(); ++i) {
      frameSyncBytes[0] = frameSyncBytes[1];
      frameSyncBytes[1] = buffer[i];
      if(isFrameSync(frameSyncBytes)) {
        const Header header(this, position + i - 1, true);
        if(header.isValid())
          return position + i - 1;
      }
    }

    position += bufferSize();
  }
}
Beispiel #7
0
ByteVector RIFF::Info::Tag::render() const
{
  ByteVector data("INFO");

  FieldListMap::ConstIterator it = d->fieldListMap.begin();
  for(; it != d->fieldListMap.end(); ++it) {
    ByteVector text = TagPrivate::stringHandler->render(it->second);
    if(text.isEmpty())
      continue;

    data.append(it->first);
    data.append(ByteVector::fromUInt(text.size() + 1, false));
    data.append(text);
    
    do {
      data.append('\0');
    } while(data.size() & 1);
  }

  if(data.size() == 4)
    return ByteVector();
  else
    return data;
}
Beispiel #8
0
void File::insert(const ByteVector &data, ulong start, ulong replace)
{
  if(!d->file)
    return;

  if(data.size() == replace) {
    seek(start);
    writeBlock(data);
    return;
  }
  else if(data.size() < replace) {
      seek(start);
      writeBlock(data);
      removeBlock(start + data.size(), replace - data.size());
      return;
  }

  // Woohoo!  Faster (about 20%) than id3lib at last.  I had to get hardcore
  // and avoid TagLib's high level API for rendering just copying parts of
  // the file that don't contain tag data.
  //
  // Now I'll explain the steps in this ugliness:

  // First, make sure that we're working with a buffer that is longer than
  // the *differnce* in the tag sizes.  We want to avoid overwriting parts
  // that aren't yet in memory, so this is necessary.

  ulong bufferLength = bufferSize();

  while(data.size() - replace > bufferLength)
    bufferLength += bufferSize();

  // Set where to start the reading and writing.

  long readPosition = start + replace;
  long writePosition = start;

  ByteVector buffer;
  ByteVector aboutToOverwrite(static_cast<uint>(bufferLength));

  // This is basically a special case of the loop below.  Here we're just
  // doing the same steps as below, but since we aren't using the same buffer
  // size -- instead we're using the tag size -- this has to be handled as a
  // special case.  We're also using File::writeBlock() just for the tag.
  // That's a bit slower than using char *'s so, we're only doing it here.

  seek(readPosition);
  int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
  readPosition += bufferLength;

  seek(writePosition);
  writeBlock(data);
  writePosition += data.size();

  buffer = aboutToOverwrite;

  // In case we've already reached the end of file...

  buffer.resize(bytesRead);

  // Ok, here's the main loop.  We want to loop until the read fails, which
  // means that we hit the end of the file.

  while(!buffer.isEmpty()) {

    // Seek to the current read position and read the data that we're about
    // to overwrite.  Appropriately increment the readPosition.

    seek(readPosition);
    bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
    aboutToOverwrite.resize(bytesRead);
    readPosition += bufferLength;

    // Check to see if we just read the last block.  We need to call clear()
    // if we did so that the last write succeeds.

    if(ulong(bytesRead) < bufferLength)
      clear();

    // Seek to the write position and write our buffer.  Increment the
    // writePosition.

    seek(writePosition);
    fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);
    writePosition += buffer.size();

    // Make the current buffer the data that we read in the beginning.

    buffer = aboutToOverwrite;

    // Again, we need this for the last write.  We don't want to write garbage
    // at the end of our file, so we need to set the buffer size to the amount
    // that we actually read.

    bufferLength = bytesRead;
  }
}
Beispiel #9
0
void Ogg::FLAC::File::scan()
{
  // Scan the metadata pages

  if(d->scanned)
    return;

  if(!isValid())
    return;

  int ipacket = 0;
  long overhead = 0;

  ByteVector metadataHeader = packet(ipacket);
  if(metadataHeader.isEmpty())
    return;

  if(!metadataHeader.startsWith("fLaC"))  {
    // FLAC 1.1.2+
    if(metadataHeader.mid(1, 4) != "FLAC")
      return;

    if(metadataHeader[5] != 1)
      return; // not version 1

    metadataHeader = metadataHeader.mid(13);
  }
  else {
    // FLAC 1.1.0 & 1.1.1
    metadataHeader = packet(++ipacket);
  }

  ByteVector header = metadataHeader.mid(0, 4);
  if(header.size() != 4) {
    debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header");
    return;
  }

  // Header format (from spec):
  // <1> Last-metadata-block flag
  // <7> BLOCK_TYPE
  //    0 : STREAMINFO
  //    1 : PADDING
  //    ..
  //    4 : VORBIS_COMMENT
  //    ..
  // <24> Length of metadata to follow

  char blockType = header[0] & 0x7f;
  bool lastBlock = (header[0] & 0x80) != 0;
  uint length = header.toUInt(1, 3, true);
  overhead += length;

  // Sanity: First block should be the stream_info metadata

  if(blockType != 0) {
    debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC stream");
    return;
  }

  d->streamInfoData = metadataHeader.mid(4, length);

  // Search through the remaining metadata

  while(!lastBlock) {
    metadataHeader = packet(++ipacket);
    header = metadataHeader.mid(0, 4);
    if(header.size() != 4) {
      debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header");
      return;
    }

    blockType = header[0] & 0x7f;
    lastBlock = (header[0] & 0x80) != 0;
    length = header.toUInt(1, 3, true);
    overhead += length;

    if(blockType == 1) {
      // debug("Ogg::FLAC::File::scan() -- Padding found");
    }
    else if(blockType == 4) {
      // debug("Ogg::FLAC::File::scan() -- Vorbis-comments found");
      d->xiphCommentData = metadataHeader.mid(4, length);
      d->hasXiphComment = true;
      d->commentPacket = ipacket;
    }
    else if(blockType > 5) {
      debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
    }
  }

  // End of metadata, now comes the datastream
  d->streamStart = overhead;
  d->streamLength = File::length() - d->streamStart;

  d->scanned = true;
}
void RIFF::WAV::Properties::read(File *file)
{
  ByteVector data;
  uint streamLength = 0;
  uint totalSamples = 0;

  for(uint i = 0; i < file->chunkCount(); ++i) {
    const ByteVector name = file->chunkName(i);
    if(name == "fmt ") {
      if(data.isEmpty())
        data = file->chunkData(i);
      else
        debug("RIFF::WAV::Properties::read() - Duplicate 'fmt ' chunk found.");
    }
    else if(name == "data") {
      if(streamLength == 0)
        streamLength = file->chunkDataSize(i) + file->chunkPadding(i);
      else
        debug("RIFF::WAV::Properties::read() - Duplicate 'data' chunk found.");
    }
    else if(name == "fact") {
      if(totalSamples == 0)
        totalSamples = file->chunkData(i).toUInt(0, false);
      else
        debug("RIFF::WAV::Properties::read() - Duplicate 'fact' chunk found.");
    }
  }

  if(data.size() < 16) {
    debug("RIFF::WAV::Properties::read() - 'fmt ' chunk not found or too short.");
    return;
  }

  if(streamLength == 0) {
    debug("RIFF::WAV::Properties::read() - 'data' chunk not found.");
    return;
  }

  d->format = data.toShort(0, false);
  if(d->format != FORMAT_PCM && totalSamples == 0) {
    debug("RIFF::WAV::Properties::read() - Non-PCM format, but 'fact' chunk not found.");
    return;
  }

  d->channels      = data.toShort(2, false);
  d->sampleRate    = data.toUInt(4, false);
  d->bitsPerSample = data.toShort(14, false);

  if(totalSamples > 0)
    d->sampleFrames = totalSamples;
  else if(d->channels > 0 && d->bitsPerSample > 0)
    d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8));

  if(d->sampleFrames > 0 && d->sampleRate > 0) {
    const double length = d->sampleFrames * 1000.0 / d->sampleRate;
    d->length  = static_cast<int>(length + 0.5);
    d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
  }
  else {
    const uint byteRate = data.toUInt(8, false);
    if(byteRate > 0) {
      d->length  = static_cast<int>(streamLength * 1000.0 / byteRate + 0.5);
      d->bitrate = static_cast<int>(byteRate * 8.0 / 1000.0 + 0.5);
    }
  }
}
Beispiel #11
0
long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
  if(!d->stream || pattern.size() > bufferSize())
      return -1;

  // The position in the file that the current buffer starts at.

  ByteVector buffer;

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

  /*
  int previousPartialMatch = -1;
  int beforePreviousPartialMatch = -1;
  */

  // 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 offset.

  if(fromOffset == 0)
    fromOffset = length();

  long bufferLength = bufferSize();
  long bufferOffset = fromOffset + pattern.size();

  // See the notes in find() for an explanation of this algorithm.

  while(true) {

    if(bufferOffset > bufferLength) {
      bufferOffset -= bufferLength;
    }
    else {
      bufferLength = bufferOffset;
      bufferOffset = 0;
    }
    seek(bufferOffset);

    buffer = readBlock(bufferLength);
    if(buffer.isEmpty())
      break;

    // TODO: (1) previous partial match

    // (2) pattern contained in current buffer

    const long location = buffer.rfind(pattern);
    if(location >= 0) {
      seek(originalPosition);
      return bufferOffset + location;
    }

    if(!before.isEmpty() && buffer.find(before) >= 0) {
      seek(originalPosition);
      return -1;
    }

    // TODO: (3) partial match
  }

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

  clear();

  seek(originalPosition);

  return -1;
}
Beispiel #12
0
long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
  if(!d->stream || pattern.size() > bufferSize())
      return -1;

  // The position in the file that the current buffer starts at.

  long bufferOffset = fromOffset;
  ByteVector buffer;

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

  int previousPartialMatch = -1;
  int beforePreviousPartialMatch = -1;

  // 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 offset.

  seek(fromOffset);

  // 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 iteration, where we will check for the rest
  // of the pattern.
  //
  // All three of these are done in two steps.  First we check for the pattern
  // and do things appropriately if a match (or partial match) is found.  We
  // then check for "before".  The order is important because it gives priority
  // to "real" matches.

  for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {

    // (1) previous partial match

    if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
      const int patternOffset = (bufferSize() - previousPartialMatch);
      if(buffer.containsAt(pattern, 0, patternOffset)) {
        seek(originalPosition);
        return bufferOffset - bufferSize() + previousPartialMatch;
      }
    }

    if(!before.isEmpty() && beforePreviousPartialMatch >= 0 && int(bufferSize()) > beforePreviousPartialMatch) {
      const int beforeOffset = (bufferSize() - beforePreviousPartialMatch);
      if(buffer.containsAt(before, 0, beforeOffset)) {
        seek(originalPosition);
        return -1;
      }
    }

    // (2) pattern contained in current buffer

    long location = buffer.find(pattern);
    if(location >= 0) {
      seek(originalPosition);
      return bufferOffset + location;
    }

    if(!before.isEmpty() && buffer.find(before) >= 0) {
      seek(originalPosition);
      return -1;
    }

    // (3) partial match

    previousPartialMatch = buffer.endsWithPartialMatch(pattern);

    if(!before.isEmpty())
      beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);

    bufferOffset += bufferSize();
  }

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

  clear();

  seek(originalPosition);

  return -1;
}