Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / ogg / flac / oggflacfile.cpp
1 /***************************************************************************
2     copyright            : (C) 2004-2005 by Allan Sandfeld Jensen
3     email                : kde@carewolf.org
4  ***************************************************************************/
5
6 /***************************************************************************
7  *   This library is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU Lesser General Public License version   *
9  *   2.1 as published by the Free Software Foundation.                     *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful, but   *
12  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the Free Software   *
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19  *   USA                                                                   *
20  *                                                                         *
21  *   Alternatively, this file is available under the Mozilla Public        *
22  *   License Version 1.1.  You may obtain a copy of the License at         *
23  *   http://www.mozilla.org/MPL/                                           *
24  ***************************************************************************/
25
26 #include <tbytevector.h>
27 #include <tstring.h>
28 #include <tdebug.h>
29
30 #include <xiphcomment.h>
31 #include "oggflacfile.h"
32
33 using namespace TagLib;
34 using TagLib::FLAC::Properties;
35
36 class Ogg::FLAC::File::FilePrivate
37 {
38 public:
39   FilePrivate() :
40     comment(0),
41     properties(0),
42     streamStart(0),
43     streamLength(0),
44     scanned(false),
45     hasXiphComment(false),
46     commentPacket(0) {}
47
48   ~FilePrivate()
49   {
50     delete comment;
51     delete properties;
52   }
53
54   Ogg::XiphComment *comment;
55
56   Properties *properties;
57   ByteVector streamInfoData;
58   ByteVector xiphCommentData;
59   long streamStart;
60   long streamLength;
61   bool scanned;
62
63   bool hasXiphComment;
64   int commentPacket;
65 };
66
67 ////////////////////////////////////////////////////////////////////////////////
68 // public members
69 ////////////////////////////////////////////////////////////////////////////////
70
71 Ogg::FLAC::File::File(FileName file, bool readProperties,
72                       Properties::ReadStyle propertiesStyle) : Ogg::File(file)
73 {
74   d = new FilePrivate;
75   read(readProperties, propertiesStyle);
76 }
77
78 Ogg::FLAC::File::~File()
79 {
80   delete d;
81 }
82
83 Ogg::XiphComment *Ogg::FLAC::File::tag() const
84 {
85   return d->comment;
86 }
87
88 Properties *Ogg::FLAC::File::audioProperties() const
89 {
90   return d->properties;
91 }
92
93
94 bool Ogg::FLAC::File::save()
95 {
96   d->xiphCommentData = d->comment->render(false);
97
98   // Create FLAC metadata-block:
99
100   // Put the size in the first 32 bit (I assume no more than 24 bit are used)
101
102   ByteVector v = ByteVector::fromUInt(d->xiphCommentData.size());
103
104   // Set the type of the metadata-block to be a Xiph / Vorbis comment
105
106   v[0] = 4;
107
108   // Append the comment-data after the 32 bit header
109
110   v.append(d->xiphCommentData);
111
112   // Save the packet at the old spot
113   // FIXME: Use padding if size is increasing
114
115   setPacket(d->commentPacket, v);
116
117   return Ogg::File::save();
118 }
119
120 ////////////////////////////////////////////////////////////////////////////////
121 // private members
122 ////////////////////////////////////////////////////////////////////////////////
123
124 void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
125 {
126   // Sanity: Check if we really have an Ogg/FLAC file
127
128 /*
129   ByteVector oggHeader = packet(0);
130
131   if (oggHeader.mid(28,4) != "fLaC") {
132     debug("Ogg::FLAC::File::read() -- Not an Ogg/FLAC file");
133     setValid(false);
134     return;
135   }*/
136
137   // Look for FLAC metadata, including vorbis comments
138
139   scan();
140
141   if (!d->scanned) {
142     setValid(false);
143     return;
144   }
145
146
147   if(d->hasXiphComment)
148     d->comment = new Ogg::XiphComment(xiphCommentData());
149   else
150     d->comment = new Ogg::XiphComment;
151
152
153   if(readProperties)
154     d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
155 }
156
157 ByteVector Ogg::FLAC::File::streamInfoData()
158 {
159   scan();
160   return d->streamInfoData;
161 }
162
163 ByteVector Ogg::FLAC::File::xiphCommentData()
164 {
165   scan();
166   return d->xiphCommentData;
167 }
168
169 long Ogg::FLAC::File::streamLength()
170 {
171   scan();
172   return d->streamLength;
173 }
174
175 void Ogg::FLAC::File::scan()
176 {
177   // Scan the metadata pages
178
179   if(d->scanned)
180     return;
181
182   if(!isValid())
183     return;
184
185   int ipacket = 0;
186   long overhead = 0;
187
188   ByteVector metadataHeader = packet(ipacket);
189   if(metadataHeader.isNull())
190     return;
191
192   ByteVector header;
193
194   if (!metadataHeader.startsWith("fLaC"))  {
195     // FLAC 1.1.2+
196     if (metadataHeader.mid(1,4) != "FLAC") return;
197
198     if (metadataHeader[5] != 1) return; // not version 1
199
200     metadataHeader = metadataHeader.mid(13);
201   }
202   else {
203     // FLAC 1.1.0 & 1.1.1
204     metadataHeader = packet(++ipacket);
205
206     if(metadataHeader.isNull())
207       return;
208
209   }
210
211   header = metadataHeader.mid(0,4);
212   // Header format (from spec):
213   // <1> Last-metadata-block flag
214   // <7> BLOCK_TYPE
215   //    0 : STREAMINFO
216   //    1 : PADDING
217   //    ..
218   //    4 : VORBIS_COMMENT
219   //    ..
220   // <24> Length of metadata to follow
221
222   char blockType = header[0] & 0x7f;
223   bool lastBlock = (header[0] & 0x80) != 0;
224   uint length = header.mid(1, 3).toUInt();
225   overhead += length;
226
227   // Sanity: First block should be the stream_info metadata
228
229   if(blockType != 0) {
230     debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC stream");
231     return;
232   }
233
234   d->streamInfoData = metadataHeader.mid(4,length);
235
236   // Search through the remaining metadata
237
238   while(!lastBlock) {
239     metadataHeader = packet(++ipacket);
240
241     if(metadataHeader.isNull())
242       return;
243
244     header = metadataHeader.mid(0, 4);
245     blockType = header[0] & 0x7f;
246     lastBlock = (header[0] & 0x80) != 0;
247     length = header.mid(1, 3).toUInt();
248     overhead += length;
249
250     if(blockType == 1) {
251       // debug("Ogg::FLAC::File::scan() -- Padding found");
252     }
253     else if(blockType == 4) {
254       // debug("Ogg::FLAC::File::scan() -- Vorbis-comments found");
255       d->xiphCommentData = metadataHeader.mid(4, length);
256       d->hasXiphComment = true;
257       d->commentPacket = ipacket;
258     }
259     else if(blockType > 5)
260       debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
261
262   }
263
264   // End of metadata, now comes the datastream
265   d->streamStart = overhead;
266   d->streamLength = File::length() - d->streamStart;
267
268   d->scanned = true;
269 }