Clementine-audio-player-Mac.../3rdparty/taglib/ogg/flac/oggflacfile.cpp

297 lines
7.4 KiB
C++

/***************************************************************************
copyright : (C) 2004-2005 by Allan Sandfeld Jensen
email : kde@carewolf.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <xiphcomment.h>
#include "oggflacfile.h"
using namespace TagLib;
using TagLib::FLAC::Properties;
class Ogg::FLAC::File::FilePrivate
{
public:
FilePrivate() :
comment(0),
properties(0),
streamStart(0),
streamLength(0),
scanned(false),
hasXiphComment(false),
commentPacket(0) {}
~FilePrivate()
{
delete comment;
delete properties;
}
Ogg::XiphComment *comment;
Properties *properties;
ByteVector streamInfoData;
ByteVector xiphCommentData;
long streamStart;
long streamLength;
bool scanned;
bool hasXiphComment;
int commentPacket;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Ogg::FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
Ogg::FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
Ogg::FLAC::File::~File()
{
delete d;
}
Ogg::XiphComment *Ogg::FLAC::File::tag() const
{
return d->comment;
}
PropertyMap Ogg::FLAC::File::properties() const
{
return d->comment->properties();
}
PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Properties *Ogg::FLAC::File::audioProperties() const
{
return d->properties;
}
bool Ogg::FLAC::File::save()
{
d->xiphCommentData = d->comment->render(false);
// Create FLAC metadata-block:
// Put the size in the first 32 bit (I assume no more than 24 bit are used)
ByteVector v = ByteVector::fromUInt(d->xiphCommentData.size());
// Set the type of the metadata-block to be a Xiph / Vorbis comment
v[0] = 4;
// Append the comment-data after the 32 bit header
v.append(d->xiphCommentData);
// Save the packet at the old spot
// FIXME: Use padding if size is increasing
setPacket(d->commentPacket, v);
return Ogg::File::save();
}
bool Ogg::FLAC::File::hasXiphComment() const
{
return d->hasXiphComment;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
// Sanity: Check if we really have an Ogg/FLAC file
/*
ByteVector oggHeader = packet(0);
if (oggHeader.mid(28,4) != "fLaC") {
debug("Ogg::FLAC::File::read() -- Not an Ogg/FLAC file");
setValid(false);
return;
}*/
// Look for FLAC metadata, including vorbis comments
scan();
if (!d->scanned) {
setValid(false);
return;
}
if(d->hasXiphComment)
d->comment = new Ogg::XiphComment(xiphCommentData());
else
d->comment = new Ogg::XiphComment;
if(readProperties)
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
}
ByteVector Ogg::FLAC::File::streamInfoData()
{
scan();
return d->streamInfoData;
}
ByteVector Ogg::FLAC::File::xiphCommentData()
{
scan();
return d->xiphCommentData;
}
long Ogg::FLAC::File::streamLength()
{
scan();
return d->streamLength;
}
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;
}