Taglib 1.5

This commit is contained in:
vspader 2009-02-21 06:16:30 +00:00
parent 70b19e6cfa
commit c391097b00
123 changed files with 30825 additions and 0 deletions

View file

@ -0,0 +1,11 @@
Scott Wheeler <wheeler@kde.org>
Author, maintainer
Ismael Orenstein <orenstein@kde.org>
Xing header implementation
Allan Sandfeld Jensen <kde@carewolf.org>
FLAC metadata implementation
Teemu Tervo <teemu.tervo@gmx.net>
Numerous bug reports and fixes
Please send all patches and questions to taglib-devel@kde.org rather than to
individual developers!

View file

@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -0,0 +1,470 @@
MOZILLA PUBLIC LICENSE
Version 1.1
---------------
1. Definitions.
1.0.1. "Commercial Use" means distribution or otherwise making the
Covered Code available to a third party.
1.1. "Contributor" means each entity that creates or contributes to
the creation of Modifications.
1.2. "Contributor Version" means the combination of the Original
Code, prior Modifications used by a Contributor, and the Modifications
made by that particular Contributor.
1.3. "Covered Code" means the Original Code or Modifications or the
combination of the Original Code and Modifications, in each case
including portions thereof.
1.4. "Electronic Distribution Mechanism" means a mechanism generally
accepted in the software development community for the electronic
transfer of data.
1.5. "Executable" means Covered Code in any form other than Source
Code.
1.6. "Initial Developer" means the individual or entity identified
as the Initial Developer in the Source Code notice required by Exhibit
A.
1.7. "Larger Work" means a work which combines Covered Code or
portions thereof with code not governed by the terms of this License.
1.8. "License" means this document.
1.8.1. "Licensable" means having the right to grant, to the maximum
extent possible, whether at the time of the initial grant or
subsequently acquired, any and all of the rights conveyed herein.
1.9. "Modifications" means any addition to or deletion from the
substance or structure of either the Original Code or any previous
Modifications. When Covered Code is released as a series of files, a
Modification is:
A. Any addition to or deletion from the contents of a file
containing Original Code or previous Modifications.
B. Any new file that contains any part of the Original Code or
previous Modifications.
1.10. "Original Code" means Source Code of computer software code
which is described in the Source Code notice required by Exhibit A as
Original Code, and which, at the time of its release under this
License is not already Covered Code governed by this License.
1.10.1. "Patent Claims" means any patent claim(s), now owned or
hereafter acquired, including without limitation, method, process,
and apparatus claims, in any patent Licensable by grantor.
1.11. "Source Code" means the preferred form of the Covered Code for
making modifications to it, including all modules it contains, plus
any associated interface definition files, scripts used to control
compilation and installation of an Executable, or source code
differential comparisons against either the Original Code or another
well known, available Covered Code of the Contributor's choice. The
Source Code can be in a compressed or archival form, provided the
appropriate decompression or de-archiving software is widely available
for no charge.
1.12. "You" (or "Your") means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this
License or a future version of this License issued under Section 6.1.
For legal entities, "You" includes any entity which controls, is
controlled by, or is under common control with You. For purposes of
this definition, "control" means (a) the power, direct or indirect,
to cause the direction or management of such entity, whether by
contract or otherwise, or (b) ownership of more than fifty percent
(50%) of the outstanding shares or beneficial ownership of such
entity.
2. Source Code License.
2.1. The Initial Developer Grant.
The Initial Developer hereby grants You a world-wide, royalty-free,
non-exclusive license, subject to third party intellectual property
claims:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Initial Developer to use, reproduce,
modify, display, perform, sublicense and distribute the Original
Code (or portions thereof) with or without Modifications, and/or
as part of a Larger Work; and
(b) under Patents Claims infringed by the making, using or
selling of Original Code, to make, have made, use, practice,
sell, and offer for sale, and/or otherwise dispose of the
Original Code (or portions thereof).
(c) the licenses granted in this Section 2.1(a) and (b) are
effective on the date Initial Developer first distributes
Original Code under the terms of this License.
(d) Notwithstanding Section 2.1(b) above, no patent license is
granted: 1) for code that You delete from the Original Code; 2)
separate from the Original Code; or 3) for infringements caused
by: i) the modification of the Original Code or ii) the
combination of the Original Code with other software or devices.
2.2. Contributor Grant.
Subject to third party intellectual property claims, each Contributor
hereby grants You a world-wide, royalty-free, non-exclusive license
(a) under intellectual property rights (other than patent or
trademark) Licensable by Contributor, to use, reproduce, modify,
display, perform, sublicense and distribute the Modifications
created by such Contributor (or portions thereof) either on an
unmodified basis, with other Modifications, as Covered Code
and/or as part of a Larger Work; and
(b) under Patent Claims infringed by the making, using, or
selling of Modifications made by that Contributor either alone
and/or in combination with its Contributor Version (or portions
of such combination), to make, use, sell, offer for sale, have
made, and/or otherwise dispose of: 1) Modifications made by that
Contributor (or portions thereof); and 2) the combination of
Modifications made by that Contributor with its Contributor
Version (or portions of such combination).
(c) the licenses granted in Sections 2.2(a) and 2.2(b) are
effective on the date Contributor first makes Commercial Use of
the Covered Code.
(d) Notwithstanding Section 2.2(b) above, no patent license is
granted: 1) for any code that Contributor has deleted from the
Contributor Version; 2) separate from the Contributor Version;
3) for infringements caused by: i) third party modifications of
Contributor Version or ii) the combination of Modifications made
by that Contributor with other software (except as part of the
Contributor Version) or other devices; or 4) under Patent Claims
infringed by Covered Code in the absence of Modifications made by
that Contributor.
3. Distribution Obligations.
3.1. Application of License.
The Modifications which You create or to which You contribute are
governed by the terms of this License, including without limitation
Section 2.2. The Source Code version of Covered Code may be
distributed only under the terms of this License or a future version
of this License released under Section 6.1, and You must include a
copy of this License with every copy of the Source Code You
distribute. You may not offer or impose any terms on any Source Code
version that alters or restricts the applicable version of this
License or the recipients' rights hereunder. However, You may include
an additional document offering the additional rights described in
Section 3.5.
3.2. Availability of Source Code.
Any Modification which You create or to which You contribute must be
made available in Source Code form under the terms of this License
either on the same media as an Executable version or via an accepted
Electronic Distribution Mechanism to anyone to whom you made an
Executable version available; and if made available via Electronic
Distribution Mechanism, must remain available for at least twelve (12)
months after the date it initially became available, or at least six
(6) months after a subsequent version of that particular Modification
has been made available to such recipients. You are responsible for
ensuring that the Source Code version remains available even if the
Electronic Distribution Mechanism is maintained by a third party.
3.3. Description of Modifications.
You must cause all Covered Code to which You contribute to contain a
file documenting the changes You made to create that Covered Code and
the date of any change. You must include a prominent statement that
the Modification is derived, directly or indirectly, from Original
Code provided by the Initial Developer and including the name of the
Initial Developer in (a) the Source Code, and (b) in any notice in an
Executable version or related documentation in which You describe the
origin or ownership of the Covered Code.
3.4. Intellectual Property Matters
(a) Third Party Claims.
If Contributor has knowledge that a license under a third party's
intellectual property rights is required to exercise the rights
granted by such Contributor under Sections 2.1 or 2.2,
Contributor must include a text file with the Source Code
distribution titled "LEGAL" which describes the claim and the
party making the claim in sufficient detail that a recipient will
know whom to contact. If Contributor obtains such knowledge after
the Modification is made available as described in Section 3.2,
Contributor shall promptly modify the LEGAL file in all copies
Contributor makes available thereafter and shall take other steps
(such as notifying appropriate mailing lists or newsgroups)
reasonably calculated to inform those who received the Covered
Code that new knowledge has been obtained.
(b) Contributor APIs.
If Contributor's Modifications include an application programming
interface and Contributor has knowledge of patent licenses which
are reasonably necessary to implement that API, Contributor must
also include this information in the LEGAL file.
(c) Representations.
Contributor represents that, except as disclosed pursuant to
Section 3.4(a) above, Contributor believes that Contributor's
Modifications are Contributor's original creation(s) and/or
Contributor has sufficient rights to grant the rights conveyed by
this License.
3.5. Required Notices.
You must duplicate the notice in Exhibit A in each file of the Source
Code. If it is not possible to put such notice in a particular Source
Code file due to its structure, then You must include such notice in a
location (such as a relevant directory) where a user would be likely
to look for such a notice. If You created one or more Modification(s)
You may add your name as a Contributor to the notice described in
Exhibit A. You must also duplicate this License in any documentation
for the Source Code where You describe recipients' rights or ownership
rights relating to Covered Code. You may choose to offer, and to
charge a fee for, warranty, support, indemnity or liability
obligations to one or more recipients of Covered Code. However, You
may do so only on Your own behalf, and not on behalf of the Initial
Developer or any Contributor. You must make it absolutely clear than
any such warranty, support, indemnity or liability obligation is
offered by You alone, and You hereby agree to indemnify the Initial
Developer and every Contributor for any liability incurred by the
Initial Developer or such Contributor as a result of warranty,
support, indemnity or liability terms You offer.
3.6. Distribution of Executable Versions.
You may distribute Covered Code in Executable form only if the
requirements of Section 3.1-3.5 have been met for that Covered Code,
and if You include a notice stating that the Source Code version of
the Covered Code is available under the terms of this License,
including a description of how and where You have fulfilled the
obligations of Section 3.2. The notice must be conspicuously included
in any notice in an Executable version, related documentation or
collateral in which You describe recipients' rights relating to the
Covered Code. You may distribute the Executable version of Covered
Code or ownership rights under a license of Your choice, which may
contain terms different from this License, provided that You are in
compliance with the terms of this License and that the license for the
Executable version does not attempt to limit or alter the recipient's
rights in the Source Code version from the rights set forth in this
License. If You distribute the Executable version under a different
license You must make it absolutely clear that any terms which differ
from this License are offered by You alone, not by the Initial
Developer or any Contributor. You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred by
the Initial Developer or such Contributor as a result of any such
terms You offer.
3.7. Larger Works.
You may create a Larger Work by combining Covered Code with other code
not governed by the terms of this License and distribute the Larger
Work as a single product. In such a case, You must make sure the
requirements of this License are fulfilled for the Covered Code.
4. Inability to Comply Due to Statute or Regulation.
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Code due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description
must be included in the LEGAL file described in Section 3.4 and must
be included with all distributions of the Source Code. Except to the
extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to
understand it.
5. Application of this License.
This License applies to code to which the Initial Developer has
attached the notice in Exhibit A and to related Covered Code.
6. Versions of the License.
6.1. New Versions.
Netscape Communications Corporation ("Netscape") may publish revised
and/or new versions of the License from time to time. Each version
will be given a distinguishing version number.
6.2. Effect of New Versions.
Once Covered Code has been published under a particular version of the
License, You may always continue to use it under the terms of that
version. You may also choose to use such Covered Code under the terms
of any subsequent version of the License published by Netscape. No one
other than Netscape has the right to modify the terms applicable to
Covered Code created under this License.
6.3. Derivative Works.
If You create or use a modified version of this License (which you may
only do in order to apply it to code which is not already Covered Code
governed by this License), You must (a) rename Your license so that
the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
"MPL", "NPL" or any confusingly similar phrase do not appear in your
license (except to note that your license differs from this License)
and (b) otherwise make it clear that Your version of the license
contains terms which differ from the Mozilla Public License and
Netscape Public License. (Filling in the name of the Initial
Developer, Original Code or Contributor in the notice described in
Exhibit A shall not of themselves be deemed to be modifications of
this License.)
7. DISCLAIMER OF WARRANTY.
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
8. TERMINATION.
8.1. This License and the rights granted hereunder will terminate
automatically if You fail to comply with terms herein and fail to cure
such breach within 30 days of becoming aware of the breach. All
sublicenses to the Covered Code which are properly granted shall
survive any termination of this License. Provisions which, by their
nature, must remain in effect beyond the termination of this License
shall survive.
8.2. If You initiate litigation by asserting a patent infringement
claim (excluding declatory judgment actions) against Initial Developer
or a Contributor (the Initial Developer or Contributor against whom
You file such action is referred to as "Participant") alleging that:
(a) such Participant's Contributor Version directly or indirectly
infringes any patent, then any and all rights granted by such
Participant to You under Sections 2.1 and/or 2.2 of this License
shall, upon 60 days notice from Participant terminate prospectively,
unless if within 60 days after receipt of notice You either: (i)
agree in writing to pay Participant a mutually agreeable reasonable
royalty for Your past and future use of Modifications made by such
Participant, or (ii) withdraw Your litigation claim with respect to
the Contributor Version against such Participant. If within 60 days
of notice, a reasonable royalty and payment arrangement are not
mutually agreed upon in writing by the parties or the litigation claim
is not withdrawn, the rights granted by Participant to You under
Sections 2.1 and/or 2.2 automatically terminate at the expiration of
the 60 day notice period specified above.
(b) any software, hardware, or device, other than such Participant's
Contributor Version, directly or indirectly infringes any patent, then
any rights granted to You by such Participant under Sections 2.1(b)
and 2.2(b) are revoked effective as of the date You first made, used,
sold, distributed, or had made, Modifications made by that
Participant.
8.3. If You assert a patent infringement claim against Participant
alleging that such Participant's Contributor Version directly or
indirectly infringes any patent where such claim is resolved (such as
by license or settlement) prior to the initiation of patent
infringement litigation, then the reasonable value of the licenses
granted by such Participant under Sections 2.1 or 2.2 shall be taken
into account in determining the amount or value of any payment or
license.
8.4. In the event of termination under Sections 8.1 or 8.2 above,
all end user license agreements (excluding distributors and resellers)
which have been validly granted by You or any distributor hereunder
prior to termination shall survive termination.
9. LIMITATION OF LIABILITY.
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
10. U.S. GOVERNMENT END USERS.
The Covered Code is a "commercial item," as that term is defined in
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
software" and "commercial computer software documentation," as such
terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
all U.S. Government End Users acquire Covered Code with only those
rights set forth herein.
11. MISCELLANEOUS.
This License represents the complete agreement concerning subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. This License shall be governed by
California law provisions (except to the extent applicable law, if
any, provides otherwise), excluding its conflict-of-law provisions.
With respect to disputes in which at least one party is a citizen of,
or an entity chartered or registered to do business in the United
States of America, any litigation relating to this License shall be
subject to the jurisdiction of the Federal Courts of the Northern
District of California, with venue lying in Santa Clara County,
California, with the losing party responsible for costs, including
without limitation, court costs and reasonable attorneys' fees and
expenses. The application of the United Nations Convention on
Contracts for the International Sale of Goods is expressly excluded.
Any law or regulation which provides that the language of a contract
shall be construed against the drafter shall not apply to this
License.
12. RESPONSIBILITY FOR CLAIMS.
As between Initial Developer and the Contributors, each party is
responsible for claims and damages arising, directly or indirectly,
out of its utilization of rights under this License and You agree to
work with Initial Developer and Contributors to distribute such
responsibility on an equitable basis. Nothing herein is intended or
shall be deemed to constitute any admission of liability.
13. MULTIPLE-LICENSED CODE.
Initial Developer may designate portions of the Covered Code as
"Multiple-Licensed". "Multiple-Licensed" means that the Initial
Developer permits you to utilize portions of the Covered Code under
Your choice of the NPL or the alternative licenses, if any, specified
by the Initial Developer in the file described in Exhibit A.
EXHIBIT A -Mozilla Public License.
``The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
The Original Code is ______________________________________.
The Initial Developer of the Original Code is ________________________.
Portions created by ______________________ are Copyright (C) ______
_______________________. All Rights Reserved.
Contributor(s): ______________________________________.
Alternatively, the contents of this file may be used under the terms
of the _____ license (the "[___] License"), in which case the
provisions of [______] License are applicable instead of those
above. If you wish to allow use of your version of this file only
under the terms of the [____] License and not to allow others to use
your version of this file under the MPL, indicate your decision by
deleting the provisions above and replace them with the notice and
other provisions required by the [___] License. If you do not delete
the provisions above, a recipient may use your version of this file
under either the MPL or the [___] License."
[NOTE: The text of this Exhibit A may differ slightly from the text of
the notices in the Source Code files of the Original Code. You should
use the text of this Exhibit A rather than the text found in the
Original Code Source Code for Your Modifications.]

View file

@ -0,0 +1,167 @@
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, a file
`config.cache' that saves the results of its tests to speed up
reconfiguring, and a file `config.log' containing compiler output
(useful mainly for debugging `configure').
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If at some point `config.cache'
contains results you don't want to keep, you may remove or edit it.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need `configure.in' if you want to change
it or regenerate `configure' using a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes a while. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Type `make install' to install the programs and any data files and
documentation.
4. You can remove the program binaries and object files from the
source code directory by typing `make clean'.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. You can give `configure'
initial values for variables by setting them in the environment. Using
a Bourne-compatible shell, you can do that on the command line like
this:
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
Or on systems that have the `env' program, you can do it like this:
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not supports the `VPATH'
variable, you have to compile the package for one architecture at a time
in the source code directory. After you have installed the package for
one architecture, use `make distclean' before reconfiguring for another
architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' can not figure out
automatically, but needs to determine by the type of host the package
will run on. Usually `configure' can figure that out, but if it prints
a message saying it can not guess the host type, give it the
`--host=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name with three fields:
CPU-COMPANY-SYSTEM
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the host type.
If you are building compiler tools for cross-compiling, you can also
use the `--target=TYPE' option to select the type of system they will
produce code for and the `--build=TYPE' option to select the type of
system on which you are compiling the package.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Operation Controls
==================
`configure' recognizes the following options to control how it
operates.
`--cache-file=FILE'
Use and save the results of the tests in FILE instead of
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
debugging `configure'.
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made.
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`configure' also accepts some other, not widely useful, options.

View file

@ -0,0 +1,65 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.in by autoheader. */
/* have cppunit */
#define HAVE_CPPUNIT 0
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* have zlib */
#define HAVE_ZLIB 1
/* Suffix for lib directories */
#define KDELIBSUFF ""
/* Name of package */
#define PACKAGE "taglib"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME ""
/* Define to the full name and version of this package. */
#define PACKAGE_STRING ""
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME ""
/* Define to the version of this package. */
#define PACKAGE_VERSION ""
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "1.5"

View file

@ -0,0 +1,170 @@
================================================================================
= APE Tag Specification, Version 2.000
================================================================================
Original Content (C) 2004, Frank Klemm <frank.klemm@elster.offl.uni-jena.de>
Formatting / Editing (C) 2004, Scott Wheeler <wheeler@kde.org>
================================================================================
= Contents
================================================================================
1 - APE Tag General Structure
2 - APE Tag Header / Footer Format
3 - APE Tag Flags
4 - APE Tag Item Format
5 - APE Tag Item Supported Keys
6 - APE Tag Item Content
7 - Data Types
7.1 - Data Types / UTF-8
7.2 - Data Types / Dates
7.3 - Data Types / Timestamps
================================================================================
= 1 - APE Tag General Structure
================================================================================
Member of Basic Components of SV8 Stream Note:
It is strongly recommended that the data size be stored in the tags. The size
should normally be in the roughly one kilobyte, never more than 8 kilobytes.
Larger data should be stored externally using link entries. Linked data is much
easier to process by normal programs, so for instance JPEG data should not be
included inside the audio file.
APE Tag Version 2.000 (with header, recommended):
/================================\
| APE Tag Header | 32 bytes |
|-------------------|------------|
| APE Tag Item 1 | > 10 bytes |
| APE Tag Item 2 | > 10 bytes |
| APE Tag Item n-1 | > 10 bytes |
| APE Tag Item n | > 10 bytes |
|-------------------|------------|
| APE Tag Footer | 32 bytes |
\================================/
APE tag items should be sorted ascending by size. When streaming, parts of the
APE tag may be dropped to reduce the danger of drop outs between tracks. This
is not required, but is strongly recommended. It would be desirable for the i
tems to be sorted by importance / size, but this is not feasible. This
convention should only be broken when adding less important small items and it
is not desirable to rewrite the entire tag. An APE tag at the end of a file
(the recommended location) must have at least a footer; an APE tag at the
beginning of a file (strongly discouraged) must have at least a header.
APE Tag Version 1.000 (without header, deprecated)
/================================\
| APE Tag Item 1 | > 10 bytes |
| APE Tag Item 2 | > 10 bytes |
| APE Tag Item n-1 | > 10 bytes |
| APE Tag Item n | > 10 bytes |
|-------------------|------------|
| APE Tag Footer | 32 bytes |
\================================/
================================================================================
= 2 - APE Tag Header / Footer Format
================================================================================
Contains number, length and attributes of all tag items
Header and Footer are different in 1 bit in the Tags Flags to distinguish
between them.
Member of APE Tag 2.0
/===========================================================================\
| Preamble | 8 bytes | { 'A', 'P', 'E', 'T', 'A', 'G', 'E', 'X' } |
|----------------|---------|------------------------------------------------|
| Version Number | 4 bytes | 1000 = Version 1.000, 2000 = Version 2.000 |
|----------------|---------|------------------------------------------------|
| Tag Size | 4 bytes | Tag size in bytes including footer and all tag |
| | | items excluding the header (for 1.000 |
| | | compatibility) |
|----------------|---------|------------------------------------------------|
| Item Count | 4 bytes | Number of items in the tag |
|----------------|---------|------------------------------------------------|
| Tag Flags | 4 bytes | Global flags |
|----------------|---------|------------------------------------------------|
| Reserved | 8 bytes | Must be zeroed |
\===========================================================================/
================================================================================
= 3 - APE Tag Flags
================================================================================
The general flag structure for either items or headers / footers is the same.
Bits 31, 30 and 29 are specific to headers / footers, whereas 2 through 0 are
item specific.
Note: APE Tags from Version 1.0 do not use any of the following. All flags in
that version are zeroed and ignored when reading.
/=================================================================\
| Contains Header | Bit 31 | 1 - has header | 0 - no header |
|-----------------|-------------|---------------------------------|
| Contains Footer | Bit 30 | 1 - has footer | 0 - no footer |
|-----------------|-------------|---------------------------------|
| Is Header | Bit 29 | 1 - is header | 0 - is footer |
|-----------------|-------------|---------------------------------|
| Undefined | Bits 28 - 3 | Undefined, must be zeroed |
|-----------------|-------------|---------------------------------|
| Encoding | Bits 2 - 1 | 00 - UTF-8 |
| | | 01 - Binary Data * |
| | | 10 - External Reference ** |
| | | 11 - Reserved |
|-----------------|-------------|---------------------------------|
| Read Only | Bit 0 | 1 - read only | 0 - read/write |
\=================================================================/
(*) Should be ignored by tools for editing text values
(**) Allowed external reference formats:
- http://host/directory/filename.ext
- ftp://host/directory/filename.ext
- filename.ext
- /directory/filename.ext
- DRIVE:/directory/filename.ext
Note: External references are also UTF-8 encoded.
================================================================================
= 4 - APE Tag Item Format
================================================================================
APE Tag Items are stored as key-value pairs. APE Tags Item Key are case
sensitive, however it is illegal to use keys which only differ in case and
it is recommended that tag reading not be case sensitive.
Every key can only occur (at most) once. It is not possible to repeat a key
to signify updated contents.
Tags can be partially or completely repeated in the streaming format. This
makes it possible to display an artist and / or title if it was missed at the
beginning of the stream. It is recommended that the important information like
artist, album and title should occur approximately every 2 minutes in the
stream and again 5 to 10 seconds before the end. However, care should be tak
en not to replicate this information too often or during passages with high
bitrate demands to avoid unnecessary drop-outs.
/==============================================================================\
| Content Size | 4 bytes | Length of the value in bytes |
|----------------|---------------|---------------------------------------------|
| Flags | 4 bytes | Item flags |
|----------------|---------------|---------------------------------------------|
| Key | 2 - 255 bytes | Item key |
|----------------|---------------|---------------------------------------------|
| Key Terminator | 1 byte | Null byte that indicates the end of the key |
|----------------|---------------|---------------------------------------------|
| Value | variable | Content (formatted according to the flags) |
\==============================================================================/
================================================================================
Sections 5 - 7 haven't yet been converted from:
http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html

View file

@ -0,0 +1,236 @@
/***************************************************************************
copyright : (C) 2004 by Allan Sandfeld Jensen
(C) 2002 - 2008 by Scott Wheeler (id3v2header.cpp)
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <iostream>
#include <bitset>
#include <tstring.h>
#include <tdebug.h>
#include "apefooter.h"
using namespace TagLib;
using namespace APE;
class Footer::FooterPrivate
{
public:
FooterPrivate() : version(0),
footerPresent(true),
headerPresent(false),
isHeader(false),
itemCount(0),
tagSize(0) {}
~FooterPrivate() {}
uint version;
bool footerPresent;
bool headerPresent;
bool isHeader;
uint itemCount;
uint tagSize;
static const uint size = 32;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
TagLib::uint Footer::size()
{
return FooterPrivate::size;
}
ByteVector Footer::fileIdentifier()
{
return ByteVector::fromCString("APETAGEX");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Footer::Footer()
{
d = new FooterPrivate;
}
Footer::Footer(const ByteVector &data)
{
d = new FooterPrivate;
parse(data);
}
Footer::~Footer()
{
delete d;
}
TagLib::uint Footer::version() const
{
return d->version;
}
bool Footer::headerPresent() const
{
return d->headerPresent;
}
bool Footer::footerPresent() const
{
return d->footerPresent;
}
bool Footer::isHeader() const
{
return d->isHeader;
}
void Footer::setHeaderPresent(bool b) const
{
d->headerPresent = b;
}
TagLib::uint Footer::itemCount() const
{
return d->itemCount;
}
void Footer::setItemCount(uint s)
{
d->itemCount = s;
}
TagLib::uint Footer::tagSize() const
{
return d->tagSize;
}
TagLib::uint Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + d->size;
else
return d->tagSize;
}
void Footer::setTagSize(uint s)
{
d->tagSize = s;
}
void Footer::setData(const ByteVector &data)
{
parse(data);
}
ByteVector Footer::renderFooter() const
{
return render(false);
}
ByteVector Footer::renderHeader() const
{
if (!d->headerPresent) return ByteVector();
return render(true);
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void Footer::parse(const ByteVector &data)
{
if(data.size() < size())
return;
// The first eight bytes, data[0..7], are the File Identifier, "APETAGEX".
// Read the version number
d->version = data.mid(8, 4).toUInt(false);
// Read the tag size
d->tagSize = data.mid(12, 4).toUInt(false);
// Read the item count
d->itemCount = data.mid(16, 4).toUInt(false);
// Read the flags
std::bitset<32> flags(data.mid(20, 4).toUInt(false));
d->headerPresent = flags[31];
d->footerPresent = !flags[30];
d->isHeader = flags[29];
}
ByteVector Footer::render(bool isHeader) const
{
ByteVector v;
// add the file identifier -- "APETAGEX"
v.append(fileIdentifier());
// add the version number -- we always render a 2.000 tag regardless of what
// the tag originally was.
v.append(ByteVector::fromUInt(2000, false));
// add the tag size
v.append(ByteVector::fromUInt(d->tagSize, false));
// add the item count
v.append(ByteVector::fromUInt(d->itemCount, false));
// render and add the flags
std::bitset<32> flags;
flags[31] = d->headerPresent;
flags[30] = false; // footer is always present
flags[29] = isHeader;
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
// add the reserved 64bit
v.append(ByteVector::fromLongLong(0));
return v;
}

View file

@ -0,0 +1,173 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_APEFOOTER_H
#define TAGLIB_APEFOOTER_H
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
namespace APE {
//! An implementation of APE footers
/*!
* This class implements APE footers (and headers). It attempts to follow, both
* semantically and programatically, the structure specified in
* the APE v2.0 standard. The API is based on the properties of APE footer and
* headers specified there.
*/
class TAGLIB_EXPORT Footer
{
public:
/*!
* Constructs an empty APE footer.
*/
Footer();
/*!
* Constructs an APE footer based on \a data. parse() is called
* immediately.
*/
Footer(const ByteVector &data);
/*!
* Destroys the footer.
*/
virtual ~Footer();
/*!
* Returns the version number. (Note: This is the 1000 or 2000.)
*/
uint version() const;
/*!
* Returns true if a header is present in the tag.
*/
bool headerPresent() const;
/*!
* Returns true if a footer is present in the tag.
*/
bool footerPresent() const;
/*!
* Returns true this is actually the header.
*/
bool isHeader() const;
/*!
* Sets whether the header should be rendered or not
*/
void setHeaderPresent(bool b) const;
/*!
* Returns the number of items in the tag.
*/
uint itemCount() const;
/*!
* Set the item count to \a s.
* \see itemCount()
*/
void setItemCount(uint s);
/*!
* Returns the tag size in bytes. This is the size of the frame content and footer.
* The size of the \e entire tag will be this plus the header size, if present.
*
* \see completeTagSize()
*/
uint tagSize() const;
/*!
* Returns the tag size, including if present, the header
* size.
*
* \see tagSize()
*/
uint completeTagSize() const;
/*!
* Set the tag size to \a s.
* \see tagSize()
*/
void setTagSize(uint s);
/*!
* Returns the size of the footer. Presently this is always 32 bytes.
*/
static uint size();
/*!
* Returns the string used to identify an APE tag inside of a file.
* Presently this is always "APETAGEX".
*/
static ByteVector fileIdentifier();
/*!
* Sets the data that will be used as the footer. 32 bytes,
* starting from \a data will be used.
*/
void setData(const ByteVector &data);
/*!
* Renders the footer back to binary format.
*/
ByteVector renderFooter() const;
/*!
* Renders the header corresponding to the footer. If headerPresent is
* set to false, it returns an empty ByteVector.
*/
ByteVector renderHeader() const;
protected:
/*!
* Called by setData() to parse the footer data. It makes this information
* available through the public API.
*/
void parse(const ByteVector &data);
/*!
* Called by renderFooter and renderHeader
*/
ByteVector render(bool isHeader) const;
private:
Footer(const Footer &);
Footer &operator=(const Footer &);
class FooterPrivate;
FooterPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,229 @@
/***************************************************************************
copyright : (C) 2004 by Allan Sandfeld Jensen
email : kde@carewolf.com
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tbytevectorlist.h>
#include <tdebug.h>
#include "apeitem.h"
using namespace TagLib;
using namespace APE;
class APE::Item::ItemPrivate
{
public:
ItemPrivate() : type(Text), readOnly(false) {}
Item::ItemTypes type;
String key;
ByteVector value;
StringList text;
bool readOnly;
};
APE::Item::Item()
{
d = new ItemPrivate;
}
APE::Item::Item(const String &key, const String &value)
{
d = new ItemPrivate;
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values)
{
d = new ItemPrivate;
d->key = key;
d->text = values;
}
APE::Item::Item(const Item &item)
{
d = new ItemPrivate(*item.d);
}
APE::Item::~Item()
{
delete d;
}
Item &APE::Item::operator=(const Item &item)
{
delete d;
d = new ItemPrivate(*item.d);
return *this;
}
void APE::Item::setReadOnly(bool readOnly)
{
d->readOnly = readOnly;
}
bool APE::Item::isReadOnly() const
{
return d->readOnly;
}
void APE::Item::setType(APE::Item::ItemTypes val)
{
d->type = val;
}
APE::Item::ItemTypes APE::Item::type() const
{
return d->type;
}
String APE::Item::key() const
{
return d->key;
}
ByteVector APE::Item::value() const
{
// This seems incorrect as it won't be actually rendering the value to keep it
// up to date.
return d->value;
}
void APE::Item::setKey(const String &key)
{
d->key = key;
}
void APE::Item::setValue(const String &value)
{
d->text = value;
}
void APE::Item::setValues(const StringList &value)
{
d->text = value;
}
void APE::Item::appendValue(const String &value)
{
d->text.append(value);
}
void APE::Item::appendValues(const StringList &values)
{
d->text.append(values);
}
int APE::Item::size() const
{
return 8 + d->key.size() + 1 + d->value.size();
}
StringList APE::Item::toStringList() const
{
return d->text;
}
StringList APE::Item::values() const
{
return d->text;
}
String APE::Item::toString() const
{
return isEmpty() ? String::null : d->text.front();
}
bool APE::Item::isEmpty() const
{
switch(d->type) {
case 0:
case 1:
if(d->text.isEmpty())
return true;
if(d->text.size() == 1 && d->text.front().isEmpty())
return true;
return false;
case 2:
return d->value.isEmpty();
default:
return false;
}
}
void APE::Item::parse(const ByteVector &data)
{
// 11 bytes is the minimum size for an APE item
if(data.size() < 11) {
debug("APE::Item::parse() -- no data in item");
return;
}
uint valueLength = data.mid(0, 4).toUInt(false);
uint flags = data.mid(4, 4).toUInt(false);
d->key = String(data.mid(8), String::UTF8);
d->value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
if(int(d->type) < 2)
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
}
ByteVector APE::Item::render() const
{
ByteVector data;
TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value;
if(isEmpty())
return data;
if(d->type != Item::Binary) {
StringList::ConstIterator it = d->text.begin();
value.append(it->data(String::UTF8));
it++;
for(; it != d->text.end(); ++it) {
value.append('\0');
value.append(it->data(String::UTF8));
}
d->value = value;
}
else
value.append(d->value);
data.append(ByteVector::fromUInt(value.size(), false));
data.append(ByteVector::fromUInt(flags, false));
data.append(d->key.data(String::UTF8));
data.append(ByteVector('\0'));
data.append(value);
return data;
}

View file

@ -0,0 +1,204 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_APEITEM_H
#define TAGLIB_APEITEM_H
#include "tbytevector.h"
#include "tstring.h"
#include "tstringlist.h"
namespace TagLib {
namespace APE {
//! An implementation of APE-items
/*!
* This class provides the features of items in the APEv2 standard.
*/
class TAGLIB_EXPORT Item
{
public:
/*!
* Enum of types an Item can have. The value of 3 is reserved.
*/
enum ItemTypes {
//! Item contains text information coded in UTF-8
Text = 0,
//! Item contains binary information
Binary = 1,
//! Item is a locator of external stored information
Locator = 2
};
/*!
* Constructs an empty item.
*/
Item();
/*!
* Constructs an item with \a key and \a value.
*/
// BIC: Remove this, StringList has a constructor from a single string
Item(const String &key, const String &value);
/*!
* Constructs an item with \a key and \a values.
*/
Item(const String &key, const StringList &values);
/*!
* Construct an item as a copy of \a item.
*/
Item(const Item &item);
/*!
* Destroys the item.
*/
virtual ~Item();
/*!
* Copies the contents of \a item into this item.
*/
Item &operator=(const Item &item);
/*!
* Returns the key.
*/
String key() const;
/*!
* Returns the binary value.
*
* \deprecated This will be removed in the next binary incompatible version
* as it is not kept in sync with the things that are set using setValue()
* and friends.
*/
ByteVector value() const;
/*!
* Sets the key for the item to \a key.
*/
void setKey(const String &key);
/*!
* Sets the value of the item to \a value and clears any previous contents.
*
* \see toString()
*/
void setValue(const String &value);
/*!
* Sets the value of the item to the list of values in \a value and clears
* any previous contents.
*
* \see toStringList()
*/
void setValues(const StringList &values);
/*!
* Appends \a value to create (or extend) the current list of values.
*
* \see toString()
*/
void appendValue(const String &value);
/*!
* Appends \a values to extend the current list of values.
*
* \see toStringList()
*/
void appendValues(const StringList &values);
/*!
* Returns the size of the full item.
*/
int size() const;
/*!
* Returns the value as a single string. In case of multiple strings,
* the first is returned.
*/
String toString() const;
/*!
* \deprecated
* \see values
*/
StringList toStringList() const;
/*!
* Returns the list of values.
*/
StringList values() const;
/*!
* Render the item to a ByteVector.
*/
ByteVector render() const;
/*!
* Parse the item from the ByteVector \a data.
*/
void parse(const ByteVector& data);
/*!
* Set the item to read-only.
*/
void setReadOnly(bool readOnly);
/*!
* Return true if the item is read-only.
*/
bool isReadOnly() const;
/*!
* Sets the type of the item to \a type.
*
* \see ItemTypes
*/
void setType(ItemTypes type);
/*!
* Returns the type of the item.
*/
ItemTypes type() const;
/*!
* Returns if the item has any real content.
*/
bool isEmpty() const;
private:
class ItemPrivate;
ItemPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,266 @@
/***************************************************************************
copyright : (C) 2004 by Allan Sandfeld Jensen
email : kde@carewolf.com
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifdef __SUNPRO_CC
// Sun Studio finds multiple specializations of Map because
// it considers specializations with and without class types
// to be different; this define forces Map to use only the
// specialization with the class keyword.
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
#endif
#include <tfile.h>
#include <tstring.h>
#include <tmap.h>
#include "apetag.h"
#include "apefooter.h"
#include "apeitem.h"
using namespace TagLib;
using namespace APE;
class APE::Tag::TagPrivate
{
public:
TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
File *file;
long footerLocation;
long tagLength;
Footer footer;
ItemListMap itemListMap;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
APE::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag()
{
d = new TagPrivate;
d->file = file;
d->footerLocation = footerLocation;
read();
}
APE::Tag::~Tag()
{
delete d;
}
ByteVector APE::Tag::fileIdentifier()
{
return ByteVector::fromCString("APETAGEX");
}
String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String::null;
return d->itemListMap["TITLE"].toString();
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String::null;
return d->itemListMap["ARTIST"].toString();
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String::null;
return d->itemListMap["ALBUM"].toString();
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String::null;
return d->itemListMap["COMMENT"].toString();
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String::null;
return d->itemListMap["GENRE"].toString();
}
TagLib::uint APE::Tag::year() const
{
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
}
TagLib::uint APE::Tag::track() const
{
if(d->itemListMap["TRACK"].isEmpty())
return 0;
return d->itemListMap["TRACK"].toString().toInt();
}
void APE::Tag::setTitle(const String &s)
{
addValue("TITLE", s, true);
}
void APE::Tag::setArtist(const String &s)
{
addValue("ARTIST", s, true);
}
void APE::Tag::setAlbum(const String &s)
{
addValue("ALBUM", s, true);
}
void APE::Tag::setComment(const String &s)
{
addValue("COMMENT", s, true);
}
void APE::Tag::setGenre(const String &s)
{
addValue("GENRE", s, true);
}
void APE::Tag::setYear(uint i)
{
if(i <= 0)
removeItem("YEAR");
else
addValue("YEAR", String::number(i), true);
}
void APE::Tag::setTrack(uint i)
{
if(i <= 0)
removeItem("TRACK");
else
addValue("TRACK", String::number(i), true);
}
APE::Footer *APE::Tag::footer() const
{
return &d->footer;
}
const APE::ItemListMap& APE::Tag::itemListMap() const
{
return d->itemListMap;
}
void APE::Tag::removeItem(const String &key)
{
Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end())
d->itemListMap.erase(it);
}
void APE::Tag::addValue(const String &key, const String &value, bool replace)
{
if(replace)
removeItem(key);
if(!value.isEmpty()) {
if(d->itemListMap.contains(key) || !replace)
d->itemListMap[key.upper()].appendValue(value);
else
setItem(key, Item(key, value));
}
}
void APE::Tag::setItem(const String &key, const Item &item)
{
d->itemListMap.insert(key.upper(), item);
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
void APE::Tag::read()
{
if(d->file && d->file->isValid()) {
d->file->seek(d->footerLocation);
d->footer.setData(d->file->readBlock(Footer::size()));
if(d->footer.tagSize() <= Footer::size() ||
d->footer.tagSize() > uint(d->file->length()))
return;
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
parse(d->file->readBlock(d->footer.tagSize() - Footer::size()));
}
}
ByteVector APE::Tag::render() const
{
ByteVector data;
uint itemCount = 0;
{
for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
it != d->itemListMap.end(); ++it)
{
data.append(it->second.render());
itemCount++;
}
}
d->footer.setItemCount(itemCount);
d->footer.setTagSize(data.size() + Footer::size());
d->footer.setHeaderPresent(true);
return d->footer.renderHeader() + data + d->footer.renderFooter();
}
void APE::Tag::parse(const ByteVector &data)
{
uint pos = 0;
// 11 bytes is the minimum size for an APE item
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
APE::Item item;
item.parse(data.mid(pos));
d->itemListMap.insert(item.key().upper(), item);
pos += item.size();
}
}

View file

@ -0,0 +1,162 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_APETAG_H
#define TAGLIB_APETAG_H
#include "tag.h"
#include "tbytevector.h"
#include "tmap.h"
#include "tstring.h"
#include "taglib_export.h"
#include "apeitem.h"
namespace TagLib {
class File;
//! An implementation of the APE tagging format
namespace APE {
class Footer;
/*!
* A mapping between a list of item names, or keys, and the associated item.
*
* \see APE::Tag::itemListMap()
*/
typedef Map<const String, Item> ItemListMap;
//! An APE tag implementation
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
/*!
* Create an APE tag with default values.
*/
Tag();
/*!
* Create an APE tag and parse the data in \a file with APE footer at
* \a tagOffset.
*/
Tag(File *file, long footerLocation);
/*!
* Destroys this Tag instance.
*/
virtual ~Tag();
/*!
* Renders the in memory values to a ByteVector suitable for writing to
* the file.
*/
ByteVector render() const;
/*!
* Returns the string "APETAGEX" suitable for usage in locating the tag in a
* file.
*/
static ByteVector fileIdentifier();
// Reimplementations.
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
/*!
* Returns a pointer to the tag's footer.
*/
Footer *footer() const;
/*!
* Returns a reference to the item list map. This is an ItemListMap of
* all of the items in the tag.
*
* This is the most powerfull structure for accessing the items of the tag.
*
* \warning You should not modify this data structure directly, instead
* use setItem() and removeItem().
*/
const ItemListMap &itemListMap() const;
/*!
* Removes the \a key item from the tag
*/
void removeItem(const String &key);
/*!
* Adds to the item specified by \a key the data \a value. If \a replace
* is true, then all of the other values on the same key will be removed
* first.
*/
void addValue(const String &key, const String &value, bool replace = true);
/*!
* Sets the \a key item to the value of \a item. If an item with the \a key is already
* present, it will be replaced.
*/
void setItem(const String &key, const Item &item);
protected:
/*!
* Reads from the file specified in the constructor.
*/
void read();
/*!
* Parses the body of the tag in \a data.
*/
void parse(const ByteVector &data);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,51 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "audioproperties.h"
using namespace TagLib;
class AudioProperties::AudioPropertiesPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::~AudioProperties()
{
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::AudioProperties(ReadStyle)
{
}

View file

@ -0,0 +1,110 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_AUDIOPROPERTIES_H
#define TAGLIB_AUDIOPROPERTIES_H
#include "taglib_export.h"
namespace TagLib {
//! A simple, abstract interface to common audio properties
/*!
* The values here are common to most audio formats. For more specific, codec
* dependant values, please see see the subclasses APIs. This is meant to
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple
* interface that is sufficient for most applications.
*/
class TAGLIB_EXPORT AudioProperties
{
public:
/*!
* Reading audio properties from a file can sometimes be very time consuming
* and for the most accurate results can often involve reading the entire
* file. Because in many situations speed is critical or the accuracy of the
* values is not particularly important this allows the level of desired
* accuracy to be set.
*/
enum ReadStyle {
//! Read as little of the file as possible
Fast,
//! Read more of the file and make better values guesses
Average,
//! Read as much of the file as needed to report accurate values
Accurate
};
/*!
* Destroys this AudioProperties instance.
*/
virtual ~AudioProperties();
/*!
* Returns the length of the file in seconds.
*/
virtual int length() const = 0;
/*!
* Returns the most appropriate bit rate for the file in kb/s. For constant
* bitrate formats this is simply the bitrate of the file. For variable
* bitrate formats this is either the average or nominal bitrate.
*/
virtual int bitrate() const = 0;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const = 0;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const = 0;
protected:
/*!
* Construct an audio properties instance. This is protected as this class
* should not be instantiated directly, but should be instantiated via its
* subclasses and can be fetched from the FileRef or File APIs.
*
* \see ReadStyle
*/
AudioProperties(ReadStyle style);
private:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}
#endif

View file

@ -0,0 +1,203 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tfile.h>
#include <tstring.h>
#include "fileref.h"
#include "mpegfile.h"
#include "vorbisfile.h"
#include "flacfile.h"
#include "oggflacfile.h"
#include "mpcfile.h"
#include "wavpackfile.h"
#include "speexfile.h"
#include "trueaudiofile.h"
using namespace TagLib;
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate(File *f) : RefCounter(), file(f) {}
~FileRefPrivate() {
delete file;
}
File *file;
static List<const FileTypeResolver *> fileTypeResolvers;
};
List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef()
{
d = new FileRefPrivate(0);
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
}
FileRef::FileRef(File *file)
{
d = new FileRefPrivate(file);
}
FileRef::FileRef(const FileRef &ref) : d(ref.d)
{
d->ref();
}
FileRef::~FileRef()
{
if(d->deref())
delete d;
}
Tag *FileRef::tag() const
{
return d->file->tag();
}
AudioProperties *FileRef::audioProperties() const
{
return d->file->audioProperties();
}
File *FileRef::file() const
{
return d->file;
}
bool FileRef::save()
{
return d->file->save();
}
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
{
FileRefPrivate::fileTypeResolvers.prepend(resolver);
return resolver;
}
StringList FileRef::defaultFileExtensions()
{
StringList l;
l.append("ogg");
l.append("flac");
l.append("oga");
l.append("mp3");
l.append("mpc");
l.append("wv");
l.append("spx");
l.append("tta");
return l;
}
bool FileRef::isNull() const
{
return !d->file || !d->file->isValid();
}
FileRef &FileRef::operator=(const FileRef &ref)
{
if(&ref == this)
return *this;
if(d->deref())
delete d;
d = ref.d;
d->ref();
return *this;
}
bool FileRef::operator==(const FileRef &ref) const
{
return ref.d->file == d->file;
}
bool FileRef::operator!=(const FileRef &ref) const
{
return ref.d->file != d->file;
}
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
// Ok, this is really dumb for now, but it works for testing.
String s;
#ifdef _WIN32
s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
#else
s = fileName;
#endif
// If this list is updated, the method defaultFileExtensions() should also be
// updated. However at some point that list should be created at the same time
// that a default file type resolver is created.
if(s.size() > 4) {
if(s.substr(s.size() - 4, 4).upper() == ".OGG")
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 4, 4).upper() == ".MP3")
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 4, 4).upper() == ".OGA")
return new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 5, 5).upper() == ".FLAC")
return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 4, 4).upper() == ".MPC")
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 3, 3).upper() == ".WV")
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 4, 4).upper() == ".SPX")
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(s.substr(s.size() - 4, 4).upper() == ".TTA")
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
}
return 0;
}

View file

@ -0,0 +1,262 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_FILEREF_H
#define TAGLIB_FILEREF_H
#include <tfile.h>
#include <tstringlist.h>
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
class Tag;
//! This class provides a simple abstraction for creating and handling files
/*!
* FileRef exists to provide a minimal, generic and value-based wrapper around
* a File. It is lightweight and implicitly shared, and as such suitable for
* pass-by-value use. This hides some of the uglier details of TagLib::File
* and the non-generic portions of the concrete file implementations.
*
* This class is useful in a "simple usage" situation where it is desirable
* to be able to get and set some of the tag information that is similar
* across file types.
*
* Also note that it is probably a good idea to plug this into your mime
* type system rather than using the constructor that accepts a file name using
* the FileTypeResolver.
*
* \see FileTypeResolver
* \see addFileTypeResolver()
*/
class TAGLIB_EXPORT FileRef
{
public:
//! A class for pluggable file type resolution.
/*!
* This class is used to add extend TagLib's very basic file name based file
* type resolution.
*
* This can be accomplished with:
*
* \code
*
* class MyFileTypeResolver : FileTypeResolver
* {
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle)
* {
* if(someCheckForAnMP3File(fileName))
* return new TagLib::MPEG::File(fileName);
* return 0;
* }
* }
*
* FileRef::addFileTypeResolver(new MyFileTypeResolver);
*
* \endcode
*
* Naturally a less contrived example would be slightly more complex. This
* can be used to plug in mime-type detection systems or to add new file types
* to TagLib.
*/
class TAGLIB_EXPORT FileTypeResolver
{
public:
// do not fix compiler warning about missing virtual destructor
// since this would not be binary compatible
// let Scott fix it whenever he thinks BIC changes can next be applied
/*!
* This method must be overridden to provide an additional file type
* resolver. If the resolver is able to determine the file type it should
* return a valid File object; if not it should return 0.
*
* \note The created file is then owned by the FileRef and should not be
* deleted. Deletion will happen automatically when the FileRef passes
* out of scope.
*/
virtual File *createFile(FileName fileName,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
};
/*!
* Creates a null FileRef.
*/
FileRef();
/*!
* Create a FileRef from \a fileName. If \a readAudioProperties is true then
* the audio properties will be read using \a audioPropertiesStyle. If
* \a readAudioProperties is false then \a audioPropertiesStyle will be
* ignored.
*
* Also see the note in the class documentation about why you may not want to
* use this method in your application.
*/
explicit FileRef(FileName fileName,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average);
/*!
* Contruct a FileRef using \a file. The FileRef now takes ownership of the
* pointer and will delete the File when it passes out of scope.
*/
explicit FileRef(File *file);
/*!
* Make a copy of \a ref.
*/
FileRef(const FileRef &ref);
/*!
* Destroys this FileRef instance.
*/
virtual ~FileRef();
/*!
* Returns a pointer to represented file's tag.
*
* \warning This pointer will become invalid when this FileRef and all
* copies pass out of scope.
*
* \see File::tag()
*/
Tag *tag() const;
/*!
* Returns the audio properties for this FileRef. If no audio properties
* were read then this will returns a null pointer.
*/
AudioProperties *audioProperties() const;
/*!
* Returns a pointer to the file represented by this handler class.
*
* As a general rule this call should be avoided since if you need to work
* with file objects directly, you are probably better served instantiating
* the File subclasses (i.e. MPEG::File) manually and working with their APIs.
*
* This <i>handle</i> exists to provide a minimal, generic and value-based
* wrapper around a File. Accessing the file directly generally indicates
* a moving away from this simplicity (and into things beyond the scope of
* FileRef).
*
* \warning This pointer will become invalid when this FileRef and all
* copies pass out of scope.
*/
File *file() const;
/*!
* Saves the file. Returns true on success.
*/
bool save();
/*!
* Adds a FileTypeResolver to the list of those used by TagLib. Each
* additional FileTypeResolver is added to the front of a list of resolvers
* that are tried. If the FileTypeResolver returns zero the next resolver
* is tried.
*
* Returns a pointer to the added resolver (the same one that's passed in --
* this is mostly so that static inialializers have something to use for
* assignment).
*
* \see FileTypeResolver
*/
static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
/*!
* As is mentioned elsewhere in this class's documentation, the default file
* type resolution code provided by TagLib only works by comparing file
* extensions.
*
* This method returns the list of file extensions that are used by default.
*
* The extensions are all returned in lowercase, though the comparison used
* by TagLib for resolution is case-insensitive.
*
* \note This does not account for any additional file type resolvers that
* are plugged in. Also note that this is not intended to replace a propper
* mime-type resolution system, but is just here for reference.
*
* \see FileTypeResolver
*/
static StringList defaultFileExtensions();
/*!
* Returns true if the file (and as such other pointers) are null.
*/
bool isNull() const;
/*!
* Assign the file pointed to by \a ref to this FileRef.
*/
FileRef &operator=(const FileRef &ref);
/*!
* Returns true if this FileRef and \a ref point to the same File object.
*/
bool operator==(const FileRef &ref) const;
/*!
* Returns true if this FileRef and \a ref do not point to the same File
* object.
*/
bool operator!=(const FileRef &ref) const;
/*!
* A simple implementation of file type guessing. If \a readAudioProperties
* is true then the audio properties will be read using
* \a audioPropertiesStyle. If \a readAudioProperties is false then
* \a audioPropertiesStyle will be ignored.
*
* \note You generally shouldn't use this method, but instead the constructor
* directly.
*
* \deprecated
*/
static File *create(FileName fileName,
bool readAudioProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
private:
class FileRefPrivate;
FileRefPrivate *d;
};
} // namespace TagLib
#endif

View file

@ -0,0 +1,431 @@
/***************************************************************************
copyright : (C) 2003-2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tlist.h>
#include <tdebug.h>
#include <tagunion.h>
#include <id3v2header.h>
#include <id3v2tag.h>
#include <id3v1tag.h>
#include <xiphcomment.h>
#include "flacfile.h"
using namespace TagLib;
namespace
{
enum { XiphIndex = 0, ID3v2Index = 1, ID3v1Index = 2 };
enum { StreamInfo = 0, Padding, Application, SeekTable, VorbisComment, CueSheet };
}
class FLAC::File::FilePrivate
{
public:
FilePrivate() :
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
properties(0),
flacStart(0),
streamStart(0),
streamLength(0),
scanned(false),
hasXiphComment(false),
hasID3v2(false),
hasID3v1(false) {}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long ID3v2Location;
uint ID3v2OriginalSize;
long ID3v1Location;
TagUnion tag;
Properties *properties;
ByteVector streamInfoData;
ByteVector xiphCommentData;
long flacStart;
long streamStart;
long streamLength;
bool scanned;
bool hasXiphComment;
bool hasID3v2;
bool hasID3v1;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
{
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
read(readProperties, propertiesStyle);
}
FLAC::File::~File()
{
delete d;
}
TagLib::Tag *FLAC::File::tag() const
{
return &d->tag;
}
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties;
}
bool FLAC::File::save()
{
if(readOnly()) {
debug("FLAC::File::save() - Cannot save to a read only file.");
return false;
}
// Create new vorbis comments
Tag::duplicate(&d->tag, xiphComment(true), true);
d->xiphCommentData = xiphComment()->render(false);
// A Xiph comment portion of the data stream starts with a 4-byte descriptor.
// The first byte indicates the frame type. The last three bytes are used
// to give the length of the data segment. Here we start
ByteVector data = ByteVector::fromUInt(d->xiphCommentData.size());
data[0] = char(VorbisComment);
data.append(d->xiphCommentData);
// If file already have comment => find and update it
// if not => insert one
// TODO: Search for padding and use that
if(d->hasXiphComment) {
long nextBlockOffset = d->flacStart;
bool isLastBlock = false;
while(!isLastBlock) {
seek(nextBlockOffset);
ByteVector header = readBlock(4);
char blockType = header[0] & 0x7f;
isLastBlock = (header[0] & 0x80) != 0;
uint blockLength = header.mid(1, 3).toUInt();
if(blockType == VorbisComment) {
data[0] = header[0];
insert(data, nextBlockOffset, blockLength + 4);
break;
}
nextBlockOffset += blockLength + 4;
}
}
else {
const long firstBlockOffset = d->flacStart;
seek(firstBlockOffset);
ByteVector header = readBlock(4);
bool isLastBlock = (header[0] & 0x80) != 0;
uint blockLength = header.mid(1, 3).toUInt();
if(isLastBlock) {
// If the first block was previously also the last block, then we want to
// mark it as no longer being the first block (the writeBlock() call) and
// then set the data for the block that we're about to write to mark our
// new block as the last block.
seek(firstBlockOffset);
writeBlock(static_cast<char>(header[0] & 0x7F));
data[0] |= 0x80;
}
insert(data, firstBlockOffset + blockLength + 4, 0);
d->hasXiphComment = true;
}
// Update ID3 tags
if(ID3v2Tag()) {
if(d->hasID3v2) {
if(d->ID3v2Location < d->flacStart)
debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
"start of the FLAC bytestream? Not writing the ID3v2 tag.");
else
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
}
else
insert(ID3v2Tag()->render(), 0, 0);
}
if(ID3v1Tag()) {
seek(-128, End);
writeBlock(ID3v1Tag()->render());
}
return true;
}
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
if(!create || d->tag[ID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
d->tag.set(ID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
}
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
}
Ogg::XiphComment *FLAC::File::xiphComment(bool create)
{
return d->tag.access<Ogg::XiphComment>(XiphIndex, create);
}
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
d->ID3v2FrameFactory = factory;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
// Look for an ID3v2 tag
d->ID3v2Location = findID3v2();
if(d->ID3v2Location >= 0) {
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
if(ID3v2Tag()->header()->tagSize() <= 0)
d->tag.set(ID3v2Index, 0);
else
d->hasID3v2 = true;
}
// Look for an ID3v1 tag
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for FLAC metadata, including vorbis comments
scan();
if(!isValid())
return;
if(d->hasXiphComment)
d->tag.set(XiphIndex, new Ogg::XiphComment(xiphCommentData()));
else
d->tag.set(XiphIndex, new Ogg::XiphComment);
if(readProperties)
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
}
ByteVector FLAC::File::streamInfoData()
{
return isValid() ? d->streamInfoData : ByteVector();
}
ByteVector FLAC::File::xiphCommentData() const
{
return (isValid() && d->hasXiphComment) ? d->xiphCommentData : ByteVector();
}
long FLAC::File::streamLength()
{
return d->streamLength;
}
void FLAC::File::scan()
{
// Scan the metadata pages
if(d->scanned)
return;
if(!isValid())
return;
long nextBlockOffset;
if(d->hasID3v2)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
else
nextBlockOffset = find("fLaC");
if(nextBlockOffset < 0) {
debug("FLAC::File::scan() -- FLAC stream not found");
setValid(false);
return;
}
nextBlockOffset += 4;
d->flacStart = nextBlockOffset;
seek(nextBlockOffset);
ByteVector header = readBlock(4);
// 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 isLastBlock = (header[0] & 0x80) != 0;
uint length = header.mid(1, 3).toUInt();
// First block should be the stream_info metadata
if(blockType != StreamInfo) {
debug("FLAC::File::scan() -- invalid FLAC stream");
setValid(false);
return;
}
d->streamInfoData = readBlock(length);
nextBlockOffset += length + 4;
// Search through the remaining metadata
while(!isLastBlock) {
header = readBlock(4);
blockType = header[0] & 0x7f;
isLastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt();
if(blockType == Padding) {
// debug("FLAC::File::scan() -- Padding found");
}
// Found the vorbis-comment
else if(blockType == VorbisComment) {
d->xiphCommentData = readBlock(length);
d->hasXiphComment = true;
}
nextBlockOffset += length + 4;
if(nextBlockOffset >= File::length()) {
debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false);
return;
}
seek(nextBlockOffset);
}
// End of metadata, now comes the datastream
d->streamStart = nextBlockOffset;
d->streamLength = File::length() - d->streamStart;
if(d->hasID3v1)
d->streamLength -= 128;
d->scanned = true;
}
long FLAC::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}
long FLAC::File::findID3v2()
{
if(!isValid())
return -1;
seek(0);
if(readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
}

View file

@ -0,0 +1,201 @@
/***************************************************************************
copyright : (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_FLACFILE_H
#define TAGLIB_FLACFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "flacproperties.h"
namespace TagLib {
class Tag;
namespace ID3v2 { class FrameFactory; class Tag; }
namespace ID3v1 { class Tag; }
namespace Ogg { class XiphComment; }
//! An implementation of FLAC metadata
/*!
* This is implementation of FLAC metadata for non-Ogg FLAC files. At some
* point when Ogg / FLAC is more common there will be a similar implementation
* under the Ogg hiearchy.
*
* This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
* properties from the file.
*/
namespace FLAC {
//! An implementation of TagLib::File with FLAC specific methods
/*!
* This implements and provides an interface for FLAC files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to FLAC files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* Contructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*/
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file. This will be a union of XiphComment,
* ID3v1 and ID3v2 tags.
*
* \see ID3v2Tag()
* \see ID3v1Tag()
* \see XiphComment()
*/
virtual TagLib::Tag *tag() const;
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Save the file. This will primarily save the XiphComment, but
* will also keep any old ID3-tags up to date. If the file
* has no XiphComment, one will be constructed from the ID3-tags.
*
* This returns true if the save was successful.
*/
virtual bool save();
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
ID3v2::Tag *ID3v2Tag(bool create = false);
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the XiphComment for the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid XiphComment. If \a create is true it will create
* a XiphComment if one does not exist.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
Ogg::XiphComment *xiphComment(bool create = false);
/*!
* Set the ID3v2::FrameFactory to something other than the default. This
* can be used to specify the way that ID3v2 frames will be interpreted
* when
*
* \see ID3v2FrameFactory
*/
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
/*!
* Returns the block of data used by FLAC::Properties for parsing the
* stream properties.
*
* \deprecated This method will not be public in a future release.
*/
ByteVector streamInfoData(); // BIC: remove
/*!
* Returns the length of the audio-stream, used by FLAC::Properties for
* calculating the bitrate.
*
* \deprecated This method will not be public in a future release.
*/
long streamLength(); // BIC: remove
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
long findID3v2();
long findID3v1();
ByteVector xiphCommentData() const;
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,150 @@
/***************************************************************************
copyright : (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tstring.h>
#include <tdebug.h>
#include "flacproperties.h"
#include "flacfile.h"
using namespace TagLib;
class FLAC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
data(d),
streamLength(st),
style(s),
length(0),
bitrate(0),
sampleRate(0),
sampleWidth(0),
channels(0) {}
ByteVector data;
long streamLength;
ReadStyle style;
int length;
int bitrate;
int sampleRate;
int sampleWidth;
int channels;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(data, streamLength, style);
read();
}
FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style);
read();
}
FLAC::Properties::~Properties()
{
delete d;
}
int FLAC::Properties::length() const
{
return d->length;
}
int FLAC::Properties::bitrate() const
{
return d->bitrate;
}
int FLAC::Properties::sampleRate() const
{
return d->sampleRate;
}
int FLAC::Properties::sampleWidth() const
{
return d->sampleWidth;
}
int FLAC::Properties::channels() const
{
return d->channels;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::Properties::read()
{
if(d->data.size() < 18) {
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
return;
}
int pos = 0;
// Minimum block size (in samples)
pos += 2;
// Maximum block size (in samples)
pos += 2;
// Minimum frame size (in bytes)
pos += 3;
// Maximum frame size (in bytes)
pos += 3;
uint flags = d->data.mid(pos, 4).toUInt(true);
d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1;
d->sampleWidth = ((flags >> 4) & 31) + 1;
// The last 4 bits are the most significant 4 bits for the 36 bit
// stream length in samples. (Audio files measured in days)
uint highLength =d->sampleRate > 0 ? (((flags & 0xf) << 28) / d->sampleRate) << 4 : 0;
pos += 4;
d->length = d->sampleRate > 0 ?
(d->data.mid(pos, 4).toUInt(true)) / d->sampleRate + highLength : 0;
pos += 4;
// Uncompressed bitrate:
//d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
// Real bitrate:
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

View file

@ -0,0 +1,92 @@
/***************************************************************************
copyright : (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_FLACPROPERTIES_H
#define TAGLIB_FLACPROPERTIES_H
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
namespace FLAC {
class File;
//! An implementation of audio property reading for FLAC
/*!
* This reads the data from an FLAC stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of FLAC::Properties with the data read from the
* ByteVector \a data.
*/
// BIC: switch to const reference
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of FLAC::Properties with the data read from the
* FLAC::File \a file.
*/
// BIC: remove
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this FLAC::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns the sample width as read from the FLAC identification
* header.
*/
int sampleWidth() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,325 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tagunion.h>
#include <tdebug.h>
#include "mpcfile.h"
#include "id3v1tag.h"
#include "id3v2header.h"
#include "apetag.h"
#include "apefooter.h"
using namespace TagLib;
namespace
{
enum { APEIndex, ID3v1Index };
}
class MPC::File::FilePrivate
{
public:
FilePrivate() :
APELocation(-1),
APESize(0),
ID3v1Location(-1),
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0),
properties(0),
scanned(false),
hasAPE(false),
hasID3v1(false),
hasID3v2(false) {}
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
long APELocation;
uint APESize;
long ID3v1Location;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
uint ID3v2Size;
TagUnion tag;
Properties *properties;
bool scanned;
// These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
bool hasAPE;
bool hasID3v1;
bool hasID3v2;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
MPC::File::~File()
{
delete d;
}
TagLib::Tag *MPC::File::tag() const
{
return &d->tag;
}
MPC::Properties *MPC::File::audioProperties() const
{
return d->properties;
}
bool MPC::File::save()
{
if(readOnly()) {
debug("MPC::File::save() -- File is read only.");
return false;
}
// Possibly strip ID3v2 tag
if(d->hasID3v2 && !d->ID3v2Header) {
removeBlock(d->ID3v2Location, d->ID3v2Size);
d->hasID3v2 = false;
if(d->hasID3v1)
d->ID3v1Location -= d->ID3v2Size;
if(d->hasAPE)
d->APELocation -= d->ID3v2Size;
}
// Update ID3v1 tag
if(ID3v1Tag()) {
if(d->hasID3v1) {
seek(d->ID3v1Location);
writeBlock(ID3v1Tag()->render());
}
else {
seek(0, End);
d->ID3v1Location = tell();
writeBlock(ID3v1Tag()->render());
d->hasID3v1 = true;
}
} else
if(d->hasID3v1) {
removeBlock(d->ID3v1Location, 128);
d->hasID3v1 = false;
if(d->hasAPE) {
if(d->APELocation > d->ID3v1Location)
d->APELocation -= 128;
}
}
// Update APE tag
if(APETag()) {
if(d->hasAPE)
insert(APETag()->render(), d->APELocation, d->APESize);
else {
if(d->hasID3v1) {
insert(APETag()->render(), d->ID3v1Location, 0);
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
d->APELocation = d->ID3v1Location;
d->ID3v1Location += d->APESize;
}
else {
seek(0, End);
d->APELocation = tell();
writeBlock(APETag()->render());
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
}
}
else
if(d->hasAPE) {
removeBlock(d->APELocation, d->APESize);
d->hasAPE = false;
if(d->hasID3v1) {
if(d->ID3v1Location > d->APELocation)
d->ID3v1Location -= d->APESize;
}
}
return true;
}
ID3v1::Tag *MPC::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
}
APE::Tag *MPC::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(APEIndex, create);
}
void MPC::File::strip(int tags)
{
if(tags & ID3v1) {
d->tag.set(ID3v1Index, 0);
APETag(true);
}
if(tags & ID3v2) {
delete d->ID3v2Header;
d->ID3v2Header = 0;
}
if(tags & APE) {
d->tag.set(APEIndex, 0);
if(!ID3v1Tag())
APETag(true);
}
}
void MPC::File::remove(int tags)
{
strip(tags);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
{
// Look for an ID3v1 tag
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for an APE tag
findAPE();
d->APELocation = findAPE();
if(d->APELocation >= 0) {
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
d->hasAPE = true;
}
if(!d->hasID3v1)
APETag(true);
// Look for and skip an ID3v2 tag
d->ID3v2Location = findID3v2();
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
d->hasID3v2 = true;
}
if(d->hasID3v2)
seek(d->ID3v2Location + d->ID3v2Size);
else
seek(0);
// Look for MPC metadata
if(readProperties) {
d->properties = new Properties(readBlock(MPC::HeaderSize),
length() - d->ID3v2Size - d->APESize);
}
}
long MPC::File::findAPE()
{
if(!isValid())
return -1;
if(d->hasID3v1)
seek(-160, End);
else
seek(-32, End);
long p = tell();
if(readBlock(8) == APE::Tag::fileIdentifier())
return p;
return -1;
}
long MPC::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}
long MPC::File::findID3v2()
{
if(!isValid())
return -1;
seek(0);
if(readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
}

View file

@ -0,0 +1,175 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_MPCFILE_H
#define TAGLIB_MPCFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "mpcproperties.h"
namespace TagLib {
class Tag;
namespace ID3v1 { class Tag; }
namespace APE { class Tag; }
//! An implementation of MPC metadata
/*!
* This is implementation of MPC metadata.
*
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
* properties from the file. ID3v2 tags are invalid in MPC-files, but will be skipped
* and ignored.
*/
namespace MPC {
//! An implementation of TagLib::File with MPC specific methods
/*!
* This implements and provides an interface for MPC files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to MPC files.
* The only invalid tag combination supported is an ID3v1 tag after an APE tag.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches ID3v1 tags.
ID3v1 = 0x0001,
//! Matches ID3v2 tags.
ID3v2 = 0x0002,
//! Matches APE tags.
APE = 0x0004,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Contructs an MPC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
* or a combination of the two.
*/
virtual TagLib::Tag *tag() const;
/*!
* Returns the MPC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Saves the file.
*/
virtual bool save();
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
* new ID3v1 tag will be placed after it.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* a APE tag if one does not exist. If there is already an ID3v1 tag, thes
* new APE tag will be placed before it.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
APE::Tag *APETag(bool create = false);
/*!
* This will remove the tags that match the OR-ed together TagTypes from the
* file. By default it removes all tags.
*
* \warning This will also invalidate pointers to the tags
* as their memory will be freed.
*
* \note In order to make the removal permanent save() still needs to be called.
*/
void strip(int tags = AllTags);
/*!
* \deprecated
* \see strip
*/
void remove(int tags = AllTags);
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
long findAPE();
long findID3v1();
long findID3v2();
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,140 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "mpcproperties.h"
#include "mpcfile.h"
using namespace TagLib;
class MPC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
data(d),
streamLength(length),
style(s),
version(0),
length(0),
bitrate(0),
sampleRate(0),
channels(0) {}
ByteVector data;
long streamLength;
ReadStyle style;
int version;
int length;
int bitrate;
int sampleRate;
int channels;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(data, streamLength, style);
read();
}
MPC::Properties::~Properties()
{
delete d;
}
int MPC::Properties::length() const
{
return d->length;
}
int MPC::Properties::bitrate() const
{
return d->bitrate;
}
int MPC::Properties::sampleRate() const
{
return d->sampleRate;
}
int MPC::Properties::channels() const
{
return d->channels;
}
int MPC::Properties::mpcVersion() const
{
return d->version;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
void MPC::Properties::read()
{
if(!d->data.startsWith("MP+"))
return;
d->version = d->data[3] & 15;
unsigned int frames;
if(d->version >= 7) {
frames = d->data.mid(4, 4).toUInt(false);
std::bitset<32> flags = d->data.mid(8, 4).toUInt(false);
d->sampleRate = sftable[flags[17] * 2 + flags[16]];
d->channels = 2;
}
else {
uint headerData = d->data.mid(0, 4).toUInt(false);
d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff;
d->sampleRate = 44100;
d->channels = 2;
if(d->version >= 5)
frames = d->data.mid(4, 4).toUInt(false);
else
frames = d->data.mid(6, 2).toUInt(false);
}
uint samples = frames * 1152 - 576;
d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
if(!d->bitrate)
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

View file

@ -0,0 +1,85 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_MPCPROPERTIES_H
#define TAGLIB_MPCPROPERTIES_H
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
namespace MPC {
class File;
static const uint HeaderSize = 8*7;
//! An implementation of audio property reading for MPC
/*!
* This reads the data from an MPC stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of MPC::Properties with the data read from the
* ByteVector \a data.
*/
Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
/*!
* Destroys this MPC::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns the version of the bitstream (SV4-SV7)
*/
int mpcVersion() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,219 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "id3v1genres.h"
using namespace TagLib;
namespace TagLib {
namespace ID3v1 {
static const int genresSize = 148;
static const String genres[] = {
"Blues",
"Classic Rock",
"Country",
"Dance",
"Disco",
"Funk",
"Grunge",
"Hip-Hop",
"Jazz",
"Metal",
"New Age",
"Oldies",
"Other",
"Pop",
"R&B",
"Rap",
"Reggae",
"Rock",
"Techno",
"Industrial",
"Alternative",
"Ska",
"Death Metal",
"Pranks",
"Soundtrack",
"Euro-Techno",
"Ambient",
"Trip-Hop",
"Vocal",
"Jazz+Funk",
"Fusion",
"Trance",
"Classical",
"Instrumental",
"Acid",
"House",
"Game",
"Sound Clip",
"Gospel",
"Noise",
"Alternative Rock",
"Bass",
"Soul",
"Punk",
"Space",
"Meditative",
"Instrumental Pop",
"Instrumental Rock",
"Ethnic",
"Gothic",
"Darkwave",
"Techno-Industrial",
"Electronic",
"Pop-Folk",
"Eurodance",
"Dream",
"Southern Rock",
"Comedy",
"Cult",
"Gangsta",
"Top 40",
"Christian Rap",
"Pop/Funk",
"Jungle",
"Native American",
"Cabaret",
"New Wave",
"Psychedelic",
"Rave",
"Showtunes",
"Trailer",
"Lo-Fi",
"Tribal",
"Acid Punk",
"Acid Jazz",
"Polka",
"Retro",
"Musical",
"Rock & Roll",
"Hard Rock",
"Folk",
"Folk/Rock",
"National Folk",
"Swing",
"Fusion",
"Bebob",
"Latin",
"Revival",
"Celtic",
"Bluegrass",
"Avantgarde",
"Gothic Rock",
"Progressive Rock",
"Psychedelic Rock",
"Symphonic Rock",
"Slow Rock",
"Big Band",
"Chorus",
"Easy Listening",
"Acoustic",
"Humour",
"Speech",
"Chanson",
"Opera",
"Chamber Music",
"Sonata",
"Symphony",
"Booty Bass",
"Primus",
"Porn Groove",
"Satire",
"Slow Jam",
"Club",
"Tango",
"Samba",
"Folklore",
"Ballad",
"Power Ballad",
"Rhythmic Soul",
"Freestyle",
"Duet",
"Punk Rock",
"Drum Solo",
"A Cappella",
"Euro-House",
"Dance Hall",
"Goa",
"Drum & Bass",
"Club-House",
"Hardcore",
"Terror",
"Indie",
"BritPop",
"Negerpunk",
"Polsk Punk",
"Beat",
"Christian Gangsta Rap",
"Heavy Metal",
"Black Metal",
"Crossover",
"Contemporary Christian",
"Christian Rock",
"Merengue",
"Salsa",
"Thrash Metal",
"Anime",
"Jpop",
"Synthpop"
};
}
}
StringList ID3v1::genreList()
{
static StringList l;
if(l.isEmpty()) {
for(int i = 0; i < genresSize; i++)
l.append(genres[i]);
}
return l;
}
ID3v1::GenreMap ID3v1::genreMap()
{
static GenreMap m;
if(m.isEmpty()) {
for(int i = 0; i < genresSize; i++)
m.insert(genres[i], i);
}
return m;
}
String ID3v1::genre(int i)
{
if(i >= 0 && i < genresSize)
return genres[i];
return String::null;
}
int ID3v1::genreIndex(const String &name)
{
if(genreMap().contains(name))
return genreMap()[name];
return 255;
}

View file

@ -0,0 +1,66 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V1GENRE_H
#define TAGLIB_ID3V1GENRE_H
#include "tmap.h"
#include "tstringlist.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v1 {
typedef Map<String, int> GenreMap;
/*!
* Returns the list of canonical ID3v1 genre names in the order that they
* are listed in the standard.
*/
StringList TAGLIB_EXPORT genreList();
/*!
* A "reverse mapping" that goes from the canonical ID3v1 genre name to the
* respective genre number. genreMap()["Rock"] ==
*/
GenreMap genreMap();
/*!
* Returns the name of the genre at \a index in the ID3v1 genre list. If
* \a index is out of range -- less than zero or greater than 146 -- a null
* string will be returned.
*/
String genre(int index);
/*!
* Returns the genre index for the (case sensitive) genre \a name. If the
* genre is not in the list 255 (which signifies an unknown genre in ID3v1)
* will be returned.
*/
int genreIndex(const String &name);
}
}
#endif

View file

@ -0,0 +1,248 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tdebug.h>
#include <tfile.h>
#include "id3v1tag.h"
#include "id3v1genres.h"
using namespace TagLib;
using namespace ID3v1;
class ID3v1::Tag::TagPrivate
{
public:
TagPrivate() : file(0), tagOffset(-1), track(0), genre(255) {}
File *file;
long tagOffset;
String title;
String artist;
String album;
String year;
String comment;
uchar track;
uchar genre;
static const StringHandler *stringHandler;
};
const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = new StringHandler;
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
String ID3v1::StringHandler::parse(const ByteVector &data) const
{
return String(data, String::Latin1).stripWhiteSpace();
}
ByteVector ID3v1::StringHandler::render(const String &s) const
{
if(!s.isLatin1())
{
return ByteVector();
}
return s.data(String::Latin1);
}
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
ID3v1::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
ID3v1::Tag::Tag(File *file, long tagOffset) : TagLib::Tag()
{
d = new TagPrivate;
d->file = file;
d->tagOffset = tagOffset;
read();
}
ID3v1::Tag::~Tag()
{
delete d;
}
ByteVector ID3v1::Tag::render() const
{
ByteVector data;
data.append(fileIdentifier());
data.append(TagPrivate::stringHandler->render(d->title).resize(30));
data.append(TagPrivate::stringHandler->render(d->artist).resize(30));
data.append(TagPrivate::stringHandler->render(d->album).resize(30));
data.append(TagPrivate::stringHandler->render(d->year).resize(4));
data.append(TagPrivate::stringHandler->render(d->comment).resize(28));
data.append(char(0));
data.append(char(d->track));
data.append(char(d->genre));
return data;
}
ByteVector ID3v1::Tag::fileIdentifier()
{
return ByteVector::fromCString("TAG");
}
String ID3v1::Tag::title() const
{
return d->title;
}
String ID3v1::Tag::artist() const
{
return d->artist;
}
String ID3v1::Tag::album() const
{
return d->album;
}
String ID3v1::Tag::comment() const
{
return d->comment;
}
String ID3v1::Tag::genre() const
{
return ID3v1::genre(d->genre);
}
TagLib::uint ID3v1::Tag::year() const
{
return d->year.toInt();
}
TagLib::uint ID3v1::Tag::track() const
{
return d->track;
}
void ID3v1::Tag::setTitle(const String &s)
{
d->title = s;
}
void ID3v1::Tag::setArtist(const String &s)
{
d->artist = s;
}
void ID3v1::Tag::setAlbum(const String &s)
{
d->album = s;
}
void ID3v1::Tag::setComment(const String &s)
{
d->comment = s;
}
void ID3v1::Tag::setGenre(const String &s)
{
d->genre = ID3v1::genreIndex(s);
}
void ID3v1::Tag::setYear(uint i)
{
d->year = i > 0 ? String::number(i) : String::null;
}
void ID3v1::Tag::setTrack(uint i)
{
d->track = i < 256 ? i : 0;
}
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{
delete TagPrivate::stringHandler;
TagPrivate::stringHandler = handler;
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
void ID3v1::Tag::read()
{
if(d->file && d->file->isValid()) {
d->file->seek(d->tagOffset);
// read the tag -- always 128 bytes
ByteVector data = d->file->readBlock(128);
// some initial sanity checking
if(data.size() == 128 && data.startsWith("TAG"))
parse(data);
else
debug("ID3v1 tag is not valid or could not be read at the specified offset.");
}
}
void ID3v1::Tag::parse(const ByteVector &data)
{
int offset = 3;
d->title = TagPrivate::stringHandler->parse(data.mid(offset, 30));
offset += 30;
d->artist = TagPrivate::stringHandler->parse(data.mid(offset, 30));
offset += 30;
d->album = TagPrivate::stringHandler->parse(data.mid(offset, 30));
offset += 30;
d->year = TagPrivate::stringHandler->parse(data.mid(offset, 4));
offset += 4;
// Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this
// is not a bug in TagLib. Since a zeroed byte is what we would expect to
// indicate the end of a C-String, specifically the comment string, a value of
// zero must be assumed to be just that.
if(data[offset + 28] == 0 && data[offset + 29] != 0) {
// ID3v1.1 detected
d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 28));
d->track = uchar(data[offset + 29]);
}
else
d->comment = data.mid(offset, 30);
offset += 30;
d->genre = uchar(data[offset]);
}

View file

@ -0,0 +1,180 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V1TAG_H
#define TAGLIB_ID3V1TAG_H
#include "tag.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
class File;
//! An ID3v1 implementation
namespace ID3v1 {
//! A abstraction for the string to data encoding in ID3v1 tags.
/*!
* ID3v1 should in theory always contain ISO-8859-1 (Latin1) data. In
* practice it does not. TagLib by default only supports ISO-8859-1 data
* in ID3v1 tags.
*
* However by subclassing this class and reimplementing parse() and render()
* and setting your reimplementation as the default with
* ID3v1::Tag::setStringHandler() you can define how you would like these
* transformations to be done.
*
* \warning It is advisable <b>not</b> to write non-ISO-8859-1 data to ID3v1
* tags. Please consider disabling the writing of ID3v1 tags in the case
* that the data is ISO-8859-1.
*
* \see ID3v1::Tag::setStringHandler()
*/
class TAGLIB_EXPORT StringHandler
{
public:
// BIC: Add virtual destructor.
/*!
* Decode a string from \a data. The default implementation assumes that
* \a data is an ISO-8859-1 (Latin1) character array.
*/
virtual String parse(const ByteVector &data) const;
/*!
* Encode a ByteVector with the data from \a s. The default implementation
* assumes that \a s is an ISO-8859-1 (Latin1) string. If the string is
* does not conform to ISO-8859-1, no value is written.
*
* \warning It is recommended that you <b>not</b> override this method, but
* instead do not write an ID3v1 tag in the case that the data is not
* ISO-8859-1.
*/
virtual ByteVector render(const String &s) const;
};
//! The main class in the ID3v1 implementation
/*!
* This is an implementation of the ID3v1 format. ID3v1 is both the simplist
* and most common of tag formats but is rather limited. Because of its
* pervasiveness and the way that applications have been written around the
* fields that it provides, the generic TagLib::Tag API is a mirror of what is
* provided by ID3v1.
*
* ID3v1 tags should generally only contain Latin1 information. However because
* many applications do not follow this rule there is now support for overriding
* the ID3v1 string handling using the ID3v1::StringHandler class. Please see
* the documentation for that class for more information.
*
* \see StringHandler
*
* \note Most fields are truncated to a maximum of 28-30 bytes. The
* truncation happens automatically when the tag is rendered.
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
/*!
* Create an ID3v1 tag with default values.
*/
Tag();
/*!
* Create an ID3v1 tag and parse the data in \a file starting at
* \a tagOffset.
*/
Tag(File *file, long tagOffset);
/*!
* Destroys this Tag instance.
*/
virtual ~Tag();
/*!
* Renders the in memory values to a ByteVector suitable for writing to
* the file.
*/
ByteVector render() const;
/*!
* Returns the string "TAG" suitable for usage in locating the tag in a
* file.
*/
static ByteVector fileIdentifier();
// Reimplementations.
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
/*!
* Sets the string handler that decides how the ID3v1 data will be
* converted to and from binary data.
*
* \see StringHandler
*/
static void setStringHandler(const StringHandler *handler);
protected:
/*!
* Reads from the file specified in the constructor.
*/
void read();
/*!
* Pareses the body of the tag in \a data.
*/
void parse(const ByteVector &data);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,170 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "attachedpictureframe.h"
#include <tstringlist.h>
#include <tdebug.h>
using namespace TagLib;
using namespace ID3v2;
class AttachedPictureFrame::AttachedPictureFramePrivate
{
public:
AttachedPictureFramePrivate() : textEncoding(String::Latin1),
type(AttachedPictureFrame::Other) {}
String::Type textEncoding;
String mimeType;
AttachedPictureFrame::Type type;
String description;
ByteVector data;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC")
{
d = new AttachedPictureFramePrivate;
}
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data)
{
d = new AttachedPictureFramePrivate;
setData(data);
}
AttachedPictureFrame::~AttachedPictureFrame()
{
delete d;
}
String AttachedPictureFrame::toString() const
{
String s = "[" + d->mimeType + "]";
return d->description.isEmpty() ? s : d->description + " " + s;
}
String::Type AttachedPictureFrame::textEncoding() const
{
return d->textEncoding;
}
void AttachedPictureFrame::setTextEncoding(String::Type t)
{
d->textEncoding = t;
}
String AttachedPictureFrame::mimeType() const
{
return d->mimeType;
}
void AttachedPictureFrame::setMimeType(const String &m)
{
d->mimeType = m;
}
AttachedPictureFrame::Type AttachedPictureFrame::type() const
{
return d->type;
}
void AttachedPictureFrame::setType(Type t)
{
d->type = t;
}
String AttachedPictureFrame::description() const
{
return d->description;
}
void AttachedPictureFrame::setDescription(const String &desc)
{
d->description = desc;
}
ByteVector AttachedPictureFrame::picture() const
{
return d->data;
}
void AttachedPictureFrame::setPicture(const ByteVector &p)
{
d->data = p;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void AttachedPictureFrame::parseFields(const ByteVector &data)
{
if(data.size() < 5) {
debug("A picture frame must contain at least 5 bytes.");
return;
}
d->textEncoding = String::Type(data[0]);
int pos = 1;
d->mimeType = readStringField(data, String::Latin1, &pos);
d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos);
}
ByteVector AttachedPictureFrame::renderFields() const
{
ByteVector data;
String::Type encoding = checkEncoding(d->description, d->textEncoding);
data.append(char(encoding));
data.append(d->mimeType.data(String::Latin1));
data.append(textDelimiter(String::Latin1));
data.append(char(d->type));
data.append(d->description.data(encoding));
data.append(textDelimiter(encoding));
data.append(d->data);
return data;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new AttachedPictureFramePrivate;
parseFields(fieldData(data));
}

View file

@ -0,0 +1,220 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ATTACHEDPICTUREFRAME_H
#define TAGLIB_ATTACHEDPICTUREFRAME_H
#include <id3v2frame.h>
#include <id3v2header.h>
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An ID3v2 attached picture frame implementation
/*!
* This is an implementation of ID3v2 attached pictures. Pictures may be
* included in tags, one per APIC frame (but there may be multiple APIC
* frames in a single tag). These pictures are usually in either JPEG or
* PNG format.
*/
class TAGLIB_EXPORT AttachedPictureFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* This describes the function or content of the picture.
*/
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
/*!
* Constructs an empty picture frame. The description, content and text
* encoding should be set manually.
*/
AttachedPictureFrame();
/*!
* Constructs an AttachedPicture frame based on \a data.
*/
explicit AttachedPictureFrame(const ByteVector &data);
/*!
* Destroys the AttahcedPictureFrame instance.
*/
virtual ~AttachedPictureFrame();
/*!
* Returns a string containing the description and mime-type
*/
virtual String toString() const;
/*!
* Returns the text encoding used for the description.
*
* \see setTextEncoding()
* \see description()
*/
String::Type textEncoding() const;
/*!
* Set the text encoding used for the description.
*
* \see description()
*/
void setTextEncoding(String::Type t);
/*!
* Returns the mime type of the image. This should in most cases be
* "image/png" or "image/jpeg".
*/
String mimeType() const;
/*!
* Sets the mime type of the image. This should in most cases be
* "image/png" or "image/jpeg".
*/
void setMimeType(const String &m);
/*!
* Returns the type of the image.
*
* \see Type
* \see setType()
*/
Type type() const;
/*!
* Sets the type for the image.
*
* \see Type
* \see type()
*/
void setType(Type t);
/*!
* Returns a text description of the image.
*
* \see setDescription()
* \see textEncoding()
* \see setTextEncoding()
*/
String description() const;
/*!
* Sets a textual description of the image to \a desc.
*
* \see description()
* \see textEncoding()
* \see setTextEncoding()
*/
void setDescription(const String &desc);
/*!
* Returns the image data as a ByteVector.
*
* \note ByteVector has a data() method that returns a const char * which
* should make it easy to export this data to external programs.
*
* \see setPicture()
* \see mimeType()
*/
ByteVector picture() const;
/*!
* Sets the image data to \a p. \a p should be of the type specified in
* this frame's mime-type specification.
*
* \see picture()
* \see mimeType()
* \see setMimeType()
*/
void setPicture(const ByteVector &p);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
AttachedPictureFrame(const ByteVector &data, Header *h);
AttachedPictureFrame(const AttachedPictureFrame &);
AttachedPictureFrame &operator=(const AttachedPictureFrame &);
class AttachedPictureFramePrivate;
AttachedPictureFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,178 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tbytevectorlist.h>
#include <id3v2tag.h>
#include <tdebug.h>
#include <tstringlist.h>
#include "commentsframe.h"
using namespace TagLib;
using namespace ID3v2;
class CommentsFrame::CommentsFramePrivate
{
public:
CommentsFramePrivate() : textEncoding(String::Latin1) {}
String::Type textEncoding;
ByteVector language;
String description;
String text;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM")
{
d = new CommentsFramePrivate;
d->textEncoding = encoding;
}
CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data)
{
d = new CommentsFramePrivate;
setData(data);
}
CommentsFrame::~CommentsFrame()
{
delete d;
}
String CommentsFrame::toString() const
{
return d->text;
}
ByteVector CommentsFrame::language() const
{
return d->language;
}
String CommentsFrame::description() const
{
return d->description;
}
String CommentsFrame::text() const
{
return d->text;
}
void CommentsFrame::setLanguage(const ByteVector &languageEncoding)
{
d->language = languageEncoding.mid(0, 3);
}
void CommentsFrame::setDescription(const String &s)
{
d->description = s;
}
void CommentsFrame::setText(const String &s)
{
d->text = s;
}
String::Type CommentsFrame::textEncoding() const
{
return d->textEncoding;
}
void CommentsFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
{
ID3v2::FrameList comments = tag->frameList("COMM");
for(ID3v2::FrameList::ConstIterator it = comments.begin();
it != comments.end();
++it)
{
CommentsFrame *frame = dynamic_cast<CommentsFrame *>(*it);
if(frame && frame->description() == d)
return frame;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void CommentsFrame::parseFields(const ByteVector &data)
{
if(data.size() < 5) {
debug("A comment frame must contain at least 5 bytes.");
return;
}
d->textEncoding = String::Type(data[0]);
d->language = data.mid(1, 3);
int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
if(l.size() == 2) {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
}
}
ByteVector CommentsFrame::renderFields() const
{
ByteVector v;
String::Type encoding = d->textEncoding;
encoding = checkEncoding(d->description, encoding);
encoding = checkEncoding(d->text, encoding);
v.append(char(encoding));
v.append(d->language.size() == 3 ? d->language : "XXX");
v.append(d->description.data(encoding));
v.append(textDelimiter(encoding));
v.append(d->text.data(encoding));
return v;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new CommentsFramePrivate();
parseFields(fieldData(data));
}

View file

@ -0,0 +1,168 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_COMMENTSFRAME_H
#define TAGLIB_COMMENTSFRAME_H
#include <id3v2frame.h>
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An implementation of ID3v2 comments
/*!
* This implements the ID3v2 comment format. An ID3v2 comment concists of
* a language encoding, a description and a single text field.
*/
class TAGLIB_EXPORT CommentsFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Construct an empty comment frame that will use the text encoding
* \a encoding.
*/
explicit CommentsFrame(String::Type encoding = String::Latin1);
/*!
* Construct a comment based on the data in \a data.
*/
explicit CommentsFrame(const ByteVector &data);
/*!
* Destroys this CommentFrame instance.
*/
virtual ~CommentsFrame();
/*!
* Returns the text of this comment.
*
* \see text()
*/
virtual String toString() const;
/*!
* Returns the language encoding as a 3 byte encoding as specified by
* <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>.
*
* \note Most taggers simply ignore this value.
*
* \see setLanguage()
*/
ByteVector language() const;
/*!
* Returns the description of this comment.
*
* \note Most taggers simply ignore this value.
*
* \see setDescription()
*/
String description() const;
/*!
* Returns the text of this comment.
*
* \see setText()
*/
String text() const;
/*!
* Set the language using the 3 byte language code from
* <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to
* \a languageCode.
*
* \see language()
*/
void setLanguage(const ByteVector &languageCode);
/*!
* Sets the description of the comment to \a s.
*
* \see decription()
*/
void setDescription(const String &s);
/*!
* Sets the text portion of the comment to \a s.
*
* \see text()
*/
virtual void setText(const String &s);
/*!
* Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor
* or read from the frame when parsed.
*
* \see setTextEncoding()
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.
*
* \see textEncoding()
* \see render()
*/
void setTextEncoding(String::Type encoding);
/*!
* Comments each have a unique description. This searches for a comment
* frame with the decription \a d and returns a pointer to it. If no
* frame is found that matches the given description null is returned.
*
* \see description()
*/
static CommentsFrame *findByDescription(const Tag *tag, const String &d);
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
/*!
* The constructor used by the FrameFactory.
*/
CommentsFrame(const ByteVector &data, Header *h);
CommentsFrame(const CommentsFrame &);
CommentsFrame &operator=(const CommentsFrame &);
class CommentsFramePrivate;
CommentsFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,176 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Aaron VonderHaar
email : avh4@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tdebug.h>
#include "generalencapsulatedobjectframe.h"
using namespace TagLib;
using namespace ID3v2;
class GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFramePrivate
{
public:
GeneralEncapsulatedObjectFramePrivate() : textEncoding(String::Latin1) {}
String::Type textEncoding;
String mimeType;
String fileName;
String description;
ByteVector data;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB")
{
d = new GeneralEncapsulatedObjectFramePrivate;
}
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data)
{
d = new GeneralEncapsulatedObjectFramePrivate;
setData(data);
}
GeneralEncapsulatedObjectFrame::~GeneralEncapsulatedObjectFrame()
{
delete d;
}
String GeneralEncapsulatedObjectFrame::toString() const
{
String text = "[" + d->mimeType + "]";
if(!d->fileName.isEmpty())
text += " " + d->fileName;
if(!d->description.isEmpty())
text += " \"" + d->description + "\"";
return text;
}
String::Type GeneralEncapsulatedObjectFrame::textEncoding() const
{
return d->textEncoding;
}
void GeneralEncapsulatedObjectFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
String GeneralEncapsulatedObjectFrame::mimeType() const
{
return d->mimeType;
}
void GeneralEncapsulatedObjectFrame::setMimeType(const String &type)
{
d->mimeType = type;
}
String GeneralEncapsulatedObjectFrame::fileName() const
{
return d->fileName;
}
void GeneralEncapsulatedObjectFrame::setFileName(const String &name)
{
d->fileName = name;
}
String GeneralEncapsulatedObjectFrame::description() const
{
return d->description;
}
void GeneralEncapsulatedObjectFrame::setDescription(const String &desc)
{
d->description = desc;
}
ByteVector GeneralEncapsulatedObjectFrame::object() const
{
return d->data;
}
void GeneralEncapsulatedObjectFrame::setObject(const ByteVector &data)
{
d->data = data;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data)
{
if(data.size() < 4) {
debug("An object frame must contain at least 4 bytes.");
return;
}
d->textEncoding = String::Type(data[0]);
int pos = 1;
d->mimeType = readStringField(data, String::Latin1, &pos);
d->fileName = readStringField(data, d->textEncoding, &pos);
d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos);
}
ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
{
ByteVector data;
data.append(char(d->textEncoding));
data.append(d->mimeType.data(String::Latin1));
data.append(textDelimiter(String::Latin1));
data.append(d->fileName.data(d->textEncoding));
data.append(textDelimiter(d->textEncoding));
data.append(d->description.data(d->textEncoding));
data.append(textDelimiter(d->textEncoding));
data.append(d->data);
return data;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new GeneralEncapsulatedObjectFramePrivate;
parseFields(fieldData(data));
}

View file

@ -0,0 +1,174 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Aaron VonderHaar
email : avh4@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_GENERALENCAPSULATEDOBJECT_H
#define TAGLIB_GENERALENCAPSULATEDOBJECT_H
#include <id3v2frame.h>
#include <id3v2header.h>
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An ID3v2 general encapsulated object frame implementation
/*!
* This is an implementation of ID3v2 general encapsulated objects.
* Arbitrary binary data may be included in tags, stored in GEOB frames.
* There may be multiple GEOB frames in a single tag. Each GEOB it
* labelled with a content description (which may be blank), a required
* mime-type, and a file name (may be blank). The content description
* uniquely identifies the GEOB frame in the tag.
*/
class TAGLIB_EXPORT GeneralEncapsulatedObjectFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Constructs an empty object frame. The description, file name and text
* encoding should be set manually.
*/
GeneralEncapsulatedObjectFrame();
/*!
* Constructs a GeneralEncapsulatedObjectFrame frame based on \a data.
*/
explicit GeneralEncapsulatedObjectFrame(const ByteVector &data);
/*!
* Destroys the GeneralEncapsulatedObjectFrame instance.
*/
virtual ~GeneralEncapsulatedObjectFrame();
/*!
* Returns a string containing the description, file name and mime-type
*/
virtual String toString() const;
/*!
* Returns the text encoding used for the description and file name.
*
* \see setTextEncoding()
* \see description()
* \see fileName()
*/
String::Type textEncoding() const;
/*!
* Set the text encoding used for the description and file name.
*
* \see description()
* \see fileName()
*/
void setTextEncoding(String::Type encoding);
/*!
* Returns the mime type of the object.
*/
String mimeType() const;
/*!
* Sets the mime type of the object.
*/
void setMimeType(const String &type);
/*!
* Returns the file name of the object.
*
* \see setFileName()
*/
String fileName() const;
/*!
* Sets the file name for the object.
*
* \see fileName()
*/
void setFileName(const String &name);
/*!
* Returns the content description of the object.
*
* \see setDescription()
* \see textEncoding()
* \see setTextEncoding()
*/
String description() const;
/*!
* Sets the content description of the object to \a desc.
*
* \see description()
* \see textEncoding()
* \see setTextEncoding()
*/
void setDescription(const String &desc);
/*!
* Returns the object data as a ByteVector.
*
* \note ByteVector has a data() method that returns a const char * which
* should make it easy to export this data to external programs.
*
* \see setObject()
* \see mimeType()
*/
ByteVector object() const;
/*!
* Sets the object data to \a data. \a data should be of the type specified in
* this frame's mime-type specification.
*
* \see object()
* \see mimeType()
* \see setMimeType()
*/
void setObject(const ByteVector &object);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h);
GeneralEncapsulatedObjectFrame(const GeneralEncapsulatedObjectFrame &);
GeneralEncapsulatedObjectFrame &operator=(const GeneralEncapsulatedObjectFrame &);
class GeneralEncapsulatedObjectFramePrivate;
GeneralEncapsulatedObjectFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,236 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tdebug.h>
#include <tmap.h>
#include "relativevolumeframe.h"
using namespace TagLib;
using namespace ID3v2;
static inline int bitsToBytes(int i)
{
return i % 8 == 0 ? i / 8 : (i - i % 8) / 8 + 1;
}
struct ChannelData
{
ChannelData() : channelType(RelativeVolumeFrame::Other), volumeAdjustment(0) {}
RelativeVolumeFrame::ChannelType channelType;
short volumeAdjustment;
RelativeVolumeFrame::PeakVolume peakVolume;
};
class RelativeVolumeFrame::RelativeVolumeFramePrivate
{
public:
String identification;
Map<ChannelType, ChannelData> channels;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2")
{
d = new RelativeVolumeFramePrivate;
}
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data)
{
d = new RelativeVolumeFramePrivate;
setData(data);
}
RelativeVolumeFrame::~RelativeVolumeFrame()
{
delete d;
}
String RelativeVolumeFrame::toString() const
{
return d->identification;
}
List<RelativeVolumeFrame::ChannelType> RelativeVolumeFrame::channels() const
{
List<ChannelType> l;
Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();
for(; it != d->channels.end(); ++it)
l.append((*it).first);
return l;
}
// deprecated
RelativeVolumeFrame::ChannelType RelativeVolumeFrame::channelType() const
{
return MasterVolume;
}
// deprecated
void RelativeVolumeFrame::setChannelType(ChannelType)
{
}
short RelativeVolumeFrame::volumeAdjustmentIndex(ChannelType type) const
{
return d->channels.contains(type) ? d->channels[type].volumeAdjustment : 0;
}
short RelativeVolumeFrame::volumeAdjustmentIndex() const
{
return volumeAdjustmentIndex(MasterVolume);
}
void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index, ChannelType type)
{
d->channels[type].volumeAdjustment = index;
}
void RelativeVolumeFrame::setVolumeAdjustmentIndex(short index)
{
setVolumeAdjustmentIndex(index, MasterVolume);
}
float RelativeVolumeFrame::volumeAdjustment(ChannelType type) const
{
return d->channels.contains(type) ? float(d->channels[type].volumeAdjustment) / float(512) : 0;
}
float RelativeVolumeFrame::volumeAdjustment() const
{
return volumeAdjustment(MasterVolume);
}
void RelativeVolumeFrame::setVolumeAdjustment(float adjustment, ChannelType type)
{
d->channels[type].volumeAdjustment = short(adjustment * float(512));
}
void RelativeVolumeFrame::setVolumeAdjustment(float adjustment)
{
setVolumeAdjustment(adjustment, MasterVolume);
}
RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume(ChannelType type) const
{
return d->channels.contains(type) ? d->channels[type].peakVolume : PeakVolume();
}
RelativeVolumeFrame::PeakVolume RelativeVolumeFrame::peakVolume() const
{
return peakVolume(MasterVolume);
}
void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak, ChannelType type)
{
d->channels[type].peakVolume = peak;
}
void RelativeVolumeFrame::setPeakVolume(const PeakVolume &peak)
{
setPeakVolume(peak, MasterVolume);
}
String RelativeVolumeFrame::identification() const
{
return d->identification;
}
void RelativeVolumeFrame::setIdentification(const String &s)
{
d->identification = s;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void RelativeVolumeFrame::parseFields(const ByteVector &data)
{
int pos = 0;
d->identification = readStringField(data, String::Latin1, &pos);
// Each channel is at least 4 bytes.
while(pos <= (int)data.size() - 4) {
ChannelType type = ChannelType(data[pos]);
pos += 1;
ChannelData &channel = d->channels[type];
channel.volumeAdjustment = data.mid(pos, 2).toShort();
pos += 2;
channel.peakVolume.bitsRepresentingPeak = data[pos];
pos += 1;
int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak);
channel.peakVolume.peakVolume = data.mid(pos, bytes);
pos += bytes;
}
}
ByteVector RelativeVolumeFrame::renderFields() const
{
ByteVector data;
data.append(d->identification.data(String::Latin1));
data.append(textDelimiter(String::Latin1));
Map<ChannelType, ChannelData>::ConstIterator it = d->channels.begin();
for(; it != d->channels.end(); ++it) {
ChannelType type = (*it).first;
const ChannelData &channel = (*it).second;
data.append(char(type));
data.append(ByteVector::fromShort(channel.volumeAdjustment));
data.append(char(channel.peakVolume.bitsRepresentingPeak));
data.append(channel.peakVolume.peakVolume);
}
return data;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new RelativeVolumeFramePrivate;
parseFields(fieldData(data));
}

View file

@ -0,0 +1,274 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_RELATIVEVOLUMEFRAME_H
#define TAGLIB_RELATIVEVOLUMEFRAME_H
#include <tlist.h>
#include <id3v2frame.h>
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An ID3v2 relative volume adjustment frame implementation
/*!
* This is an implementation of ID3v2 relative volume adjustment. The
* presence of this frame makes it possible to specify an increase in volume
* for an audio file or specific audio tracks in that file.
*
* Multiple relative volume adjustment frames may be present in the tag
* each with a unique identification and describing volume adjustment for
* different channel types.
*/
class TAGLIB_EXPORT RelativeVolumeFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* This indicates the type of volume adjustment that should be applied.
*/
enum ChannelType {
//! A type not enumerated below
Other = 0x00,
//! The master volume for the track
MasterVolume = 0x01,
//! The front right audio channel
FrontRight = 0x02,
//! The front left audio channel
FrontLeft = 0x03,
//! The back right audio channel
BackRight = 0x04,
//! The back left audio channel
BackLeft = 0x05,
//! The front center audio channel
FrontCentre = 0x06,
//! The back center audio channel
BackCentre = 0x07,
//! The subwoofer audio channel
Subwoofer = 0x08
};
//! Struct that stores the relevant values for ID3v2 peak volume
/*!
* The peak volume is described as a series of bits that is padded to fill
* a block of bytes. These two values should always be updated in tandem.
*/
struct PeakVolume
{
/*!
* Constructs an empty peak volume description.
*/
PeakVolume() : bitsRepresentingPeak(0) {}
/*!
* The number of bits (in the range of 0 to 255) used to describe the
* peak volume.
*/
unsigned char bitsRepresentingPeak;
/*!
* The array of bits (represented as a series of bytes) used to describe
* the peak volume.
*/
ByteVector peakVolume;
};
/*!
* Constructs a RelativeVolumeFrame. The relevant data should be set
* manually.
*/
RelativeVolumeFrame();
/*!
* Constructs a RelativeVolumeFrame based on the contents of \a data.
*/
RelativeVolumeFrame(const ByteVector &data);
/*!
* Destroys the RelativeVolumeFrame instance.
*/
virtual ~RelativeVolumeFrame();
/*!
* Returns the frame's identification.
*
* \see identification()
*/
virtual String toString() const;
/*!
* Returns a list of channels with information currently in the frame.
*/
List<ChannelType> channels() const;
/*!
* \deprecated Always returns master volume.
*/
ChannelType channelType() const;
/*!
* \deprecated This method no longer has any effect.
*/
void setChannelType(ChannelType t);
/*
* There was a terrible API goof here, and while this can't be changed to
* the way it appears below for binary compaibility reasons, let's at
* least pretend that it looks clean.
*/
#ifdef DOXYGEN
/*!
* Returns the relative volume adjustment "index". As indicated by the
* ID3v2 standard this is a 16-bit signed integer that reflects the
* decibils of adjustment when divided by 512.
*
* This defaults to returning the value for the master volume channel if
* available and returns 0 if the specified channel does not exist.
*
* \see setVolumeAdjustmentIndex()
* \see volumeAjustment()
*/
short volumeAdjustmentIndex(ChannelType type = MasterVolume) const;
/*!
* Set the volume adjustment to \a index. As indicated by the ID3v2
* standard this is a 16-bit signed integer that reflects the decibils of
* adjustment when divided by 512.
*
* By default this sets the value for the master volume.
*
* \see volumeAdjustmentIndex()
* \see setVolumeAjustment()
*/
void setVolumeAdjustmentIndex(short index, ChannelType type = MasterVolume);
/*!
* Returns the relative volume adjustment in decibels.
*
* \note Because this is actually stored internally as an "index" to this
* value the value returned by this method may not be identical to the
* value set using setVolumeAdjustment().
*
* This defaults to returning the value for the master volume channel if
* available and returns 0 if the specified channel does not exist.
*
* \see setVolumeAdjustment()
* \see volumeAdjustmentIndex()
*/
float volumeAdjustment(ChannelType type = MasterVolume) const;
/*!
* Set the relative volume adjustment in decibels to \a adjustment.
*
* By default this sets the value for the master volume.
*
* \note Because this is actually stored internally as an "index" to this
* value the value set by this method may not be identical to the one
* returned by volumeAdjustment().
*
* \see setVolumeAdjustment()
* \see volumeAdjustmentIndex()
*/
void setVolumeAdjustment(float adjustment, ChannelType type = MasterVolume);
/*!
* Returns the peak volume (represented as a length and a string of bits).
*
* This defaults to returning the value for the master volume channel if
* available and returns 0 if the specified channel does not exist.
*
* \see setPeakVolume()
*/
PeakVolume peakVolume(ChannelType type = MasterVolume) const;
/*!
* Sets the peak volume to \a peak.
*
* By default this sets the value for the master volume.
*
* \see peakVolume()
*/
void setPeakVolume(const PeakVolume &peak, ChannelType type = MasterVolume);
#else
// BIC: Combine each of the following pairs of functions (or maybe just
// rework this junk altogether).
short volumeAdjustmentIndex(ChannelType type) const;
short volumeAdjustmentIndex() const;
void setVolumeAdjustmentIndex(short index, ChannelType type);
void setVolumeAdjustmentIndex(short index);
float volumeAdjustment(ChannelType type) const;
float volumeAdjustment() const;
void setVolumeAdjustment(float adjustment, ChannelType type);
void setVolumeAdjustment(float adjustment);
PeakVolume peakVolume(ChannelType type) const;
PeakVolume peakVolume() const;
void setPeakVolume(const PeakVolume &peak, ChannelType type);
void setPeakVolume(const PeakVolume &peak);
#endif
/*!
* Returns the identification for this frame.
*/
String identification() const;
/*!
* Sets the identification of the frame to \a s. The string
* is used to identify the situation and/or device where this
* adjustment should apply.
*/
void setIdentification(const String &s);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
RelativeVolumeFrame(const ByteVector &data, Header *h);
RelativeVolumeFrame(const RelativeVolumeFrame &);
RelativeVolumeFrame &operator=(const RelativeVolumeFrame &);
class RelativeVolumeFramePrivate;
RelativeVolumeFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,271 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tbytevectorlist.h>
#include <id3v2tag.h>
#include "textidentificationframe.h"
using namespace TagLib;
using namespace ID3v2;
class TextIdentificationFrame::TextIdentificationFramePrivate
{
public:
TextIdentificationFramePrivate() : textEncoding(String::Latin1) {}
String::Type textEncoding;
StringList fieldList;
};
////////////////////////////////////////////////////////////////////////////////
// TextIdentificationFrame public members
////////////////////////////////////////////////////////////////////////////////
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) :
Frame(type)
{
d = new TextIdentificationFramePrivate;
d->textEncoding = encoding;
}
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) :
Frame(data)
{
d = new TextIdentificationFramePrivate;
setData(data);
}
TextIdentificationFrame::~TextIdentificationFrame()
{
delete d;
}
void TextIdentificationFrame::setText(const StringList &l)
{
d->fieldList = l;
}
void TextIdentificationFrame::setText(const String &s)
{
d->fieldList = s;
}
String TextIdentificationFrame::toString() const
{
return d->fieldList.toString();
}
StringList TextIdentificationFrame::fieldList() const
{
return d->fieldList;
}
String::Type TextIdentificationFrame::textEncoding() const
{
return d->textEncoding;
}
void TextIdentificationFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
////////////////////////////////////////////////////////////////////////////////
// TextIdentificationFrame protected members
////////////////////////////////////////////////////////////////////////////////
void TextIdentificationFrame::parseFields(const ByteVector &data)
{
// Don't try to parse invalid frames
if(data.size() < 2)
return;
// read the string data type (the first byte of the field data)
d->textEncoding = String::Type(data[0]);
// split the byte array into chunks based on the string type (two byte delimiter
// for unicode encodings)
int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
// build a small counter to strip nulls off the end of the field
int dataLength = data.size() - 1;
while(dataLength > 0 && data[dataLength] == 0)
dataLength--;
while(dataLength % byteAlign != 0)
dataLength++;
ByteVectorList l = ByteVectorList::split(data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign);
d->fieldList.clear();
// append those split values to the list and make sure that the new string's
// type is the same specified for this frame
for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
if(!(*it).isEmpty()) {
String s(*it, d->textEncoding);
d->fieldList.append(s);
}
}
}
ByteVector TextIdentificationFrame::renderFields() const
{
String::Type encoding = checkEncoding(d->fieldList, d->textEncoding);
ByteVector v;
v.append(char(encoding));
for(StringList::ConstIterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
// Since the field list is null delimited, if this is not the first
// element in the list, append the appropriate delimiter for this
// encoding.
if(it != d->fieldList.begin())
v.append(textDelimiter(encoding));
v.append((*it).data(encoding));
}
return v;
}
////////////////////////////////////////////////////////////////////////////////
// TextIdentificationFrame private members
////////////////////////////////////////////////////////////////////////////////
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new TextIdentificationFramePrivate;
parseFields(fieldData(data));
}
////////////////////////////////////////////////////////////////////////////////
// UserTextIdentificationFrame public members
////////////////////////////////////////////////////////////////////////////////
UserTextIdentificationFrame::UserTextIdentificationFrame(String::Type encoding) :
TextIdentificationFrame("TXXX", encoding),
d(0)
{
StringList l;
l.append(String::null);
l.append(String::null);
setText(l);
}
UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data) :
TextIdentificationFrame(data)
{
checkFields();
}
String UserTextIdentificationFrame::toString() const
{
return "[" + description() + "] " + fieldList().toString();
}
String UserTextIdentificationFrame::description() const
{
return !TextIdentificationFrame::fieldList().isEmpty()
? TextIdentificationFrame::fieldList().front()
: String::null;
}
StringList UserTextIdentificationFrame::fieldList() const
{
// TODO: remove this function
return TextIdentificationFrame::fieldList();
}
void UserTextIdentificationFrame::setText(const String &text)
{
if(description().isEmpty())
setDescription(String::null);
TextIdentificationFrame::setText(StringList(description()).append(text));
}
void UserTextIdentificationFrame::setText(const StringList &fields)
{
if(description().isEmpty())
setDescription(String::null);
TextIdentificationFrame::setText(StringList(description()).append(fields));
}
void UserTextIdentificationFrame::setDescription(const String &s)
{
StringList l = fieldList();
if(l.isEmpty())
l.append(s);
else
l[0] = s;
TextIdentificationFrame::setText(l);
}
UserTextIdentificationFrame *UserTextIdentificationFrame::find(
ID3v2::Tag *tag, const String &description) // static
{
FrameList l = tag->frameList("TXXX");
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) {
UserTextIdentificationFrame *f = dynamic_cast<UserTextIdentificationFrame *>(*it);
if(f && f->description() == description)
return f;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// UserTextIdentificationFrame private members
////////////////////////////////////////////////////////////////////////////////
UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data, Header *h) :
TextIdentificationFrame(data, h)
{
checkFields();
}
void UserTextIdentificationFrame::checkFields()
{
int fields = fieldList().size();
if(fields == 0)
setDescription(String::null);
if(fields <= 1)
setText(String::null);
}

View file

@ -0,0 +1,258 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_TEXTIDENTIFICATIONFRAME_H
#define TAGLIB_TEXTIDENTIFICATIONFRAME_H
#include <tstringlist.h>
#include "taglib_export.h"
#include <id3v2frame.h>
namespace TagLib {
namespace ID3v2 {
class Tag;
//! An ID3v2 text identification frame implementation
/*!
* This is an implementation of the most common type of ID3v2 frame -- text
* identification frames. There are a number of variations on this. Those
* enumerated in the ID3v2.4 standard are:
*
* <ul>
* <li><b>TALB</b> Album/Movie/Show title</li>
* <li><b>TBPM</b> BPM (beats per minute)</li>
* <li><b>TCOM</b> Composer</li>
* <li><b>TCON</b> Content type</li>
* <li><b>TCOP</b> Copyright message</li>
* <li><b>TDEN</b> Encoding time</li>
* <li><b>TDLY</b> Playlist delay</li>
* <li><b>TDOR</b> Original release time</li>
* <li><b>TDRC</b> Recording time</li>
* <li><b>TDRL</b> Release time</li>
* <li><b>TDTG</b> Tagging time</li>
* <li><b>TENC</b> Encoded by</li>
* <li><b>TEXT</b> Lyricist/Text writer</li>
* <li><b>TFLT</b> File type</li>
* <li><b>TIPL</b> Involved people list</li>
* <li><b>TIT1</b> Content group description</li>
* <li><b>TIT2</b> Title/songname/content description</li>
* <li><b>TIT3</b> Subtitle/Description refinement</li>
* <li><b>TKEY</b> Initial key</li>
* <li><b>TLAN</b> Language(s)</li>
* <li><b>TLEN</b> Length</li>
* <li><b>TMCL</b> Musician credits list</li>
* <li><b>TMED</b> Media type</li>
* <li><b>TMOO</b> Mood</li>
* <li><b>TOAL</b> Original album/movie/show title</li>
* <li><b>TOFN</b> Original filename</li>
* <li><b>TOLY</b> Original lyricist(s)/text writer(s)</li>
* <li><b>TOPE</b> Original artist(s)/performer(s)</li>
* <li><b>TOWN</b> File owner/licensee</li>
* <li><b>TPE1</b> Lead performer(s)/Soloist(s)</li>
* <li><b>TPE2</b> Band/orchestra/accompaniment</li>
* <li><b>TPE3</b> Conductor/performer refinement</li>
* <li><b>TPE4</b> Interpreted, remixed, or otherwise modified by</li>
* <li><b>TPOS</b> Part of a set</li>
* <li><b>TPRO</b> Produced notice</li>
* <li><b>TPUB</b> Publisher</li>
* <li><b>TRCK</b> Track number/Position in set</li>
* <li><b>TRSN</b> Internet radio station name</li>
* <li><b>TRSO</b> Internet radio station owner</li>
* <li><b>TSOA</b> Album sort order</li>
* <li><b>TSOP</b> Performer sort order</li>
* <li><b>TSOT</b> Title sort order</li>
* <li><b>TSRC</b> ISRC (international standard recording code)</li>
* <li><b>TSSE</b> Software/Hardware and settings used for encoding</li>
* <li><b>TSST</b> Set subtitle</li>
* </ul>
*
* The ID3v2 Frames document gives a description of each of these formats
* and the expected order of strings in each. ID3v2::Header::frameID() can
* be used to determine the frame type.
*
* \note If non-Latin1 compatible strings are used with this class, even if
* the text encoding is set to Latin1, the frame will be written using UTF8
* (with the encoding flag appropriately set in the output).
*/
class TAGLIB_EXPORT TextIdentificationFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Construct an empty frame of type \a type. Uses \a encoding as the
* default text encoding.
*
* \note In this case you must specify the text encoding as it
* resolves the ambiguity between constructors.
*
* \note Please see the note in the class description regarding Latin1.
*/
TextIdentificationFrame(const ByteVector &type, String::Type encoding);
/*!
* This is a dual purpose constructor. \a data can either be binary data
* that should be parsed or (at a minimum) the frame ID.
*/
explicit TextIdentificationFrame(const ByteVector &data);
/*!
* Destroys this TextIdentificationFrame instance.
*/
virtual ~TextIdentificationFrame();
/*!
* Text identification frames are a list of string fields.
*
* This function will accept either a StringList or a String (using the
* StringList constructor that accepts a single String).
*
* \note This will not change the text encoding of the frame even if the
* strings passed in are not of the same encoding. Please use
* setEncoding(s.type()) if you wish to change the encoding of the frame.
*/
void setText(const StringList &l);
// Reimplementations.
virtual void setText(const String &s);
virtual String toString() const;
/*!
* Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor
* or read from the frame when parsed.
*
* \note Please see the note in the class description regarding Latin1.
*
* \see setTextEncoding()
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.
*
* \note Please see the note in the class description regarding Latin1.
*
* \see textEncoding()
* \see render()
*/
void setTextEncoding(String::Type encoding);
/*!
* Returns a list of the strings in this frame.
*/
StringList fieldList() const;
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
/*!
* The constructor used by the FrameFactory.
*/
TextIdentificationFrame(const ByteVector &data, Header *h);
private:
TextIdentificationFrame(const TextIdentificationFrame &);
TextIdentificationFrame &operator=(const TextIdentificationFrame &);
class TextIdentificationFramePrivate;
TextIdentificationFramePrivate *d;
};
/*!
* This is a specialization of text identification frames that allows for
* user defined entries. Each entry has a description in addition to the
* normal list of fields that a text identification frame has.
*
* This description identifies the frame and must be unique.
*/
//! An ID3v2 custom text identification frame implementationx
class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame
{
friend class FrameFactory;
public:
/*!
* Constructs an empty user defined text identification frame. For this to be
* a useful frame both a description and text must be set.
*/
explicit UserTextIdentificationFrame(String::Type encoding = String::Latin1);
/*!
* Creates a frame based on \a data.
*/
explicit UserTextIdentificationFrame(const ByteVector &data);
virtual String toString() const;
/*!
* Returns the description for this frame.
*/
String description() const;
/*!
* Sets the description of the frame to \a s. \a s must be unique. You can
* check for the presence of another user defined text frame of the same type
* using find() and testing for null.
*/
void setDescription(const String &s);
StringList fieldList() const;
void setText(const String &text);
void setText(const StringList &fields);
/*!
* Searches for the user defined text frame with the description \a description
* in \a tag. This returns null if no matching frames were found.
*/
static UserTextIdentificationFrame *find(Tag *tag, const String &description);
private:
UserTextIdentificationFrame(const ByteVector &data, Header *h);
UserTextIdentificationFrame(const TextIdentificationFrame &);
UserTextIdentificationFrame &operator=(const UserTextIdentificationFrame &);
void checkFields();
class UserTextIdentificationFramePrivate;
UserTextIdentificationFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,118 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tbytevectorlist.h>
#include <tdebug.h>
#include "uniquefileidentifierframe.h"
using namespace TagLib;
using namespace ID3v2;
class UniqueFileIdentifierFrame::UniqueFileIdentifierFramePrivate
{
public:
String owner;
ByteVector identifier;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) :
ID3v2::Frame(data)
{
d = new UniqueFileIdentifierFramePrivate;
setData(data);
}
UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) :
ID3v2::Frame("UFID")
{
d = new UniqueFileIdentifierFramePrivate;
d->owner = owner;
d->identifier = id;
}
UniqueFileIdentifierFrame::~UniqueFileIdentifierFrame()
{
delete d;
}
String UniqueFileIdentifierFrame::owner() const
{
return d->owner;
}
ByteVector UniqueFileIdentifierFrame::identifier() const
{
return d->identifier;
}
void UniqueFileIdentifierFrame::setOwner(const String &s)
{
d->owner = s;
}
void UniqueFileIdentifierFrame::setIdentifier(const ByteVector &v)
{
d->identifier = v;
}
String UniqueFileIdentifierFrame::toString() const
{
return String::null;
}
void UniqueFileIdentifierFrame::parseFields(const ByteVector &data)
{
if(data.size() < 1) {
debug("An UFID frame must contain at least 1 byte.");
return;
}
int pos = 0;
d->owner = readStringField(data, String::Latin1, &pos);
d->identifier = data.mid(pos);
}
ByteVector UniqueFileIdentifierFrame::renderFields() const
{
ByteVector data;
data.append(d->owner.data(String::Latin1));
data.append(char(0));
data.append(d->identifier);
return data;
}
UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) :
Frame(h)
{
d = new UniqueFileIdentifierFramePrivate;
parseFields(fieldData(data));
}

View file

@ -0,0 +1,113 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_UNIQUEFILEIDENTIFIERFRAME
#define TAGLIB_UNIQUEFILEIDENTIFIERFRAME
#include <id3v2frame.h>
namespace TagLib {
namespace ID3v2 {
/*!
* This is an implementation of ID3v2 unique file identifier frames. This
* frame is used to identify the file in an arbitrary database identified
* by the owner field.
*/
//! An implementation of ID3v2 unique identifier frames
class TAGLIB_EXPORT UniqueFileIdentifierFrame : public ID3v2::Frame
{
friend class FrameFactory;
public:
/*!
* Creates a uniqe file identifier frame based on \a data.
*/
UniqueFileIdentifierFrame(const ByteVector &data);
/*!
* Creates a unique file identifier frame with the owner \a owner and
* the identification \a id.
*/
UniqueFileIdentifierFrame(const String &owner, const ByteVector &id);
/*!
* Destroys the frame.
*/
~UniqueFileIdentifierFrame();
/*!
* Returns the owner for the frame; essentially this is the key for
* determining which identification scheme this key belongs to. This
* will usually either be an email address or URL for the person or tool
* used to create the unique identifier.
*
* \see setOwner()
*/
String owner() const;
/*!
* Returns the unique identifier. Though sometimes this is a text string
* it also may be binary data and as much should be assumed when handling
* it.
*/
ByteVector identifier() const;
/*!
* Sets the owner of the identification scheme to \a s.
*
* \see owner()
*/
void setOwner(const String &s);
/*!
* Sets the unique file identifier to \a v.
*
* \see identifier()
*/
void setIdentifier(const ByteVector &v);
virtual String toString() const;
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame &operator=(UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame(const ByteVector &data, Header *h);
class UniqueFileIdentifierFramePrivate;
UniqueFileIdentifierFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,84 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "unknownframe.h"
using namespace TagLib;
using namespace ID3v2;
class UnknownFrame::UnknownFramePrivate
{
public:
ByteVector fieldData;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data)
{
d = new UnknownFramePrivate;
setData(data);
}
UnknownFrame::~UnknownFrame()
{
delete d;
}
String UnknownFrame::toString() const
{
return String::null;
}
ByteVector UnknownFrame::data() const
{
return d->fieldData;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void UnknownFrame::parseFields(const ByteVector &data)
{
d->fieldData = data;
}
ByteVector UnknownFrame::renderFields() const
{
return d->fieldData;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new UnknownFramePrivate;
parseFields(fieldData(data));
}

View file

@ -0,0 +1,79 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_UNKNOWNFRAME_H
#define TAGLIB_UNKNOWNFRAME_H
#include <id3v2frame.h>
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! A frame type \e unknown to TagLib.
/*!
* This class represents a frame type not known (or more often simply
* unimplemented) in TagLib. This is here provide a basic API for
* manipulating the binary data of unknown frames and to provide a means
* of rendering such \e unknown frames.
*
* Please note that a cleaner way of handling frame types that TagLib
* does not understand is to subclass ID3v2::Frame and ID3v2::FrameFactory
* to have your frame type supported through the standard ID3v2 mechanism.
*/
class TAGLIB_EXPORT UnknownFrame : public Frame
{
friend class FrameFactory;
public:
UnknownFrame(const ByteVector &data);
virtual ~UnknownFrame();
virtual String toString() const;
/*!
* Returns the field data (everything but the header) for this frame.
*/
ByteVector data() const;
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
UnknownFrame(const ByteVector &data, Header *h);
UnknownFrame(const UnknownFrame &);
UnknownFrame &operator=(const UnknownFrame &);
class UnknownFramePrivate;
UnknownFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,162 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "unsynchronizedlyricsframe.h"
#include <tbytevectorlist.h>
#include <tdebug.h>
using namespace TagLib;
using namespace ID3v2;
class UnsynchronizedLyricsFrame::UnsynchronizedLyricsFramePrivate
{
public:
UnsynchronizedLyricsFramePrivate() : textEncoding(String::Latin1) {}
String::Type textEncoding;
ByteVector language;
String description;
String text;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) :
Frame("USLT")
{
d = new UnsynchronizedLyricsFramePrivate;
d->textEncoding = encoding;
}
UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) :
Frame(data)
{
d = new UnsynchronizedLyricsFramePrivate;
setData(data);
}
UnsynchronizedLyricsFrame::~UnsynchronizedLyricsFrame()
{
delete d;
}
String UnsynchronizedLyricsFrame::toString() const
{
return d->text;
}
ByteVector UnsynchronizedLyricsFrame::language() const
{
return d->language;
}
String UnsynchronizedLyricsFrame::description() const
{
return d->description;
}
String UnsynchronizedLyricsFrame::text() const
{
return d->text;
}
void UnsynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding)
{
d->language = languageEncoding.mid(0, 3);
}
void UnsynchronizedLyricsFrame::setDescription(const String &s)
{
d->description = s;
}
void UnsynchronizedLyricsFrame::setText(const String &s)
{
d->text = s;
}
String::Type UnsynchronizedLyricsFrame::textEncoding() const
{
return d->textEncoding;
}
void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data)
{
if(data.size() < 5) {
debug("An unsynchronized lyrics frame must contain at least 5 bytes.");
return;
}
d->textEncoding = String::Type(data[0]);
d->language = data.mid(1, 3);
int byteAlign
= d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
ByteVectorList l =
ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
if(l.size() == 2) {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
}
}
ByteVector UnsynchronizedLyricsFrame::renderFields() const
{
ByteVector v;
v.append(char(d->textEncoding));
v.append(d->language.size() == 3 ? d->language : "XXX");
v.append(d->description.data(d->textEncoding));
v.append(textDelimiter(d->textEncoding));
v.append(d->text.data(d->textEncoding));
return v;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h)
: Frame(h)
{
d = new UnsynchronizedLyricsFramePrivate();
parseFields(fieldData(data));
}

View file

@ -0,0 +1,157 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H
#define TAGLIB_UNSYNCHRONIZEDLYRICSFRAME_H
#include <id3v2frame.h>
namespace TagLib {
namespace ID3v2 {
//! ID3v2 unsynchronized lyrics frame
/*!
* An implementation of ID3v2 unsynchronized lyrics.
*/
class TAGLIB_EXPORT UnsynchronizedLyricsFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Construct an empty unsynchronized lyrics frame that will use the text encoding
* \a encoding.
*/
explicit UnsynchronizedLyricsFrame(String::Type encoding = String::Latin1);
/*!
* Construct a unsynchronized lyrics frame based on the data in \a data.
*/
explicit UnsynchronizedLyricsFrame(const ByteVector &data);
/*!
* Destroys this UnsynchronizedLyricsFrame instance.
*/
virtual ~UnsynchronizedLyricsFrame();
/*!
* Returns the text of this unsynchronized lyrics frame.
*
* \see text()
*/
virtual String toString() const;
/*!
* Returns the language encoding as a 3 byte encoding as specified by
* <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>.
*
* \note Most taggers simply ignore this value.
*
* \see setLanguage()
*/
ByteVector language() const;
/*!
* Returns the description of this unsynchronized lyrics frame.
*
* \note Most taggers simply ignore this value.
*
* \see setDescription()
*/
String description() const;
/*!
* Returns the text of this unsynchronized lyrics frame.
*
* \see setText()
*/
String text() const;
/*!
* Set the language using the 3 byte language code from
* <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to
* \a languageCode.
*
* \see language()
*/
void setLanguage(const ByteVector &languageCode);
/*!
* Sets the description of the unsynchronized lyrics frame to \a s.
*
* \see decription()
*/
void setDescription(const String &s);
/*!
* Sets the text portion of the unsynchronized lyrics frame to \a s.
*
* \see text()
*/
virtual void setText(const String &s);
/*!
* Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor
* or read from the frame when parsed.
*
* \see setTextEncoding()
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.
*
* \see textEncoding()
* \see render()
*/
void setTextEncoding(String::Type encoding);
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
/*!
* The constructor used by the FrameFactory.
*/
UnsynchronizedLyricsFrame(const ByteVector &data, Header *h);
UnsynchronizedLyricsFrame(const UnsynchronizedLyricsFrame &);
UnsynchronizedLyricsFrame &operator=(const UnsynchronizedLyricsFrame &);
class UnsynchronizedLyricsFramePrivate;
UnsynchronizedLyricsFramePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,192 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "urllinkframe.h"
#include <tdebug.h>
#include <tstringlist.h>
using namespace TagLib;
using namespace ID3v2;
class UrlLinkFrame::UrlLinkFramePrivate
{
public:
String url;
};
class UserUrlLinkFrame::UserUrlLinkFramePrivate
{
public:
UserUrlLinkFramePrivate() : textEncoding(String::Latin1) {}
String::Type textEncoding;
String description;
};
UrlLinkFrame::UrlLinkFrame(const ByteVector &data) :
Frame(data)
{
d = new UrlLinkFramePrivate;
setData(data);
}
UrlLinkFrame::~UrlLinkFrame()
{
delete d;
}
void UrlLinkFrame::setUrl(const String &s)
{
d->url = s;
}
String UrlLinkFrame::url() const
{
return d->url;
}
void UrlLinkFrame::setText(const String &s)
{
setUrl(s);
}
String UrlLinkFrame::toString() const
{
return url();
}
void UrlLinkFrame::parseFields(const ByteVector &data)
{
d->url = String(data);
}
ByteVector UrlLinkFrame::renderFields() const
{
return d->url.data(String::Latin1);
}
UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new UrlLinkFramePrivate;
parseFields(fieldData(data));
}
UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) :
UrlLinkFrame("WXXX")
{
d = new UserUrlLinkFramePrivate;
d->textEncoding = encoding;
}
UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) :
UrlLinkFrame(data)
{
d = new UserUrlLinkFramePrivate;
setData(data);
}
UserUrlLinkFrame::~UserUrlLinkFrame()
{
delete d;
}
String UserUrlLinkFrame::toString() const
{
return "[" + description() + "] " + url();
}
String::Type UserUrlLinkFrame::textEncoding() const
{
return d->textEncoding;
}
void UserUrlLinkFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
String UserUrlLinkFrame::description() const
{
return d->description;
}
void UserUrlLinkFrame::setDescription(const String &s)
{
d->description = s;
}
void UserUrlLinkFrame::parseFields(const ByteVector &data)
{
if(data.size() < 2) {
debug("A user URL link frame must contain at least 2 bytes.");
return;
}
int pos = 0;
d->textEncoding = String::Type(data[0]);
pos += 1;
if(d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8) {
int offset = data.find(textDelimiter(d->textEncoding), pos);
if(offset < pos)
return;
d->description = String(data.mid(pos, offset - pos), d->textEncoding);
pos = offset + 1;
}
else {
int len = data.mid(pos).find(textDelimiter(d->textEncoding), 0, 2);
if(len < 0)
return;
d->description = String(data.mid(pos, len), d->textEncoding);
pos += len + 2;
}
setUrl(String(data.mid(pos)));
}
ByteVector UserUrlLinkFrame::renderFields() const
{
ByteVector v;
String::Type encoding = checkEncoding(d->description, d->textEncoding);
v.append(char(encoding));
v.append(d->description.data(encoding));
v.append(textDelimiter(encoding));
v.append(url().data(String::Latin1));
return v;
}
UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h)
{
d = new UserUrlLinkFramePrivate;
parseFields(fieldData(data));
}

View file

@ -0,0 +1,172 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_URLLINKFRAME_H
#define TAGLIB_URLLINKFRAME_H
#include <id3v2frame.h>
namespace TagLib {
namespace ID3v2 {
//! ID3v2 URL frame
/*!
* An implementation of ID3v2 URL link frames.
*/
class TAGLIB_EXPORT UrlLinkFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* This is a dual purpose constructor. \a data can either be binary data
* that should be parsed or (at a minimum) the frame ID.
*/
explicit UrlLinkFrame(const ByteVector &data);
/*!
* Destroys this UrlLinkFrame instance.
*/
virtual ~UrlLinkFrame();
/*!
* Returns the URL.
*/
virtual String url() const;
/*!
* Sets the URL to \a s.
*/
virtual void setUrl(const String &s);
// Reimplementations.
virtual void setText(const String &s);
virtual String toString() const;
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
/*!
* The constructor used by the FrameFactory.
*/
UrlLinkFrame(const ByteVector &data, Header *h);
private:
UrlLinkFrame(const UrlLinkFrame &);
UrlLinkFrame &operator=(const UrlLinkFrame &);
class UrlLinkFramePrivate;
UrlLinkFramePrivate *d;
};
//! ID3v2 User defined URL frame
/*!
* This is a specialization of URL link frames that allows for
* user defined entries. Each entry has a description in addition to the
* normal list of fields that a URL link frame has.
*
* This description identifies the frame and must be unique.
*/
class TAGLIB_EXPORT UserUrlLinkFrame : public UrlLinkFrame
{
friend class FrameFactory;
public:
/*!
* Constructs an empty user defined URL link frame. For this to be
* a useful frame both a description and text must be set.
*/
explicit UserUrlLinkFrame(String::Type encoding = String::Latin1);
/*!
* This is a dual purpose constructor. \a data can either be binary data
* that should be parsed or (at a minimum) the frame ID.
*/
explicit UserUrlLinkFrame(const ByteVector &data);
/*!
* Destroys this UserUrlLinkFrame instance.
*/
virtual ~UserUrlLinkFrame();
// Reimplementations.
virtual String toString() const;
/*!
* Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor
* or read from the frame when parsed.
*
* \see setTextEncoding()
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.
*
* \see textEncoding()
* \see render()
*/
void setTextEncoding(String::Type encoding);
/*!
* Returns the description for this frame.
*/
String description() const;
/*!
* Sets the description of the frame to \a s. \a s must be unique.
*/
void setDescription(const String &s);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
/*!
* The constructor used by the FrameFactory.
*/
UserUrlLinkFrame(const ByteVector &data, Header *h);
private:
UserUrlLinkFrame(const UserUrlLinkFrame &);
UserUrlLinkFrame &operator=(const UserUrlLinkFrame &);
class UserUrlLinkFramePrivate;
UserUrlLinkFramePrivate *d;
};
}
}
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,733 @@
Informal standard M. Nilsson
Document: id3v2.4.0-structure.txt 16 September 2001
ID3 tag version 2.4.0 - Main Structure
Status of this document
This document is an informal standard and replaces the ID3v2.3.0
standard [ID3v2]. A formal standard will use another revision number
even if the content is identical to document. The contents in this
document may change for clarifications but never for added or altered
functionallity.
Distribution of this document is unlimited.
Abstract
This document describes the main structure of ID3v2.4.0, which is a
revised version of the ID3v2 informal standard [ID3v2] version
2.3.0. The ID3v2 offers a flexible way of storing audio meta
information within the audio file itself. The information may be
technical information, such as equalisation curves, as well as
title, performer, copyright etc.
ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
to allow for implementations to be revised as easily as possible.
1. Table of contents
Status of this document
Abstract
1. Table of contents
2. Conventions in this document
2. Standard overview
3. ID3v2 overview
3.1. ID3v2 header
3.2. ID3v2 extended header
3.3. Padding
3.4. ID3v2 footer
4. ID3v2 frames overview
4.1. Frame header flags
4.1.1. Frame status flags
4.1.2. Frame format flags
5. Tag location
6. Unsynchronisation
6.1. The unsynchronisation scheme
6.2. Synchsafe integers
7. Copyright
8. References
9. Author's Address
2. Conventions in this document
Text within "" is a text string exactly as it appears in a tag.
Numbers preceded with $ are hexadecimal and numbers preceded with %
are binary. $xx is used to indicate a byte with unknown content. %x
is used to indicate a bit with unknown content. The most significant
bit (MSB) of a byte is called 'bit 7' and the least significant bit
(LSB) is called 'bit 0'.
A tag is the whole tag described in this document. A frame is a block
of information in the tag. The tag consists of a header, frames and
optional padding. A field is a piece of information; one value, a
string etc. A numeric string is a string that consists of the
characters "0123456789" only.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in RFC 2119 [KEYWORDS].
3. ID3v2 overview
ID3v2 is a general tagging format for audio, which makes it possible
to store meta data about the audio inside the audio file itself. The
ID3 tag described in this document is mainly targeted at files
encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III
and MPEG-2.5, but may work with other types of encoded audio or as a
stand alone format for audio meta data.
ID3v2 is designed to be as flexible and expandable as possible to
meet new meta information needs that might arise. To achieve that
ID3v2 is constructed as a container for several information blocks,
called frames, whose format need not be known to the software that
encounters them. At the start of every frame is an unique and
predefined identifier, a size descriptor that allows software to skip
unknown frames and a flags field. The flags describes encoding
details and if the frame should remain in the tag, should it be
unknown to the software, if the file is altered.
The bitorder in ID3v2 is most significant bit first (MSB). The
byteorder in multibyte numbers is most significant byte first (e.g.
$12345678 would be encoded $12 34 56 78), also known as big endian
and network byte order.
Overall tag structure:
+-----------------------------+
| Header (10 bytes) |
+-----------------------------+
| Extended Header |
| (variable length, OPTIONAL) |
+-----------------------------+
| Frames (variable length) |
+-----------------------------+
| Padding |
| (variable length, OPTIONAL) |
+-----------------------------+
| Footer (10 bytes, OPTIONAL) |
+-----------------------------+
In general, padding and footer are mutually exclusive. See details in
sections 3.3, 3.4 and 5.
3.1. ID3v2 header
The first part of the ID3v2 tag is the 10 byte tag header, laid out
as follows:
ID3v2/file identifier "ID3"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
The first three bytes of the tag are always "ID3", to indicate that
this is an ID3v2 tag, directly followed by the two version bytes. The
first byte of ID3v2 version is its major version, while the second
byte is its revision number. In this case this is ID3v2.4.0. All
revisions are backwards compatible while major versions are not. If
software with ID3v2.4.0 and below support should encounter version
five or higher it should simply ignore the whole tag. Version or
revision will never be $FF.
The version is followed by the ID3v2 flags field, of which currently
four flags are used.
a - Unsynchronisation
Bit 7 in the 'ID3v2 flags' indicates whether or not
unsynchronisation is applied on all frames (see section 6.1 for
details); a set bit indicates usage.
b - Extended header
The second bit (bit 6) indicates whether or not the header is
followed by an extended header. The extended header is described in
section 3.2. A set bit indicates the presence of an extended
header.
c - Experimental indicator
The third bit (bit 5) is used as an 'experimental indicator'. This
flag SHALL always be set when the tag is in an experimental stage.
d - Footer present
Bit 4 indicates that a footer (section 3.4) is present at the very
end of the tag. A set bit indicates the presence of a footer.
All the other flags MUST be cleared. If one of these undefined flags
are set, the tag might not be readable for a parser that does not
know the flags function.
The ID3v2 tag size is stored as a 32 bit synchsafe integer (section
6.2), making a total of 28 effective bits (representing up to 256MB).
The ID3v2 tag size is the sum of the byte length of the extended
header, the padding and the frames after unsynchronisation. If a
footer is present this equals to ('total size' - 20) bytes, otherwise
('total size' - 10) bytes.
An ID3v2 tag can be detected with the following pattern:
$49 44 33 yy yy xx zz zz zz zz
Where yy is less than $FF, xx is the 'flags' byte and zz is less than
$80.
3.2. Extended header
The extended header contains information that can provide further
insight in the structure of the tag, but is not vital to the correct
parsing of the tag information; hence the extended header is
optional.
Extended header size 4 * %0xxxxxxx
Number of flag bytes $01
Extended Flags $xx
Where the 'Extended header size' is the size of the whole extended
header, stored as a 32 bit synchsafe integer. An extended header can
thus never have a size of fewer than six bytes.
The extended flags field, with its size described by 'number of flag
bytes', is defined as:
%0bcd0000
Each flag that is set in the extended header has data attached, which
comes in the order in which the flags are encountered (i.e. the data
for flag 'b' comes before the data for flag 'c'). Unset flags cannot
have any attached data. All unknown flags MUST be unset and their
corresponding data removed when a tag is modified.
Every set flag's data starts with a length byte, which contains a
value between 0 and 127 ($00 - $7f), followed by data that has the
field length indicated by the length byte. If a flag has no attached
data, the value $00 is used as length byte.
b - Tag is an update
If this flag is set, the present tag is an update of a tag found
earlier in the present file or stream. If frames defined as unique
are found in the present tag, they are to override any
corresponding ones found in the earlier tag. This flag has no
corresponding data.
Flag data length $00
c - CRC data present
If this flag is set, a CRC-32 [ISO-3309] data is included in the
extended header. The CRC is calculated on all the data between the
header and footer as indicated by the header's tag length field,
minus the extended header. Note that this includes the padding (if
there is any), but excludes the footer. The CRC-32 is stored as an
35 bit synchsafe integer, leaving the upper four bits always
zeroed.
Flag data length $05
Total frame CRC 5 * %0xxxxxxx
d - Tag restrictions
For some applications it might be desired to restrict a tag in more
ways than imposed by the ID3v2 specification. Note that the
presence of these restrictions does not affect how the tag is
decoded, merely how it was restricted before encoding. If this flag
is set the tag is restricted as follows:
Flag data length $01
Restrictions %ppqrrstt
p - Tag size restrictions
00 No more than 128 frames and 1 MB total tag size.
01 No more than 64 frames and 128 KB total tag size.
10 No more than 32 frames and 40 KB total tag size.
11 No more than 32 frames and 4 KB total tag size.
q - Text encoding restrictions
0 No restrictions
1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
UTF-8 [UTF-8].
r - Text fields size restrictions
00 No restrictions
01 No string is longer than 1024 characters.
10 No string is longer than 128 characters.
11 No string is longer than 30 characters.
Note that nothing is said about how many bytes is used to
represent those characters, since it is encoding dependent. If a
text frame consists of more than one string, the sum of the
strungs is restricted as stated.
s - Image encoding restrictions
0 No restrictions
1 Images are encoded only with PNG [PNG] or JPEG [JFIF].
t - Image size restrictions
00 No restrictions
01 All images are 256x256 pixels or smaller.
10 All images are 64x64 pixels or smaller.
11 All images are exactly 64x64 pixels, unless required
otherwise.
3.3. Padding
It is OPTIONAL to include padding after the final frame (at the end
of the ID3 tag), making the size of all the frames together smaller
than the size given in the tag header. A possible purpose of this
padding is to allow for adding a few additional frames or enlarge
existing frames within the tag without having to rewrite the entire
file. The value of the padding bytes must be $00. A tag MUST NOT have
any padding between the frames or between the tag header and the
frames. Furthermore it MUST NOT have any padding when a tag footer is
added to the tag.
3.4. ID3v2 footer
To speed up the process of locating an ID3v2 tag when searching from
the end of a file, a footer can be added to the tag. It is REQUIRED
to add a footer to an appended tag, i.e. a tag located after all
audio data. The footer is a copy of the header, but with a different
identifier.
ID3v2 identifier "3DI"
ID3v2 version $04 00
ID3v2 flags %abcd0000
ID3v2 size 4 * %0xxxxxxx
4. ID3v2 frame overview
All ID3v2 frames consists of one frame header followed by one or more
fields containing the actual information. The header is always 10
bytes and laid out as follows:
Frame ID $xx xx xx xx (four characters)
Size 4 * %0xxxxxxx
Flags $xx xx
The frame ID is made out of the characters capital A-Z and 0-9.
Identifiers beginning with "X", "Y" and "Z" are for experimental
frames and free for everyone to use, without the need to set the
experimental bit in the tag header. Bear in mind that someone else
might have used the same identifier as you. All other identifiers are
either used or reserved for future use.
The frame ID is followed by a size descriptor containing the size of
the data in the final frame, after encryption, compression and
unsynchronisation. The size is excluding the frame header ('total
frame size' - 10 bytes) and stored as a 32 bit synchsafe integer.
In the frame header the size descriptor is followed by two flag
bytes. These flags are described in section 4.1.
There is no fixed order of the frames' appearance in the tag,
although it is desired that the frames are arranged in order of
significance concerning the recognition of the file. An example of
such order: UFID, TIT2, MCDI, TRCK ...
A tag MUST contain at least one frame. A frame must be at least 1
byte big, excluding the header.
If nothing else is said, strings, including numeric strings and URLs
[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the
range $20 - $FF. Such strings are represented in frame descriptions
as <text string>, or <full text string> if newlines are allowed. If
nothing else is said newline character is forbidden. In ISO-8859-1 a
newline is represented, when allowed, with $0A only.
Frames that allow different types of text encoding contains a text
encoding description byte. Possible encodings:
$00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.
$01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
strings in the same frame SHALL have the same byteorder.
Terminated with $00 00.
$02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
Terminated with $00 00.
$03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
Strings dependent on encoding are represented in frame descriptions
as <text string according to encoding>, or <full text string
according to encoding> if newlines are allowed. Any empty strings of
type $01 which are NULL-terminated may have the Unicode BOM followed
by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).
The timestamp fields are based on a subset of ISO 8601. When being as
precise as possible the format of a time string is
yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
24), ":", minutes, ":", seconds), but the precision may be reduced by
removing as many time indicators as wanted. Hence valid timestamps
are
yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
the slash character as described in 8601, and for multiple non-
contiguous dates, use multiple strings, if allowed by the frame
definition.
The three byte language field, present in several frames, is used to
describe the language of the frame's content, according to ISO-639-2
[ISO-639-2]. The language should be represented in lower case. If the
language is not known the string "XXX" should be used.
All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt".
If a frame is longer than it should be, e.g. having more fields than
specified in this document, that indicates that additions to the
frame have been made in a later version of the ID3v2 standard. This
is reflected by the revision number in the header of the tag.
4.1. Frame header flags
In the frame header the size descriptor is followed by two flag
bytes. All unused flags MUST be cleared. The first byte is for
'status messages' and the second byte is a format description. If an
unknown flag is set in the first byte the frame MUST NOT be changed
without that bit cleared. If an unknown flag is set in the second
byte the frame is likely to not be readable. Some flags in the second
byte indicates that extra information is added to the header. These
fields of extra information is ordered as the flags that indicates
them. The flags field is defined as follows (l and o left out because
ther resemblence to one and zero):
%0abc0000 %0h00kmnp
Some frame format flags indicate that additional information fields
are added to the frame. This information is added after the frame
header and before the frame data in the same order as the flags that
indicates them. I.e. the four bytes of decompressed size will precede
the encryption method byte. These additions affects the 'frame size'
field, but are not subject to encryption or compression.
The default status flags setting for a frame is, unless stated
otherwise, 'preserved if tag is altered' and 'preserved if file is
altered', i.e. %00000000.
4.1.1. Frame status flags
a - Tag alter preservation
This flag tells the tag parser what to do with this frame if it is
unknown and the tag is altered in any way. This applies to all
kinds of alterations, including adding more padding and reordering
the frames.
0 Frame should be preserved.
1 Frame should be discarded.
b - File alter preservation
This flag tells the tag parser what to do with this frame if it is
unknown and the file, excluding the tag, is altered. This does not
apply when the audio is completely replaced with other audio data.
0 Frame should be preserved.
1 Frame should be discarded.
c - Read only
This flag, if set, tells the software that the contents of this
frame are intended to be read only. Changing the contents might
break something, e.g. a signature. If the contents are changed,
without knowledge of why the frame was flagged read only and
without taking the proper means to compensate, e.g. recalculating
the signature, the bit MUST be cleared.
4.1.2. Frame format flags
h - Grouping identity
This flag indicates whether or not this frame belongs in a group
with other frames. If set, a group identifier byte is added to the
frame. Every frame with the same group identifier belongs to the
same group.
0 Frame does not contain group information
1 Frame contains group information
k - Compression
This flag indicates whether or not the frame is compressed.
A 'Data Length Indicator' byte MUST be included in the frame.
0 Frame is not compressed.
1 Frame is compressed using zlib [zlib] deflate method.
If set, this requires the 'Data Length Indicator' bit
to be set as well.
m - Encryption
This flag indicates whether or not the frame is encrypted. If set,
one byte indicating with which method it was encrypted will be
added to the frame. See description of the ENCR frame for more
information about encryption method registration. Encryption
should be done after compression. Whether or not setting this flag
requires the presence of a 'Data Length Indicator' depends on the
specific algorithm used.
0 Frame is not encrypted.
1 Frame is encrypted.
n - Unsynchronisation
This flag indicates whether or not unsynchronisation was applied
to this frame. See section 6 for details on unsynchronisation.
If this flag is set all data from the end of this header to the
end of this frame has been unsynchronised. Although desirable, the
presence of a 'Data Length Indicator' is not made mandatory by
unsynchronisation.
0 Frame has not been unsynchronised.
1 Frame has been unsyrchronised.
p - Data length indicator
This flag indicates that a data length indicator has been added to
the frame. The data length indicator is the value one would write
as the 'Frame length' if all of the frame format flags were
zeroed, represented as a 32 bit synchsafe integer.
0 There is no Data Length Indicator.
1 A data length Indicator has been added to the frame.
5. Tag location
The default location of an ID3v2 tag is prepended to the audio so
that players can benefit from the information when the data is
streamed. It is however possible to append the tag, or make a
prepend/append combination. When deciding upon where an unembedded
tag should be located, the following order of preference SHOULD be
considered.
1. Prepend the tag.
2. Prepend a tag with all vital information and add a second tag at
the end of the file, before tags from other tagging systems. The
first tag is required to have a SEEK frame.
3. Add a tag at the end of the file, before tags from other tagging
systems.
In case 2 and 3 the tag can simply be appended if no other known tags
are present. The suggested method to find ID3v2 tags are:
1. Look for a prepended tag using the pattern found in section 3.1.
2. If a SEEK frame was found, use its values to guide further
searching.
3. Look for a tag footer, scanning from the back of the file.
For every new tag that is found, the old tag should be discarded
unless the update flag in the extended header (section 3.2) is set.
6. Unsynchronisation
The only purpose of unsynchronisation is to make the ID3v2 tag as
compatible as possible with existing software and hardware. There is
no use in 'unsynchronising' tags if the file is only to be processed
only by ID3v2 aware software and hardware. Unsynchronisation is only
useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC
files.
6.1. The unsynchronisation scheme
Whenever a false synchronisation is found within the tag, one zeroed
byte is inserted after the first false synchronisation byte. The
format of synchronisations that should be altered by ID3 encoders is
as follows:
%11111111 111xxxxx
and should be replaced with:
%11111111 00000000 111xxxxx
This has the side effect that all $FF 00 combinations have to be
altered, so they will not be affected by the decoding process.
Therefore all the $FF 00 combinations have to be replaced with the
$FF 00 00 combination during the unsynchronisation.
To indicate usage of the unsynchronisation, the unsynchronisation
flag in the frame header should be set. This bit MUST be set if the
frame was altered by the unsynchronisation and SHOULD NOT be set if
unaltered. If all frames in the tag are unsynchronised the
unsynchronisation flag in the tag header SHOULD be set. It MUST NOT
be set if the tag has a frame which is not unsynchronised.
Assume the first byte of the audio to be $FF. The special case when
the last byte of the last frame is $FF and no padding nor footer is
used will then introduce a false synchronisation. This can be solved
by adding a footer, adding padding or unsynchronising the frame and
add $00 to the end of the frame data, thus adding more byte to the
frame size than a normal unsynchronisation would. Although not
preferred, it is allowed to apply the last method on all frames
ending with $FF.
It is preferred that the tag is either completely unsynchronised or
not unsynchronised at all. A completely unsynchronised tag has no
false synchonisations in it, as defined above, and does not end with
$FF. A completely non-unsynchronised tag contains no unsynchronised
frames, and thus the unsynchronisation flag in the header is cleared.
Do bear in mind, that if compression or encryption is used, the
unsynchronisation scheme MUST be applied afterwards. When decoding an
unsynchronised frame, the unsynchronisation scheme MUST be reversed
first, encryption and decompression afterwards.
6.2. Synchsafe integers
In some parts of the tag it is inconvenient to use the
unsychronisation scheme because the size of unsynchronised data is
not known in advance, which is particularly problematic with size
descriptors. The solution in ID3v2 is to use synchsafe integers, in
which there can never be any false synchs. Synchsafe integers are
integers that keep its highest bit (bit 7) zeroed, making seven bits
out of eight available. Thus a 32 bit synchsafe integer can store 28
bits of information.
Example:
255 (%11111111) encoded as a 16 bit synchsafe integer is 383
(%00000001 01111111).
7. Copyright
Copyright (C) Martin Nilsson 2000. All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that a reference to this document is included on all
such copies and derivative works. However, this document itself may
not be modified in any way and reissued as the original document.
The limited permissions granted above are perpetual and will not be
revoked.
This document and the information contained herein is provided on an
'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
8. References
[ID3v2] Martin Nilsson, 'ID3v2 informal standard'.
<url:http://www.id3.org/id3v2.3.0.txt>
[ISO-639-2] ISO/FDIS 639-2.
'Codes for the representation of names of languages, Part 2: Alpha-3
code.' Technical committee / subcommittee: TC 37 / SC 2
[ISO-3309] ISO 3309
'Information Processing Systems--Data Communication High-Level Data
Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd
Edition.
[ISO-8859-1] ISO/IEC DIS 8859-1.
'8-bit single-byte coded graphic character sets, Part 1: Latin
alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2
[JFIF] 'JPEG File Interchange Format, version 1.02'
<url:http://www.w3.org/Graphics/JPEG/jfif.txt>
[KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
Requirement Levels', RFC 2119, March 1997.
<url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
[MPEG] ISO/IEC 11172-3:1993.
'Coding of moving pictures and associated audio for digital storage
media at up to about 1,5 Mbit/s, Part 3: Audio.'
Technical committee / subcommittee: JTC 1 / SC 29
and
ISO/IEC 13818-3:1995
'Generic coding of moving pictures and associated audio information,
Part 3: Audio.'
Technical committee / subcommittee: JTC 1 / SC 29
and
ISO/IEC DIS 13818-3
'Generic coding of moving pictures and associated audio information,
Part 3: Audio (Revision of ISO/IEC 13818-3:1995)'
[PNG] 'Portable Network Graphics, version 1.0'
<url:http://www.w3.org/TR/REC-png-multi.html>
[UNICODE] The Unicode Consortium,
'The Unicode Standard Version 3.0', ISBN 0-201-61633-5.
<url:http://www.unicode.org/unicode/standard/versions/Unicode3.0.htm>
[URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource
Locators (URL)', RFC 1738, December 1994.
<url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
[UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646',
RFC 2279, January 1998.
<url:ftp://ftp.isi.edu/in-notes/rfc2279.txt>
[UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781,
February 2000.
<url:ftp://ftp.isi.edu/in-notes/rfc2781.txt>
[ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB
Compressed Data Format Specification version 3.3', RFC 1950,
May 1996.
<url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
9. Author's Address
Written by
Martin Nilsson
Rydsvägen 246 C. 30
SE-584 34 Linköping
Sweden
Email: nilsson@id3.org

View file

@ -0,0 +1,71 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "id3v2extendedheader.h"
#include "id3v2synchdata.h"
using namespace TagLib;
using namespace ID3v2;
class ExtendedHeader::ExtendedHeaderPrivate
{
public:
ExtendedHeaderPrivate() : size(0) {}
uint size;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
ExtendedHeader::ExtendedHeader()
{
d = new ExtendedHeaderPrivate();
}
ExtendedHeader::~ExtendedHeader()
{
delete d;
}
TagLib::uint ExtendedHeader::size() const
{
return d->size;
}
void ExtendedHeader::setData(const ByteVector &data)
{
parse(data);
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void ExtendedHeader::parse(const ByteVector &data)
{
d->size = SynchData::toUInt(data.mid(0, 4)); // (structure 3.2 "Extended header size")
}

View file

@ -0,0 +1,93 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2EXTENDEDHEADER_H
#define TAGLIB_ID3V2EXTENDEDHEADER_H
#include "taglib_export.h"
#include "tbytevector.h"
#include "taglib.h"
namespace TagLib {
namespace ID3v2 {
//! ID3v2 extended header implementation
/*!
* This class implements ID3v2 extended headers. It attempts to follow,
* both semantically and programatically, the structure specified in
* the ID3v2 standard. The API is based on the properties of ID3v2 extended
* headers specified there. If any of the terms used in this documentation
* are unclear please check the specification in the linked section.
* (Structure, <a href="id3v2-structure.html#3.2">3.2</a>)
*/
class TAGLIB_EXPORT ExtendedHeader
{
public:
/*!
* Constructs an empty ID3v2 extended header.
*/
ExtendedHeader();
/*!
* Destroys the extended header.
*/
virtual ~ExtendedHeader();
/*!
* Returns the size of the extended header. This is variable for the
* extended header.
*/
uint size() const;
/*!
* Sets the data that will be used as the extended header. Since the
* length is not known before the extended header has been parsed, this
* should just be a pointer to the first byte of the extended header. It
* will determine the length internally and make that available through
* size().
*/
void setData(const ByteVector &data);
protected:
/*!
* Called by setData() to parse the extended header data. It makes this
* information available through the public API.
*/
void parse(const ByteVector &data);
private:
ExtendedHeader(const ExtendedHeader &);
ExtendedHeader &operator=(const ExtendedHeader &);
class ExtendedHeaderPrivate;
ExtendedHeaderPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,60 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "id3v2footer.h"
#include "id3v2header.h"
using namespace TagLib;
using namespace ID3v2;
class Footer::FooterPrivate
{
public:
static const uint size = 10;
};
Footer::Footer()
{
}
Footer::~Footer()
{
}
const unsigned int Footer::size()
{
return FooterPrivate::size;
}
ByteVector Footer::render(const Header *header) const
{
ByteVector headerData = header->render();
headerData[0] = '3';
headerData[1] = 'D';
headerData[2] = 'I';
return headerData;
}

View file

@ -0,0 +1,82 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2FOOTER_H
#define TAGLIB_ID3V2FOOTER_H
#include "taglib_export.h"
#include "tbytevector.h"
namespace TagLib {
namespace ID3v2 {
class Header;
//! ID3v2 footer implementation
/*!
* Per the ID3v2 specification, the tag's footer is just a copy of the
* information in the header. As such there is no API for reading the
* data from the header, it can just as easily be done from the header.
*
* In fact, at this point, TagLib does not even parse the footer since
* it is not useful internally. However, if the flag to include a footer
* has been set in the ID3v2::Tag, TagLib will render a footer.
*/
class TAGLIB_EXPORT Footer
{
public:
/*!
* Constructs an empty ID3v2 footer.
*/
Footer();
/*!
* Destroys the footer.
*/
virtual ~Footer();
/*!
* Returns the size of the footer. Presently this is always 10 bytes.
*/
static const unsigned int size();
/*!
* Renders the footer based on the data in \a header.
*/
ByteVector render(const Header *header) const;
private:
Footer(const Footer &);
Footer &operator=(const Footer &);
class FooterPrivate;
FooterPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,549 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef HAVE_ZLIB
#include <config.h>
#endif
#if HAVE_ZLIB
#include <zlib.h>
#endif
#include <bitset>
#include <tdebug.h>
#include <tstringlist.h>
#include "id3v2frame.h"
#include "id3v2synchdata.h"
using namespace TagLib;
using namespace ID3v2;
class Frame::FramePrivate
{
public:
FramePrivate() :
header(0)
{}
~FramePrivate()
{
delete header;
}
Frame::Header *header;
};
namespace
{
bool isValidFrameID(const ByteVector &frameID)
{
if(frameID.size() != 4)
return false;
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
return false;
}
}
return true;
}
}
////////////////////////////////////////////////////////////////////////////////
// static methods
////////////////////////////////////////////////////////////////////////////////
TagLib::uint Frame::headerSize()
{
return Header::size();
}
TagLib::uint Frame::headerSize(uint version)
{
return Header::size(version);
}
ByteVector Frame::textDelimiter(String::Type t)
{
ByteVector d = char(0);
if(t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE)
d.append(char(0));
return d;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Frame::~Frame()
{
delete d;
}
ByteVector Frame::frameID() const
{
if(d->header)
return d->header->frameID();
else
return ByteVector::null;
}
TagLib::uint Frame::size() const
{
if(d->header)
return d->header->frameSize();
else
return 0;
}
void Frame::setData(const ByteVector &data)
{
parse(data);
}
void Frame::setText(const String &)
{
}
ByteVector Frame::render() const
{
ByteVector fieldData = renderFields();
d->header->setFrameSize(fieldData.size());
ByteVector headerData = d->header->render();
return headerData + fieldData;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
Frame::Frame(const ByteVector &data)
{
d = new FramePrivate;
d->header = new Header(data);
}
Frame::Frame(Header *h)
{
d = new FramePrivate;
d->header = h;
}
Frame::Header *Frame::header() const
{
return d->header;
}
void Frame::setHeader(Header *h, bool deleteCurrent)
{
if(deleteCurrent)
delete d->header;
d->header = h;
}
void Frame::parse(const ByteVector &data)
{
if(d->header)
d->header->setData(data);
else
d->header = new Header(data);
parseFields(fieldData(data));
}
ByteVector Frame::fieldData(const ByteVector &frameData) const
{
uint headerSize = Header::size(d->header->version());
uint frameDataOffset = headerSize;
uint frameDataLength = size();
if(d->header->compression() || d->header->dataLengthIndicator()) {
frameDataLength = SynchData::toUInt(frameData.mid(headerSize, 4));
frameDataOffset += 4;
}
#if HAVE_ZLIB
if(d->header->compression() &&
!d->header->encryption())
{
ByteVector data(frameDataLength);
uLongf uLongTmp = frameDataLength;
::uncompress((Bytef *) data.data(),
(uLongf *) &uLongTmp,
(Bytef *) frameData.data() + frameDataOffset,
size());
return data;
}
else
#endif
return frameData.mid(frameDataOffset, frameDataLength);
}
String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position)
{
int start = 0;
if(!position)
position = &start;
ByteVector delimiter = textDelimiter(encoding);
int end = data.find(delimiter, *position, delimiter.size());
if(end < *position)
return String::null;
String str = String(data.mid(*position, end - *position), encoding);
*position = end + delimiter.size();
return str;
}
String::Type Frame::checkEncoding(const StringList &fields, String::Type encoding) // static
{
if(encoding != String::Latin1)
return encoding;
for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) {
if(!(*it).isLatin1()) {
debug("Frame::checkEncoding() -- Rendering using UTF8.");
return String::UTF8;
}
}
return String::Latin1;
}
////////////////////////////////////////////////////////////////////////////////
// Frame::Header class
////////////////////////////////////////////////////////////////////////////////
class Frame::Header::HeaderPrivate
{
public:
HeaderPrivate() :
frameSize(0),
version(4),
tagAlterPreservation(false),
fileAlterPreservation(false),
readOnly(false),
groupingIdentity(false),
compression(false),
encryption(false),
unsynchronisation(false),
dataLengthIndicator(false)
{}
ByteVector frameID;
uint frameSize;
uint version;
// flags
bool tagAlterPreservation;
bool fileAlterPreservation;
bool readOnly;
bool groupingIdentity;
bool compression;
bool encryption;
bool unsynchronisation;
bool dataLengthIndicator;
};
////////////////////////////////////////////////////////////////////////////////
// static members (Frame::Header)
////////////////////////////////////////////////////////////////////////////////
TagLib::uint Frame::Header::size()
{
return size(4);
}
TagLib::uint Frame::Header::size(uint version)
{
switch(version) {
case 0:
case 1:
case 2:
return 6;
case 3:
case 4:
default:
return 10;
}
}
////////////////////////////////////////////////////////////////////////////////
// public members (Frame::Header)
////////////////////////////////////////////////////////////////////////////////
Frame::Header::Header(const ByteVector &data, bool synchSafeInts)
{
d = new HeaderPrivate;
setData(data, synchSafeInts);
}
Frame::Header::Header(const ByteVector &data, uint version)
{
d = new HeaderPrivate;
setData(data, version);
}
Frame::Header::~Header()
{
delete d;
}
void Frame::Header::setData(const ByteVector &data, bool synchSafeInts)
{
setData(data, uint(synchSafeInts ? 4 : 3));
}
void Frame::Header::setData(const ByteVector &data, uint version)
{
d->version = version;
switch(version) {
case 0:
case 1:
case 2:
{
// ID3v2.2
if(data.size() < 3) {
debug("You must at least specify a frame ID.");
return;
}
// Set the frame ID -- the first three bytes
d->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) {
d->frameSize = 0;
return;
}
d->frameSize = data.mid(3, 3).toUInt();
break;
}
case 3:
{
// ID3v2.3
if(data.size() < 4) {
debug("You must at least specify a frame ID.");
return;
}
// Set the frame ID -- the first four bytes
d->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) {
d->frameSize = 0;
return;
}
// Set the size -- the frame size is the four bytes starting at byte four in
// the frame header (structure 4)
d->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)
}
{ // 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)
}
break;
}
case 4:
default:
{
// ID3v2.4
if(data.size() < 4) {
debug("You must at least specify a frame ID.");
return;
}
// Set the frame ID -- the first four bytes
d->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) {
d->frameSize = 0;
return;
}
// Set the size -- the frame size is the four bytes starting at byte four in
// the frame header (structure 4)
d->frameSize = SynchData::toUInt(data.mid(4, 4));
#ifndef NO_ITUNES_HACKS
// iTunes writes v2.4 tags with v2.3-like frame sizes
if(d->frameSize > 127) {
if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
unsigned int uintSize = data.mid(4, 4).toUInt();
if(isValidFrameID(data.mid(uintSize + 10, 4))) {
d->frameSize = uintSize;
}
}
}
#endif
{ // 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)
}
{ // 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->unsynchronisation = flags[1]; // (structure 4.1.2.n)
d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)
}
break;
}
}
}
ByteVector Frame::Header::frameID() const
{
return d->frameID;
}
void Frame::Header::setFrameID(const ByteVector &id)
{
d->frameID = id.mid(0, 4);
}
TagLib::uint Frame::Header::frameSize() const
{
return d->frameSize;
}
void Frame::Header::setFrameSize(uint size)
{
d->frameSize = size;
}
TagLib::uint Frame::Header::version() const
{
return d->version;
}
bool Frame::Header::tagAlterPreservation() const
{
return d->tagAlterPreservation;
}
void Frame::Header::setTagAlterPreservation(bool preserve)
{
d->tagAlterPreservation = preserve;
}
bool Frame::Header::fileAlterPreservation() const
{
return d->fileAlterPreservation;
}
bool Frame::Header::readOnly() const
{
return d->readOnly;
}
bool Frame::Header::groupingIdentity() const
{
return d->groupingIdentity;
}
bool Frame::Header::compression() const
{
return d->compression;
}
bool Frame::Header::encryption() const
{
return d->encryption;
}
bool Frame::Header::unsycronisation() const
{
return unsynchronisation();
}
bool Frame::Header::unsynchronisation() const
{
return d->unsynchronisation;
}
bool Frame::Header::dataLengthIndicator() const
{
return d->dataLengthIndicator;
}
ByteVector Frame::Header::render() const
{
ByteVector flags(2, char(0)); // just blank for the moment
ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags;
return v;
}
bool Frame::Header::frameAlterPreservation() const
{
return fileAlterPreservation();
}

View file

@ -0,0 +1,414 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2FRAME_H
#define TAGLIB_ID3V2FRAME_H
#include "tstring.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
class StringList;
namespace ID3v2 {
class Tag;
class FrameFactory;
//! ID3v2 frame implementation
/*!
* This class is the main ID3v2 frame implementation. In ID3v2, a tag is
* split between a collection of frames (which are in turn split into fields
* (Structure, <a href="id3v2-structure.html#4">4</a>)
* (<a href="id3v2-frames.html">Frames</a>). This class provides an API for
* gathering information about and modifying ID3v2 frames. Funtionallity
* specific to a given frame type is handed in one of the many subclasses.
*/
class TAGLIB_EXPORT Frame
{
friend class Tag;
friend class FrameFactory;
public:
/*!
* Destroys this Frame instance.
*/
virtual ~Frame();
/*!
* Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
* (Frames, <a href="id3v2-frames.html#4">4</a>)
*/
ByteVector frameID() const;
/*!
* Returns the size of the frame.
*/
uint size() const;
/*!
* Returns the size of the frame header
*
* \deprecated This is only accurate for ID3v2.3 or ID3v2.4. Please use
* the call below which accepts an ID3v2 version number. In the next
* non-binary compatible release this will be made into a non-static
* member that checks the internal ID3v2 version.
*/
static uint headerSize(); // BIC: remove and make non-static
/*!
* Returns the size of the frame header for the given ID3v2 version.
*
* \deprecated Please see the explanation above.
*/
static uint headerSize(uint version); // BIC: remove and make non-static
/*!
* Sets the data that will be used as the frame. Since the length is not
* known before the frame has been parsed, this should just be a pointer to
* the first byte of the frame. It will determine the length internally
* and make that available through size().
*/
void setData(const ByteVector &data);
/*!
* Set the text of frame in the sanest way possible. This should only be
* reimplemented in frames where there is some logical mapping to text.
*
* \note If the frame type supports multiple text encodings, this will not
* change the text encoding of the frame; the string will be converted to
* that frame's encoding. Please use the specific APIs of the frame types
* to set the encoding if that is desired.
*/
virtual void setText(const String &text);
/*!
* This returns the textual representation of the data in the frame.
* Subclasses must reimplement this method to provide a string
* representation of the frame's data.
*/
virtual String toString() const = 0;
/*!
* Render the frame back to its binary format in a ByteVector.
*/
ByteVector render() const;
/*!
* Returns the text delimiter that is used between fields for the string
* type \a t.
*/
static ByteVector textDelimiter(String::Type t);
protected:
class Header;
/*!
* Constructs an ID3v2 frame using \a data to read the header information.
* All other processing of \a data should be handled in a subclass.
*
* \note This need not contain anything more than a frame ID, but
* \e must constain at least that.
*/
explicit Frame(const ByteVector &data);
/*!
* This creates an Frame using the header \a h.
*
* The ownership of this header will be assigned to the frame and the
* header will be deleted when the frame is destroyed.
*/
Frame(Header *h);
/*!
* Returns a pointer to the frame header.
*/
Header *header() const;
/*!
* Sets the header to \a h. If \a deleteCurrent is true, this will free
* the memory of the current header.
*
* The ownership of this header will be assigned to the frame and the
* header will be deleted when the frame is destroyed.
*/
void setHeader(Header *h, bool deleteCurrent = true);
/*!
* Called by setData() to parse the frame data. It makes this information
* available through the public API.
*/
void parse(const ByteVector &data);
/*!
* Called by parse() to parse the field data. It makes this information
* available through the public API. This must be overridden by the
* subclasses.
*/
virtual void parseFields(const ByteVector &data) = 0;
/*!
* Render the field data back to a binary format in a ByteVector. This
* must be overridden by subclasses.
*/
virtual ByteVector renderFields() const = 0;
/*!
* Returns a ByteVector containing the field data given the frame data.
* This correctly adjusts for the header size plus any additional frame
* data that's specified in the frame header flags.
*/
ByteVector fieldData(const ByteVector &frameData) const;
/*!
* Reads a String of type \a encodiong from the ByteVector \a data. If \a
* position is passed in it is used both as the starting point and is
* updated to replect the position just after the string that has been read.
* This is useful for reading strings sequentially.
*/
String readStringField(const ByteVector &data, String::Type encoding,
int *positon = 0);
/*!
* Checks a the list of string values to see if they can be used with the
* specified encoding and returns the recommended encoding.
*/
static String::Type checkEncoding(const StringList &fields,
String::Type encoding);
private:
Frame(const Frame &);
Frame &operator=(const Frame &);
class FramePrivate;
friend class FramePrivate;
FramePrivate *d;
};
//! ID3v2 frame header implementation
/*!
* The ID3v2 Frame Header (Structure, <a href="id3v2-structure.html#4">4</a>)
*
* Every ID3v2::Frame has an associated header that gives some general
* properties of the frame and also makes it possible to identify the frame
* type.
*
* As such when reading an ID3v2 tag ID3v2::FrameFactory first creates the
* frame headers and then creates the appropriate Frame subclass based on
* the type and attaches the header.
*/
class TAGLIB_EXPORT Frame::Header
{
public:
/*!
* Construct a Frame Header based on \a data. \a data must at least
* contain a 4 byte frame ID, and optionally can contain flag data and the
* frame size. i.e. Just the frame id -- "TALB" -- is a valid value.
*
* \deprecated Please use the constructor below that accepts a version
* number.
*/
Header(const ByteVector &data, bool synchSafeInts);
/*!
* Construct a Frame Header based on \a data. \a data must at least
* contain a 4 byte frame ID, and optionally can contain flag data and the
* frame size. i.e. Just the frame id -- "TALB" -- is a valid value.
*
* \a version should be the ID3v2 version of the tag.
*/
explicit Header(const ByteVector &data, uint version = 4);
/*!
* Destroys this Header instance.
*/
virtual ~Header();
/*!
* Sets the data for the Header.
*
* \deprecated Please use the version below that accepts an ID3v2 version
* number.
*/
void setData(const ByteVector &data, bool synchSafeInts);
/*!
* Sets the data for the Header. \a version should indicate the ID3v2
* version number of the tag that this frame is contained in.
*/
void setData(const ByteVector &data, uint version = 4);
/*!
* Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
* (Frames, <a href="id3v2-frames.html#4">4</a>)
*/
ByteVector frameID() const;
/*!
* Sets the frame's ID to \a id. Only the first four bytes of \a id will
* be used.
*
* \warning This method should in general be avoided. It exists simply to
* provide a mechanism for transforming frames from a deprecated frame type
* to a newer one -- i.e. TYER to TDRC from ID3v2.3 to ID3v2.4.
*/
void setFrameID(const ByteVector &id);
/*!
* Returns the size of the frame data portion, as set when setData() was
* called or set explicitly via setFrameSize().
*/
uint frameSize() const;
/*!
* Sets the size of the frame data portion.
*/
void setFrameSize(uint size);
/*!
* Returns the ID3v2 version of the header (as passed in from the
* construction of the header).
*/
uint version() const;
/*!
* Returns the size of the frame header in bytes.
*
* \deprecated Please use the version of this method that accepts a
* version. This is only accurate for ID3v2.3 and ID3v2.4. This will be
* removed in the next binary incompatible release (2.0) and will be
* replaced with a non-static method that checks the frame version.
*/
static uint size();
/*!
* Returns the size of the frame header in bytes for the ID3v2 version
* that's given.
*
* \deprecated Please see the explanation in the version above.
*/
static uint size(uint version);
/*!
* Returns true if the flag for tag alter preservation is set.
*
* The semantics are a little backwards from what would seem natural
* (setting the preservation flag to throw away the frame), but this
* follows the ID3v2 standard.
*
* \see setTagAlterPreservation()
*/
bool tagAlterPreservation() const;
/*!
* Sets the flag for preservation of this frame if the tag is set. If
* this is set to true the frame will not be written when the tag is
* saved.
*
* The semantics are a little backwards from what would seem natural
* (setting the preservation flag to throw away the frame), but this
* follows the ID3v2 standard.
*
* \see tagAlterPreservation()
*/
void setTagAlterPreservation(bool discard);
/*!
* Returns true if the flag for file alter preservation is set.
*
* \note This flag is currently ignored internally in TagLib.
*/
bool fileAlterPreservation() const;
/*!
* Returns true if the frame is meant to be read only.
*
* \note This flag is currently ignored internally in TagLib.
*/
bool readOnly() const;
/*!
* Returns true if the flag for the grouping identifity is set.
*
* \note This flag is currently ignored internally in TagLib.
*/
bool groupingIdentity() const;
/*!
* Returns true if compression is enabled for this frame.
*
* \note This flag is currently ignored internally in TagLib.
*/
bool compression() const;
/*!
* Returns true if encryption is enabled for this frame.
*
* \note This flag is currently ignored internally in TagLib.
*/
bool encryption() const;
#ifndef DO_NOT_DOCUMENT
bool unsycronisation() const;
#endif
/*!
* Returns true if unsynchronisation is enabled for this frame.
*/
bool unsynchronisation() const;
/*!
* Returns true if the flag for a data length indicator is set.
*/
bool dataLengthIndicator() const;
/*!
* Render the Header back to binary format in a ByteVector.
*/
ByteVector render() const;
/*!
* \deprecated
*/
bool frameAlterPreservation() const;
private:
Header(const Header &);
Header &operator=(const Header &);
class HeaderPrivate;
HeaderPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,407 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef HAVE_ZLIB
#include <config.h>
#endif
#include <tdebug.h>
#include "id3v2framefactory.h"
#include "id3v2synchdata.h"
#include "frames/attachedpictureframe.h"
#include "frames/commentsframe.h"
#include "frames/relativevolumeframe.h"
#include "frames/textidentificationframe.h"
#include "frames/uniquefileidentifierframe.h"
#include "frames/unknownframe.h"
#include "frames/generalencapsulatedobjectframe.h"
#include "frames/urllinkframe.h"
#include "frames/unsynchronizedlyricsframe.h"
using namespace TagLib;
using namespace ID3v2;
class FrameFactory::FrameFactoryPrivate
{
public:
FrameFactoryPrivate() :
defaultEncoding(String::Latin1),
useDefaultEncoding(false) {}
String::Type defaultEncoding;
bool useDefaultEncoding;
template <class T> void setTextEncoding(T *frame)
{
if(useDefaultEncoding)
frame->setTextEncoding(defaultEncoding);
}
};
FrameFactory *FrameFactory::factory = 0;
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FrameFactory *FrameFactory::instance()
{
if(!factory)
factory = new FrameFactory;
return factory;
}
Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
{
return createFrame(data, uint(synchSafeInts ? 4 : 3));
}
Frame *FrameFactory::createFrame(const ByteVector &data, uint version) const
{
Header tagHeader;
tagHeader.setMajorVersion(version);
return createFrame(data, &tagHeader);
}
Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const
{
ByteVector data = origData;
uint version = tagHeader->majorVersion();
Frame::Header *header = new Frame::Header(data, version);
ByteVector frameID = header->frameID();
// A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
// characters. Also make sure that there is data in the frame.
if(!frameID.size() == (version < 3 ? 3 : 4) ||
header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
header->frameSize() > data.size())
{
delete header;
return 0;
}
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
delete header;
return 0;
}
}
if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) {
// Data lengths are not part of the encoded data, but since they are synch-safe
// integers they will be never actually encoded.
ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize());
frameData = SynchData::decode(frameData);
data = data.mid(0, Frame::Header::size(version)) + frameData;
}
// TagLib doesn't mess with encrypted frames, so just treat them
// as unknown frames.
#if HAVE_ZLIB == 0
if(header->compression()) {
debug("Compressed frames are currently not supported.");
return new UnknownFrame(data, header);
}
#endif
if(header->encryption()) {
debug("Encrypted frames are currently not supported.");
return new UnknownFrame(data, header);
}
if(!updateFrame(header)) {
header->setTagAlterPreservation(true);
return new UnknownFrame(data, header);
}
// updateFrame() might have updated the frame ID.
frameID = header->frameID();
// This is where things get necissarily nasty. Here we determine which
// Frame subclass (or if none is found simply an Frame) based
// on the frame ID. Since there are a lot of possibilities, that means
// a lot of if blocks.
// Text Identification (frames 4.2)
if(frameID.startsWith("T")) {
TextIdentificationFrame *f = frameID != "TXXX"
? new TextIdentificationFrame(data, header)
: new UserTextIdentificationFrame(data, header);
d->setTextEncoding(f);
if(frameID == "TCON")
updateGenre(f);
return f;
}
// Comments (frames 4.10)
if(frameID == "COMM") {
CommentsFrame *f = new CommentsFrame(data, header);
d->setTextEncoding(f);
return f;
}
// Attached Picture (frames 4.14)
if(frameID == "APIC") {
AttachedPictureFrame *f = new AttachedPictureFrame(data, header);
d->setTextEncoding(f);
return f;
}
// Relative Volume Adjustment (frames 4.11)
if(frameID == "RVA2")
return new RelativeVolumeFrame(data, header);
// Unique File Identifier (frames 4.1)
if(frameID == "UFID")
return new UniqueFileIdentifierFrame(data, header);
// General Encapsulated Object (frames 4.15)
if(frameID == "GEOB") {
GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header);
d->setTextEncoding(f);
return f;
}
// URL link (frames 4.3)
if(frameID.startsWith("W")) {
if(frameID != "WXXX") {
return new UrlLinkFrame(data, header);
}
else {
UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
d->setTextEncoding(f);
return f;
}
}
// Unsynchronized lyric/text transcription (frames 4.8)
if(frameID == "USLT") {
UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header);
if(d->useDefaultEncoding)
f->setTextEncoding(d->defaultEncoding);
return f;
}
return new UnknownFrame(data, header);
}
String::Type FrameFactory::defaultTextEncoding() const
{
return d->defaultEncoding;
}
void FrameFactory::setDefaultTextEncoding(String::Type encoding)
{
d->useDefaultEncoding = true;
d->defaultEncoding = encoding;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
FrameFactory::FrameFactory()
{
d = new FrameFactoryPrivate;
}
FrameFactory::~FrameFactory()
{
delete d;
}
bool FrameFactory::updateFrame(Frame::Header *header) const
{
TagLib::ByteVector frameID = header->frameID();
switch(header->version()) {
case 2: // ID3v2.2
{
if(frameID == "CRM" ||
frameID == "EQU" ||
frameID == "LNK" ||
frameID == "RVA" ||
frameID == "TIM" ||
frameID == "TSI")
{
debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
". It will be discarded from the tag.");
return false;
}
// ID3v2.2 only used 3 bytes for the frame ID, so we need to convert all of
// the frames to their 4 byte ID3v2.4 equivalent.
convertFrame("BUF", "RBUF", header);
convertFrame("CNT", "PCNT", header);
convertFrame("COM", "COMM", header);
convertFrame("CRA", "AENC", header);
convertFrame("ETC", "ETCO", header);
convertFrame("GEO", "GEOB", header);
convertFrame("IPL", "TIPL", header);
convertFrame("MCI", "MCDI", header);
convertFrame("MLL", "MLLT", header);
convertFrame("PIC", "APIC", header);
convertFrame("POP", "POPM", header);
convertFrame("REV", "RVRB", header);
convertFrame("SLT", "SYLT", header);
convertFrame("STC", "SYTC", header);
convertFrame("TAL", "TALB", header);
convertFrame("TBP", "TBPM", header);
convertFrame("TCM", "TCOM", header);
convertFrame("TCO", "TCON", header);
convertFrame("TCR", "TCOP", header);
convertFrame("TDA", "TDRC", header);
convertFrame("TDY", "TDLY", header);
convertFrame("TEN", "TENC", header);
convertFrame("TFT", "TFLT", header);
convertFrame("TKE", "TKEY", header);
convertFrame("TLA", "TLAN", header);
convertFrame("TLE", "TLEN", header);
convertFrame("TMT", "TMED", header);
convertFrame("TOA", "TOAL", header);
convertFrame("TOF", "TOFN", header);
convertFrame("TOL", "TOLY", header);
convertFrame("TOR", "TDOR", header);
convertFrame("TOT", "TOAL", header);
convertFrame("TP1", "TPE1", header);
convertFrame("TP2", "TPE2", header);
convertFrame("TP3", "TPE3", header);
convertFrame("TP4", "TPE4", header);
convertFrame("TPA", "TPOS", header);
convertFrame("TPB", "TPUB", header);
convertFrame("TRC", "TSRC", header);
convertFrame("TRD", "TDRC", header);
convertFrame("TRK", "TRCK", header);
convertFrame("TSS", "TSSE", header);
convertFrame("TT1", "TIT1", header);
convertFrame("TT2", "TIT2", header);
convertFrame("TT3", "TIT3", header);
convertFrame("TXT", "TOLY", header);
convertFrame("TXX", "TXXX", header);
convertFrame("TYE", "TDRC", header);
convertFrame("UFI", "UFID", header);
convertFrame("ULT", "USLT", header);
convertFrame("WAF", "WOAF", header);
convertFrame("WAR", "WOAR", header);
convertFrame("WAS", "WOAS", header);
convertFrame("WCM", "WCOM", header);
convertFrame("WCP", "WCOP", header);
convertFrame("WPB", "WPUB", header);
convertFrame("WXX", "WXXX", header);
break;
}
case 3: // ID3v2.3
{
if(frameID == "EQUA" ||
frameID == "RVAD" ||
frameID == "TIME" ||
frameID == "TRDA" ||
frameID == "TSIZ" ||
frameID == "TDAT")
{
debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
". It will be discarded from the tag.");
return false;
}
convertFrame("TORY", "TDOR", header);
convertFrame("TYER", "TDRC", header);
break;
}
default:
// This should catch a typo that existed in TagLib up to and including
// version 1.1 where TRDC was used for the year rather than TDRC.
convertFrame("TRDC", "TDRC", header);
break;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FrameFactory::convertFrame(const char *from, const char *to,
Frame::Header *header) const
{
if(header->frameID() != from)
return;
// debug("ID3v2.4 no longer supports the frame type " + String(from) + " It has" +
// "been converted to the type " + String(to) + ".");
header->setFrameID(to);
}
void FrameFactory::updateGenre(TextIdentificationFrame *frame) const
{
StringList fields;
String s = frame->toString();
while(s.startsWith("(")) {
int closing = s.find(")");
if(closing < 0)
break;
fields.append(s.substr(1, closing - 1));
s = s.substr(closing + 1);
}
if(!s.isEmpty())
fields.append(s);
if(fields.isEmpty())
fields.append(String::null);
frame->setText(fields);
}

View file

@ -0,0 +1,167 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2FRAMEFACTORY_H
#define TAGLIB_ID3V2FRAMEFACTORY_H
#include "taglib_export.h"
#include "tbytevector.h"
#include "id3v2frame.h"
#include "id3v2header.h"
namespace TagLib {
namespace ID3v2 {
class TAGLIB_EXPORT TextIdentificationFrame;
//! A factory for creating ID3v2 frames during parsing
/*!
* This factory abstracts away the frame creation process and instantiates
* the appropriate ID3v2::Frame subclasses based on the contents of the
* data.
*
* Reimplementing this factory is the key to adding support for frame types
* not directly supported by TagLib to your application. To do so you would
* subclass this factory reimplement createFrame(). Then by setting your
* factory to be the default factory in ID3v2::Tag constructor or with
* MPEG::File::setID3v2FrameFactory() you can implement behavior that will
* allow for new ID3v2::Frame subclasses (also provided by you) to be used.
*
* This implements both <i>abstract factory</i> and <i>singleton</i> patterns
* of which more information is available on the web and in software design
* textbooks (Notably <i>Design Patters</i>).
*
* \note You do not need to use this factory to create new frames to add to
* an ID3v2::Tag. You can instantiate frame subclasses directly (with new)
* and add them to a tag using ID3v2::Tag::addFrame()
*
* \see ID3v2::Tag::addFrame()
*/
class TAGLIB_EXPORT FrameFactory
{
public:
static FrameFactory *instance();
/*!
* Create a frame based on \a data. \a synchSafeInts should only be set
* false if we are parsing an old tag (v2.3 or older) that does not support
* synchsafe ints.
*
* \deprecated Please use the method below that accepts a ID3v2::Header
* instance in new code.
*/
Frame *createFrame(const ByteVector &data, bool synchSafeInts) const;
/*!
* Create a frame based on \a data. \a version should indicate the ID3v2
* version of the tag. As ID3v2.4 is the most current version of the
* standard 4 is the default.
*
* \deprecated Please use the method below that accepts a ID3v2::Header
* instance in new code.
*/
Frame *createFrame(const ByteVector &data, uint version = 4) const;
/*!
* Create a frame based on \a data. \a tagHeader should be a valid
* ID3v2::Header instance.
*/
// BIC: make virtual
Frame *createFrame(const ByteVector &data, Header *tagHeader) const;
/*!
* Returns the default text encoding for text frames. If setTextEncoding()
* has not been explicitly called this will only be used for new text
* frames. However, if this value has been set explicitly all frames will be
* converted to this type (unless it's explitly set differently for the
* individual frame) when being rendered.
*
* \see setDefaultTextEncoding()
*/
String::Type defaultTextEncoding() const;
/*!
* Set the default text encoding for all text frames that are created to
* \a encoding. If no value is set the frames with either default to the
* encoding type that was parsed and new frames default to Latin1.
*
* Valid string types for ID3v2 tags are Latin1, UTF8, UTF16 and UTF16BE.
*
* \see defaultTextEncoding()
*/
void setDefaultTextEncoding(String::Type encoding);
protected:
/*!
* Constructs a frame factory. Because this is a singleton this method is
* protected, but may be used for subclasses.
*/
FrameFactory();
/*!
* Destroys the frame factory. In most cases this will never be called (as
* is typical of singletons).
*/
virtual ~FrameFactory();
/*!
* This method checks for compliance to the current ID3v2 standard (2.4)
* and does nothing in the common case. However if a frame is found that
* is not compatible with the current standard, this method either updates
* the frame or indicates that it should be discarded.
*
* This method with return true (with or without changes to the frame) if
* this frame should be kept or false if it should be discarded.
*
* See the id3v2.4.0-changes.txt document for further information.
*/
virtual bool updateFrame(Frame::Header *header) const;
private:
FrameFactory(const FrameFactory &);
FrameFactory &operator=(const FrameFactory &);
/*!
* This method is used internally to convert a frame from ID \a from to ID
* \a to. If the frame matches the \a from pattern and converts the frame
* ID in the \a header or simply does nothing if the frame ID does not match.
*/
void convertFrame(const char *from, const char *to,
Frame::Header *header) const;
void updateGenre(TextIdentificationFrame *frame) const;
static FrameFactory *factory;
class FrameFactoryPrivate;
FrameFactoryPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,243 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <iostream>
#include <bitset>
#include <tstring.h>
#include <tdebug.h>
#include "id3v2header.h"
#include "id3v2footer.h"
#include "id3v2synchdata.h"
using namespace TagLib;
using namespace ID3v2;
class Header::HeaderPrivate
{
public:
HeaderPrivate() : majorVersion(4),
revisionNumber(0),
unsynchronisation(false),
extendedHeader(false),
experimentalIndicator(false),
footerPresent(false),
tagSize(0) {}
~HeaderPrivate() {}
uint majorVersion;
uint revisionNumber;
bool unsynchronisation;
bool extendedHeader;
bool experimentalIndicator;
bool footerPresent;
uint tagSize;
static const uint size = 10;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
TagLib::uint Header::size()
{
return HeaderPrivate::size;
}
ByteVector Header::fileIdentifier()
{
return ByteVector::fromCString("ID3");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Header::Header()
{
d = new HeaderPrivate;
}
Header::Header(const ByteVector &data)
{
d = new HeaderPrivate;
parse(data);
}
Header::~Header()
{
delete d;
}
TagLib::uint Header::majorVersion() const
{
return d->majorVersion;
}
void Header::setMajorVersion(TagLib::uint version)
{
d->majorVersion = version;
}
TagLib::uint Header::revisionNumber() const
{
return d->revisionNumber;
}
bool Header::unsynchronisation() const
{
return d->unsynchronisation;
}
bool Header::extendedHeader() const
{
return d->extendedHeader;
}
bool Header::experimentalIndicator() const
{
return d->experimentalIndicator;
}
bool Header::footerPresent() const
{
return d->footerPresent;
}
TagLib::uint Header::tagSize() const
{
return d->tagSize;
}
TagLib::uint Header::completeTagSize() const
{
if(d->footerPresent)
return d->tagSize + d->size + Footer::size();
else
return d->tagSize + d->size;
}
void Header::setTagSize(uint s)
{
d->tagSize = s;
}
void Header::setData(const ByteVector &data)
{
parse(data);
}
ByteVector Header::render() const
{
ByteVector v;
// add the file identifier -- "ID3"
v.append(fileIdentifier());
// add the version number -- we always render a 2.4.0 tag regardless of what
// the tag originally was.
v.append(char(4));
v.append(char(0));
// Currently we don't actually support writing extended headers, footers or
// unsynchronized tags, make sure that the flags are set accordingly.
d->extendedHeader = false;
d->footerPresent = false;
d->unsynchronisation = false;
// render and add the flags
std::bitset<8> flags;
flags[7] = d->unsynchronisation;
flags[6] = d->extendedHeader;
flags[5] = d->experimentalIndicator;
flags[4] = d->footerPresent;
v.append(char(flags.to_ulong()));
// add the size
v.append(SynchData::fromUInt(d->tagSize));
return v;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void Header::parse(const ByteVector &data)
{
if(data.size() < size())
return;
// do some sanity checking -- even in ID3v2.3.0 and less the tag size is a
// synch-safe integer, so all bytes must be less than 128. If this is not
// true then this is an invalid tag.
// note that we're doing things a little out of order here -- the size is
// later in the bytestream than the version
ByteVector sizeData = data.mid(6, 4);
if(sizeData.size() != 4) {
d->tagSize = 0;
debug("TagLib::ID3v2::Header::parse() - The tag size as read was 0 bytes!");
return;
}
for(ByteVector::Iterator it = sizeData.begin(); it != sizeData.end(); it++) {
if(uchar(*it) >= 128) {
d->tagSize = 0;
debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128.");
return;
}
}
// The first three bytes, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier")
// Read the version number from the fourth and fifth bytes.
d->majorVersion = data[3]; // (structure 3.1 "major version")
d->revisionNumber = data[4]; // (structure 3.1 "revision number")
// Read the flags, the first four bits of the sixth byte.
std::bitset<8> flags(data[5]);
d->unsynchronisation = flags[7]; // (structure 3.1.a)
d->extendedHeader = flags[6]; // (structure 3.1.b)
d->experimentalIndicator = flags[5]; // (structure 3.1.c)
d->footerPresent = flags[4]; // (structure 3.1.d)
// Get the size from the remaining four bytes (read above)
d->tagSize = SynchData::toUInt(sizeData); // (structure 3.1 "size")
}

View file

@ -0,0 +1,175 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2HEADER_H
#define TAGLIB_ID3V2HEADER_H
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An implementation of ID3v2 headers
/*!
* This class implements ID3v2 headers. It attempts to follow, both
* semantically and programatically, the structure specified in
* the ID3v2 standard. The API is based on the properties of ID3v2 headers
* specified there. If any of the terms used in this documentation are
* unclear please check the specification in the linked section.
* (Structure, <a href="id3v2-structure.html#3.1">3.1</a>)
*/
class TAGLIB_EXPORT Header
{
public:
/*!
* Constructs an empty ID3v2 header.
*/
Header();
/*!
* Constructs an ID3v2 header based on \a data. parse() is called
* immediately.
*/
Header(const ByteVector &data);
/*!
* Destroys the header.
*/
virtual ~Header();
/*!
* Returns the major version number. (Note: This is the 4, not the 2 in
* ID3v2.4.0. The 2 is implied.)
*/
uint majorVersion() const;
/*!
* Set the the major version number to \a version. (Note: This is
* the 4, not the 2 in ID3v2.4.0. The 2 is implied.)
* \see majorVersion()
*
* \note This is used by the internal parser; this will not change the
* version which is written and in general should not be called by API
* users.
*/
void setMajorVersion(uint version);
/*!
* Returns the revision number. (Note: This is the 0, not the 4 in
* ID3v2.4.0. The 2 is implied.)
*/
uint revisionNumber() const;
/*!
* Returns true if unsynchronisation has been applied to all frames.
*/
bool unsynchronisation() const;
/*!
* Returns true if an extended header is present in the tag.
*/
bool extendedHeader() const;
/*!
* Returns true if the experimental indicator flag is set.
*/
bool experimentalIndicator() const;
/*!
* Returns true if a footer is present in the tag.
*/
bool footerPresent() const;
/*!
* Returns the tag size in bytes. This is the size of the frame content.
* The size of the \e entire tag will be this plus the header size (10
* bytes) and, if present, the footer size (potentially another 10 bytes).
*
* \note This is the value as read from the header to which TagLib attempts
* to provide an API to; it was not a design decision on the part of TagLib
* to not include the mentioned portions of the tag in the \e size.
*
* \see completeTagSize()
*/
uint tagSize() const;
/*!
* Returns the tag size, including the header and, if present, the footer
* size.
*
* \see tagSize()
*/
uint completeTagSize() const;
/*!
* Set the tag size to \a s.
* \see tagSize()
*/
void setTagSize(uint s);
/*!
* Returns the size of the header. Presently this is always 10 bytes.
*/
static uint size();
/*!
* Returns the string used to identify and ID3v2 tag inside of a file.
* Presently this is always "ID3".
*/
static ByteVector fileIdentifier();
/*!
* Sets the data that will be used as the header. 10 bytes, starting from
* the beginning of \a data are used.
*/
void setData(const ByteVector &data);
/*!
* Renders the Header back to binary format.
*/
ByteVector render() const;
protected:
/*!
* Called by setData() to parse the header data. It makes this information
* available through the public API.
*/
void parse(const ByteVector &data);
private:
Header(const Header &);
Header &operator=(const Header &);
class HeaderPrivate;
HeaderPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,63 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <iostream>
#include "id3v2synchdata.h"
using namespace TagLib;
using namespace ID3v2;
TagLib::uint SynchData::toUInt(const ByteVector &data)
{
uint sum = 0;
int last = data.size() > 4 ? 3 : data.size() - 1;
for(int i = 0; i <= last; i++)
sum |= (data[i] & 0x7f) << ((last - i) * 7);
return sum;
}
ByteVector SynchData::fromUInt(uint value)
{
ByteVector v(4, 0);
for(int i = 0; i < 4; i++)
v[i] = uchar(value >> ((3 - i) * 7) & 0x7f);
return v;
}
ByteVector SynchData::decode(const ByteVector &data)
{
ByteVector result = data;
ByteVector pattern(2, char(0));
pattern[0] = '\xFF';
pattern[1] = '\x00';
return result.replace(pattern, '\xFF');
}

View file

@ -0,0 +1,70 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2SYNCHDATA_H
#define TAGLIB_ID3V2SYNCHDATA_H
#include "tbytevector.h"
#include "taglib.h"
namespace TagLib {
namespace ID3v2 {
//! A few functions for ID3v2 synch safe integer conversion
/*!
* In the ID3v2.4 standard most integer values are encoded as "synch safe"
* integers which are encoded in such a way that they will not give false
* MPEG syncs and confuse MPEG decoders. This namespace provides some
* methods for converting to and from these values to ByteVectors for
* things rendering and parsing ID3v2 data.
*/
namespace SynchData
{
/*!
* This returns the unsigned integer value of \a data where \a data is a
* ByteVector that contains a \e synchsafe integer (Structure,
* <a href="id3v2-structure.html#6.2">6.2</a>). The default \a length of
* 4 is used if another value is not specified.
*/
TAGLIB_EXPORT uint toUInt(const ByteVector &data);
/*!
* Returns a 4 byte (32 bit) synchsafe integer based on \a value.
*/
TAGLIB_EXPORT ByteVector fromUInt(uint value);
/*!
* Convert the data from unsynchronized data to its original format.
*/
TAGLIB_EXPORT ByteVector decode(const ByteVector &input);
}
}
}
#endif

View file

@ -0,0 +1,474 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tfile.h>
#include <tdebug.h>
#include "id3v2tag.h"
#include "id3v2header.h"
#include "id3v2extendedheader.h"
#include "id3v2footer.h"
#include "id3v2synchdata.h"
#include "id3v1genres.h"
#include "frames/textidentificationframe.h"
#include "frames/commentsframe.h"
using namespace TagLib;
using namespace ID3v2;
class ID3v2::Tag::TagPrivate
{
public:
TagPrivate() : file(0), tagOffset(-1), extendedHeader(0), footer(0), paddingSize(0)
{
frameList.setAutoDelete(true);
}
~TagPrivate()
{
delete extendedHeader;
delete footer;
}
File *file;
long tagOffset;
const FrameFactory *factory;
Header header;
ExtendedHeader *extendedHeader;
Footer *footer;
int paddingSize;
FrameListMap frameListMap;
FrameList frameList;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ID3v2::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
d->factory = FrameFactory::instance();
}
ID3v2::Tag::Tag(File *file, long tagOffset, const FrameFactory *factory) :
TagLib::Tag()
{
d = new TagPrivate;
d->file = file;
d->tagOffset = tagOffset;
d->factory = factory;
read();
}
ID3v2::Tag::~Tag()
{
delete d;
}
String ID3v2::Tag::title() const
{
if(!d->frameListMap["TIT2"].isEmpty())
return d->frameListMap["TIT2"].front()->toString();
return String::null;
}
String ID3v2::Tag::artist() const
{
if(!d->frameListMap["TPE1"].isEmpty())
return d->frameListMap["TPE1"].front()->toString();
return String::null;
}
String ID3v2::Tag::album() const
{
if(!d->frameListMap["TALB"].isEmpty())
return d->frameListMap["TALB"].front()->toString();
return String::null;
}
String ID3v2::Tag::comment() const
{
const FrameList &comments = d->frameListMap["COMM"];
if(comments.isEmpty())
return String::null;
for(FrameList::ConstIterator it = comments.begin(); it != comments.end(); ++it)
{
if(static_cast<CommentsFrame *>(*it)->description().isEmpty())
return (*it)->toString();
}
return comments.front()->toString();
}
String ID3v2::Tag::genre() const
{
// TODO: In the next major version (TagLib 2.0) a list of multiple genres
// should be separated by " / " instead of " ". For the moment to keep
// the behavior the same as released versions it is being left with " ".
if(d->frameListMap["TCON"].isEmpty() ||
!dynamic_cast<TextIdentificationFrame *>(d->frameListMap["TCON"].front()))
{
return String::null;
}
// ID3v2.4 lists genres as the fields in its frames field list. If the field
// is simply a number it can be assumed that it is an ID3v1 genre number.
// Here was assume that if an ID3v1 string is present that it should be
// appended to the genre string. Multiple fields will be appended as the
// string is built.
TextIdentificationFrame *f = static_cast<TextIdentificationFrame *>(
d->frameListMap["TCON"].front());
StringList fields = f->fieldList();
StringList genres;
for(StringList::Iterator it = fields.begin(); it != fields.end(); ++it) {
bool isNumber = true;
for(String::ConstIterator charIt = (*it).begin();
isNumber && charIt != (*it).end();
++charIt)
{
isNumber = *charIt >= '0' && *charIt <= '9';
}
if(isNumber) {
int number = (*it).toInt();
if(number >= 0 && number <= 255)
*it = ID3v1::genre(number);
}
if(std::find(genres.begin(), genres.end(), *it) == genres.end())
genres.append(*it);
}
return genres.toString();
}
TagLib::uint ID3v2::Tag::year() const
{
if(!d->frameListMap["TDRC"].isEmpty())
return d->frameListMap["TDRC"].front()->toString().substr(0, 4).toInt();
return 0;
}
TagLib::uint ID3v2::Tag::track() const
{
if(!d->frameListMap["TRCK"].isEmpty())
return d->frameListMap["TRCK"].front()->toString().toInt();
return 0;
}
void ID3v2::Tag::setTitle(const String &s)
{
setTextFrame("TIT2", s);
}
void ID3v2::Tag::setArtist(const String &s)
{
setTextFrame("TPE1", s);
}
void ID3v2::Tag::setAlbum(const String &s)
{
setTextFrame("TALB", s);
}
void ID3v2::Tag::setComment(const String &s)
{
if(s.isEmpty()) {
removeFrames("COMM");
return;
}
if(!d->frameListMap["COMM"].isEmpty())
d->frameListMap["COMM"].front()->setText(s);
else {
CommentsFrame *f = new CommentsFrame(d->factory->defaultTextEncoding());
addFrame(f);
f->setText(s);
}
}
void ID3v2::Tag::setGenre(const String &s)
{
if(s.isEmpty()) {
removeFrames("TCON");
return;
}
// iTunes can't handle correctly encoded ID3v2.4 numerical genres. Just use
// strings until iTunes sucks less.
#ifdef NO_ITUNES_HACKS
int index = ID3v1::genreIndex(s);
if(index != 255)
setTextFrame("TCON", String::number(index));
else
setTextFrame("TCON", s);
#else
setTextFrame("TCON", s);
#endif
}
void ID3v2::Tag::setYear(uint i)
{
if(i <= 0) {
removeFrames("TDRC");
return;
}
setTextFrame("TDRC", String::number(i));
}
void ID3v2::Tag::setTrack(uint i)
{
if(i <= 0) {
removeFrames("TRCK");
return;
}
setTextFrame("TRCK", String::number(i));
}
bool ID3v2::Tag::isEmpty() const
{
return d->frameList.isEmpty();
}
Header *ID3v2::Tag::header() const
{
return &(d->header);
}
ExtendedHeader *ID3v2::Tag::extendedHeader() const
{
return d->extendedHeader;
}
Footer *ID3v2::Tag::footer() const
{
return d->footer;
}
const FrameListMap &ID3v2::Tag::frameListMap() const
{
return d->frameListMap;
}
const FrameList &ID3v2::Tag::frameList() const
{
return d->frameList;
}
const FrameList &ID3v2::Tag::frameList(const ByteVector &frameID) const
{
return d->frameListMap[frameID];
}
void ID3v2::Tag::addFrame(Frame *frame)
{
d->frameList.append(frame);
d->frameListMap[frame->frameID()].append(frame);
}
void ID3v2::Tag::removeFrame(Frame *frame, bool del)
{
// remove the frame from the frame list
FrameList::Iterator it = d->frameList.find(frame);
d->frameList.erase(it);
// ...and from the frame list map
it = d->frameListMap[frame->frameID()].find(frame);
d->frameListMap[frame->frameID()].erase(it);
// ...and delete as desired
if(del)
delete frame;
}
void ID3v2::Tag::removeFrames(const ByteVector &id)
{
FrameList l = d->frameListMap[id];
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
removeFrame(*it, true);
}
ByteVector ID3v2::Tag::render() const
{
// 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.
ByteVector 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++) {
if(!(*it)->header()->tagAlterPreservation())
tagData.append((*it)->render());
}
// Compute the amount of padding, and append that to tagData.
uint paddingSize = 0;
uint originalSize = d->header.tagSize();
if(tagData.size() < originalSize)
paddingSize = originalSize - tagData.size();
else
paddingSize = 1024;
tagData.append(ByteVector(paddingSize, char(0)));
// Set the tag size.
d->header.setTagSize(tagData.size());
// TODO: This should eventually include d->footer->render().
return d->header.render() + tagData;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void ID3v2::Tag::read()
{
if(d->file && d->file->isOpen()) {
d->file->seek(d->tagOffset);
d->header.setData(d->file->readBlock(Header::size()));
// if the tag size is 0, then this is an invalid tag (tags must contain at
// least one frame)
if(d->header.tagSize() == 0)
return;
parse(d->file->readBlock(d->header.tagSize()));
}
}
void ID3v2::Tag::parse(const ByteVector &origData)
{
ByteVector data = origData;
if(d->header.unsynchronisation() && d->header.majorVersion() <= 3)
data = SynchData::decode(data);
uint frameDataPosition = 0;
uint frameDataLength = data.size();
// check for extended header
if(d->header.extendedHeader()) {
if(!d->extendedHeader)
d->extendedHeader = new ExtendedHeader;
d->extendedHeader->setData(data);
if(d->extendedHeader->size() <= data.size()) {
frameDataPosition += d->extendedHeader->size();
frameDataLength -= d->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(d->header.footerPresent() && Footer::size() <= frameDataLength)
frameDataLength -= 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 - Frame::headerSize(d->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(d->header.footerPresent())
debug("Padding *and* a footer found. This is not allowed by the spec.");
d->paddingSize = frameDataLength - frameDataPosition;
return;
}
Frame *frame = d->factory->createFrame(data.mid(frameDataPosition),
&d->header);
if(!frame)
return;
// Checks to make sure that frame parsed correctly.
if(frame->size() <= 0) {
delete frame;
return;
}
frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion());
addFrame(frame);
}
}
void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value)
{
if(value.isEmpty()) {
removeFrames(id);
return;
}
if(!d->frameListMap[id].isEmpty())
d->frameListMap[id].front()->setText(value);
else {
const String::Type encoding = d->factory->defaultTextEncoding();
TextIdentificationFrame *f = new TextIdentificationFrame(id, encoding);
addFrame(f);
f->setText(value);
}
}

View file

@ -0,0 +1,300 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_ID3V2TAG_H
#define TAGLIB_ID3V2TAG_H
#include "tag.h"
#include "tbytevector.h"
#include "tstring.h"
#include "tlist.h"
#include "tmap.h"
#include "taglib_export.h"
#include "id3v2framefactory.h"
namespace TagLib {
class File;
//! An ID3v2 implementation
/*!
* This is a relatively complete and flexible framework for working with ID3v2
* tags.
*
* \see ID3v2::Tag
*/
namespace ID3v2 {
class Header;
class ExtendedHeader;
class Footer;
typedef List<Frame *> FrameList;
typedef Map<ByteVector, FrameList> FrameListMap;
//! The main class in the ID3v2 implementation
/*!
* This is the main class in the ID3v2 implementation. It serves two
* functions. This first, as is obvious from the public API, is to provide a
* container for the other ID3v2 related classes. In addition, through the
* read() and parse() protected methods, it provides the most basic level of
* parsing. In these methods the ID3v2 tag is extracted from the file and
* split into data components.
*
* ID3v2 tags have several parts, TagLib attempts to provide an interface
* for them all. header(), footer() and extendedHeader() corespond to those
* data structures in the ID3v2 standard and the APIs for the classes that
* they return attempt to reflect this.
*
* Also ID3v2 tags are built up from a list of frames, which are in turn
* have a header and a list of fields. TagLib provides two ways of accessing
* the list of frames that are in a given ID3v2 tag. The first is simply
* via the frameList() method. This is just a list of pointers to the frames.
* The second is a map from the frame type -- i.e. "COMM" for comments -- and
* a list of frames of that type. (In some cases ID3v2 allows for multiple
* frames of the same type, hence this being a map to a list rather than just
* a map to an individual frame.)
*
* More information on the structure of frames can be found in the ID3v2::Frame
* class.
*
* read() and parse() pass binary data to the other ID3v2 class structures,
* they do not handle parsing of flags or fields, for instace. Those are
* handled by similar functions within those classes.
*
* \note All pointers to data structures within the tag will become invalid
* when the tag is destroyed.
*
* \warning Dealing with the nasty details of ID3v2 is not for the faint of
* heart and should not be done without much meditation on the spec. It's
* rather long, but if you're planning on messing with this class and others
* that deal with the details of ID3v2 (rather than the nice, safe, abstract
* TagLib::Tag and friends), it's worth your time to familiarize yourself
* with said spec (which is distrubuted with the TagLib sources). TagLib
* tries to do most of the work, but with a little luck, you can still
* convince it to generate invalid ID3v2 tags. The APIs for ID3v2 assume a
* working knowledge of ID3v2 structure. You're been warned.
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
/*!
* Constructs an empty ID3v2 tag.
*
* \note You must create at least one frame for this tag to be valid.
*/
Tag();
/*!
* Constructs an ID3v2 tag read from \a file starting at \a tagOffset.
* \a factory specifies which FrameFactory will be used for the
* construction of new frames.
*
* \note You should be able to ignore the \a factory parameter in almost
* all situations. You would want to specify your own FrameFactory
* subclass in the case that you are extending TagLib to support additional
* frame types, which would be incorperated into your factory.
*
* \see FrameFactory
*/
Tag(File *file, long tagOffset,
const FrameFactory *factory = FrameFactory::instance());
/*!
* Destroys this Tag instance.
*/
virtual ~Tag();
// Reimplementations.
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
virtual bool isEmpty() const;
/*!
* Returns a pointer to the tag's header.
*/
Header *header() const;
/*!
* Returns a pointer to the tag's extended header or null if there is no
* extended header.
*/
ExtendedHeader *extendedHeader() const;
/*!
* Returns a pointer to the tag's footer or null if there is no footer.
*
* \deprecated I don't see any reason to keep this around since there's
* nothing useful to be retrieved from the footer, but well, again, I'm
* prone to change my mind, so this gets to stay around until near a
* release.
*/
Footer *footer() const;
/*!
* Returns a reference to the frame list map. This is an FrameListMap of
* all of the frames in the tag.
*
* This is the most convenient structure for accessing the tag's frames.
* Many frame types allow multiple instances of the same frame type so this
* is a map of lists. In most cases however there will only be a single
* frame of a certain type.
*
* Let's say for instance that you wanted to access the frame for total
* beats per minute -- the TBPM frame.
*
* \code
* TagLib::MPEG::File f("foo.mp3");
*
* // Check to make sure that it has an ID3v2 tag
*
* if(f.ID3v2Tag()) {
*
* // Get the list of frames for a specific frame type
*
* TagLib::ID3v2::FrameList l = f.ID3v2Tag()->frameListMap()["TBPM"];
*
* if(!l.isEmpty())
* std::cout << l.front()->toString() << std::endl;
* }
*
* \endcode
*
* \warning You should not modify this data structure directly, instead
* use addFrame() and removeFrame().
*
* \see frameList()
*/
const FrameListMap &frameListMap() const;
/*!
* Returns a reference to the frame list. This is an FrameList of all of
* the frames in the tag in the order that they were parsed.
*
* This can be useful if for example you want iterate over the tag's frames
* in the order that they occur in the tag.
*
* \warning You should not modify this data structure directly, instead
* use addFrame() and removeFrame().
*/
const FrameList &frameList() const;
/*!
* Returns the frame list for frames with the id \a frameID or an empty
* list if there are no frames of that type. This is just a convenience
* and is equivalent to:
*
* \code
* frameListMap()[frameID];
* \endcode
*
* \see frameListMap()
*/
const FrameList &frameList(const ByteVector &frameID) const;
/*!
* Add a frame to the tag. At this point the tag takes ownership of
* the frame and will handle freeing its memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by frameList()
*/
void addFrame(Frame *frame);
/*!
* Remove a frame from the tag. If \a del is true the frame's memory
* will be freed; if it is false, it must be deleted by the user.
*
* \note Using this method will invalidate any pointers on the list
* returned by frameList()
*/
void removeFrame(Frame *frame, bool del = true);
/*!
* Remove all frames of type \a id from the tag and free their memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by frameList()
*/
void removeFrames(const ByteVector &id);
/*!
* Render the tag back to binary data, suitable to be written to disk.
*/
ByteVector render() const;
protected:
/*!
* Reads data from the file specified in the constructor. It does basic
* parsing of the data in the largest chunks. It partitions the tag into
* the Header, the body of the tag (which contains the ExtendedHeader and
* frames) and Footer.
*/
void read();
/*!
* This is called by read to parse the body of the tag. It determines if an
* extended header exists and adds frames to the FrameListMap.
*/
void parse(const ByteVector &data);
/*!
* Sets the value of the text frame with the Frame ID \a id to \a value.
* If the frame does not exist, it is created.
*/
void setTextFrame(const ByteVector &id, const String &value);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,593 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tagunion.h>
#include <id3v2tag.h>
#include <id3v2header.h>
#include <id3v1tag.h>
#include <apefooter.h>
#include <apetag.h>
#include <tdebug.h>
#include <bitset>
#include "mpegfile.h"
#include "mpegheader.h"
using namespace TagLib;
namespace
{
enum { ID3v2Index = 0, APEIndex = 1, ID3v1Index = 2 };
}
class MPEG::File::FilePrivate
{
public:
FilePrivate(ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory),
ID3v2Location(-1),
ID3v2OriginalSize(0),
APELocation(-1),
APEFooterLocation(-1),
APEOriginalSize(0),
ID3v1Location(-1),
hasID3v2(false),
hasID3v1(false),
hasAPE(false),
properties(0)
{
}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long ID3v2Location;
uint ID3v2OriginalSize;
long APELocation;
long APEFooterLocation;
uint APEOriginalSize;
long ID3v1Location;
TagUnion tag;
// These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
bool hasID3v2;
bool hasID3v1;
bool hasAPE;
Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPEG::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
}
MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
{
d = new FilePrivate(frameFactory);
if(isOpen())
read(readProperties, propertiesStyle);
}
MPEG::File::~File()
{
delete d;
}
TagLib::Tag *MPEG::File::tag() const
{
return &d->tag;
}
MPEG::Properties *MPEG::File::audioProperties() const
{
return d->properties;
}
bool MPEG::File::save()
{
return save(AllTags);
}
bool MPEG::File::save(int tags)
{
return save(tags, true);
}
bool MPEG::File::save(int tags, bool stripOthers)
{
if(tags == NoTags && stripOthers)
return strip(AllTags);
if(!ID3v2Tag() && !ID3v1Tag() && !APETag()) {
if((d->hasID3v1 || d->hasID3v2 || d->hasAPE) && stripOthers)
return strip(AllTags);
return true;
}
if(readOnly()) {
debug("MPEG::File::save() -- File is read only.");
return false;
}
// Create the tags if we've been asked to. Copy the values from the tag that
// does exist into the new tag.
if((tags & ID3v2) && ID3v1Tag())
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
if((tags & ID3v1) && d->tag[ID3v2Index])
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
bool success = true;
if(ID3v2 & tags) {
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
if(!d->hasID3v2)
d->ID3v2Location = 0;
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
d->hasID3v2 = true;
// v1 tag location has changed, update if it exists
if(ID3v1Tag())
d->ID3v1Location = findID3v1();
// APE tag location has changed, update if it exists
if(APETag())
findAPE();
}
else if(stripOthers)
success = strip(ID3v2, false) && success;
}
else if(d->hasID3v2 && stripOthers)
success = strip(ID3v2) && success;
if(ID3v1 & tags) {
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
int offset = d->hasID3v1 ? -128 : 0;
seek(offset, End);
writeBlock(ID3v1Tag()->render());
d->hasID3v1 = true;
d->ID3v1Location = findID3v1();
}
else if(stripOthers)
success = strip(ID3v1) && success;
}
else if(d->hasID3v1 && stripOthers)
success = strip(ID3v1, false) && success;
// Dont save an APE-tag unless one has been created
if((APE & tags) && APETag()) {
if(d->hasAPE)
insert(APETag()->render(), d->APELocation, d->APEOriginalSize);
else {
if(d->hasID3v1) {
insert(APETag()->render(), d->ID3v1Location, 0);
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
d->APELocation = d->ID3v1Location;
d->ID3v1Location += d->APEOriginalSize;
}
else {
seek(0, End);
d->APELocation = tell();
d->APEFooterLocation = d->APELocation
+ d->tag.access<APE::Tag>(APEIndex, false)->footer()->completeTagSize()
- APE::Footer::size();
writeBlock(APETag()->render());
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
}
}
else if(d->hasAPE && stripOthers)
success = strip(APE, false) && success;
return success;
}
ID3v2::Tag *MPEG::File::ID3v2Tag(bool create)
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
}
ID3v1::Tag *MPEG::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
}
APE::Tag *MPEG::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(APEIndex, create);
}
bool MPEG::File::strip(int tags)
{
return strip(tags, true);
}
bool MPEG::File::strip(int tags, bool freeMemory)
{
if(readOnly()) {
debug("MPEG::File::strip() - Cannot strip tags from a read only file.");
return false;
}
if((tags & ID3v2) && d->hasID3v2) {
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
d->ID3v2Location = -1;
d->ID3v2OriginalSize = 0;
d->hasID3v2 = false;
if(freeMemory)
d->tag.set(ID3v2Index, 0);
// v1 tag location has changed, update if it exists
if(ID3v1Tag())
d->ID3v1Location = findID3v1();
// APE tag location has changed, update if it exists
if(APETag())
findAPE();
}
if((tags & ID3v1) && d->hasID3v1) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
d->hasID3v1 = false;
if(freeMemory)
d->tag.set(ID3v1Index, 0);
}
if((tags & APE) && d->hasAPE) {
removeBlock(d->APELocation, d->APEOriginalSize);
d->APELocation = -1;
d->APEFooterLocation = -1;
d->hasAPE = false;
if(d->hasID3v1) {
if(d->ID3v1Location > d->APELocation)
d->ID3v1Location -= d->APEOriginalSize;
}
if(freeMemory)
d->tag.set(APEIndex, 0);
}
return true;
}
void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
d->ID3v2FrameFactory = factory;
}
long MPEG::File::nextFrameOffset(long position)
{
bool foundLastSyncPattern = false;
ByteVector buffer;
while(true) {
seek(position);
buffer = readBlock(bufferSize());
if(buffer.size() <= 0)
return -1;
if(foundLastSyncPattern && secondSynchByte(buffer[0]))
return position - 1;
for(uint i = 0; i < buffer.size() - 1; i++) {
if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
return position + i;
}
foundLastSyncPattern = uchar(buffer[buffer.size() - 1]) == 0xff;
position += buffer.size();
}
}
long MPEG::File::previousFrameOffset(long position)
{
bool foundFirstSyncPattern = false;
ByteVector buffer;
while (position > 0) {
long size = ulong(position) < bufferSize() ? position : bufferSize();
position -= size;
seek(position);
buffer = readBlock(size);
if(buffer.size() <= 0)
break;
if(foundFirstSyncPattern && uchar(buffer[buffer.size() - 1]) == 0xff)
return position + buffer.size() - 1;
for(int i = buffer.size() - 2; i >= 0; i--) {
if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
return position + i;
}
foundFirstSyncPattern = secondSynchByte(buffer[0]);
}
return -1;
}
long MPEG::File::firstFrameOffset()
{
long position = 0;
if(ID3v2Tag())
position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize();
return nextFrameOffset(position);
}
long MPEG::File::lastFrameOffset()
{
return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
// Look for an ID3v2 tag
d->ID3v2Location = findID3v2();
if(d->ID3v2Location >= 0) {
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
if(ID3v2Tag()->header()->tagSize() <= 0)
d->tag.set(ID3v2Index, 0);
else
d->hasID3v2 = true;
}
// Look for an ID3v1 tag
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for an APE tag
findAPE();
if(d->APELocation >= 0) {
d->tag.set(APEIndex, new APE::Tag(this, d->APEFooterLocation));
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
if(readProperties)
d->properties = new Properties(this, propertiesStyle);
// Make sure that we have our default tag types available.
ID3v2Tag(true);
ID3v1Tag(true);
}
long MPEG::File::findID3v2()
{
// This method is based on the contents of TagLib::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;
ByteVector 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(uchar(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(uchar(255)), firstSynchByte + 1);
}
// (3) partial match
previousPartialMatch = buffer.endsWithPartialMatch(ID3v2::Header::fileIdentifier());
bufferOffset += bufferSize();
}
// Since we hit the end of the file, reset the status before continuing.
clear();
seek(originalPosition);
}
return -1;
}
long MPEG::File::findID3v1()
{
if(isValid()) {
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
}
return -1;
}
void MPEG::File::findAPE()
{
if(isValid()) {
seek(d->hasID3v1 ? -160 : -32, End);
long p = tell();
if(readBlock(8) == APE::Tag::fileIdentifier()) {
d->APEFooterLocation = p;
seek(d->APEFooterLocation);
APE::Footer footer(readBlock(APE::Footer::size()));
d->APELocation = d->APEFooterLocation - footer.completeTagSize()
+ APE::Footer::size();
return;
}
}
d->APELocation = -1;
d->APEFooterLocation = -1;
}
bool MPEG::File::secondSynchByte(char byte)
{
if(uchar(byte) == 0xff)
return false;
std::bitset<8> b(byte);
// check to see if the byte matches 111xxxxx
return b.test(7) && b.test(6) && b.test(5);
}

View file

@ -0,0 +1,277 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_MPEGFILE_H
#define TAGLIB_MPEGFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "mpegproperties.h"
namespace TagLib {
namespace ID3v2 { class Tag; class FrameFactory; }
namespace ID3v1 { class Tag; }
namespace APE { class Tag; }
//! An implementation of TagLib::File with MPEG (MP3) specific methods
namespace MPEG {
//! An MPEG file class with some useful methods specific to MPEG
/*!
* This implements the generic TagLib::File API and additionally provides
* access to properties that are distinct to MPEG files, notably access
* to the different ID3 tags.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches ID3v1 tags.
ID3v1 = 0x0001,
//! Matches ID3v2 tags.
ID3v2 = 0x0002,
//! Matches APE tags.
APE = 0x0004,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Contructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored. The frames will be created using
* \a frameFactory.
*/
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns a pointer to a tag that is the union of the ID3v2 and ID3v1
* tags. The ID3v2 tag is given priority in reading the information -- if
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
* the information from the ID3v2 tag will be returned.
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use the tag-type specific calls.
*
* \note As this tag is not implemented as an ID3v2 tag or an ID3v1 tag,
* but a union of the two this pointer may not be cast to the specific
* tag types.
*
* \see ID3v1Tag()
* \see ID3v2Tag()
* \see APETag()
*/
virtual Tag *tag() const;
/*!
* Returns the MPEG::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Save the file. If at least one tag -- ID3v1 or ID3v2 -- exists this
* will duplicate its content into the other tag. This returns true
* if saving was successful.
*
* If neither exists or if both tags are empty, this will strip the tags
* from the file.
*
* This is the same as calling save(AllTags);
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use paramaterized save call below.
*
* \see save(int tags)
*/
virtual bool save();
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* This strips all tags not included in the mask, but does not modify them
* in memory, so later calls to save() which make use of these tags will
* remain valid. This also strips empty tags.
*/
bool save(int tags);
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* If \a stripOthers is true this strips all tags not included in the mask,
* but does not modify them in memory, so later calls to save() which make
* use of these tags will remain valid. This also strips empty tags.
*/
// BIC: combine with the above method
bool save(int tags, bool stripOthers);
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
ID3v2::Tag *ID3v2Tag(bool create = false);
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
APE::Tag *APETag(bool create = false);
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns true if the tags are
* successfully stripped.
*
* This is equivalent to strip(tags, true)
*
* \note This will also invalidate pointers to the ID3 and APE tags
* as their memory will be freed.
*/
bool strip(int tags = AllTags);
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns true if the tags are
* successfully stripped.
*
* If \a freeMemory is true the ID3 and APE tags will be deleted and
* pointers to them will be invalidated.
*/
// BIC: merge with the method above
bool strip(int tags, bool freeMemory);
/*!
* Set the ID3v2::FrameFactory to something other than the default.
*
* \see ID3v2FrameFactory
*/
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
/*!
* Returns the position in the file of the first MPEG frame.
*/
long firstFrameOffset();
/*!
* Returns the position in the file of the next MPEG frame,
* using the current position as start
*/
long nextFrameOffset(long position);
/*!
* Returns the position in the file of the previous MPEG frame,
* using the current position as start
*/
long previousFrameOffset(long position);
/*!
* Returns the position in the file of the last MPEG frame.
*/
long lastFrameOffset();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
long findID3v2();
long findID3v1();
void findAPE();
/*!
* MPEG frames can be recognized by the bit pattern 11111111 111, so the
* first byte is easy to check for, however checking to see if the second byte
* starts with \e 111 is a bit more tricky, hence this member function.
*/
static bool secondSynchByte(char byte);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,276 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <bitset>
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include "mpegheader.h"
using namespace TagLib;
class MPEG::Header::HeaderPrivate : public RefCounter
{
public:
HeaderPrivate() :
isValid(false),
version(Version1),
layer(0),
protectionEnabled(false),
sampleRate(0),
isPadded(false),
channelMode(Stereo),
isCopyrighted(false),
isOriginal(false),
frameLength(0),
samplesPerFrame(0) {}
bool isValid;
Version version;
int layer;
bool protectionEnabled;
int bitrate;
int sampleRate;
bool isPadded;
ChannelMode channelMode;
bool isCopyrighted;
bool isOriginal;
int frameLength;
int samplesPerFrame;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPEG::Header::Header(const ByteVector &data)
{
d = new HeaderPrivate;
parse(data);
}
MPEG::Header::Header(const Header &h) : d(h.d)
{
d->ref();
}
MPEG::Header::~Header()
{
if (d->deref())
delete d;
}
bool MPEG::Header::isValid() const
{
return d->isValid;
}
MPEG::Header::Version MPEG::Header::version() const
{
return d->version;
}
int MPEG::Header::layer() const
{
return d->layer;
}
bool MPEG::Header::protectionEnabled() const
{
return d->protectionEnabled;
}
int MPEG::Header::bitrate() const
{
return d->bitrate;
}
int MPEG::Header::sampleRate() const
{
return d->sampleRate;
}
bool MPEG::Header::isPadded() const
{
return d->isPadded;
}
MPEG::Header::ChannelMode MPEG::Header::channelMode() const
{
return d->channelMode;
}
bool MPEG::Header::isCopyrighted() const
{
return d->isCopyrighted;
}
bool MPEG::Header::isOriginal() const
{
return d->isOriginal;
}
int MPEG::Header::frameLength() const
{
return d->frameLength;
}
int MPEG::Header::samplesPerFrame() const
{
return d->samplesPerFrame;
}
MPEG::Header &MPEG::Header::operator=(const Header &h)
{
if(&h == this)
return *this;
if(d->deref())
delete d;
d = h.d;
d->ref();
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void MPEG::Header::parse(const ByteVector &data)
{
if(data.size() < 4 || uchar(data[0]) != 0xff) {
debug("MPEG::Header::parse() -- First byte did not mactch MPEG synch.");
return;
}
std::bitset<32> flags(data.toUInt());
// Check for the second byte's part of the MPEG synch
if(!flags[23] || !flags[22] || !flags[21]) {
debug("MPEG::Header::parse() -- Second byte did not mactch MPEG synch.");
return;
}
// Set the MPEG version
if(!flags[20] && !flags[19])
d->version = Version2_5;
else if(flags[20] && !flags[19])
d->version = Version2;
else if(flags[20] && flags[19])
d->version = Version1;
// Set the MPEG layer
if(!flags[18] && flags[17])
d->layer = 3;
else if(flags[18] && !flags[17])
d->layer = 2;
else if(flags[18] && flags[17])
d->layer = 1;
d->protectionEnabled = !flags[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 = d->version == Version1 ? 0 : 1;
const int layerIndex = d->layer > 0 ? d->layer - 1 : 0;
// The bitrate index is encoded as the first 4 bits of the 3rd byte,
// i.e. 1111xxxx
int i = uchar(data[2]) >> 4;
d->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 = uchar(data[2]) >> 2 & 0x03;
d->sampleRate = sampleRates[d->version][i];
if(d->sampleRate == 0) {
debug("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
d->channelMode = ChannelMode((uchar(data[3]) & 0xC0) >> 6);
// TODO: Add mode extension for completeness
d->isOriginal = flags[2];
d->isCopyrighted = flags[3];
d->isPadded = flags[9];
// Calculate the frame length
if(d->layer == 1)
d->frameLength = 24000 * 2 * d->bitrate / d->sampleRate + int(d->isPadded);
else
d->frameLength = 72000 * d->bitrate / d->sampleRate + int(d->isPadded);
// Samples per frame
static const int samplesPerFrame[3][2] = {
// MPEG1, 2/2.5
{ 384, 384 }, // Layer I
{ 1152, 1152 }, // Layer II
{ 1152, 576 } // Layer III
};
d->samplesPerFrame = samplesPerFrame[layerIndex][versionIndex];
// Now that we're done parsing, set this to be a valid frame.
d->isValid = true;
}

View file

@ -0,0 +1,166 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_MPEGHEADER_H
#define TAGLIB_MPEGHEADER_H
#include "taglib_export.h"
namespace TagLib {
class ByteVector;
namespace MPEG {
//! An implementation of MP3 frame headers
/*!
* This is an implementation of MPEG Layer III headers. The API follows more
* or less the binary format of these headers. I've used
* <a href="http://www.mp3-tech.org/programmer/frame_header.html">this</a>
* document as a reference.
*/
class TAGLIB_EXPORT Header
{
public:
/*!
* Parses an MPEG header based on \a data.
*/
Header(const ByteVector &data);
/*!
* Does a shallow copy of \a h.
*/
Header(const Header &h);
/*!
* Destroys this Header instance.
*/
virtual ~Header();
/*!
* Returns true if the frame is at least an appropriate size and has
* legal values.
*/
bool isValid() const;
/*!
* The MPEG Version.
*/
enum Version {
//! MPEG Version 1
Version1 = 0,
//! MPEG Version 2
Version2 = 1,
//! MPEG Version 2.5
Version2_5 = 2
};
/*!
* Returns the MPEG Version of the header.
*/
Version version() const;
/*!
* Returns the layer version. This will be between the values 1-3.
*/
int layer() const;
/*!
* Returns true if the MPEG protection bit is enabled.
*/
bool protectionEnabled() const;
/*!
* Returns the bitrate encoded in the header.
*/
int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
int sampleRate() const;
/*!
* Returns true if the frame is padded.
*/
bool isPadded() const;
/*!
* There are a few combinations or one or two channel audio that are
* possible:
*/
enum ChannelMode {
//! Stereo
Stereo = 0,
//! Stereo
JointStereo = 1,
//! Dual Mono
DualChannel = 2,
//! Mono
SingleChannel = 3
};
/*!
* Returns the channel mode for this frame.
*/
ChannelMode channelMode() const;
/*!
* Returns true if the copyrighted bit is set.
*/
bool isCopyrighted() const;
/*!
* Returns true if the "original" bit is set.
*/
bool isOriginal() const;
/*!
* Returns the frame length.
*/
int frameLength() const;
/*!
* Returns the number of frames per sample.
*/
int samplesPerFrame() const;
/*!
* Makes a shallow copy of the header.
*/
Header &operator=(const Header &h);
private:
void parse(const ByteVector &data);
class HeaderPrivate;
HeaderPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,252 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tdebug.h>
#include <tstring.h>
#include "mpegproperties.h"
#include "mpegfile.h"
#include "xingheader.h"
using namespace TagLib;
class MPEG::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(File *f, ReadStyle s) :
file(f),
xingHeader(0),
style(s),
length(0),
bitrate(0),
sampleRate(0),
channels(0),
layer(0),
version(Header::Version1),
channelMode(Header::Stereo),
protectionEnabled(false),
isCopyrighted(false),
isOriginal(false) {}
~PropertiesPrivate()
{
delete xingHeader;
}
File *file;
XingHeader *xingHeader;
ReadStyle style;
int length;
int bitrate;
int sampleRate;
int channels;
int layer;
Header::Version version;
Header::ChannelMode channelMode;
bool protectionEnabled;
bool isCopyrighted;
bool isOriginal;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(file, style);
if(file && file->isOpen())
read();
}
MPEG::Properties::~Properties()
{
delete d;
}
int MPEG::Properties::length() const
{
return d->length;
}
int MPEG::Properties::bitrate() const
{
return d->bitrate;
}
int MPEG::Properties::sampleRate() const
{
return d->sampleRate;
}
int MPEG::Properties::channels() const
{
return d->channels;
}
const MPEG::XingHeader *MPEG::Properties::xingHeader() const
{
return d->xingHeader;
}
MPEG::Header::Version MPEG::Properties::version() const
{
return d->version;
}
int MPEG::Properties::layer() const
{
return d->layer;
}
bool MPEG::Properties::protectionEnabled() const
{
return d->protectionEnabled;
}
MPEG::Header::ChannelMode MPEG::Properties::channelMode() const
{
return d->channelMode;
}
bool MPEG::Properties::isCopyrighted() const
{
return d->isCopyrighted;
}
bool MPEG::Properties::isOriginal() const
{
return d->isOriginal;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void MPEG::Properties::read()
{
// Since we've likely just looked for the ID3v1 tag, start at the end of the
// file where we're least likely to have to have to move the disk head.
long last = d->file->lastFrameOffset();
if(last < 0) {
debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.");
return;
}
d->file->seek(last);
Header lastHeader(d->file->readBlock(4));
long first = d->file->firstFrameOffset();
if(first < 0) {
debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream.");
return;
}
if(!lastHeader.isValid()) {
long pos = last;
while(pos > first) {
pos = d->file->previousFrameOffset(pos);
if(pos < 0)
break;
d->file->seek(pos);
Header header(d->file->readBlock(4));
if(header.isValid()) {
lastHeader = header;
last = pos;
break;
}
}
}
// Now jump back to the front of the file and read what we need from there.
d->file->seek(first);
Header firstHeader(d->file->readBlock(4));
if(!firstHeader.isValid() || !lastHeader.isValid()) {
debug("MPEG::Properties::read() -- Page headers were invalid.");
return;
}
// Check for a Xing header that will help us in gathering information about a
// VBR stream.
int xingHeaderOffset = MPEG::XingHeader::xingHeaderOffset(firstHeader.version(),
firstHeader.channelMode());
d->file->seek(first + xingHeaderOffset);
d->xingHeader = new XingHeader(d->file->readBlock(16));
// Read the length and the bitrate from the Xing header.
if(d->xingHeader->isValid() &&
firstHeader.sampleRate() > 0 &&
d->xingHeader->totalFrames() > 0)
{
double timePerFrame =
double(firstHeader.samplesPerFrame()) / firstHeader.sampleRate();
d->length = int(timePerFrame * d->xingHeader->totalFrames());
d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / d->length / 1000 : 0;
}
else {
// Since there was no valid Xing header found, we hope that we're in a constant
// bitrate file.
delete d->xingHeader;
d->xingHeader = 0;
// TODO: Make this more robust with audio property detection for VBR without a
// Xing header.
if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) {
int frames = (last - first) / firstHeader.frameLength() + 1;
d->length = int(float(firstHeader.frameLength() * frames) /
float(firstHeader.bitrate() * 125) + 0.5);
d->bitrate = firstHeader.bitrate();
}
}
d->sampleRate = firstHeader.sampleRate();
d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2;
d->version = firstHeader.version();
d->layer = firstHeader.layer();
d->protectionEnabled = firstHeader.protectionEnabled();
d->channelMode = firstHeader.channelMode();
d->isCopyrighted = firstHeader.isCopyrighted();
d->isOriginal = firstHeader.isOriginal();
}

View file

@ -0,0 +1,118 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_MPEGPROPERTIES_H
#define TAGLIB_MPEGPROPERTIES_H
#include "taglib_export.h"
#include "audioproperties.h"
#include "mpegheader.h"
namespace TagLib {
namespace MPEG {
class File;
class XingHeader;
//! An implementation of audio property reading for MP3
/*!
* This reads the data from an MPEG Layer III stream found in the
* AudioProperties API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of MPEG::Properties with the data read from the
* MPEG::File \a file.
*/
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this MPEG Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns a pointer to the XingHeader if one exists or null if no
* XingHeader was found.
*/
const XingHeader *xingHeader() const;
/*!
* Returns the MPEG Version of the file.
*/
Header::Version version() const;
/*!
* Returns the layer version. This will be between the values 1-3.
*/
int layer() const;
/*!
* Returns true if the MPEG protection bit is enabled.
*/
bool protectionEnabled() const;
/*!
* Returns the channel mode for this frame.
*/
Header::ChannelMode channelMode() const;
/*!
* Returns true if the copyrighted bit is set.
*/
bool isCopyrighted() const;
/*!
* Returns true if the "original" bit is set.
*/
bool isOriginal() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,115 @@
/***************************************************************************
copyright : (C) 2003 by Ismael Orenstein
email : orenstein@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "xingheader.h"
using namespace TagLib;
class MPEG::XingHeader::XingHeaderPrivate
{
public:
XingHeaderPrivate() :
frames(0),
size(0),
valid(false)
{}
uint frames;
uint size;
bool valid;
};
MPEG::XingHeader::XingHeader(const ByteVector &data)
{
d = new XingHeaderPrivate;
parse(data);
}
MPEG::XingHeader::~XingHeader()
{
delete d;
}
bool MPEG::XingHeader::isValid() const
{
return d->valid;
}
TagLib::uint MPEG::XingHeader::totalFrames() const
{
return d->frames;
}
TagLib::uint MPEG::XingHeader::totalSize() const
{
return d->size;
}
int MPEG::XingHeader::xingHeaderOffset(TagLib::MPEG::Header::Version v,
TagLib::MPEG::Header::ChannelMode c)
{
if(v == MPEG::Header::Version1) {
if(c == MPEG::Header::SingleChannel)
return 0x15;
else
return 0x24;
}
else {
if(c == MPEG::Header::SingleChannel)
return 0x0D;
else
return 0x15;
}
}
void MPEG::XingHeader::parse(const ByteVector &data)
{
// Check to see if a valid Xing header is available.
if(!data.startsWith("Xing") && !data.startsWith("Info"))
return;
// If the XingHeader doesn't contain the number of frames and the total stream
// info it's invalid.
if(!(data[7] & 0x01)) {
debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total number of frames.");
return;
}
if(!(data[7] & 0x02)) {
debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total stream size.");
return;
}
d->frames = data.mid(8, 4).toUInt();
d->size = data.mid(12, 4).toUInt();
d->valid = true;
}

View file

@ -0,0 +1,100 @@
/***************************************************************************
copyright : (C) 2003 by Ismael Orenstein
email : orenstein@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_XINGHEADER_H
#define TAGLIB_XINGHEADER_H
#include "mpegheader.h"
#include "taglib_export.h"
namespace TagLib {
class ByteVector;
namespace MPEG {
//! An implementation of the Xing VBR headers
/*!
* This is a minimalistic implementation of the Xing VBR headers. Xing
* headers are often added to VBR (variable bit rate) MP3 streams to make it
* easy to compute the length and quality of a VBR stream. Our implementation
* is only concerned with the total size of the stream (so that we can
* calculate the total playing time and the average bitrate). It uses
* <a href="http://home.pcisys.net/~melanson/codecs/mp3extensions.txt">this text</a>
* and the XMMS sources as references.
*/
class TAGLIB_EXPORT XingHeader
{
public:
/*!
* Parses a Xing header based on \a data. The data must be at least 16
* bytes long (anything longer than this is discarded).
*/
XingHeader(const ByteVector &data);
/*!
* Destroy this XingHeader instance.
*/
virtual ~XingHeader();
/*!
* Returns true if the data was parsed properly and if there is a valid
* Xing header present.
*/
bool isValid() const;
/*!
* Returns the total number of frames.
*/
uint totalFrames() const;
/*!
* Returns the total size of stream in bytes.
*/
uint totalSize() const;
/*!
* Returns the offset for the start of this Xing header, given the
* version and channels of the frame
*/
// BIC: rename to offset()
static int xingHeaderOffset(TagLib::MPEG::Header::Version v,
TagLib::MPEG::Header::ChannelMode c);
private:
XingHeader(const XingHeader &);
XingHeader &operator=(const XingHeader &);
void parse(const ByteVector &data);
class XingHeaderPrivate;
XingHeaderPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,269 @@
/***************************************************************************
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <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;
read(readProperties, propertiesStyle);
}
Ogg::FLAC::File::~File()
{
delete d;
}
Ogg::XiphComment *Ogg::FLAC::File::tag() const
{
return d->comment;
}
Properties *Ogg::FLAC::File::audioProperties() const
{
return d->properties;
}
bool Ogg::FLAC::File::save()
{
d->xiphCommentData = d->comment->render();
// 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();
}
////////////////////////////////////////////////////////////////////////////////
// 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.isNull())
return;
ByteVector header;
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);
if(metadataHeader.isNull())
return;
}
header = metadataHeader.mid(0,4);
// 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.mid(1, 3).toUInt();
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);
if(metadataHeader.isNull())
return;
header = metadataHeader.mid(0, 4);
blockType = header[0] & 0x7f;
lastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt();
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;
}

View file

@ -0,0 +1,118 @@
/***************************************************************************
copyright : (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_OGGFLACFILE_H
#define TAGLIB_OGGFLACFILE_H
#include "taglib_export.h"
#include "oggfile.h"
#include "xiphcomment.h"
#include "flacproperties.h"
namespace TagLib {
class Tag;
namespace Ogg {
//! An implementation of Ogg FLAC metadata
/*!
* This is implementation of FLAC metadata for Ogg FLAC files. For "pure"
* FLAC files look under the FLAC hiearchy.
*
* Unlike "pure" FLAC-files, Ogg FLAC only supports Xiph-comments,
* while the audio-properties are the same.
*/
namespace FLAC {
using TagLib::FLAC::Properties;
//! An implementation of TagLib::File with Ogg/FLAC specific methods
/*!
* This implements and provides an interface for Ogg/FLAC files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to Ogg FLAC files.
*/
class TAGLIB_EXPORT File : public Ogg::File
{
public:
/*!
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
* the file's audio properties will also be read using \a propertiesStyle.
* If false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file. This will always be a XiphComment.
*/
virtual XiphComment *tag() const;
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Save the file. This will primarily save and update the XiphComment.
* Returns true if the save is successful.
*/
virtual bool save();
/*!
* Returns the length of the audio-stream, used by FLAC::Properties for
* calculating the bitrate.
*/
long streamLength();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
ByteVector streamInfoData();
ByteVector xiphCommentData();
class FilePrivate;
FilePrivate *d;
};
} // namespace FLAC
} // namespace Ogg
} // namespace TagLib
#endif

View file

@ -0,0 +1,336 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tbytevectorlist.h>
#include <tmap.h>
#include <tstring.h>
#include <tdebug.h>
#include "oggfile.h"
#include "oggpage.h"
#include "oggpageheader.h"
using namespace TagLib;
class Ogg::File::FilePrivate
{
public:
FilePrivate() :
streamSerialNumber(0),
firstPageHeader(0),
lastPageHeader(0),
currentPage(0),
currentPacketPage(0)
{
pages.setAutoDelete(true);
}
~FilePrivate()
{
delete firstPageHeader;
delete lastPageHeader;
}
uint streamSerialNumber;
List<Page *> pages;
PageHeader *firstPageHeader;
PageHeader *lastPageHeader;
std::vector< List<int> > packetToPageMap;
Map<int, ByteVector> dirtyPackets;
List<int> dirtyPages;
//! The current page for the reader -- used by nextPage()
Page *currentPage;
//! The current page for the packet parser -- used by packet()
Page *currentPacketPage;
//! The packets for the currentPacketPage -- used by packet()
ByteVectorList currentPackets;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Ogg::File::~File()
{
delete d;
}
ByteVector Ogg::File::packet(uint i)
{
// Check to see if we're called setPacket() for this packet since the last
// save:
if(d->dirtyPackets.contains(i))
return d->dirtyPackets[i];
// If we haven't indexed the page where the packet we're interested in starts,
// begin reading pages until we have.
while(d->packetToPageMap.size() <= i) {
if(!nextPage()) {
debug("Ogg::File::packet() -- Could not find the requested packet.");
return ByteVector::null;
}
}
// Start reading at the first page that contains part (or all) of this packet.
// If the last read stopped at the packet that we're interested in, don't
// reread its packet list. (This should make sequential packet reads fast.)
uint pageIndex = d->packetToPageMap[i].front();
if(d->currentPacketPage != d->pages[pageIndex]) {
d->currentPacketPage = d->pages[pageIndex];
d->currentPackets = d->currentPacketPage->packets();
}
// If the packet is completely contained in the first page that it's in, then
// just return it now.
if(d->currentPacketPage->containsPacket(i) & Page::CompletePacket)
return d->currentPackets[i - d->currentPacketPage->firstPacketIndex()];
// If the packet is *not* completely contained in the first page that it's a
// part of then that packet trails off the end of the page. Continue appending
// the pages' packet data until we hit a page that either does not end with the
// packet that we're fetching or where the last packet is complete.
ByteVector packet = d->currentPackets.back();
while(d->currentPacketPage->containsPacket(i) & Page::EndsWithPacket &&
!d->currentPacketPage->header()->lastPacketCompleted())
{
pageIndex++;
if(pageIndex == d->pages.size()) {
if(!nextPage()) {
debug("Ogg::File::packet() -- Could not find the requested packet.");
return ByteVector::null;
}
}
d->currentPacketPage = d->pages[pageIndex];
d->currentPackets = d->currentPacketPage->packets();
packet.append(d->currentPackets.front());
}
return packet;
}
void Ogg::File::setPacket(uint i, const ByteVector &p)
{
while(d->packetToPageMap.size() <= i) {
if(!nextPage()) {
debug("Ogg::File::setPacket() -- Could not set the requested packet.");
return;
}
}
List<int>::ConstIterator it = d->packetToPageMap[i].begin();
for(; it != d->packetToPageMap[i].end(); ++it)
d->dirtyPages.sortedInsert(*it, true);
d->dirtyPackets.insert(i, p);
}
const Ogg::PageHeader *Ogg::File::firstPageHeader()
{
if(d->firstPageHeader)
return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
long firstPageHeaderOffset = find("OggS");
if(firstPageHeaderOffset < 0)
return 0;
d->firstPageHeader = new PageHeader(this, firstPageHeaderOffset);
return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
}
const Ogg::PageHeader *Ogg::File::lastPageHeader()
{
if(d->lastPageHeader)
return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
long lastPageHeaderOffset = rfind("OggS");
if(lastPageHeaderOffset < 0)
return 0;
d->lastPageHeader = new PageHeader(this, lastPageHeaderOffset);
return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
}
bool Ogg::File::save()
{
if(readOnly()) {
debug("Ogg::File::save() - Cannot save to a read only file.");
return false;
}
List<int> pageGroup;
for(List<int>::ConstIterator it = d->dirtyPages.begin(); it != d->dirtyPages.end(); ++it) {
if(!pageGroup.isEmpty() && pageGroup.back() + 1 != *it) {
writePageGroup(pageGroup);
pageGroup.clear();
}
else
pageGroup.append(*it);
}
writePageGroup(pageGroup);
d->dirtyPages.clear();
d->dirtyPackets.clear();
return true;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
Ogg::File::File(FileName file) : TagLib::File(file)
{
d = new FilePrivate;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
bool Ogg::File::nextPage()
{
long nextPageOffset;
int currentPacket;
if(d->pages.isEmpty()) {
currentPacket = 0;
nextPageOffset = find("OggS");
if(nextPageOffset < 0)
return false;
}
else {
if(d->currentPage->header()->lastPageOfStream())
return false;
if(d->currentPage->header()->lastPacketCompleted())
currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount();
else
currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount() - 1;
nextPageOffset = d->currentPage->fileOffset() + d->currentPage->size();
}
// Read the next page and add it to the page list.
d->currentPage = new Page(this, nextPageOffset);
if(!d->currentPage->header()->isValid()) {
delete d->currentPage;
d->currentPage = 0;
return false;
}
d->currentPage->setFirstPacketIndex(currentPacket);
if(d->pages.isEmpty())
d->streamSerialNumber = d->currentPage->header()->streamSerialNumber();
d->pages.append(d->currentPage);
// Loop through the packets in the page that we just read appending the
// current page number to the packet to page map for each packet.
for(uint i = 0; i < d->currentPage->packetCount(); i++) {
uint currentPacket = d->currentPage->firstPacketIndex() + i;
if(d->packetToPageMap.size() <= currentPacket)
d->packetToPageMap.push_back(List<int>());
d->packetToPageMap[currentPacket].append(d->pages.size() - 1);
}
return true;
}
void Ogg::File::writePageGroup(const List<int> &pageGroup)
{
if(pageGroup.isEmpty())
return;
ByteVectorList packets;
// If the first page of the group isn't dirty, append its partial content here.
if(!d->dirtyPages.contains(d->pages[pageGroup.front()]->firstPacketIndex()))
packets.append(d->pages[pageGroup.front()]->packets().front());
int previousPacket = -1;
int originalSize = 0;
for(List<int>::ConstIterator it = pageGroup.begin(); it != pageGroup.end(); ++it) {
uint firstPacket = d->pages[*it]->firstPacketIndex();
uint lastPacket = firstPacket + d->pages[*it]->packetCount() - 1;
List<int>::ConstIterator last = --pageGroup.end();
for(uint i = firstPacket; i <= lastPacket; i++) {
if(it == last && i == lastPacket && !d->dirtyPages.contains(i))
packets.append(d->pages[*it]->packets().back());
else if(int(i) != previousPacket) {
previousPacket = i;
packets.append(packet(i));
}
}
originalSize += d->pages[*it]->size();
}
const bool continued = d->pages[pageGroup.front()]->header()->firstPacketContinued();
const bool completed = d->pages[pageGroup.back()]->header()->lastPacketCompleted();
// TODO: This pagination method isn't accurate for what's being done here.
// This should account for real possibilities like non-aligned packets and such.
List<Page *> pages = Page::paginate(packets, Page::SinglePagePerGroup,
d->streamSerialNumber, pageGroup.front(),
continued, completed);
ByteVector data;
for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it)
data.append((*it)->render());
// The insertion algorithms could also be improve to queue and prioritize data
// on the way out. Currently it requires rewriting the file for every page
// group rather than just once; however, for tagging applications there will
// generally only be one page group, so it's not worth the time for the
// optimization at the moment.
insert(data, d->pages[pageGroup.front()]->fileOffset(), originalSize);
// Update the page index to include the pages we just created and to delete the
// old pages.
for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it) {
const int index = (*it)->header()->pageSequenceNumber();
delete d->pages[index];
d->pages[index] = *it;
}
}

View file

@ -0,0 +1,112 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "taglib_export.h"
#include "tfile.h"
#include "tbytevectorlist.h"
#ifndef TAGLIB_OGGFILE_H
#define TAGLIB_OGGFILE_H
namespace TagLib {
//! A namespace for the classes used by Ogg-based metadata files
namespace Ogg {
class PageHeader;
//! An implementation of TagLib::File with some helpers for Ogg based formats
/*!
* This is an implementation of Ogg file page and packet rendering and is of
* use to Ogg based formats. While the API is small this handles the
* non-trivial details of breaking up an Ogg stream into packets and makes
* these available (via subclassing) to the codec meta data implementations.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
virtual ~File();
/*!
* Returns the packet contents for the i-th packet (starting from zero)
* in the Ogg bitstream.
*
* \warning The requires reading at least the packet header for every page
* up to the requested page.
*/
ByteVector packet(uint i);
/*!
* Sets the packet with index \a i to the value \a p.
*/
void setPacket(uint i, const ByteVector &p);
/*!
* Returns a pointer to the PageHeader for the first page in the stream or
* null if the page could not be found.
*/
const PageHeader *firstPageHeader();
/*!
* Returns a pointer to the PageHeader for the last page in the stream or
* null if the page could not be found.
*/
const PageHeader *lastPageHeader();
virtual bool save();
protected:
/*!
* Contructs an Ogg file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note This constructor is protected since Ogg::File shouldn't be
* instantiated directly but rather should be used through the codec
* specific subclasses.
*/
File(FileName file);
private:
File(const File &);
File &operator=(const File &);
/*!
* Reads the next page and updates the internal "current page" pointer.
*/
bool nextPage();
void writePageGroup(const List<int> &group);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,256 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tstring.h>
#include <tdebug.h>
#include "oggpage.h"
#include "oggpageheader.h"
#include "oggfile.h"
using namespace TagLib;
class Ogg::Page::PagePrivate
{
public:
PagePrivate(File *f = 0, long pageOffset = -1) :
file(f),
fileOffset(pageOffset),
packetOffset(0),
header(f, pageOffset),
firstPacketIndex(-1)
{
if(file) {
packetOffset = fileOffset + header.size();
packetSizes = header.packetSizes();
dataSize = header.dataSize();
}
}
File *file;
long fileOffset;
long packetOffset;
int dataSize;
List<int> packetSizes;
PageHeader header;
int firstPacketIndex;
ByteVectorList packets;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Ogg::Page::Page(Ogg::File *file, long pageOffset)
{
d = new PagePrivate(file, pageOffset);
}
Ogg::Page::~Page()
{
delete d;
}
long Ogg::Page::fileOffset() const
{
return d->fileOffset;
}
const Ogg::PageHeader *Ogg::Page::header() const
{
return &d->header;
}
int Ogg::Page::firstPacketIndex() const
{
return d->firstPacketIndex;
}
void Ogg::Page::setFirstPacketIndex(int index)
{
d->firstPacketIndex = index;
}
Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
{
int lastPacketIndex = d->firstPacketIndex + packetCount() - 1;
if(index < d->firstPacketIndex || index > lastPacketIndex)
return DoesNotContainPacket;
ContainsPacketFlags flags = DoesNotContainPacket;
if(index == d->firstPacketIndex)
flags = ContainsPacketFlags(flags | BeginsWithPacket);
if(index == lastPacketIndex)
flags = ContainsPacketFlags(flags | EndsWithPacket);
// If there's only one page and it's complete:
if(packetCount() == 1 &&
!d->header.firstPacketContinued() &&
d->header.lastPacketCompleted())
{
flags = ContainsPacketFlags(flags | CompletePacket);
}
// Or if the page is (a) the first page and it's complete or (b) the last page
// and it's complete or (c) a page in the middle.
else if((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
(flags & EndsWithPacket && d->header.lastPacketCompleted()) ||
(!(flags & BeginsWithPacket) && !(flags & EndsWithPacket)))
{
flags = ContainsPacketFlags(flags | CompletePacket);
}
return flags;
}
TagLib::uint Ogg::Page::packetCount() const
{
return d->header.packetSizes().size();
}
ByteVectorList Ogg::Page::packets() const
{
if(!d->packets.isEmpty())
return d->packets;
ByteVectorList l;
if(d->file && d->header.isValid()) {
d->file->seek(d->packetOffset);
List<int> packetSizes = d->header.packetSizes();
List<int>::ConstIterator it = packetSizes.begin();
for(; it != packetSizes.end(); ++it)
l.append(d->file->readBlock(*it));
}
else
debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
return l;
}
int Ogg::Page::size() const
{
return d->header.size() + d->header.dataSize();
}
ByteVector Ogg::Page::render() const
{
ByteVector data;
data.append(d->header.render());
if(d->packets.isEmpty()) {
if(d->file) {
d->file->seek(d->packetOffset);
data.append(d->file->readBlock(d->dataSize));
}
else
debug("Ogg::Page::render() -- this page is empty!");
}
else {
ByteVectorList::ConstIterator it = d->packets.begin();
for(; it != d->packets.end(); ++it)
data.append(*it);
}
// Compute and set the checksum for the Ogg page. The checksum is taken over
// the entire page with the 4 bytes reserved for the checksum zeroed and then
// inserted in bytes 22-25 of the page header.
ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
for(int i = 0; i < 4; i++)
data[i + 22] = checksum[i];
return data;
}
List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
PaginationStrategy strategy,
uint streamSerialNumber,
int firstPage,
bool firstPacketContinued,
bool lastPacketCompleted,
bool containsLastPacket)
{
List<Page *> l;
int totalSize = 0;
for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
totalSize += (*it).size();
if(strategy == Repaginate || totalSize + packets.size() > 255 * 256) {
debug("Ogg::Page::paginate() -- Sorry! Repagination is not yet implemented.");
return l;
}
// TODO: Handle creation of multiple pages here with appropriate pagination.
Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
lastPacketCompleted, containsLastPacket);
l.append(p);
return l;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
Ogg::Page::Page(const ByteVectorList &packets,
uint streamSerialNumber,
int pageNumber,
bool firstPacketContinued,
bool lastPacketCompleted,
bool containsLastPacket)
{
d = new PagePrivate;
ByteVector data;
List<int> packetSizes;
d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued);
d->header.setLastPageOfStream(containsLastPacket);
d->header.setFirstPacketContinued(firstPacketContinued);
d->header.setLastPacketCompleted(lastPacketCompleted);
d->header.setStreamSerialNumber(streamSerialNumber);
d->header.setPageSequenceNumber(pageNumber);
// Build a page from the list of packets.
for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
packetSizes.append((*it).size());
data.append(*it);
}
d->packets = packets;
d->header.setPacketSizes(packetSizes);
}

View file

@ -0,0 +1,203 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_OGGPAGE_H
#define TAGLIB_OGGPAGE_H
#include "taglib_export.h"
#include "tbytevectorlist.h"
namespace TagLib {
namespace Ogg {
class File;
class PageHeader;
//! An implementation of Ogg pages
/*!
* This is an implementation of the pages that make up an Ogg stream.
* This handles parsing pages and breaking them down into packets and handles
* the details of packets spanning multiple pages and pages that contiain
* multiple packets.
*
* In most Xiph.org formats the comments are found in the first few packets,
* this however is a reasonably complete implementation of Ogg pages that
* could potentially be useful for non-meta data purposes.
*/
class TAGLIB_EXPORT Page
{
public:
/*!
* Read an Ogg page from the \a file at the position \a pageOffset.
*/
Page(File *file, long pageOffset);
virtual ~Page();
/*!
* Returns the page's position within the file (in bytes).
*/
long fileOffset() const;
/*!
* Returns a pointer to the header for this page. This pointer will become
* invalid when the page is deleted.
*/
const PageHeader *header() const;
/*!
* Returns the index of the first packet wholly or partially contained in
* this page.
*
* \see setFirstPacketIndex()
*/
int firstPacketIndex() const;
/*!
* Sets the index of the first packet in the page.
*
* \see firstPacketIndex()
*/
void setFirstPacketIndex(int index);
/*!
* When checking to see if a page contains a given packet this set of flags
* represents the possible values for that packets status in the page.
*
* \see containsPacket()
*/
enum ContainsPacketFlags {
//! No part of the packet is contained in the page
DoesNotContainPacket = 0x0000,
//! The packet is wholly contained in the page
CompletePacket = 0x0001,
//! The page starts with the given packet
BeginsWithPacket = 0x0002,
//! The page ends with the given packet
EndsWithPacket = 0x0004
};
/*!
* Checks to see if the specified \a packet is contained in the current
* page.
*
* \see ContainsPacketFlags
*/
ContainsPacketFlags containsPacket(int index) const;
/*!
* Returns the number of packets (whole or partial) in this page.
*/
uint packetCount() const;
/*!
* Returns a list of the packets in this page.
*
* \note Either or both the first and last packets may be only partial.
* \see PageHeader::firstPacketContinued()
*/
ByteVectorList packets() const;
/*!
* Returns the size of the page in bytes.
*/
int size() const;
ByteVector render() const;
/*!
* Defines a strategy for pagination, or grouping pages into Ogg packets,
* for use with pagination methods.
*
* \note Yes, I'm aware that this is not a canonical "Strategy Pattern",
* the term was simply convenient.
*/
enum PaginationStrategy {
/*!
* Attempt to put the specified set of packets into a single Ogg packet.
* If the sum of the packet data is greater than will fit into a single
* Ogg page -- 65280 bytes -- this will fall back to repagination using
* the recommended page sizes.
*/
SinglePagePerGroup,
/*!
* Split the packet or group of packets into pages that conform to the
* sizes recommended in the Ogg standard.
*/
Repaginate
};
/*!
* Pack \a packets into Ogg pages using the \a strategy for pagination.
* The page number indicater inside of the rendered packets will start
* with \a firstPage and be incremented for each page rendered.
* \a containsLastPacket should be set to true if \a packets contains the
* last page in the stream and will set the appropriate flag in the last
* rendered Ogg page's header. \a streamSerialNumber should be set to
* the serial number for this stream.
*
* \note The "absolute granule position" is currently always zeroed using
* this method as this suffices for the comment headers.
*
* \warning The pages returned by this method must be deleted by the user.
* You can use List<T>::setAutoDelete(true) to set these pages to be
* automatically deleted when this list passes out of scope.
*
* \see PaginationStrategy
* \see List::setAutoDelete()
*/
static List<Page *> paginate(const ByteVectorList &packets,
PaginationStrategy strategy,
uint streamSerialNumber,
int firstPage,
bool firstPacketContinued = false,
bool lastPacketCompleted = true,
bool containsLastPacket = false);
protected:
/*!
* Creates an Ogg packet based on the data in \a packets. The page number
* for each page will be set to \a pageNumber.
*/
Page(const ByteVectorList &packets,
uint streamSerialNumber,
int pageNumber,
bool firstPacketContinued = false,
bool lastPacketCompleted = true,
bool containsLastPacket = false);
private:
Page(const Page &);
Page &operator=(const Page &);
class PagePrivate;
PagePrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,323 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <stdlib.h>
#include <bitset>
#include <tstring.h>
#include <tdebug.h>
#include <taglib.h>
#include "oggpageheader.h"
#include "oggfile.h"
using namespace TagLib;
class Ogg::PageHeader::PageHeaderPrivate
{
public:
PageHeaderPrivate(File *f, long pageOffset) :
file(f),
fileOffset(pageOffset),
isValid(false),
firstPacketContinued(false),
lastPacketCompleted(false),
firstPageOfStream(false),
lastPageOfStream(false),
absoluteGranularPosition(0),
streamSerialNumber(0),
pageSequenceNumber(-1),
size(0),
dataSize(0)
{}
File *file;
long fileOffset;
bool isValid;
List<int> packetSizes;
bool firstPacketContinued;
bool lastPacketCompleted;
bool firstPageOfStream;
bool lastPageOfStream;
long long absoluteGranularPosition;
uint streamSerialNumber;
int pageSequenceNumber;
int size;
int dataSize;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Ogg::PageHeader::PageHeader(Ogg::File *file, long pageOffset)
{
d = new PageHeaderPrivate(file, pageOffset);
if(file && pageOffset >= 0)
read();
}
Ogg::PageHeader::~PageHeader()
{
delete d;
}
bool Ogg::PageHeader::isValid() const
{
return d->isValid;
}
List<int> Ogg::PageHeader::packetSizes() const
{
return d->packetSizes;
}
void Ogg::PageHeader::setPacketSizes(const List<int> &sizes)
{
d->packetSizes = sizes;
}
bool Ogg::PageHeader::firstPacketContinued() const
{
return d->firstPacketContinued;
}
void Ogg::PageHeader::setFirstPacketContinued(bool continued)
{
d->firstPacketContinued = continued;
}
bool Ogg::PageHeader::lastPacketCompleted() const
{
return d->lastPacketCompleted;
}
void Ogg::PageHeader::setLastPacketCompleted(bool completed)
{
d->lastPacketCompleted = completed;
}
bool Ogg::PageHeader::firstPageOfStream() const
{
return d->firstPageOfStream;
}
void Ogg::PageHeader::setFirstPageOfStream(bool first)
{
d->firstPageOfStream = first;
}
bool Ogg::PageHeader::lastPageOfStream() const
{
return d->lastPageOfStream;
}
void Ogg::PageHeader::setLastPageOfStream(bool last)
{
d->lastPageOfStream = last;
}
long long Ogg::PageHeader::absoluteGranularPosition() const
{
return d->absoluteGranularPosition;
}
void Ogg::PageHeader::setAbsoluteGranularPosition(long long agp)
{
d->absoluteGranularPosition = agp;
}
int Ogg::PageHeader::pageSequenceNumber() const
{
return d->pageSequenceNumber;
}
void Ogg::PageHeader::setPageSequenceNumber(int sequenceNumber)
{
d->pageSequenceNumber = sequenceNumber;
}
TagLib::uint Ogg::PageHeader::streamSerialNumber() const
{
return d->streamSerialNumber;
}
void Ogg::PageHeader::setStreamSerialNumber(uint n)
{
d->streamSerialNumber = n;
}
int Ogg::PageHeader::size() const
{
return d->size;
}
int Ogg::PageHeader::dataSize() const
{
return d->dataSize;
}
ByteVector Ogg::PageHeader::render() const
{
ByteVector data;
// capture patern
data.append("OggS");
// stream structure version
data.append(char(0));
// header type flag
std::bitset<8> flags;
flags[0] = d->firstPacketContinued;
flags[1] = d->pageSequenceNumber == 0;
flags[2] = d->lastPageOfStream;
data.append(char(flags.to_ulong()));
// absolute granular position
data.append(ByteVector::fromLongLong(d->absoluteGranularPosition, false));
// stream serial number
data.append(ByteVector::fromUInt(d->streamSerialNumber, false));
// page sequence number
data.append(ByteVector::fromUInt(d->pageSequenceNumber, false));
// checksum -- this is left empty and should be filled in by the Ogg::Page
// class
data.append(ByteVector(4, 0));
// page segment count and page segment table
ByteVector pageSegments = lacingValues();
data.append(char(uchar(pageSegments.size())));
data.append(pageSegments);
return data;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Ogg::PageHeader::read()
{
d->file->seek(d->fileOffset);
// An Ogg page header is at least 27 bytes, so we'll go ahead and read that
// much and then get the rest when we're ready for it.
ByteVector data = d->file->readBlock(27);
// Sanity check -- make sure that we were in fact able to read as much data as
// we asked for and that the page begins with "OggS".
if(data.size() != 27 || !data.startsWith("OggS")) {
debug("Ogg::PageHeader::read() -- error reading page header");
return;
}
std::bitset<8> flags(data[5]);
d->firstPacketContinued = flags.test(0);
d->firstPageOfStream = flags.test(1);
d->lastPageOfStream = flags.test(2);
d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false);
d->streamSerialNumber = data.mid(14, 4).toUInt(false);
d->pageSequenceNumber = data.mid(18, 4).toUInt(false);
// Byte number 27 is the number of page segments, which is the only variable
// length portion of the page header. After reading the number of page
// segments we'll then read in the corresponding data for this count.
int pageSegmentCount = uchar(data[26]);
ByteVector pageSegments = d->file->readBlock(pageSegmentCount);
// Another sanity check.
if(pageSegmentCount < 1 || int(pageSegments.size()) != pageSegmentCount)
return;
// The base size of an Ogg page 27 bytes plus the number of lacing values.
d->size = 27 + pageSegmentCount;
int packetSize = 0;
for(int i = 0; i < pageSegmentCount; i++) {
d->dataSize += uchar(pageSegments[i]);
packetSize += uchar(pageSegments[i]);
if(uchar(pageSegments[i]) < 255) {
d->packetSizes.append(packetSize);
packetSize = 0;
}
}
if(packetSize > 0) {
d->packetSizes.append(packetSize);
d->lastPacketCompleted = false;
}
else
d->lastPacketCompleted = true;
d->isValid = true;
}
ByteVector Ogg::PageHeader::lacingValues() const
{
ByteVector data;
List<int> sizes = d->packetSizes;
for(List<int>::ConstIterator it = sizes.begin(); it != sizes.end(); ++it) {
// The size of a packet in an Ogg page is indicated by a series of "lacing
// values" where the sum of the values is the packet size in bytes. Each of
// these values is a byte. A value of less than 255 (0xff) indicates the end
// of the packet.
div_t n = div(*it, 255);
for(int i = 0; i < n.quot; i++)
data.append(char(uchar(255)));
if(it != --sizes.end() || d->lastPacketCompleted)
data.append(char(uchar(n.rem)));
}
return data;
}

View file

@ -0,0 +1,232 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_OGGPAGEHEADER_H
#define TAGLIB_OGGPAGEHEADER_H
#include "tlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
namespace Ogg {
class File;
//! An implementation of the page headers associated with each Ogg::Page
/*!
* This class implements Ogg page headers which contain the information
* about Ogg pages needed to break them into packets which can be passed on
* to the codecs.
*/
class TAGLIB_EXPORT PageHeader
{
public:
/*!
* Reads a PageHeader from \a file starting at \a pageOffset. The defaults
* create a page with no (and as such, invalid) data that must be set
* later.
*/
PageHeader(File *file = 0, long pageOffset = -1);
/*!
* Deletes this instance of the PageHeader.
*/
virtual ~PageHeader();
/*!
* Returns true if the header parsed properly and is valid.
*/
bool isValid() const;
/*!
* Ogg pages contain a list of packets (which are used by the contained
* codecs). The sizes of these pages is encoded in the page header. This
* returns a list of the packet sizes in bytes.
*
* \see setPacketSizes()
*/
List<int> packetSizes() const;
/*!
* Sets the sizes of the packets in this page to \a sizes. Internally this
* updates the lacing values in the header.
*
* \see packetSizes()
*/
void setPacketSizes(const List<int> &sizes);
/*!
* Some packets can be <i>continued</i> across multiple pages. If the
* first packet in the current page is a continuation this will return
* true. If this is page starts with a new packet this will return false.
*
* \see lastPacketCompleted()
* \see setFirstPacketContinued()
*/
bool firstPacketContinued() const;
/*!
* Sets the internal flag indicating if the first packet in this page is
* continued to \a continued.
*
* \see firstPacketContinued()
*/
void setFirstPacketContinued(bool continued);
/*!
* Returns true if the last packet of this page is completely contained in
* this page.
*
* \see firstPacketContinued()
* \see setLastPacketCompleted()
*/
bool lastPacketCompleted() const;
/*!
* Sets the internal flag indicating if the last packet in this page is
* complete to \a completed.
*
* \see lastPacketCompleted()
*/
void setLastPacketCompleted(bool completed);
/*!
* This returns true if this is the first page of the Ogg (logical) stream.
*
* \see setFirstPageOfStream()
*/
bool firstPageOfStream() const;
/*!
* Marks this page as the first page of the Ogg stream.
*
* \see firstPageOfStream()
*/
void setFirstPageOfStream(bool first);
/*!
* This returns true if this is the last page of the Ogg (logical) stream.
*
* \see setLastPageOfStream()
*/
bool lastPageOfStream() const;
/*!
* Marks this page as the last page of the Ogg stream.
*
* \see lastPageOfStream()
*/
void setLastPageOfStream(bool last);
/*!
* A special value of containing the position of the packet to be
* interpreted by the codec. In the case of Vorbis this contains the PCM
* value and is used to calculate the length of the stream.
*
* \see setAbsoluteGranularPosition()
*/
long long absoluteGranularPosition() const;
/*!
* A special value of containing the position of the packet to be
* interpreted by the codec. It is only supported here so that it may be
* coppied from one page to another.
*
* \see absoluteGranularPosition()
*/
void setAbsoluteGranularPosition(long long agp);
/*!
* Every Ogg logical stream is given a random serial number which is common
* to every page in that logical stream. This returns the serial number of
* the stream associated with this packet.
*
* \see setStreamSerialNumber()
*/
uint streamSerialNumber() const;
/*!
* Every Ogg logical stream is given a random serial number which is common
* to every page in that logical stream. This sets this pages serial
* number. This method should be used when adding new pages to a logical
* stream.
*
* \see streamSerialNumber()
*/
void setStreamSerialNumber(uint n);
/*!
* Returns the index of the page within the Ogg stream. This helps make it
* possible to determine if pages have been lost.
*
* \see setPageSequenceNumber()
*/
int pageSequenceNumber() const;
/*!
* Sets the page's position in the stream to \a sequenceNumber.
*
* \see pageSequenceNumber()
*/
void setPageSequenceNumber(int sequenceNumber);
/*!
* Returns the complete header size.
*/
int size() const;
/*!
* Returns the size of the data portion of the page -- i.e. the size of the
* page less the header size.
*/
int dataSize() const;
/*!
* Render the page header to binary data.
*
* \note The checksum -- bytes 22 - 25 -- will be left empty and must be
* filled in when rendering the entire page.
*/
ByteVector render() const;
private:
PageHeader(const PageHeader &);
PageHeader &operator=(const PageHeader &);
void read();
ByteVector lacingValues() const;
class PageHeaderPrivate;
PageHeaderPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,112 @@
/***************************************************************************
copyright : (C) 2006 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <bitset>
#include <tstring.h>
#include <tdebug.h>
#include "speexfile.h"
using namespace TagLib;
using namespace TagLib::Ogg;
class Speex::File::FilePrivate
{
public:
FilePrivate() :
comment(0),
properties(0) {}
~FilePrivate()
{
delete comment;
delete properties;
}
Ogg::XiphComment *comment;
Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Speex::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
Speex::File::~File()
{
delete d;
}
Ogg::XiphComment *Speex::File::tag() const
{
return d->comment;
}
Speex::Properties *Speex::File::audioProperties() const
{
return d->properties;
}
bool Speex::File::save()
{
if(!d->comment)
d->comment = new Ogg::XiphComment;
setPacket(1, d->comment->render());
return Ogg::File::save();
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Speex::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
ByteVector speexHeaderData = packet(0);
if(!speexHeaderData.startsWith("Speex ")) {
debug("Speex::File::read() -- invalid Speex identification header");
return;
}
ByteVector commentHeaderData = packet(1);
d->comment = new Ogg::XiphComment(commentHeaderData);
if(readProperties)
d->properties = new Properties(this, propertiesStyle);
}

View file

@ -0,0 +1,99 @@
/***************************************************************************
copyright : (C) 2006 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_SPEEXFILE_H
#define TAGLIB_SPEEXFILE_H
#include <oggfile.h>
#include <xiphcomment.h>
#include "speexproperties.h"
namespace TagLib {
namespace Ogg {
//! A namespace containing classes for Speex metadata
namespace Speex {
//! An implementation of Ogg::File with Speex specific methods
/*!
* This is the central class in the Ogg Speex metadata processing collection
* of classes. It's built upon Ogg::File which handles processing of the Ogg
* logical bitstream and breaking it down into pages which are handled by
* the codec implementations, in this case Speex specifically.
*/
class TAGLIB_EXPORT File : public Ogg::File
{
public:
/*!
* Contructs a Speex file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the XiphComment for this file. XiphComment implements the tag
* interface, so this serves as the reimplementation of
* TagLib::File::tag().
*/
virtual Ogg::XiphComment *tag() const;
/*!
* Returns the Speex::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
virtual bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
}
#endif

View file

@ -0,0 +1,170 @@
/***************************************************************************
copyright : (C) 2006 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tstring.h>
#include <tdebug.h>
#include <oggpageheader.h>
#include "speexproperties.h"
#include "speexfile.h"
using namespace TagLib;
using namespace TagLib::Ogg;
class Speex::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(File *f, ReadStyle s) :
file(f),
style(s),
length(0),
bitrate(0),
sampleRate(0),
channels(0),
speexVersion(0),
vbr(false),
mode(0) {}
File *file;
ReadStyle style;
int length;
int bitrate;
int sampleRate;
int channels;
int speexVersion;
bool vbr;
int mode;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Speex::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(file, style);
read();
}
Speex::Properties::~Properties()
{
delete d;
}
int Speex::Properties::length() const
{
return d->length;
}
int Speex::Properties::bitrate() const
{
return int(float(d->bitrate) / float(1000) + 0.5);
}
int Speex::Properties::sampleRate() const
{
return d->sampleRate;
}
int Speex::Properties::channels() const
{
return d->channels;
}
int Speex::Properties::speexVersion() const
{
return d->speexVersion;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Speex::Properties::read()
{
// Get the identification header from the Ogg implementation.
ByteVector data = d->file->packet(0);
int pos = 28;
// speex_version_id; /**< Version for Speex (for checking compatibility) */
d->speexVersion = data.mid(pos, 4).toUInt(false);
pos += 4;
// header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
pos += 4;
// rate; /**< Sampling rate used */
d->sampleRate = data.mid(pos, 4).toUInt(false);
pos += 4;
// mode; /**< Mode used (0 for narrowband, 1 for wideband) */
d->mode = data.mid(pos, 4).toUInt(false);
pos += 4;
// mode_bitstream_version; /**< Version ID of the bit-stream */
pos += 4;
// nb_channels; /**< Number of channels encoded */
d->channels = data.mid(pos, 4).toUInt(false);
pos += 4;
// bitrate; /**< Bit-rate used */
d->bitrate = data.mid(pos, 4).toUInt(false);
pos += 4;
// frame_size; /**< Size of frames */
// unsigned int frameSize = data.mid(pos, 4).toUInt(false);
pos += 4;
// vbr; /**< 1 for a VBR encoding, 0 otherwise */
d->vbr = data.mid(pos, 4).toUInt(false) == 1;
pos += 4;
// frames_per_packet; /**< Number of frames stored per Ogg packet */
// unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false);
const Ogg::PageHeader *first = d->file->firstPageHeader();
const Ogg::PageHeader *last = d->file->lastPageHeader();
if(first && last) {
long long start = first->absoluteGranularPosition();
long long end = last->absoluteGranularPosition();
if(start >= 0 && end >= 0 && d->sampleRate > 0)
d->length = (int) ((end - start) / (long long) d->sampleRate);
else
debug("Speex::Properties::read() -- Either the PCM values for the start or "
"end of this file was incorrect or the sample rate is zero.");
}
else
debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages.");
}

View file

@ -0,0 +1,89 @@
/***************************************************************************
copyright : (C) 2006 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_SPEEXPROPERTIES_H
#define TAGLIB_SPEEXPROPERTIES_H
#include <audioproperties.h>
namespace TagLib {
namespace Ogg {
namespace Speex {
class File;
//! An implementation of audio property reading for Ogg Speex
/*!
* This reads the data from an Ogg Speex stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of Speex::Properties with the data read from the
* Speex::File \a file.
*/
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this Speex::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns the Speex version, currently "0" (as specified by the spec).
*/
int speexVersion() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
}
#endif

View file

@ -0,0 +1,117 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <bitset>
#include <tstring.h>
#include <tdebug.h>
#include "vorbisfile.h"
using namespace TagLib;
class Vorbis::File::FilePrivate
{
public:
FilePrivate() :
comment(0),
properties(0) {}
~FilePrivate()
{
delete comment;
delete properties;
}
Ogg::XiphComment *comment;
Properties *properties;
};
namespace TagLib {
/*!
* Vorbis headers can be found with one type ID byte and the string "vorbis" in
* an Ogg stream. 0x03 indicates the comment header.
*/
static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Vorbis::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
Vorbis::File::~File()
{
delete d;
}
Ogg::XiphComment *Vorbis::File::tag() const
{
return d->comment;
}
Vorbis::Properties *Vorbis::File::audioProperties() const
{
return d->properties;
}
bool Vorbis::File::save()
{
ByteVector v(vorbisCommentHeaderID);
if(!d->comment)
d->comment = new Ogg::XiphComment;
v.append(d->comment->render());
setPacket(1, v);
return Ogg::File::save();
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Vorbis::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
ByteVector commentHeaderData = packet(1);
if(commentHeaderData.mid(0, 7) != vorbisCommentHeaderID) {
debug("Vorbis::File::read() - Could not find the Vorbis comment header.");
setValid(false);
return;
}
d->comment = new Ogg::XiphComment(commentHeaderData.mid(7));
if(readProperties)
d->properties = new Properties(this, propertiesStyle);
}

View file

@ -0,0 +1,118 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_VORBISFILE_H
#define TAGLIB_VORBISFILE_H
#include "taglib_export.h"
#include "oggfile.h"
#include "xiphcomment.h"
#include "vorbisproperties.h"
namespace TagLib {
/*
* This is just to make this appear to be in the Ogg namespace in the
* documentation. The typedef below will make this work with the current code.
* In the next BIC version of TagLib this will be really moved into the Ogg
* namespace.
*/
#ifdef DOXYGEN
namespace Ogg {
#endif
//! A namespace containing classes for Vorbis metadata
namespace Vorbis {
//! An implementation of Ogg::File with Vorbis specific methods
/*!
* This is the central class in the Ogg Vorbis metadata processing collection
* of classes. It's built upon Ogg::File which handles processing of the Ogg
* logical bitstream and breaking it down into pages which are handled by
* the codec implementations, in this case Vorbis specifically.
*/
class TAGLIB_EXPORT File : public Ogg::File
{
public:
/*!
* Contructs a Vorbis file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the XiphComment for this file. XiphComment implements the tag
* interface, so this serves as the reimplementation of
* TagLib::File::tag().
*/
virtual Ogg::XiphComment *tag() const;
/*!
* Returns the Vorbis::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
virtual bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
/*
* To keep compatibility with the current version put Vorbis in the Ogg namespace
* only in the docs and provide a typedef to make it work. In the next BIC
* version this will be removed and it will only exist in the Ogg namespace.
*/
#ifdef DOXYGEN
}
#else
namespace Ogg { namespace Vorbis { typedef TagLib::Vorbis::File File; } }
#endif
}
#endif

View file

@ -0,0 +1,183 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tstring.h>
#include <tdebug.h>
#include <oggpageheader.h>
#include "vorbisproperties.h"
#include "vorbisfile.h"
using namespace TagLib;
class Vorbis::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(File *f, ReadStyle s) :
file(f),
style(s),
length(0),
bitrate(0),
sampleRate(0),
channels(0),
vorbisVersion(0),
bitrateMaximum(0),
bitrateNominal(0),
bitrateMinimum(0) {}
File *file;
ReadStyle style;
int length;
int bitrate;
int sampleRate;
int channels;
int vorbisVersion;
int bitrateMaximum;
int bitrateNominal;
int bitrateMinimum;
};
namespace TagLib {
/*!
* Vorbis headers can be found with one type ID byte and the string "vorbis" in
* an Ogg stream. 0x01 indicates the setup header.
*/
static const char vorbisSetupHeaderID[] = { 0x01, 'v', 'o', 'r', 'b', 'i', 's', 0 };
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Vorbis::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(file, style);
read();
}
Vorbis::Properties::~Properties()
{
delete d;
}
int Vorbis::Properties::length() const
{
return d->length;
}
int Vorbis::Properties::bitrate() const
{
return int(float(d->bitrate) / float(1000) + 0.5);
}
int Vorbis::Properties::sampleRate() const
{
return d->sampleRate;
}
int Vorbis::Properties::channels() const
{
return d->channels;
}
int Vorbis::Properties::vorbisVersion() const
{
return d->vorbisVersion;
}
int Vorbis::Properties::bitrateMaximum() const
{
return d->bitrateMaximum;
}
int Vorbis::Properties::bitrateNominal() const
{
return d->bitrateNominal;
}
int Vorbis::Properties::bitrateMinimum() const
{
return d->bitrateMinimum;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Vorbis::Properties::read()
{
// Get the identification header from the Ogg implementation.
ByteVector data = d->file->packet(0);
int pos = 0;
if(data.mid(pos, 7) != vorbisSetupHeaderID) {
debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
return;
}
pos += 7;
d->vorbisVersion = data.mid(pos, 4).toUInt(false);
pos += 4;
d->channels = uchar(data[pos]);
pos += 1;
d->sampleRate = data.mid(pos, 4).toUInt(false);
pos += 4;
d->bitrateMaximum = data.mid(pos, 4).toUInt(false);
pos += 4;
d->bitrateNominal = data.mid(pos, 4).toUInt(false);
pos += 4;
d->bitrateMinimum = data.mid(pos, 4).toUInt(false);
// TODO: Later this should be only the "fast" mode.
d->bitrate = d->bitrateNominal;
// Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/
// for my notes on the topic.
const Ogg::PageHeader *first = d->file->firstPageHeader();
const Ogg::PageHeader *last = d->file->lastPageHeader();
if(first && last) {
long long start = first->absoluteGranularPosition();
long long end = last->absoluteGranularPosition();
if(start >= 0 && end >= 0 && d->sampleRate > 0)
d->length = (end - start) / (long long) d->sampleRate;
else
debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
"end of this file was incorrect or the sample rate is zero.");
}
else
debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages.");
}

View file

@ -0,0 +1,125 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_VORBISPROPERTIES_H
#define TAGLIB_VORBISPROPERTIES_H
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
/*
* This is just to make this appear to be in the Ogg namespace in the
* documentation. The typedef below will make this work with the current code.
* In the next BIC version of TagLib this will be really moved into the Ogg
* namespace.
*/
#ifdef DOXYGEN
namespace Ogg {
#endif
namespace Vorbis {
class File;
//! An implementation of audio property reading for Ogg Vorbis
/*!
* This reads the data from an Ogg Vorbis stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of Vorbis::Properties with the data read from the
* Vorbis::File \a file.
*/
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this VorbisProperties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* Returns the Vorbis version, currently "0" (as specified by the spec).
*/
int vorbisVersion() const;
/*!
* Returns the maximum bitrate as read from the Vorbis identification
* header.
*/
int bitrateMaximum() const;
/*!
* Returns the nominal bitrate as read from the Vorbis identification
* header.
*/
int bitrateNominal() const;
/*!
* Returns the minimum bitrate as read from the Vorbis identification
* header.
*/
int bitrateMinimum() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
/*
* To keep compatibility with the current version put Vorbis in the Ogg namespace
* only in the docs and provide a typedef to make it work. In the next BIC
* version this will be removed and it will only exist in the Ogg namespace.
*/
#ifdef DOXYGEN
}
#else
namespace Ogg { namespace Vorbis { typedef TagLib::AudioProperties AudioProperties; } }
#endif
}
#endif

View file

@ -0,0 +1,313 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <tdebug.h>
#include <xiphcomment.h>
using namespace TagLib;
class Ogg::XiphComment::XiphCommentPrivate
{
public:
FieldListMap fieldListMap;
String vendorID;
String commentField;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Ogg::XiphComment::XiphComment() : TagLib::Tag()
{
d = new XiphCommentPrivate;
}
Ogg::XiphComment::XiphComment(const ByteVector &data) : TagLib::Tag()
{
d = new XiphCommentPrivate;
parse(data);
}
Ogg::XiphComment::~XiphComment()
{
delete d;
}
String Ogg::XiphComment::title() const
{
if(d->fieldListMap["TITLE"].isEmpty())
return String::null;
return d->fieldListMap["TITLE"].front();
}
String Ogg::XiphComment::artist() const
{
if(d->fieldListMap["ARTIST"].isEmpty())
return String::null;
return d->fieldListMap["ARTIST"].front();
}
String Ogg::XiphComment::album() const
{
if(d->fieldListMap["ALBUM"].isEmpty())
return String::null;
return d->fieldListMap["ALBUM"].front();
}
String Ogg::XiphComment::comment() const
{
if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
d->commentField = "DESCRIPTION";
return d->fieldListMap["DESCRIPTION"].front();
}
if(!d->fieldListMap["COMMENT"].isEmpty()) {
d->commentField = "COMMENT";
return d->fieldListMap["COMMENT"].front();
}
return String::null;
}
String Ogg::XiphComment::genre() const
{
if(d->fieldListMap["GENRE"].isEmpty())
return String::null;
return d->fieldListMap["GENRE"].front();
}
TagLib::uint Ogg::XiphComment::year() const
{
if(d->fieldListMap["DATE"].isEmpty())
return 0;
return d->fieldListMap["DATE"].front().toInt();
}
TagLib::uint Ogg::XiphComment::track() const
{
if(d->fieldListMap["TRACKNUMBER"].isEmpty())
return 0;
return d->fieldListMap["TRACKNUMBER"].front().toInt();
}
void Ogg::XiphComment::setTitle(const String &s)
{
addField("TITLE", s);
}
void Ogg::XiphComment::setArtist(const String &s)
{
addField("ARTIST", s);
}
void Ogg::XiphComment::setAlbum(const String &s)
{
addField("ALBUM", s);
}
void Ogg::XiphComment::setComment(const String &s)
{
addField(d->commentField.isEmpty() ? "DESCRIPTION" : d->commentField, s);
}
void Ogg::XiphComment::setGenre(const String &s)
{
addField("GENRE", s);
}
void Ogg::XiphComment::setYear(uint i)
{
if(i == 0)
removeField("DATE");
else
addField("DATE", String::number(i));
}
void Ogg::XiphComment::setTrack(uint i)
{
if(i == 0)
removeField("TRACKNUMBER");
else
addField("TRACKNUMBER", String::number(i));
}
bool Ogg::XiphComment::isEmpty() const
{
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it)
if(!(*it).second.isEmpty())
return false;
return true;
}
TagLib::uint Ogg::XiphComment::fieldCount() const
{
uint count = 0;
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it)
count += (*it).second.size();
return count;
}
const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
{
return d->fieldListMap;
}
String Ogg::XiphComment::vendorID() const
{
return d->vendorID;
}
void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
{
if(replace)
removeField(key.upper());
if(!key.isEmpty() && !value.isEmpty())
d->fieldListMap[key.upper()].append(value);
}
void Ogg::XiphComment::removeField(const String &key, const String &value)
{
if(!value.isNull()) {
StringList::Iterator it = d->fieldListMap[key].begin();
while(it != d->fieldListMap[key].end()) {
if(value == *it)
it = d->fieldListMap[key].erase(it);
else
it++;
}
}
else
d->fieldListMap.erase(key);
}
bool Ogg::XiphComment::contains(const String &key) const
{
return d->fieldListMap.contains(key) && !d->fieldListMap[key].isEmpty();
}
ByteVector Ogg::XiphComment::render() const
{
return render(true);
}
ByteVector Ogg::XiphComment::render(bool addFramingBit) const
{
ByteVector data;
// Add the vendor ID length and the vendor ID. It's important to use the
// length of the data(String::UTF8) rather than the length of the the string
// since this is UTF8 text and there may be more characters in the data than
// in the UTF16 string.
ByteVector vendorData = d->vendorID.data(String::UTF8);
data.append(ByteVector::fromUInt(vendorData.size(), false));
data.append(vendorData);
// Add the number of fields.
data.append(ByteVector::fromUInt(fieldCount(), false));
// Iterate over the the field lists. Our iterator returns a
// std::pair<String, StringList> where the first String is the field name and
// the StringList is the values associated with that field.
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it) {
// And now iterate over the values of the current list.
String fieldName = (*it).first;
StringList values = (*it).second;
StringList::ConstIterator valuesIt = values.begin();
for(; valuesIt != values.end(); ++valuesIt) {
ByteVector fieldData = fieldName.data(String::UTF8);
fieldData.append('=');
fieldData.append((*valuesIt).data(String::UTF8));
data.append(ByteVector::fromUInt(fieldData.size(), false));
data.append(fieldData);
}
}
// Append the "framing bit".
if(addFramingBit)
data.append(char(1));
return data;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void Ogg::XiphComment::parse(const ByteVector &data)
{
// The first thing in the comment data is the vendor ID length, followed by a
// UTF8 string with the vendor ID.
int pos = 0;
int vendorLength = data.mid(0, 4).toUInt(false);
pos += 4;
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
pos += vendorLength;
// Next the number of fields in the comment vector.
int commentFields = data.mid(pos, 4).toUInt(false);
pos += 4;
for(int i = 0; i < commentFields; i++) {
// Each comment field is in the format "KEY=value" in a UTF8 string and has
// 4 bytes before the text starts that gives the length.
int commentLength = data.mid(pos, 4).toUInt(false);
pos += 4;
String comment = String(data.mid(pos, commentLength), String::UTF8);
pos += commentLength;
int commentSeparatorPosition = comment.find("=");
String key = comment.substr(0, commentSeparatorPosition);
String value = comment.substr(commentSeparatorPosition + 1);
addField(key, value, false);
}
}

View file

@ -0,0 +1,202 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_VORBISCOMMENT_H
#define TAGLIB_VORBISCOMMENT_H
#include "tag.h"
#include "tlist.h"
#include "tmap.h"
#include "tstring.h"
#include "tstringlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
namespace Ogg {
/*!
* A mapping between a list of field names, or keys, and a list of values
* associated with that field.
*
* \see XiphComment::fieldListMap()
*/
typedef Map<String, StringList> FieldListMap;
//! Ogg Vorbis comment implementation
/*!
* This class is an implementation of the Ogg Vorbis comment specification,
* to be found in section 5 of the Ogg Vorbis specification. Because this
* format is also used in other (currently unsupported) Xiph.org formats, it
* has been made part of a generic implementation rather than being limited
* to strictly Vorbis.
*
* Vorbis comments are a simple vector of keys and values, called fields.
* Multiple values for a given key are supported.
*
* \see fieldListMap()
*/
class TAGLIB_EXPORT XiphComment : public TagLib::Tag
{
public:
/*!
* Constructs an empty Vorbis comment.
*/
XiphComment();
/*!
* Constructs a Vorbis comment from \a data.
*/
XiphComment(const ByteVector &data);
/*!
* Destroys this instance of the XiphComment.
*/
virtual ~XiphComment();
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
virtual bool isEmpty() const;
/*!
* Returns the number of fields present in the comment.
*/
uint fieldCount() const;
/*!
* Returns a reference to the map of field lists. Because Xiph comments
* support multiple fields with the same key, a pure Map would not work.
* As such this is a Map of string lists, keyed on the comment field name.
*
* The standard set of Xiph/Vorbis fields (which may or may not be
* contained in any specific comment) is:
*
* <ul>
* <li>TITLE</li>
* <li>VERSION</li>
* <li>ALBUM</li>
* <li>ARTIST</li>
* <li>PERFORMER</li>
* <li>COPYRIGHT</li>
* <li>ORGANIZATION</li>
* <li>DESCRIPTION</li>
* <li>GENRE</li>
* <li>DATE</li>
* <li>LOCATION</li>
* <li>CONTACT</li>
* <li>ISRC</li>
* </ul>
*
* For a more detailed description of these fields, please see the Ogg
* Vorbis specification, section 5.2.2.1.
*
* \note The Ogg Vorbis comment specification does allow these key values
* to be either upper or lower case. However, it is conventional for them
* to be upper case. As such, TagLib, when parsing a Xiph/Vorbis comment,
* converts all fields to uppercase. When you are using this data
* structure, you will need to specify the field name in upper case.
*
* \warning You should not modify this data structure directly, instead
* use addField() and removeField().
*/
const FieldListMap &fieldListMap() const;
/*!
* Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the
* most common case always returns "Xiph.Org libVorbis I 20020717".
*/
String vendorID() const;
/*!
* Add the field specified by \a key with the data \a value. If \a replace
* is true, then all of the other fields with the same key will be removed
* first.
*
* If the field value is empty, the field will be removed.
*/
void addField(const String &key, const String &value, bool replace = true);
/*!
* Remove the field specified by \a key with the data \a value. If
* \a value is null, all of the fields with the given key will be removed.
*/
void removeField(const String &key, const String &value = String::null);
/*!
* Returns true if the field is contained within the comment.
*
* \note This is safer than checking for membership in the FieldListMap.
*/
bool contains(const String &key) const;
/*!
* Renders the comment to a ByteVector suitable for inserting into a file.
*/
ByteVector render() const; // BIC: remove and merge with below
/*!
* Renders the comment to a ByteVector suitable for inserting into a file.
*
* If \a addFramingBit is true the standard Vorbis comment framing bit will
* be appended. However some formats (notably FLAC) do not work with this
* in place.
*/
ByteVector render(bool addFramingBit) const;
protected:
/*!
* Reads the tag from the file specified in the constructor and fills the
* FieldListMap.
*/
void parse(const ByteVector &data);
private:
XiphComment(const XiphComment &);
XiphComment &operator=(const XiphComment &);
class XiphCommentPrivate;
XiphCommentPrivate *d;
};
}
}
#endif

View file

@ -0,0 +1,83 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "tag.h"
using namespace TagLib;
class Tag::TagPrivate
{
};
Tag::Tag()
{
}
Tag::~Tag()
{
}
bool Tag::isEmpty() const
{
return (title().isEmpty() &&
artist().isEmpty() &&
album().isEmpty() &&
comment().isEmpty() &&
genre().isEmpty() &&
year() == 0 &&
track() == 0);
}
void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static
{
if(overwrite) {
target->setTitle(source->title());
target->setArtist(source->artist());
target->setAlbum(source->album());
target->setComment(source->comment());
target->setGenre(source->genre());
target->setYear(source->year());
target->setTrack(source->track());
}
else {
if(target->title().isEmpty())
target->setTitle(source->title());
if(target->artist().isEmpty())
target->setArtist(source->artist());
if(target->album().isEmpty())
target->setAlbum(source->album());
if(target->comment().isEmpty())
target->setComment(source->comment());
if(target->genre().isEmpty())
target->setGenre(source->genre());
if(target->year() <= 0)
target->setYear(source->year());
if(target->track() <= 0)
target->setTrack(source->track());
}
}

View file

@ -0,0 +1,173 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_TAG_H
#define TAGLIB_TAG_H
#include "taglib_export.h"
#include "tstring.h"
namespace TagLib {
//! A simple, generic interface to common audio meta data fields
/*!
* This is an attempt to abstract away the difference in the meta data formats
* of various audio codecs and tagging schemes. As such it is generally a
* subset of what is available in the specific formats but should be suitable
* for most applications. This is meant to compliment the generic APIs found
* in TagLib::AudioProperties, TagLib::File and TagLib::FileRef.
*/
class TAGLIB_EXPORT Tag
{
public:
/*!
* Detroys this Tag instance.
*/
virtual ~Tag();
/*!
* Returns the track name; if no track name is present in the tag
* String::null will be returned.
*/
virtual String title() const = 0;
/*!
* Returns the artist name; if no artist name is present in the tag
* String::null will be returned.
*/
virtual String artist() const = 0;
/*!
* Returns the album name; if no album name is present in the tag
* String::null will be returned.
*/
virtual String album() const = 0;
/*!
* Returns the track comment; if no comment is present in the tag
* String::null will be returned.
*/
virtual String comment() const = 0;
/*!
* Returns the genre name; if no genre is present in the tag String::null
* will be returned.
*/
virtual String genre() const = 0;
/*!
* Returns the year; if there is no year set, this will return 0.
*/
virtual uint year() const = 0;
/*!
* Returns the track number; if there is no track number set, this will
* return 0.
*/
virtual uint track() const = 0;
/*!
* Sets the title to \a s. If \a s is String::null then this value will be
* cleared.
*/
virtual void setTitle(const String &s) = 0;
/*!
* Sets the artist to \a s. If \a s is String::null then this value will be
* cleared.
*/
virtual void setArtist(const String &s) = 0;
/*!
* Sets the album to \a s. If \a s is String::null then this value will be
* cleared.
*/
virtual void setAlbum(const String &s) = 0;
/*!
* Sets the comment to \a s. If \a s is String::null then this value will be
* cleared.
*/
virtual void setComment(const String &s) = 0;
/*!
* Sets the genre to \a s. If \a s is String::null then this value will be
* cleared. For tag formats that use a fixed set of genres, the appropriate
* value will be selected based on a string comparison. A list of available
* genres for those formats should be available in that type's
* implementation.
*/
virtual void setGenre(const String &s) = 0;
/*!
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
*/
virtual void setYear(uint i) = 0;
/*!
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
*/
virtual void setTrack(uint i) = 0;
/*!
* Returns true if the tag does not contain any data. This should be
* reimplemented in subclasses that provide more than the basic tagging
* abilities in this class.
*/
virtual bool isEmpty() const;
/*!
* Copies the generic data from one tag to another.
*
* \note This will no affect any of the lower level details of the tag. For
* instance if any of the tag type specific data (maybe a URL for a band) is
* set, this will not modify or copy that. This just copies using the API
* in this class.
*
* If \a overwrite is true then the values will be unconditionally copied.
* If false only empty values will be overwritten.
*/
static void duplicate(const Tag *source, Tag *target, bool overwrite = true);
protected:
/*!
* Construct a Tag. This is protected since tags should only be instantiated
* through subclasses.
*/
Tag();
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
#endif

View file

@ -0,0 +1,218 @@
######################################################################
# Automatically generated by qmake (2.01a) Fri Feb 1 15:35:13 2008
######################################################################
TEMPLATE = lib
CONFIG += lib_bundle
CONFIG += x86 ppc
CONFIG -= qt
DEFINES += HAVE_ZLIB=1
LIBS += -lz
TARGET = TagLib
VERSION = 1.5
DEPENDPATH += . \
ape \
flac \
mpc \
mpeg \
ogg \
ogg/speex \
toolkit \
trueaudio \
wavpack \
mpeg/id3v1 \
mpeg/id3v2 \
ogg/flac \
ogg/vorbis \
mpeg/id3v2/frames
INCLUDEPATH += . \
toolkit \
mpeg \
ogg/vorbis \
ogg \
flac \
ogg/flac \
mpc \
wavpack \
ogg/speex \
trueaudio \
ape \
mpeg/id3v2 \
mpeg/id3v1 \
mpeg/id3v2/frames
# Input
HEADERS += audioproperties.h \
fileref.h \
tag.h \
taglib_export.h \
tagunion.h \
ape/apefooter.h \
ape/apeitem.h \
ape/apetag.h \
flac/flacfile.h \
flac/flacproperties.h \
mpc/mpcfile.h \
mpc/mpcproperties.h \
mpeg/mpegfile.h \
mpeg/mpegheader.h \
mpeg/mpegproperties.h \
mpeg/xingheader.h \
ogg/oggfile.h \
ogg/oggpage.h \
ogg/oggpageheader.h \
ogg/xiphcomment.h \
ogg/speex/speexfile.h \
ogg/speex/speexproperties.h \
toolkit/taglib.h \
toolkit/tbytevector.h \
toolkit/tbytevectorlist.h \
toolkit/tdebug.h \
toolkit/tfile.h \
toolkit/tlist.h \
toolkit/tmap.h \
toolkit/tstring.h \
toolkit/tstringlist.h \
toolkit/unicode.h \
trueaudio/trueaudiofile.h \
trueaudio/trueaudioproperties.h \
wavpack/wavpackfile.h \
wavpack/wavpackproperties.h \
mpeg/id3v1/id3v1genres.h \
mpeg/id3v1/id3v1tag.h \
mpeg/id3v2/id3v2extendedheader.h \
mpeg/id3v2/id3v2footer.h \
mpeg/id3v2/id3v2frame.h \
mpeg/id3v2/id3v2framefactory.h \
mpeg/id3v2/id3v2header.h \
mpeg/id3v2/id3v2synchdata.h \
mpeg/id3v2/id3v2tag.h \
ogg/flac/oggflacfile.h \
ogg/vorbis/vorbisfile.h \
ogg/vorbis/vorbisproperties.h \
mpeg/id3v2/frames/attachedpictureframe.h \
mpeg/id3v2/frames/commentsframe.h \
mpeg/id3v2/frames/generalencapsulatedobjectframe.h \
mpeg/id3v2/frames/relativevolumeframe.h \
mpeg/id3v2/frames/textidentificationframe.h \
mpeg/id3v2/frames/uniquefileidentifierframe.h \
mpeg/id3v2/frames/unknownframe.h \
mpeg/id3v2/frames/unsynchronizedlyricsframe.h \
mpeg/id3v2/frames/urllinkframe.h \
toolkit/tlist.tcc \
toolkit/tmap.tcc
SOURCES += audioproperties.cpp \
fileref.cpp \
tag.cpp \
tagunion.cpp \
ape/apefooter.cpp \
ape/apeitem.cpp \
ape/apetag.cpp \
flac/flacfile.cpp \
flac/flacproperties.cpp \
mpc/mpcfile.cpp \
mpc/mpcproperties.cpp \
mpeg/mpegfile.cpp \
mpeg/mpegheader.cpp \
mpeg/mpegproperties.cpp \
mpeg/xingheader.cpp \
ogg/oggfile.cpp \
ogg/oggpage.cpp \
ogg/oggpageheader.cpp \
ogg/xiphcomment.cpp \
ogg/speex/speexfile.cpp \
ogg/speex/speexproperties.cpp \
toolkit/tbytevector.cpp \
toolkit/tbytevectorlist.cpp \
toolkit/tdebug.cpp \
toolkit/tfile.cpp \
toolkit/tstring.cpp \
toolkit/tstringlist.cpp \
toolkit/unicode.cpp \
trueaudio/trueaudiofile.cpp \
trueaudio/trueaudioproperties.cpp \
wavpack/wavpackfile.cpp \
wavpack/wavpackproperties.cpp \
mpeg/id3v1/id3v1genres.cpp \
mpeg/id3v1/id3v1tag.cpp \
mpeg/id3v2/id3v2extendedheader.cpp \
mpeg/id3v2/id3v2footer.cpp \
mpeg/id3v2/id3v2frame.cpp \
mpeg/id3v2/id3v2framefactory.cpp \
mpeg/id3v2/id3v2header.cpp \
mpeg/id3v2/id3v2synchdata.cpp \
mpeg/id3v2/id3v2tag.cpp \
ogg/flac/oggflacfile.cpp \
ogg/vorbis/vorbisfile.cpp \
ogg/vorbis/vorbisproperties.cpp \
mpeg/id3v2/frames/attachedpictureframe.cpp \
mpeg/id3v2/frames/commentsframe.cpp \
mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp \
mpeg/id3v2/frames/relativevolumeframe.cpp \
mpeg/id3v2/frames/textidentificationframe.cpp \
mpeg/id3v2/frames/uniquefileidentifierframe.cpp \
mpeg/id3v2/frames/unknownframe.cpp \
mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp \
mpeg/id3v2/frames/urllinkframe.cpp
FRAMEWORK_HEADERS.version = Versions
FRAMEWORK_HEADERS.files = \
audioproperties.h \
fileref.h \
tag.h \
taglib_export.h \
ape/apefooter.h \
ape/apeitem.h \
ape/apetag.h \
flac/flacfile.h \
flac/flacproperties.h \
mpc/mpcfile.h \
mpc/mpcproperties.h \
mpeg/mpegfile.h \
mpeg/mpegheader.h \
mpeg/mpegproperties.h \
mpeg/xingheader.h \
ogg/oggfile.h \
ogg/oggpage.h \
ogg/oggpageheader.h \
ogg/xiphcomment.h \
ogg/speex/speexfile.h \
ogg/speex/speexproperties.h \
toolkit/taglib.h \
toolkit/tbytevector.h \
toolkit/tbytevectorlist.h \
toolkit/tfile.h \
toolkit/tlist.h \
toolkit/tmap.h \
toolkit/tstring.h \
toolkit/tstringlist.h \
toolkit/unicode.h \
trueaudio/trueaudiofile.h \
trueaudio/trueaudioproperties.h \
wavpack/wavpackfile.h \
wavpack/wavpackproperties.h \
mpeg/id3v1/id3v1genres.h \
mpeg/id3v1/id3v1tag.h \
mpeg/id3v2/id3v2extendedheader.h \
mpeg/id3v2/id3v2footer.h \
mpeg/id3v2/id3v2frame.h \
mpeg/id3v2/id3v2framefactory.h \
mpeg/id3v2/id3v2header.h \
mpeg/id3v2/id3v2synchdata.h \
mpeg/id3v2/id3v2tag.h \
ogg/flac/oggflacfile.h \
ogg/vorbis/vorbisfile.h \
ogg/vorbis/vorbisproperties.h \
mpeg/id3v2/frames/attachedpictureframe.h \
mpeg/id3v2/frames/commentsframe.h \
mpeg/id3v2/frames/generalencapsulatedobjectframe.h \
mpeg/id3v2/frames/relativevolumeframe.h \
mpeg/id3v2/frames/textidentificationframe.h \
mpeg/id3v2/frames/uniquefileidentifierframe.h \
mpeg/id3v2/frames/unknownframe.h \
mpeg/id3v2/frames/unsynchronizedlyricsframe.h \
mpeg/id3v2/frames/urllinkframe.h \
toolkit/tlist.tcc \
toolkit/tmap.tcc
FRAMEWORK_HEADERS.path = Headers
QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS

View file

@ -0,0 +1,39 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_EXPORT_H
#define TAGLIB_EXPORT_H
#if defined(_WIN32) || defined(_WIN64)
#ifdef MAKE_TAGLIB_LIB
#define TAGLIB_EXPORT __declspec(dllexport)
#else
#define TAGLIB_EXPORT __declspec(dllimport)
#endif
#else
#define TAGLIB_EXPORT
#endif
#endif

View file

@ -0,0 +1,185 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "tagunion.h"
using namespace TagLib;
#define stringUnion(method) \
if(tag(0) && !tag(0)->method().isEmpty()) \
return tag(0)->method(); \
if(tag(1) && !tag(1)->method().isEmpty()) \
return tag(1)->method(); \
if(tag(2) && !tag(2)->method().isEmpty()) \
return tag(2)->method(); \
return String::null \
#define numberUnion(method) \
if(tag(0) && tag(0)->method() > 0) \
return tag(0)->method(); \
if(tag(1) && tag(1)->method() > 0) \
return tag(1)->method(); \
if(tag(2) && tag(2)->method() > 0) \
return tag(2)->method(); \
return 0
#define setUnion(method, value) \
if(tag(0)) \
tag(0)->set##method(value); \
if(tag(1)) \
tag(1)->set##method(value); \
if(tag(2)) \
tag(2)->set##method(value); \
class TagUnion::TagUnionPrivate
{
public:
TagUnionPrivate() : tags(3, static_cast<Tag *>(0))
{
}
~TagUnionPrivate()
{
delete tags[0];
delete tags[1];
delete tags[2];
}
std::vector<Tag *> tags;
};
TagUnion::TagUnion(Tag *first, Tag *second, Tag *third)
{
d = new TagUnionPrivate;
d->tags[0] = first;
d->tags[1] = second;
d->tags[2] = third;
}
TagUnion::~TagUnion()
{
delete d;
}
Tag *TagUnion::operator[](int index) const
{
return tag(index);
}
Tag *TagUnion::tag(int index) const
{
return d->tags[index];
}
void TagUnion::set(int index, Tag *tag)
{
delete d->tags[index];
d->tags[index] = tag;
}
String TagUnion::title() const
{
stringUnion(title);
}
String TagUnion::artist() const
{
stringUnion(artist);
}
String TagUnion::album() const
{
stringUnion(album);
}
String TagUnion::comment() const
{
stringUnion(comment);
}
String TagUnion::genre() const
{
stringUnion(genre);
}
TagLib::uint TagUnion::year() const
{
numberUnion(year);
}
TagLib::uint TagUnion::track() const
{
numberUnion(track);
}
void TagUnion::setTitle(const String &s)
{
setUnion(Title, s);
}
void TagUnion::setArtist(const String &s)
{
setUnion(Artist, s);
}
void TagUnion::setAlbum(const String &s)
{
setUnion(Album, s);
}
void TagUnion::setComment(const String &s)
{
setUnion(Comment, s);
}
void TagUnion::setGenre(const String &s)
{
setUnion(Genre, s);
}
void TagUnion::setYear(uint i)
{
setUnion(Year, i);
}
void TagUnion::setTrack(uint i)
{
setUnion(Track, i);
}
bool TagUnion::isEmpty() const
{
if(d->tags[0] && !d->tags[0]->isEmpty())
return false;
if(d->tags[1] && !d->tags[1]->isEmpty())
return false;
if(d->tags[2] && !d->tags[2]->isEmpty())
return false;
return true;
}

View file

@ -0,0 +1,95 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_TAGUNION_H
#define TAGLIB_TAGUNION_H
#include "tag.h"
#ifndef DO_NOT_DOCUMENT
namespace TagLib {
/*!
* \internal
*/
class TagUnion : public Tag
{
public:
enum AccessType { Read, Write };
/*!
* Creates a TagLib::Tag that is the union of \a first, \a second, and
* \a third. The TagUnion takes ownership of these tags and will handle
* their deletion.
*/
TagUnion(Tag *first = 0, Tag *second = 0, Tag *third = 0);
virtual ~TagUnion();
Tag *operator[](int index) const;
Tag *tag(int index) const;
void set(int index, Tag *tag);
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
virtual bool isEmpty() const;
template <class T> T *access(int index, bool create)
{
if(!create || tag(index))
return static_cast<T *>(tag(index));
set(index, new T);
return static_cast<T *>(tag(index));
}
private:
TagUnion(const Tag &);
TagUnion &operator=(const Tag &);
class TagUnionPrivate;
TagUnionPrivate *d;
};
}
#endif
#endif

View file

@ -0,0 +1,177 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_H
#define TAGLIB_H
#define TAGLIB_MAJOR_VERSION 1
#define TAGLIB_MINOR_VERSION 5
#define TAGLIB_PATCH_VERSION 0
#include <string>
//! A namespace for all TagLib related classes and functions
/*!
* This namespace contains everything in TagLib. For projects working with
* TagLib extensively it may be conveniten to add a
* \code
* using namespace TagLib;
* \endcode
*/
namespace TagLib {
class String;
typedef wchar_t wchar;
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
/*!
* Unfortunately std::wstring isn't defined on some systems, (i.e. GCC < 3)
* so I'm providing something here that should be constant.
*/
typedef std::basic_string<wchar> wstring;
#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
/*!
* \internal
* This is just used as a base class for shared classes in TagLib.
*
* \warning This <b>is not</b> part of the TagLib public API!
*/
class RefCounter
{
public:
RefCounter() : refCount(1) {}
void ref() { refCount++; }
bool deref() { return ! --refCount ; }
int count() { return refCount; }
private:
uint refCount;
};
#endif // DO_NOT_DOCUMENT
}
/*!
* \mainpage TagLib
*
* \section intro Introduction
*
* TagLib is a library for reading and editing audio meta data, commonly know as \e tags.
*
* Features:
* - A clean, high level, C++ API to handling audio meta data.
* - Format specific APIs for advanced API users.
* - ID3v1, ID3v2, APE, FLAC and Xiph tag formats.
* - MP3, MPC, FLAC, Ogg FLAC, Ogg Vorbis and Speex file formats.
* - Basic audio file properties such as length, sample rate, etc.
* - Long term binary and source compatibility.
* - Extensible design, notably the ability to add other formats or extend current formats as a library user.
* - Full support for unicode and internationalized tags.
* - Dual <a href="http://www.mozilla.org/MPL/MPL-1.1.html">MPL</a> and
* <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">LGPL</a> licenses.
* - No external toolkit dependancies.
*
* \section why Why TagLib?
*
* TagLib originally was written to provide an updated and improved ID3v2 implementation in C++ for use
* in a variety of Open Source projects. Since development began in 2002 and the 1.0 release in 2004
* it has expanded to cover a wide variety of tag and file formats and is used in a wide variety of
* Open Source and proprietary applications. It now supports a variety of UNIXes, including Apple's OS
* X, as well as Microsoft Windows.
*
* \section commercial Usage in Commercial Applications
*
* TagLib's licenses \e do allow usage within propriety (\e closed) applications, however TagLib is \e not
* public domain. Please note the requirements of the LGPL or MPL, and adhere to at least one of them.
* In simple terms, you must at a minimum note your usage of TagLib, note the licensing terms of TagLib and
* if you make changes to TagLib publish them. Please review the licenses above before using TagLib in your
* software. Note that you may choose either the MPL or the LGPL, you do not have to fulfill the
* requirements of both.
*
* \section installing Installing TagLib
*
* Please see the <a href="http://developer.kde.org/~wheeler/taglib.html">TagLib website</a> for the latest
* downloads.
*
* Instructions for installing TagLib vary per platform, but generally speaking on UNIX standard configure and
* make commands are provided. TagLib installs a taglib-config and package-config file to make it easier to
* integrate into various build systems. Note that TagLib's include install directory \e must be included in
* the header include path. Simply adding <taglib/tag.h> will \e not work.
*
* On Windows, TagLib can be built using the CMake build systems.
*
* \section start Getting Started
*
* TagLib provides both simple, abstract APIs which make it possible to ignore the differences between tagging
* formats and format specific APIs which allow programmers to work with the features of specific tagging
* schemes. There is a similar abstraction mechanism for AudioProperties.
*
* The best place to start is with the <b>Class Hierarchy</b> linked at the top of the page. The File and
* AudioProperties classes and their subclasses are the core of TagLib. The FileRef class is also a convenient
* way for using a value-based handle.
*
* \note When working with FileRef please consider that it has only the most basic (extension-based) file
* type resolution. Please see its documentation on how to plug in more advanced file type resolution. (Such
* resolution may be part of later TagLib releases by default.)
*
* Here's a very simple example with TagLib:
*
* \code
*
* TagLib::FileRef f("Latex Solar Beef.mp3");
* TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa"
*
* f.tag()->setAlbum("Fillmore East");
* f.save();
*
* TagLib::FileRef g("Free City Rhymes.ogg");
* TagLib::String album = g.tag()->album(); // album == "NYC Ghosts & Flowers"
*
* g.tag()->setTrack(1);
* g.save();
*
* \endcode
*
* More examples can be found in the \e examples directory of the source distribution.
*
* \section Contact
*
* Questions about TagLib should be directed to the TagLib mailing list, not directly to the author.
*
* - <a href="http://developer.kde.org/~wheeler/taglib/">TagLib Homepage</a>
* - <a href="https://mail.kde.org/mailman/listinfo/taglib-devel">TagLib Mailing List (taglib-devel@kde.org)</a>
*
* \author Scott Wheeler <wheeler@kde.org> et al.
*
*/
#endif

View file

@ -0,0 +1,677 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 <iostream>
#include <tstring.h>
#include <tdebug.h>
#include <string.h>
#include "tbytevector.h"
// This is a bit ugly to keep writing over and over again.
// A rather obscure feature of the C++ spec that I hadn't thought of that makes
// working with C libs much more effecient. There's more here:
//
// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
#define DATA(x) (&(x->data[0]))
namespace TagLib {
static const uint crcTable[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/*!
* A templatized KMP find that works both with a ByteVector and a ByteVectorMirror.
*/
template <class Vector>
int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
{
if(pattern.size() > v.size() || offset > v.size() - 1)
return -1;
// Let's go ahead and special case a pattern of size one since that's common
// and easy to make fast.
if(pattern.size() == 1) {
char p = pattern[0];
for(uint i = offset; i < v.size(); i++) {
if(v[i] == p && (i - offset) % byteAlign == 0)
return i;
}
return -1;
}
uchar lastOccurrence[256];
for(uint i = 0; i < 256; ++i)
lastOccurrence[i] = uchar(pattern.size());
for(uint i = 0; i < pattern.size() - 1; ++i)
lastOccurrence[uchar(pattern[i])] = uchar(pattern.size() - i - 1);
for(uint i = pattern.size() - 1 + offset; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
int iBuffer = i;
int iPattern = pattern.size() - 1;
while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
--iBuffer;
--iPattern;
}
if(-1 == iPattern && (iBuffer + 1 - offset) % byteAlign == 0)
return iBuffer + 1;
}
return -1;
}
/*!
* Wraps the accessors to a ByteVector to make the search algorithm access the
* elements in reverse.
*
* \see vectorFind()
* \see ByteVector::rfind()
*/
class ByteVectorMirror
{
public:
ByteVectorMirror(const ByteVector &source) : v(source) {}
const char operator[](int index) const
{
return v[v.size() - index - 1];
}
const char at(int index) const
{
return v.at(v.size() - index - 1);
}
ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
{
return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
}
uint size() const
{
return v.size();
}
int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
{
ByteVectorMirror v(*this);
if(offset > 0) {
offset = size() - offset - pattern.size();
if(offset >= size())
offset = 0;
}
const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);
// If the offset is zero then we need to adjust the location in the search
// to be appropriately reversed. If not we need to account for the fact
// that the recursive call (called from the above line) has already ajusted
// for this but that the normal templatized find above will add the offset
// to the returned value.
//
// This is a little confusing at first if you don't first stop to think
// through the logic involved in the forward search.
if(pos == -1)
return -1;
return size() - pos - pattern.size();
}
private:
const ByteVector &v;
};
template <class T>
T toNumber(const std::vector<char> &data, bool mostSignificantByteFirst)
{
T sum = 0;
if(data.size() <= 0) {
debug("ByteVectorMirror::toNumber<T>() -- data is empty, returning 0");
return sum;
}
uint size = sizeof(T);
uint last = data.size() > size ? size - 1 : data.size() - 1;
for(uint i = 0; i <= last; i++)
sum |= (T) uchar(data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
return sum;
}
template <class T>
ByteVector fromNumber(T value, bool mostSignificantByteFirst)
{
int size = sizeof(T);
ByteVector v(size, 0);
for(int i = 0; i < size; i++)
v[i] = uchar(value >> ((mostSignificantByteFirst ? size - 1 - i : i) * 8) & 0xff);
return v;
}
}
using namespace TagLib;
class ByteVector::ByteVectorPrivate : public RefCounter
{
public:
ByteVectorPrivate() : RefCounter(), size(0) {}
ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v), size(v.size()) {}
ByteVectorPrivate(TagLib::uint len, char value) : RefCounter(), data(len, value), size(len) {}
std::vector<char> data;
// std::vector<T>::size() is very slow, so we'll cache the value
uint size;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
ByteVector ByteVector::null;
ByteVector ByteVector::fromCString(const char *s, uint length)
{
ByteVector v;
if(length == 0xffffffff)
v.setData(s);
else
v.setData(s, length);
return v;
}
ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
{
return fromNumber<uint>(value, mostSignificantByteFirst);
}
ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst)
{
return fromNumber<short>(value, mostSignificantByteFirst);
}
ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
{
return fromNumber<long long>(value, mostSignificantByteFirst);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ByteVector::ByteVector()
{
d = new ByteVectorPrivate;
}
ByteVector::ByteVector(uint size, char value)
{
d = new ByteVectorPrivate(size, value);
}
ByteVector::ByteVector(const ByteVector &v) : d(v.d)
{
d->ref();
}
ByteVector::ByteVector(char c)
{
d = new ByteVectorPrivate;
d->data.push_back(c);
d->size = 1;
}
ByteVector::ByteVector(const char *data, uint length)
{
d = new ByteVectorPrivate;
setData(data, length);
}
ByteVector::ByteVector(const char *data)
{
d = new ByteVectorPrivate;
setData(data);
}
ByteVector::~ByteVector()
{
if(d->deref())
delete d;
}
ByteVector &ByteVector::setData(const char *data, uint length)
{
detach();
resize(length);
if(length > 0)
::memcpy(DATA(d), data, length);
return *this;
}
ByteVector &ByteVector::setData(const char *data)
{
return setData(data, ::strlen(data));
}
char *ByteVector::data()
{
detach();
return size() > 0 ? DATA(d) : 0;
}
const char *ByteVector::data() const
{
return size() > 0 ? DATA(d) : 0;
}
ByteVector ByteVector::mid(uint index, uint length) const
{
ByteVector v;
if(index > size())
return v;
ConstIterator endIt;
if(length < 0xffffffff && length + index < size())
endIt = d->data.begin() + index + length;
else
endIt = d->data.end();
v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
v.d->size = v.d->data.size();
return v;
}
char ByteVector::at(uint index) const
{
return index < size() ? d->data[index] : 0;
}
int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
{
return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
}
int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
{
// Ok, this is a little goofy, but pretty cool after it sinks in. Instead of
// reversing the find method's Boyer-Moore search algorithm I created a "mirror"
// for a ByteVector to reverse the behavior of the accessors.
ByteVectorMirror v(*this);
ByteVectorMirror p(pattern);
return v.find(p, offset, byteAlign);
}
bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
{
if(pattern.size() < patternLength)
patternLength = pattern.size();
// do some sanity checking -- all of these things are needed for the search to be valid
if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
return false;
// loop through looking for a mismatch
for(uint i = 0; i < patternLength - patternOffset; i++) {
if(at(i + offset) != pattern[i + patternOffset])
return false;
}
return true;
}
bool ByteVector::startsWith(const ByteVector &pattern) const
{
return containsAt(pattern, 0);
}
bool ByteVector::endsWith(const ByteVector &pattern) const
{
return containsAt(pattern, size() - pattern.size());
}
ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with)
{
if(pattern.size() == 0 || pattern.size() > size())
return *this;
const int patternSize = pattern.size();
const int withSize = with.size();
int offset = find(pattern);
while(offset >= 0) {
const int originalSize = size();
if(withSize > patternSize)
resize(originalSize + withSize - patternSize);
if(patternSize != withSize)
::memcpy(data() + offset + withSize, mid(offset + patternSize).data(), originalSize - offset - patternSize);
if(withSize < patternSize)
resize(originalSize + withSize - patternSize);
::memcpy(data() + offset, with.data(), withSize);
offset = find(pattern, offset + withSize);
}
return *this;
}
int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
{
if(pattern.size() > size())
return -1;
const int startIndex = size() - pattern.size();
// try to match the last n-1 bytes from the vector (where n is the pattern
// size) -- continue trying to match n-2, n-3...1 bytes
for(uint i = 1; i < pattern.size(); i++) {
if(containsAt(pattern, startIndex + i, 0, pattern.size() - i))
return startIndex + i;
}
return -1;
}
ByteVector &ByteVector::append(const ByteVector &v)
{
if(v.d->size == 0)
return *this; // Simply return if appending nothing.
detach();
uint originalSize = d->size;
resize(d->size + v.d->size);
::memcpy(DATA(d) + originalSize, DATA(v.d), v.size());
return *this;
}
ByteVector &ByteVector::clear()
{
detach();
d->data.clear();
d->size = 0;
return *this;
}
TagLib::uint ByteVector::size() const
{
return d->size;
}
ByteVector &ByteVector::resize(uint size, char padding)
{
if(d->size < size) {
d->data.reserve(size);
d->data.insert(d->data.end(), size - d->size, padding);
}
else
d->data.erase(d->data.begin() + size, d->data.end());
d->size = size;
return *this;
}
ByteVector::Iterator ByteVector::begin()
{
return d->data.begin();
}
ByteVector::ConstIterator ByteVector::begin() const
{
return d->data.begin();
}
ByteVector::Iterator ByteVector::end()
{
return d->data.end();
}
ByteVector::ConstIterator ByteVector::end() const
{
return d->data.end();
}
bool ByteVector::isNull() const
{
return d == null.d;
}
bool ByteVector::isEmpty() const
{
return d->data.size() == 0;
}
TagLib::uint ByteVector::checksum() const
{
uint sum = 0;
for(ByteVector::ConstIterator it = begin(); it != end(); ++it)
sum = (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ uchar(*it)];
return sum;
}
TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
{
return toNumber<uint>(d->data, mostSignificantByteFirst);
}
short ByteVector::toShort(bool mostSignificantByteFirst) const
{
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
}
long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
{
return toNumber<unsigned long long>(d->data, mostSignificantByteFirst);
}
const char &ByteVector::operator[](int index) const
{
return d->data[index];
}
char &ByteVector::operator[](int index)
{
detach();
return d->data[index];
}
bool ByteVector::operator==(const ByteVector &v) const
{
if(d->size != v.d->size)
return false;
return ::memcmp(data(), v.data(), size()) == 0;
}
bool ByteVector::operator!=(const ByteVector &v) const
{
return !operator==(v);
}
bool ByteVector::operator==(const char *s) const
{
if(d->size != ::strlen(s))
return false;
return ::memcmp(data(), s, d->size) == 0;
}
bool ByteVector::operator!=(const char *s) const
{
return !operator==(s);
}
bool ByteVector::operator<(const ByteVector &v) const
{
int result = ::memcmp(data(), v.data(), d->size < v.d->size ? d->size : v.d->size);
if(result != 0)
return result < 0;
else
return size() < v.size();
}
bool ByteVector::operator>(const ByteVector &v) const
{
return v < *this;
}
ByteVector ByteVector::operator+(const ByteVector &v) const
{
ByteVector sum(*this);
sum.append(v);
return sum;
}
ByteVector &ByteVector::operator=(const ByteVector &v)
{
if(&v == this)
return *this;
if(d->deref())
delete d;
d = v.d;
d->ref();
return *this;
}
ByteVector &ByteVector::operator=(char c)
{
*this = ByteVector(c);
return *this;
}
ByteVector &ByteVector::operator=(const char *data)
{
*this = ByteVector(data);
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void ByteVector::detach()
{
if(d->count() > 1) {
d->deref();
d = new ByteVectorPrivate(d->data);
}
}
////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////
std::ostream &operator<<(std::ostream &s, const ByteVector &v)
{
for(TagLib::uint i = 0; i < v.size(); i++)
s << v[i];
return s;
}

View file

@ -0,0 +1,409 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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/ *
***************************************************************************/
#ifndef TAGLIB_BYTEVECTOR_H
#define TAGLIB_BYTEVECTOR_H
#include "taglib.h"
#include "taglib_export.h"
#include <vector>
#include <ostream>
namespace TagLib {
//! A byte vector
/*!
* This class provides a byte vector with some methods that are useful for
* tagging purposes. Many of the search functions are tailored to what is
* useful for finding tag related paterns in a data array.
*/
class TAGLIB_EXPORT ByteVector
{
public:
#ifndef DO_NOT_DOCUMENT
typedef std::vector<char>::iterator Iterator;
typedef std::vector<char>::const_iterator ConstIterator;
#endif
/*!
* Constructs an empty byte vector.
*/
ByteVector();
/*!
* Construct a vector of size \a size with all values set to \a value by
* default.
*/
ByteVector(uint size, char value = 0);
/*!
* Contructs a byte vector that is a copy of \a v.
*/
ByteVector(const ByteVector &v);
/*!
* Contructs a byte vector that contains \a c.
*/
ByteVector(char c);
/*!
* Constructs a byte vector that copies \a data for up to \a length bytes.
*/
ByteVector(const char *data, uint length);
/*!
* Constructs a byte vector that copies \a data up to the first null
* byte. The behavior is undefined if \a data is not null terminated.
* This is particularly useful for constructing byte arrays from string
* constants.
*/
ByteVector(const char *data);
/*!
* Destroys this ByteVector instance.
*/
virtual ~ByteVector();
/*!
* Sets the data for the byte array using the first \a length bytes of \a data
*/
ByteVector &setData(const char *data, uint length);
/*!
* Sets the data for the byte array copies \a data up to the first null
* byte. The behavior is undefined if \a data is not null terminated.
*/
ByteVector &setData(const char *data);
/*!
* Returns a pointer to the internal data structure.
*
* \warning Care should be taken when modifying this data structure as it is
* easy to corrupt the ByteVector when doing so. Specifically, while the
* data may be changed, its length may not be.
*/
char *data();
/*!
* Returns a pointer to the internal data structure which may not be modified.
*/
const char *data() const;
/*!
* Returns a byte vector made up of the bytes starting at \a index and
* for \a length bytes. If \a length is not specified it will return the bytes
* from \a index to the end of the vector.
*/
ByteVector mid(uint index, uint length = 0xffffffff) const;
/*!
* This essentially performs the same as operator[](), but instead of causing
* a runtime error if the index is out of bounds, it will return a null byte.
*/
char at(uint index) const;
/*!
* Searches the ByteVector for \a pattern starting at \a offset and returns
* the offset. Returns -1 if the pattern was not found. If \a byteAlign is
* specified the pattern will only be matched if it starts on a byte divisible
* by \a byteAlign (starting from \a offset).
*/
int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
/*!
* Searches the ByteVector for \a pattern starting from either the end of the
* vector or \a offset and returns the offset. Returns -1 if the pattern was
* not found. If \a byteAlign is specified the pattern will only be matched
* if it starts on a byte divisible by \a byteAlign (starting from \a offset).
*/
int rfind(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
/*!
* Checks to see if the vector contains the \a pattern starting at position
* \a offset. Optionally, if you only want to search for part of the pattern
* you can specify an offset within the pattern to start from. Also, you can
* specify to only check for the first \a patternLength bytes of \a pattern with
* the \a patternLength argument.
*/
bool containsAt(const ByteVector &pattern, uint offset, uint patternOffset = 0, uint patternLength = 0xffffffff) const;
/*!
* Returns true if the vector starts with \a pattern.
*/
bool startsWith(const ByteVector &pattern) const;
/*!
* Returns true if the vector ends with \a pattern.
*/
bool endsWith(const ByteVector &pattern) const;
/*!
* Replaces \a pattern with \a with and returns a reference to the ByteVector
* after the operation. This \e does modify the vector.
*/
ByteVector &replace(const ByteVector &pattern, const ByteVector &with);
/*!
* Checks for a partial match of \a pattern at the end of the vector. It
* returns the offset of the partial match within the vector, or -1 if the
* pattern is not found. This method is particularly useful when searching for
* patterns that start in one vector and end in another. When combined with
* startsWith() it can be used to find a pattern that overlaps two buffers.
*
* \note This will not match the complete pattern at the end of the string; use
* endsWith() for that.
*/
int endsWithPartialMatch(const ByteVector &pattern) const;
/*!
* Appends \a v to the end of the ByteVector.
*/
ByteVector &append(const ByteVector &v);
/*!
* Clears the data.
*/
ByteVector &clear();
/*!
* Returns the size of the array.
*/
uint size() const;
/*!
* Resize the vector to \a size. If the vector is currently less than
* \a size, pad the remaining spaces with \a padding. Returns a reference
* to the resized vector.
*/
ByteVector &resize(uint size, char padding = 0);
/*!
* Returns an Iterator that points to the front of the vector.
*/
Iterator begin();
/*!
* Returns a ConstIterator that points to the front of the vector.
*/
ConstIterator begin() const;
/*!
* Returns an Iterator that points to the back of the vector.
*/
Iterator end();
/*!
* Returns a ConstIterator that points to the back of the vector.
*/
ConstIterator end() const;
/*!
* Returns true if the vector is null.
*
* \note A vector may be empty without being null.
* \see isEmpty()
*/
bool isNull() const;
/*!
* Returns true if the ByteVector is empty.
*
* \see size()
* \see isNull()
*/
bool isEmpty() const;
/*!
* Returns a CRC checksum of the byte vector's data.
*/
uint checksum() const;
/*!
* Converts the first 4 bytes of the vector to an unsigned integer.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
* 0x01000000 == 1.
*
* \see fromUInt()
*/
uint toUInt(bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 2 bytes of the vector to a short.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
*
* \see fromShort()
*/
short toShort(bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 8 bytes of the vector to a (signed) long long.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1,
* if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
*
* \see fromUInt()
*/
long long toLongLong(bool mostSignificantByteFirst = true) const;
/*!
* Creates a 4 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
* in building the ByteVector. For example if \a mostSignificantByteFirst is
* true then $00 00 00 01 == 0x00000001 == 1, if false, $01 00 00 00 ==
* 0x01000000 == 1.
*
* \see toUInt()
*/
static ByteVector fromUInt(uint value, bool mostSignificantByteFirst = true);
/*!
* Creates a 2 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
* in building the ByteVector. For example if \a mostSignificantByteFirst is
* true then $00 01 == 0x0001 == 1, if false, $01 00 == 0x0100 == 1.
*
* \see toShort()
*/
static ByteVector fromShort(short value, bool mostSignificantByteFirst = true);
/*!
* Creates a 8 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
* in building the ByteVector. For example if \a mostSignificantByteFirst is
* true then $00 00 00 01 == 0x0000000000000001 == 1, if false,
* $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
*
* \see toLongLong()
*/
static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true);
/*!
* Returns a ByteVector based on the CString \a s.
*/
static ByteVector fromCString(const char *s, uint length = 0xffffffff);
/*!
* Returns a const refernence to the byte at \a index.
*/
const char &operator[](int index) const;
/*!
* Returns a reference to the byte at \a index.
*/
char &operator[](int index);
/*!
* Returns true if this ByteVector and \a v are equal.
*/
bool operator==(const ByteVector &v) const;
/*!
* Returns true if this ByteVector and \a v are not equal.
*/
bool operator!=(const ByteVector &v) const;
/*!
* Returns true if this ByteVector and the null terminated C string \a s
* contain the same data.
*/
bool operator==(const char *s) const;
/*!
* Returns true if this ByteVector and the null terminated C string \a s
* do not contain the same data.
*/
bool operator!=(const char *s) const;
/*!
* Returns true if this ByteVector is less than \a v. The value of the
* vectors is determined by evaluating the character from left to right, and
* in the event one vector is a superset of the other, the size is used.
*/
bool operator<(const ByteVector &v) const;
/*!
* Returns true if this ByteVector is greater than \a v.
*/
bool operator>(const ByteVector &v) const;
/*!
* Returns a vector that is \a v appended to this vector.
*/
ByteVector operator+(const ByteVector &v) const;
/*!
* Copies ByteVector \a v.
*/
ByteVector &operator=(const ByteVector &v);
/*!
* Copies ByteVector \a v.
*/
ByteVector &operator=(char c);
/*!
* Copies ByteVector \a v.
*/
ByteVector &operator=(const char *data);
/*!
* A static, empty ByteVector which is convenient and fast (since returning
* an empty or "null" value does not require instantiating a new ByteVector).
*/
static ByteVector null;
protected:
/*
* If this ByteVector is being shared via implicit sharing, do a deep copy
* of the data and separate from the shared members. This should be called
* by all non-const subclass members.
*/
void detach();
private:
class ByteVectorPrivate;
ByteVectorPrivate *d;
};
}
/*!
* \relates TagLib::ByteVector
* Streams the ByteVector \a v to the output stream \a s.
*/
TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v);
#endif

View file

@ -0,0 +1,102 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* 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 "tbytevectorlist.h"
using namespace TagLib;
class ByteVectorListPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern,
int byteAlign)
{
return split(v, pattern, byteAlign, 0);
}
ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &pattern,
int byteAlign, int max)
{
ByteVectorList l;
uint previousOffset = 0;
for(int offset = v.find(pattern, 0, byteAlign);
offset != -1 && (max == 0 || max > int(l.size()) + 1);
offset = v.find(pattern, offset + pattern.size(), byteAlign))
{
if(offset - previousOffset > 1)
l.append(v.mid(previousOffset, offset - previousOffset));
else
l.append(ByteVector::null);
previousOffset = offset + pattern.size();
}
if(previousOffset < v.size())
l.append(v.mid(previousOffset, v.size() - previousOffset));
return l;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ByteVectorList::ByteVectorList() : List<ByteVector>()
{
}
ByteVectorList::ByteVectorList(const ByteVectorList &l) : List<ByteVector>(l)
{
}
ByteVectorList::~ByteVectorList()
{
}
ByteVector ByteVectorList::toByteVector(const ByteVector &separator) const
{
ByteVector v;
ConstIterator it = begin();
while(it != end()) {
v.append(*it);
it++;
if(it != end())
v.append(separator);
}
return v;
}

Some files were not shown because too many files have changed in this diff Show more