diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6552d24e2..14ac26dd4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,8 +29,10 @@ INCLUDE( MacroOptionalFindPackage )
INCLUDE( MacroLogFeature )
# required
-macro_optional_find_package(LibLastFm 0.3.3)
-macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork")
+#While we distribute our own liblastfm2, don't need to look for it
+#macro_optional_find_package(LibLastFm 0.3.3)
+#macro_log_feature(LIBLASTFM_FOUND "LastFm" "Qt library for the Last.fm webservices" "https://github.com/mxcl/liblastfm" FALSE "" "liblastfm is needed for scrobbling tracks to Last.fm and fetching cover artwork")
+set(LIBLASTFM_FOUND true)
macro_optional_find_package(LibEchonest 1.1.1)
macro_log_feature(LIBECHONEST_FOUND "Echonest" "Qt library for communicating with The Echo Nest" "http://projects.kde.org/libechonest" TRUE "" "libechonest is needed for dynamic playlists and the infosystem")
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index babe44b77..d3d50b2cc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -123,6 +123,7 @@ INCLUDE_DIRECTORIES(
.
${TOMAHAWK_INC_DIR}
${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src
audio
database
@@ -205,7 +206,7 @@ MESSAGE( STATUS "OS_SPECIFIC_LINK_LIBRARIES: ${OS_SPECIFIC_LINK_LIBRARIES}" )
SET(LINK_LIBRARIES "")
IF(LIBLASTFM_FOUND)
- SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${LIBLASTFM_LIBRARY} )
+ SET(LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_lastfm2 )
ENDIF(LIBLASTFM_FOUND)
IF(GLOOX_FOUND)
SET(LINK_LIBRARIES ${LINK_LIBRARIES} ${GLOOX_LIBRARIES} )
diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt
index 2d2836537..f28fdff43 100644
--- a/thirdparty/CMakeLists.txt
+++ b/thirdparty/CMakeLists.txt
@@ -2,6 +2,7 @@ add_subdirectory( jdns )
add_subdirectory( qtweetlib )
ADD_SUBDIRECTORY( libportfwd )
ADD_SUBDIRECTORY( qxt )
+ADD_SUBDIRECTORY( liblastfm2 )
IF( UNIX AND NOT APPLE )
ADD_SUBDIRECTORY( alsa-playback )
diff --git a/thirdparty/liblastfm2/CMakeLists.txt b/thirdparty/liblastfm2/CMakeLists.txt
new file mode 100644
index 000000000..e8b020b78
--- /dev/null
+++ b/thirdparty/liblastfm2/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 2.6)
+
+set( CMAKE_MODULE_PATH
+ ${CMAKE_MODULE_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules
+ )
+
+if(${CMAKE_BUILD_TYPE} MATCHES "Release")
+ add_definitions(-DNDEBUG)
+endif(${CMAKE_BUILD_TYPE} MATCHES "Release")
+
+# Set up definitions and paths
+add_definitions(${QT_DEFINITIONS})
+include(${QT_USE_FILE})
+
+# Main Library
+add_subdirectory(src)
+
+# Optionally build the fingerprint library
+option(BUILD_FINGERPRINT "Build the lastfm-fingerprint library" ON)
+find_package(LibSamplerate)
+find_package(LibFFTW3)
+
+if (BUILD_FINGERPRINT AND LIBFFTW3_FOUND AND LIBSAMPLERATE_FOUND)
+ add_subdirectory(src/fingerprint)
+endif (BUILD_FINGERPRINT AND LIBFFTW3_FOUND AND LIBSAMPLERATE_FOUND)
+
diff --git a/thirdparty/liblastfm2/COPYING b/thirdparty/liblastfm2/COPYING
new file mode 100644
index 000000000..94a045322
--- /dev/null
+++ b/thirdparty/liblastfm2/COPYING
@@ -0,0 +1,621 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
diff --git a/thirdparty/liblastfm2/README b/thirdparty/liblastfm2/README
new file mode 100644
index 000000000..d39417ecc
--- /dev/null
+++ b/thirdparty/liblastfm2/README
@@ -0,0 +1,138 @@
+liblastfm
+=========
+liblastfm is a collection of libraries to help you integrate Last.fm services
+into your rich desktop software. It is officially supported software developed
+by Last.fm staff.
+
+Max Howell http://twitter.com/mxcl
+Jono Cole http://twitter.com/jonocole
+Doug Mansell http://twitter.com/dougma
+
+Fork it: http://github.com/mxcl/liblastfm
+
+
+Dependencies
+============
+liblastfm dynamically links to:
+
+* Qt 4.4
+ http://www.qtsoftware.com
+* FFTW 3.2
+ Compiled with single precision
+ http://www.fftw.org
+* Secret Rabbit code (aka libsamplerate)
+ http://www.mega-nerd.com/SRC
+
+Additionally, to build you will need Ruby and GNU make (or Microsoft nmake).
+
+Mac OS X
+--------
+ sudo port selfupdate
+ sudo port upgrade installed
+ sudo port install libsamplerate fftw-3 qt4-mac-devel
+
+qt4-mac-devel will take a long time to build. So you may want to install the
+Trolltech binary package instead.
+
+MacPorts carries liblastfm now, so you may have downloaded this package simply
+to perform this next line:
+
+ sudo port install liblastfm
+
+Linux/*NIX
+----------
+Do something like this:
+
+ sudo apt-get install qt4-qmake pkg-config libsamplerate-dev libfftw3-dev ruby g++ libqt4-dev
+
+Additionally on Linux the configure process requires lsb_release. This is
+usually already installed (correct me if I'm wrong).
+
+Please note, we have only tested on Linux, but we think it'll work on all
+varieties of UNIX. If it doesn't, report the bug to mxcl on GitHub.
+
+Windows
+-------
+Install Ruby. Install Visual Studio 2005 or higher. Install Qt. Install the
+Windows Server 2003 Platform SDK r2:
+
+http://www.microsoft.com/Downloads/details.aspx?FamilyID=484269e2-3b89-47e3-8eb7-1f2be6d7123a
+
+Set up your environment variables so all include paths and tools are
+available.
+
+Build and install FFTW and Secret Rabbit Code.
+
+Open a plain Windows shell (Cygwin will work but we don't recommend it), and
+see the next section.
+
+
+Installing liblastfm
+====================
+ ruby configure --release --prefix /usr/local && make && sudo make install
+
+Packaging liblastfm
+-------------------
+DESTDIR is supported.
+
+liblastfm builds to two dynamic libraries (liblastfm.so and
+liblastfm_fingerprint.so). liblastfm.so links only to Qt, but the
+fingerprinting part has additional dependencies. So ideally, you would
+distribute two packages.
+
+
+Using liblastfm
+===============
+We have copied the API at http://last.fm/api onto C++, so like you find
+artist.getInfo there you will find an lastfm::Artist::getInfo function in our
+C++ API. lastfm is a namespace, Artist a class and getInfo a function.
+
+Thus the API is quite easy to learn. We suggest installing and checking the
+include/lastfm/* directory to find out all capabilities.
+
+The demos directory shows some further basic usage including Audioscrobbling
+and getting metadata for music via our fingerprinting technology.
+
+You need an API key from http://last.fm/api to use the webservice API.
+
+Your link line needs to include the following:
+
+ -llastfm -lQtCore -lQtNetwork -lQtXml
+
+Radio
+-----
+Please set an identifiable UserAgent on your HTTP requests for the actual MP3s,
+in extreme cases we'll contact you directly and demand you do so :P
+
+HTTP & Networking
+-----------------
+You can specify your own QNetworkAccessManager derived class for liblastfm to
+use with lastfm::setNetworkAccessManager(). Our default is pretty good
+though, auto-determining proxy settings on Windows and OS X for instance.
+
+
+Using liblastfm_fingerprint
+===========================
+The liblastfm_fingerprint library does not decode audio files. We anticipate
+that Phonon will soon do that work for us. In the meantime, sample *Source
+files for MP3, Ogg Vorbis, FLAC, and AAC/MP4 are available in
+src/fingerprint/contrib. If you want to fingerprint files or get metadata
+suggestions, you either need to add the *Source files to your project, or
+implement your own.
+
+
+Development
+===========
+Public Headers
+--------------
+1. Header guards should be prefixed with LASTFM, eg. LASTFM_WS_REPLY_H
+2. #includes should be to the system path eg. #include
+3. Don't make a header public unless it is absolutely required!
+4. To make the header public edit the headers.files line in the pro file
+
+Private Headers
+---------------
+1. For consistency and to make it more obvious it is a private header, don't
+ prefix the header guard with LASTFM
+2. #includes should be the full source tree path, eg.
+ #include "../core/UrlBuilder.h"
diff --git a/thirdparty/liblastfm2/admin/lastfm.h.rb b/thirdparty/liblastfm2/admin/lastfm.h.rb
new file mode 100755
index 000000000..1582b7086
--- /dev/null
+++ b/thirdparty/liblastfm2/admin/lastfm.h.rb
@@ -0,0 +1,5 @@
+#!/usr/bin/ruby
+f = File.new(ARGV[0], "w")
+Dir["_include/lastfm/*"].each do |h|
+ f.write %Q{#include "lastfm/#{File.basename h}"\n}
+end
\ No newline at end of file
diff --git a/thirdparty/liblastfm2/admin/platform.rb b/thirdparty/liblastfm2/admin/platform.rb
new file mode 100644
index 000000000..54eda1db1
--- /dev/null
+++ b/thirdparty/liblastfm2/admin/platform.rb
@@ -0,0 +1,101 @@
+#
+# platform.rb: naive platform detection for Ruby
+# author: Matt Mower
+#
+
+# == Platform
+#
+# Platform is a simple module which parses the Ruby constant
+# RUBY_PLATFORM and works out the OS, it's implementation,
+# and the architecture it's running on.
+#
+# The motivation for writing this was coming across a case where
+#
+# +if RUBY_PLATFORM =~ /win/+
+#
+# didn't behave as expected (i.e. on powerpc-darwin-8.1.0)
+#
+# It is hoped that providing a library for parsing the platform
+# means that we can cover all the cases and have something which
+# works reliably 99% of the time.
+#
+# Please report any anomalies or new combinations to the author(s).
+#
+# == Use
+#
+# require "platform"
+#
+# defines
+#
+# Platform::OS (:unix,:win32,:vms,:os2)
+# Platform::IMPL (:macosx,:linux,:mswin)
+# Platform::ARCH (:powerpc,:x86,:alpha)
+#
+# if an unknown configuration is encountered any (or all) of
+# these constant may have the value :unknown.
+#
+# To display the combination for your setup run
+#
+# ruby platform.rb
+#
+module Platform
+
+ if RUBY_PLATFORM =~ /darwin/i
+ OS = :unix
+ IMPL = :macosx
+ elsif RUBY_PLATFORM =~ /linux/i
+ OS = :unix
+ IMPL = :linux
+ elsif RUBY_PLATFORM =~ /freebsd/i
+ OS = :unix
+ IMPL = :freebsd
+ elsif RUBY_PLATFORM =~ /netbsd/i
+ OS = :unix
+ IMPL = :netbsd
+ elsif RUBY_PLATFORM =~ /mswin/i
+ OS = :win32
+ IMPL = :mswin
+ elsif RUBY_PLATFORM =~ /cygwin/i
+ OS = :win32
+ IMPL = :mswin
+ elsif RUBY_PLATFORM =~ /mingw/i
+ OS = :win32
+ IMPL = :mingw
+ elsif RUBY_PLATFORM =~ /bccwin/i
+ OS = :win32
+ IMPL = :bccwin
+ elsif RUBY_PLATFORM =~ /wince/i
+ OS = :win32
+ IMPL = :wince
+ elsif RUBY_PLATFORM =~ /vms/i
+ OS = :vms
+ IMPL = :vms
+ elsif RUBY_PLATFORM =~ /os2/i
+ OS = :os2
+ IMPL = :os2 # maybe there is some better choice here?
+ else
+ OS = :unknown
+ IMPL = :unknown
+ end
+
+ # whither AIX, SOLARIS, and the other unixen?
+
+ if RUBY_PLATFORM =~ /(i\d86)/i
+ ARCH = :x86
+ elsif RUBY_PLATFORM =~ /ia64/i
+ ARCH = :ia64
+ elsif RUBY_PLATFORM =~ /powerpc/i
+ ARCH = :powerpc
+ elsif RUBY_PLATFORM =~ /alpha/i
+ ARCH = :alpha
+ else
+ ARCH = :unknown
+ end
+
+ # What about AMD, Turion, Motorola, etc..?
+
+end
+
+if __FILE__ == $0
+ puts "Platform OS=#{Platform::OS}, IMPL=#{Platform::IMPL}, ARCH=#{Platform::ARCH}"
+end
diff --git a/thirdparty/liblastfm2/admin/qpp b/thirdparty/liblastfm2/admin/qpp
new file mode 100755
index 000000000..a99c5ce34
--- /dev/null
+++ b/thirdparty/liblastfm2/admin/qpp
@@ -0,0 +1,79 @@
+#!/usr/bin/ruby
+# Usage examples:
+# qpp foo.pro => ./_file.qmake
+# qpp foo/bar/ => ./_file.qmake
+#
+cwd = File.dirname( __FILE__ )
+require 'find'
+require 'ftools'
+require "#{cwd}/platform.rb"
+
+def find_sources()
+ Find.find( '.' ) do |path|
+ if File.directory?( path )
+ excludes = ['.svn', 'tests', '_build', 'contrib']
+ case Platform::IMPL
+ when :macosx then excludes << 'win' << 'linux'
+ when :mswin, :cygwin then excludes << 'mac' << 'linux'
+ when :linux, :freebsd then excludes << 'win' << 'mac'
+ else excludes << 'win' << 'mac' << 'linux'
+ end
+ Find.prune if excludes.include?( File.basename( path ) ) or (path != "." and not Dir["#{path}/*.pro"].empty? )
+ elsif File.file?( path )
+ case Platform::IMPL
+ when :macosx then next if /_(linux|win)\.(cpp|h)$/.match( path )
+ when :mswin, :cygwin then next if /_(mac|linux)\.(cpp|h)$/.match( path )
+ when :linux, :freebsd then next if /_(mac|win)\.(cpp|h)$/.match( path )
+ end
+ yield( path, File.extname( path ) ) unless File.basename(path) == 'EXAMPLE.cpp'
+ end
+ end
+end
+
+########################################################################### impl
+sources = Array.new
+headers = Array.new
+forms = Array.new
+resources = Array.new
+
+abort "usage: qpp file.pro.in" unless File.file? ARGV[0]
+
+File.open( ARGV[0] ).each_line do |line|
+ line.chomp!
+
+ matches = /^\s*TEMPLATE += (.*)$/.match( line )
+ if !matches.nil?
+ exit if matches[1].downcase == 'subdirs'
+ end
+
+ matches = /^\s*VERSION += +((\d\.){0,2}\d)/.match( line )
+ if !matches.nil? && !File.file?( "_version.h" )
+ File.open( "_version.h", 'w' ) { |f| f.write( "#define VERSION \"#{matches[1]}\"\n" ) }
+ end
+end
+
+Dir.chdir File.dirname(ARGV[0]) do
+ find_sources do |path, ext|
+ path.sub!( /^.\//, '' )
+ case ext
+ when ".h" then headers << path
+ when ".ui" then forms << path
+ when ".qrc" then resources << path
+ when ".cpp" then sources << path
+ when ".mm" then sources << path if Platform::IMPL == :macosx
+ when ".m" then sources << path if Platform::IMPL == :macosx
+ end
+ end
+end
+
+def write_section( section, array )
+ return if array.empty?
+ print "#{section} +="
+ array.each { |path| print " \\\n\t#{path}" }
+ puts
+end
+
+write_section( "SOURCES", sources )
+write_section( "HEADERS", headers )
+write_section( "FORMS", forms )
+write_section( "RESOURCES", resources )
diff --git a/thirdparty/liblastfm2/admin/utils.rb b/thirdparty/liblastfm2/admin/utils.rb
new file mode 100644
index 000000000..ddcb01917
--- /dev/null
+++ b/thirdparty/liblastfm2/admin/utils.rb
@@ -0,0 +1,40 @@
+cwd = File.dirname( __FILE__ )
+require "#{cwd}/platform.rb"
+
+def h(s, n)
+ case Platform::IMPL
+ when :mswin
+ puts '==> '+s
+ else
+ puts "\033[0;#{n}m==>\033[0;0;1m #{s} \033[0;0m"
+ end
+end
+
+def h1 s
+ h(s, 34)
+end
+
+def h2 s
+ h(s, 33)
+ yield
+end
+
+def qmake_env(env, qenv)
+ env=Array.new(1,env) if env.instance_of? String
+ values=Array.new
+ env.each { |x| values << ENV[x] if ENV[x] }
+ if values.size > 0
+ "#{qenv} = #{values.join(' ')}\n"
+ else
+ nil
+ end
+end
+
+class PkgConfigNotFound < RuntimeError; end
+class PkgNotFound < RuntimeError; end
+
+def pkgconfig pkg, prettyname
+ system "pkg-config --exists '#{pkg}'"
+ raise PkgConfigNotFound if $? == 127
+ raise PkgNotFound.new(prettyname) if $? != 0
+end
\ No newline at end of file
diff --git a/thirdparty/liblastfm2/admin/which_qmake.rb b/thirdparty/liblastfm2/admin/which_qmake.rb
new file mode 100644
index 000000000..f621de3e9
--- /dev/null
+++ b/thirdparty/liblastfm2/admin/which_qmake.rb
@@ -0,0 +1,38 @@
+require "#{File.dirname __FILE__}/platform.rb"
+
+class QMakeNotFound < RuntimeError; end
+class QMakeTooOld < RuntimeError; end
+
+def which_qmake
+ args = '-v'
+ args += ' 2> /dev/null' unless Platform::IMPL == :mswin
+
+ versions = Hash.new
+ ['qmake','qmake-qt4'].each do |qmake|
+ begin
+ /^Using Qt version (\d\.\d\.\d)(-(.+))?/.match( `#{qmake} #{args}` )
+ rescue
+ end
+ versions[qmake] = $1 unless $1.nil?
+ end
+
+ raise QMakeNotFound if versions.empty?
+
+ versions.each do |key, v|
+ i = 1
+ j = 0
+ v.split( '.' ).reverse.each {|n| j += (n.to_i * i); i *= 100}
+ versions[key] = j
+ end
+
+ versions.sort {|a,b| a[1]<=>b[1]}
+
+ versions.each do |k, v|
+ if v >= 40400
+ return k
+ end
+ raise QMakeTooOld
+ end
+end
+
+puts which_qmake if __FILE__ == $0
\ No newline at end of file
diff --git a/thirdparty/liblastfm2/cmake/modules/FindLibFFTW3.cmake b/thirdparty/liblastfm2/cmake/modules/FindLibFFTW3.cmake
new file mode 100644
index 000000000..fa6419a2b
--- /dev/null
+++ b/thirdparty/liblastfm2/cmake/modules/FindLibFFTW3.cmake
@@ -0,0 +1,45 @@
+# This file is copyrighted under the BSD-license for buildsystem files of KDE
+# copyright 2010, Patrick von Reth
+#
+#
+# - Try to find the LIBFFTW3 library
+# Once done this will define
+#
+# LIBFFTW3_FOUND Set to TRUE if LIBFFTW3 librarys and include directory is found
+# LIBFFTW3_INCLUDE_DIR The libfftw3 include directory
+# LIBFFTW3_LIBRARY The libfftw3 librarys
+
+if(NOT LIBFFTW3_PRECISION)
+ message(STATUS "Searching for LIBFFTW3, using default precision float")
+ set(LIBFFTW3_PRECISION FLOAT)
+endif(NOT LIBFFTW3_PRECISION)
+
+find_path(LIBFFTW3_INCLUDE_DIR fftw3.h)
+
+if(LIBFFTW3_PRECISION STREQUAL FLOAT)
+ set(LIBFFTW3_PRECISION_SUFFIX f)
+endif(LIBFFTW3_PRECISION STREQUAL FLOAT)
+
+if(LIBFFTW3_PRECISION STREQUAL DOUBLE)
+ set(LIBFFTW3_PRECISION_SUFFIX "")
+endif(LIBFFTW3_PRECISION STREQUAL DOUBLE)
+
+if(LIBFFTW3_PRECISION STREQUAL LDOUBLE)
+ set(LIBFFTW3_PRECISION_SUFFIX l)
+endif(LIBFFTW3_PRECISION STREQUAL LDOUBLE)
+
+find_library(LIBFFTW3_LIBRARY NAMES fftw3${LIBFFTW3_PRECISION_SUFFIX} libfftw3${LIBFFTW3_PRECISION_SUFFIX}-3 fftw3${LIBFFTW3_PRECISION_SUFFIX}-3)
+
+if(FIND_LIBFFTW3_VERBOSE)
+ message(STATUS
+ "LIBFFTW3_PRECISION ${LIBFFTW3_PRECISION}, searched for fftw3${LIBFFTW3_PRECISION_SUFFIX} libfftw3${LIBFFTW3_PRECISION_SUFFIX}-3 fftw3${LIBFFTW3_PRECISION_SUFFIX}-3
+ and found ${LIBFFTW3_LIBRARY}"
+ )
+endif(FIND_LIBFFTW3_VERBOSE)
+
+if(LIBFFTW3_LIBRARY AND LIBFFTW3_INCLUDE_DIR)
+ set(LIBFFTW3_FOUND TRUE)
+ message(STATUS "Found libfftw3 ${LIBFFTW3_LIBRARY}")
+else(LIBFFTW3_LIBRARY AND LIBFFTW3_PLUGIN_PATH)
+ message(STATUS "Could not find libfftw3, get it http://www.fftw.org/")
+endif(LIBFFTW3_LIBRARY AND LIBFFTW3_INCLUDE_DIR)
diff --git a/thirdparty/liblastfm2/cmake/modules/FindLibSamplerate.cmake b/thirdparty/liblastfm2/cmake/modules/FindLibSamplerate.cmake
new file mode 100644
index 000000000..d77536b2f
--- /dev/null
+++ b/thirdparty/liblastfm2/cmake/modules/FindLibSamplerate.cmake
@@ -0,0 +1,22 @@
+# This file is copyrighted under the BSD-license for buildsystem files of KDE
+# copyright 2010, Patrick von Reth
+#
+#
+# - Try to find the libsamplerate library
+# Once done this will define
+#
+# LIBSAMPLERATE_FOUND Set to TRUE if libsamplerate librarys and include directory is found
+# LIBSAMPLERATE_LIBRARY The libsamplerate librarys
+# LIBSAMPLERATE_INCLUDE_DIR The libsamplerate include directory
+
+
+find_library(LIBSAMPLERATE_LIBRARY NAMES samplerate libsamplerate-0 samplerate-0)
+
+find_path(LIBSAMPLERATE_INCLUDE_DIR samplerate.h)
+
+if(LIBSAMPLERATE_LIBRARY AND LIBSAMPLERATE_INCLUDE_DIR)
+ set(LIBSAMPLERATE_FOUND TRUE)
+ message(STATUS "Found libsamplerate ${LIBSAMPLERATE_LIBRARY}")
+else(LIBSAMPLERATE_LIBRARY AND LIBSAMPLERATE_PLUGIN_PATH)
+ message(STATUS "Could not find libsamplerate, get it http://www.mega-nerd.com/SRC/")
+endif(LIBSAMPLERATE_LIBRARY AND LIBSAMPLERATE_INCLUDE_DIR)
diff --git a/thirdparty/liblastfm2/configure b/thirdparty/liblastfm2/configure
new file mode 100755
index 000000000..757edcbc1
--- /dev/null
+++ b/thirdparty/liblastfm2/configure
@@ -0,0 +1,127 @@
+#!/usr/bin/ruby
+if ARGV.include? '--help'
+ puts "usage: ./configure [--prefix ] [--release] [--no-strip] [--skip-checks]"
+ exit
+end
+
+cwd = File.dirname( __FILE__ )
+require "#{cwd}/admin/platform.rb"
+require "#{cwd}/admin/which_qmake.rb"
+require "#{cwd}/admin/utils.rb"
+
+begin
+ IO.read("#{cwd}/src/global.h") =~ /LASTFM_VERSION_STRING\s+"((\d\.)*\d)"/
+ abort "Couldn't determine our version!" if $1.nil?
+ LFM_VERSION=$1
+ ENV['LFM_VERSION']=LFM_VERSION
+
+ h1 "Configuring liblastfm-#{LFM_VERSION}..."
+
+ unless ARGV.include? '--skip-checks'
+ $qmake=which_qmake
+ pkgconfig 'samplerate', 'libsamplerate'
+ pkgconfig 'fftw3f', 'fftw'
+ puts 'Using '+`which #{$qmake}` unless Platform::IMPL == :mswin
+ else
+ $qmake='qmake'
+ end
+
+ h2 'Determining installation prefix' do
+ if ARGV.include? '--prefix'
+ n=ARGV.index '--prefix'
+ ENV['LFM_PREFIX'] = ARGV[n+1]
+ end
+ ENV['LFM_PREFIX'] = '/usr/local' if ENV['LFM_PREFIX'].nil?
+ if File.exists? ENV['LFM_PREFIX'] and !File.directory? ENV['LFM_PREFIX']
+ abort "Installation prefix exists but isn't a directory: "+ENV['LFM_PREFIX']
+ end
+ puts "Will install to: "+ENV['LFM_PREFIX']
+ end
+
+ h1 'Generating Build System'
+
+ h2 'Generating .qmake.env' do
+ f = File.new("#{cwd}/.qmake.env", 'w')
+ f.write qmake_env('CC', 'QMAKE_CC')
+ f.write qmake_env('CXX', 'QMAKE_CXX')
+ f.write qmake_env('LDFLAGS', 'QMAKE_LFLAGS_RELEASE')
+ f.write qmake_env(['CFLAGS', 'CPPFLAGS'], 'QMAKE_CFLAGS_RELEASE')
+ f.write qmake_env(['CXXFLAGS', 'CPPFLAGS'], 'QMAKE_CXXFLAGS_RELEASE')
+ f.close
+ end unless Platform::IMPL == :mswin
+
+ h2 "Running qpp..." do
+ ['src/lastfm.pro','src/fingerprint/fingerprint.pro'].each do |p|
+ d="#{cwd}/#{File.dirname p}"
+ f=File.new "#{d}/_files.qmake", 'w'
+ f.write `ruby admin/qpp #{p}`
+ # on Windows VERSION produces lastfm0.dll, the 0 breaks the build
+ f.puts "VERSION = #{LFM_VERSION}" unless Platform::OS == :win32
+ end
+ end
+
+ h2 "Configuring qmake..." do
+ args=Array.new
+ args << '-spec macx-g++' if Platform::IMPL == :macosx
+ if ARGV.include? '--release'
+ args << '-config release'
+ args << '"CONFIG += app_bundle"' if Platform::IMPL == :macosx and ARGV.include? '--bundle'
+ else
+ args << '-config debug'
+ end
+ if ARGV.include? '--no-strip'
+ args << '"CONFIG += nostrip"'
+ end
+ ENV['LFM_QMAKE'] = "#{$qmake} #{args.join(' ')}"
+ end
+
+ h2 "Generating Makefile..." do
+ hs = Array.new
+ hs << 'global.h'
+ hs << 'core/UrlBuilder.h' << 'core/XmlQuery.h' << 'core/misc.h'
+ hs << 'fingerprint/Fingerprint.h' << 'fingerprint/FingerprintableSource.h'
+ hs << 'radio/RadioStation.h' << 'radio/RadioTuner.h'
+ hs << 'scrobble/Audioscrobbler.h' << 'scrobble/ScrobblePoint.h' << 'scrobble/ScrobbleCache.h'
+ hs << 'types/AbstractType.h' << 'types/Track.h' << 'types/Mbid.h' << 'types/Artist.h' << 'types/Album.h' << 'types/FingerprintId.h' << 'types/Playlist.h' << 'types/Tag.h' << 'types/User.h' << 'types/Xspf.h'
+ hs << 'ws/ws.h' << 'ws/InternetConnectionMonitor.h' << 'ws/NetworkAccessManager.h'
+
+ File.new("#{cwd}/Makefile", 'w').write `ruby admin/Makefile.rb #{hs.join(' ')}`
+ end
+
+ case Platform::IMPL
+ when :mswin then make='nmake all'
+ else make='make' # NOTE only tested with GNU make, sorry :(
+ end
+
+ puts
+ puts "Good, your configure is finished! Now type: #{make}"
+
+rescue QMakeTooOld
+ puts <<-sput
+
+ Your version of Qt seems to be too old, we require Qt 4.4 or above.
+
+ It is possible you have Qt3 and Qt4 both installed. Locate your Qt4
+ installation and ensure it is placed first in the path, eg:
+
+ PATH=/opt/qt4/bin:\$PATH ./configure
+
+ sput
+ exit 1
+rescue QMakeNotFound
+ puts "Sorry, qmake was not found, is Qt4 installed?"
+ exit 2
+rescue PkgNotFound => e
+ puts <<-sput
+
+ Sorry, we couldn't find #{e}.
+ You can try to compile anyway by forcing configure to finish:
+
+ ./configure --skip-checks
+
+ sput
+ exit 3
+rescue PkgConfigNotFound
+ puts "Sorry, pkg-config could not be found. You should install it!"
+ exit 4
+end
diff --git a/thirdparty/liblastfm2/demos/demo1.cpp b/thirdparty/liblastfm2/demos/demo1.cpp
new file mode 100644
index 000000000..b0fd8df99
--- /dev/null
+++ b/thirdparty/liblastfm2/demos/demo1.cpp
@@ -0,0 +1,95 @@
+/*
+ This software is in the public domain, furnished "as is", without technical
+ support, and with no warranty, express or implied, as to its usefulness for
+ any purpose.
+*/
+#include // this includes everything in liblastfm, you may prefer
+#include // to just include what you need with your project. Still
+#include // we've given you the option.
+#include
+#include
+
+class ArtistList : public QListWidget
+{
+ Q_OBJECT
+
+ QPointer reply;
+ QString artist;
+
+public:
+ ArtistList()
+ {
+ connect( this,
+ SIGNAL(itemActivated( QListWidgetItem* )),
+ SLOT(onItemActivated( QListWidgetItem* )) );
+ }
+
+ void getSimilar( const QString& artist )
+ {
+ this->artist = artist;
+ setWindowTitle( "Loading " + artist + "..." );
+
+ // deleting a reply cancels the request and disconnects all signals
+ delete reply;
+ reply = lastfm::Artist( artist ).getSimilar();
+ connect( reply, SIGNAL(finished()), SLOT(onGotSimilar()) );
+ }
+
+private slots:
+ void onGotSimilar()
+ {
+ QNetworkReply* r = static_cast(sender());
+ // always enclose retrieval functions in a try block, as they will
+ // throw if they can't parse the data
+ try
+ {
+ // you decode the response using the equivalent static function
+ QMap artists = lastfm::Artist::getSimilar( r );
+
+ clear();
+
+ // we iterate backwards because best match is last because the map
+ // sorts itself by key
+ QStringListIterator i( artists.values() );
+ i.toBack();
+ while (i.hasPrevious())
+ addItem( i.previous() );
+
+ setWindowTitle( artist );
+ }
+ catch (std::runtime_error& e)
+ {
+ // if getSimilar() failed to parse the QNetworkReply, then e will
+ // be of type lastfm::ws::ParseError, which derives
+ // std::runtime_error
+ qWarning() << e.what();
+ }
+ }
+
+ void onItemActivated( QListWidgetItem* item )
+ {
+ getSimilar( item->text() );
+ }
+};
+
+
+int main( int argc, char** argv )
+{
+ QApplication app( argc, argv );
+ app.setApplicationName( "liblastfm" ); // used to generate UserAgent
+
+ // all you need for non-authenticated webservices is your API key
+ // this one is a public one, it can only do artist.getSimilar calls, so
+ // I suggest you don't use it :P
+ lastfm::ws::ApiKey = "b25b959554ed76058ac220b7b2e0a026";
+
+ ArtistList artists;
+ artists.getSimilar( "nirvana" );
+ artists.resize( 300, 400 ); // Qt picks truly asanine default sizes for its widgets
+ artists.show();
+
+ return app.exec();
+}
+
+
+#include "demo1.moc"
diff --git a/thirdparty/liblastfm2/demos/demo2.cpp b/thirdparty/liblastfm2/demos/demo2.cpp
new file mode 100644
index 000000000..53c9a7ec9
--- /dev/null
+++ b/thirdparty/liblastfm2/demos/demo2.cpp
@@ -0,0 +1,114 @@
+/*
+ This software is in the public domain, furnished "as is", without technical
+ support, and with no warranty, express or implied, as to its usefulness for
+ any purpose.
+*/
+#include
+#include
+
+
+struct MyCoreApp : QCoreApplication
+{
+ Q_OBJECT
+
+public:
+ MyCoreApp( int& argc, char**& argv ) : QCoreApplication( argc, argv )
+ {}
+
+private slots:
+ void onWsError( lastfm::ws::Error e )
+ {
+ // QNetworkReply will invoke this slot on application level errors
+ // mostly this is only stuff like Ws::InvalidSessionKey and
+ // Ws::InvalidApiKey
+ qWarning() << e;
+ }
+};
+
+
+int main( int argc, char** argv )
+{
+ MyCoreApp app( argc, argv );
+ // this is used to generate the UserAgent for webservice requests
+ // please set it to something sensible in your application
+ app.setApplicationName( "liblastfm" );
+
+////// you'll need to fill these in for this demo to work
+ lastfm::ws::Username =
+ lastfm::ws::ApiKey =
+ lastfm::ws::SharedSecret =
+ QString password =
+
+////// Usually you never have to construct an Last.fm WS API call manually
+ // eg. Track.getTopTags() just returns a QNetworkReply* but authentication is
+ // different.
+ // We're using getMobileSession here as we're a console app, but you
+ // should use getToken if you can as the user will be presented with a
+ // route that feels my trustworthy to them than entering their password
+ // into some random app they just downloaded... ;)
+ QMap params;
+ params["method"] = "auth.getMobileSession";
+ params["username"] = lastfm::ws::Username;
+ params["authToken"] = lastfm::md5( (lastfm::ws::Username + lastfm::md5( password.toUtf8() )).toUtf8() );
+ QNetworkReply* reply = lastfm::ws::post( params );
+
+ // never do this when an event loop is running it's a real HACK
+ QEventLoop loop;
+ loop.connect( reply, SIGNAL(finished()), SLOT(quit()) );
+ loop.exec();
+
+ try
+ {
+ ////// Usually there is a convenience function to decode the output from
+ // ws calls too, but again, authentication is different. We think you
+ // need to handle it yourselves :P Also conveniently it means you
+ // can learn more about what our webservices return, eg. this service
+ // will return an XML document like this:
+ //
+ //
+ //
+ // mxcl
+ // d580d57f32848f5dcf574d1ce18d78b2
+ // 1
+ //
+ //
+ //
+ // If status is not "ok" then this function throws
+ lastfm::XmlQuery const lfm = lastfm::ws::parse( reply );
+
+ // replace username; because eg. perhaps the user typed their
+ // username with the wrong case
+ lastfm::ws::Username = lfm["session"]["name"].text();
+
+ // we now have a session key, you should save this, forever! Really.
+ // DO NOT AUTHENTICATE EVERY TIME THE APP STARTS! You only have to do
+ // this once. Or again if the user deletes your key on the site. If
+ // that happens you'll get notification to your onWsError() function,
+ // see above.
+ lastfm::ws::SessionKey = lfm["session"]["key"].text();
+
+ qDebug() << "sk:" << lastfm::ws::SessionKey;
+
+ ////// because the SessionKey is now set, the AuthenticatedUser class will
+ // work. And we can call authenticated calls
+ QNetworkReply* reply = lastfm::AuthenticatedUser().getRecommendedArtists();
+
+ // again, you shouldn't do this.. ;)
+ QEventLoop loop;
+ loop.connect( reply, SIGNAL(finished()), SLOT(quit()) );
+ loop.exec();
+
+ // yay, a list rec'd artists to stderr :)
+ qDebug() << lastfm::Artist::list( reply );
+ }
+ catch (std::runtime_error& e)
+ {
+ // lastfm::ws::parse() can throw lastfm::ws::ParseError, this
+ // exception derives std::runtime_error
+ qWarning() << e.what();
+ return 1;
+ }
+}
+
+
+#include "demo2.moc"
diff --git a/thirdparty/liblastfm2/demos/demo3.cpp b/thirdparty/liblastfm2/demos/demo3.cpp
new file mode 100644
index 000000000..2bf3c1db3
--- /dev/null
+++ b/thirdparty/liblastfm2/demos/demo3.cpp
@@ -0,0 +1,63 @@
+/*
+ This software is in the public domain, furnished "as is", without technical
+ support, and with no warranty, express or implied, as to its usefulness for
+ any purpose.
+*/
+#include
+#include
+#include
+#include "src/_version.h"
+
+
+struct MyCoreApp : QCoreApplication
+{
+ Q_OBJECT
+
+public:
+ MyCoreApp( int& argc, char** argv ) : QCoreApplication( argc, argv )
+ {}
+
+public slots:
+ void onStatus( int status )
+ {
+ qDebug() << lastfm::Audioscrobbler::Status(status);
+ }
+};
+
+
+int main( int argc, char** argv )
+{
+ // all 6 of these lines are REQUIRED in order to scrobble
+ // this demo requires you to fill in the blanks as well...
+ lastfm::ws::Username =
+ lastfm::ws::ApiKey =
+ lastfm::ws::SharedSecret =
+ lastfm::ws::SessionKey = // you need to auth to get this... try demo2
+ QCoreApplication::setApplicationName( "liblastfm" );
+ QCoreApplication::setApplicationVersion( VERSION );
+
+ MyCoreApp app( argc, argv );
+
+ lastfm::MutableTrack t;
+ t.setArtist( "Max Howell" );
+ t.setTitle( "I Told You Not To Trust Me With Your Daughter" );
+ t.setDuration( 30 );
+ t.stamp(); //sets track start time
+
+ lastfm::Audioscrobbler as( "ass" );
+ as.nowPlaying( t );
+ // Audioscrobbler will submit whatever is in the cache when you call submit.
+ // And the cache is persistent between sessions. So you should cache at the
+ // scrobble point usually, not before
+ as.cache( t );
+
+ //FIXME I don't get it, but the timer never triggers! pls fork and fix!
+ QTimer::singleShot( 31*1000, &as, SLOT(submit()) );
+
+ app.connect( &as, SIGNAL(status(int)), SLOT(onStatus(int)) );
+
+ return app.exec();
+}
+
+
+#include "demo3.moc"
diff --git a/thirdparty/liblastfm2/demos/demos.pro b/thirdparty/liblastfm2/demos/demos.pro
new file mode 100644
index 000000000..356edff74
--- /dev/null
+++ b/thirdparty/liblastfm2/demos/demos.pro
@@ -0,0 +1,3 @@
+QT = core gui network xml
+LIBS += -llastfm -L$$DESTDIR
+SOURCES = demo1.cpp # change to demo2.cpp (etc.) to compile that demo
diff --git a/thirdparty/liblastfm2/src/CMakeLists.txt b/thirdparty/liblastfm2/src/CMakeLists.txt
new file mode 100644
index 000000000..c7d569442
--- /dev/null
+++ b/thirdparty/liblastfm2/src/CMakeLists.txt
@@ -0,0 +1,114 @@
+cmake_minimum_required(VERSION 2.6)
+
+# Macro to copy and rename headers
+macro(copy_header from to)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/${from}
+ ${CMAKE_CURRENT_BINARY_DIR}/lastfm/${to}
+ COPY_ONLY
+ )
+ install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/lastfm/${to}
+ DESTINATION include/lastfm/
+ )
+endmacro(copy_header)
+
+# Copy headers
+copy_header(core/misc.h misc.h)
+copy_header(core/XmlQuery.h XmlQuery)
+copy_header(core/UrlBuilder.h UrlBuilder)
+copy_header(global.h global.h)
+copy_header(radio/RadioTuner.h RadioTuner)
+copy_header(radio/RadioStation.h RadioStation)
+copy_header(scrobble/Audioscrobbler.h Audioscrobbler)
+copy_header(scrobble/ScrobbleCache.h ScrobbleCache)
+copy_header(scrobble/ScrobblePoint.h ScrobblePoint)
+copy_header(types/AbstractType.h AbstractType)
+copy_header(types/Album.h Album)
+copy_header(types/Artist.h Artist)
+copy_header(types/FingerprintId.h FingerprintId)
+copy_header(types/Mbid.h Mbid)
+copy_header(types/Playlist.h Playlist)
+copy_header(types/Tag.h Tag)
+copy_header(types/Track.h Track)
+copy_header(types/User.h User)
+copy_header(types/User.h UserList)
+copy_header(types/Xspf.h Xspf)
+copy_header(ws/ws.h ws.h)
+copy_header(ws/InternetConnectionMonitor.h InternetConnectionMonitor)
+copy_header(ws/NetworkAccessManager.h NetworkAccessManager)
+
+include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
+
+set(SOURCES
+ scrobble/ScrobbleCache.cpp
+ scrobble/Audioscrobbler.cpp
+ types/FingerprintId.cpp
+ types/Artist.cpp
+ types/Tag.cpp
+ types/Track.cpp
+ types/User.cpp
+ types/Xspf.cpp
+ types/Album.cpp
+ types/Playlist.cpp
+ types/Mbid.cpp
+ radio/RadioTuner.cpp
+ radio/RadioStation.cpp
+ core/UrlBuilder.cpp
+ core/misc.cpp
+ core/XmlQuery.cpp
+ ws/NetworkAccessManager.cpp
+ ws/ws.cpp
+ ws/InternetConnectionMonitor.cpp
+ ws/NetworkConnectionMonitor.cpp
+)
+
+set(MOC_HEADERS
+ scrobble/Audioscrobbler.h
+ types/Track.h
+ radio/RadioTuner.h
+ ws/NetworkConnectionMonitor.h
+ ws/InternetConnectionMonitor.h
+ ws/NetworkAccessManager.h
+)
+
+if(UNIX)
+ if(APPLE)
+ set(SOURCES ${SOURCES} ws/mac/MNetworkConnectionMonitor_mac.cpp)
+ set(MOC_HEADERS ${MOC_HEADERS} ws/mac/MNetworkConnectionMonitor.h)
+ else(APPLE)
+ set(SOURCES ${SOURCES} ws/linux/LNetworkConnectionMonitor_linux.cpp)
+ set(MOC_HEADERS ${MOC_HEADERS} ws/linux/LNetworkConnectionMonitor.h)
+ endif(APPLE)
+endif(UNIX)
+if(WIN32)
+ set(SOURCES ${SOURCES} ws/win/WNetworkConnectionMonitor_win.cpp)
+ set(MOC_HEADERS ${MOC_HEADERS} ws/win/WNetworkConnectionMonitor.h)
+endif(WIN32)
+
+qt4_wrap_cpp(MOC_SOURCES ${MOC_HEADERS})
+
+add_library(tomahawk_lastfm2 SHARED
+ ${SOURCES}
+ ${MOC_SOURCES}
+)
+
+target_link_libraries(tomahawk_lastfm2
+ ${QT_LIBRARIES}
+ ${QT_QTDBUS_LIBRARY}
+)
+
+set_target_properties(tomahawk_lastfm2 PROPERTIES COMPILE_FLAGS "-DLASTFM_OHAI_QMAKE" )
+
+if(APPLE)
+ target_link_libraries(tomahawk_lastfm2
+ /System/Library/Frameworks/CoreFoundation.framework
+ /System/Library/Frameworks/SystemConfiguration.framework
+ )
+endif(APPLE)
+
+install(TARGETS tomahawk_lastfm2
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib${LIB_SUFFIX}
+ ARCHIVE DESTINATION lib${LIB_SUFFIX}
+)
diff --git a/thirdparty/liblastfm2/src/core/README b/thirdparty/liblastfm2/src/core/README
new file mode 100644
index 000000000..b725eac20
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/README
@@ -0,0 +1,8 @@
+Files in lastfm-core are basically extensions to fundamental Qt classes.
+They may be useful to you, but mainly they are here because they are useful to
+liblastfm in general.
+
+A lot of the time they are convenience functions that hopefully at some point
+Qt will make obsolete.
+
+
diff --git a/thirdparty/liblastfm2/src/core/UrlBuilder.cpp b/thirdparty/liblastfm2/src/core/UrlBuilder.cpp
new file mode 100644
index 000000000..0f451c7e5
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/UrlBuilder.cpp
@@ -0,0 +1,83 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include "UrlBuilder.h"
+#include
+#include
+
+
+QUrl
+lastfm::UrlBuilder::url() const
+{
+ QUrl url;
+ url.setScheme( "http" );
+ url.setHost( host() );
+ url.setEncodedPath( path );
+ return url;
+}
+
+
+QByteArray //static
+lastfm::UrlBuilder::encode( QString s )
+{
+ foreach (QChar c, QList() << '&' << '/' << ';' << '+' << '#' << '%')
+ if (s.contains( c ))
+ // the middle step may seem odd but this is what the site does
+ // eg. search for the exact string "Radiohead 2 + 2 = 5"
+ return QUrl::toPercentEncoding( s ).replace( "%20", "+" ).toPercentEncoding( "", "+" );;
+
+ return QUrl::toPercentEncoding( s.replace( ' ', '+' ), "+" );
+}
+
+
+QString //static
+lastfm::UrlBuilder::host( const QLocale& locale )
+{
+ switch (locale.language())
+ {
+ case QLocale::Portuguese: return "www.lastfm.com.br";
+ case QLocale::Turkish: return "www.lastfm.com.tr";
+ case QLocale::French: return "www.lastfm.fr";
+ case QLocale::Italian: return "www.lastfm.it";
+ case QLocale::German: return "www.lastfm.de";
+ case QLocale::Spanish: return "www.lastfm.es";
+ case QLocale::Polish: return "www.lastfm.pl";
+ case QLocale::Russian: return "www.lastfm.ru";
+ case QLocale::Japanese: return "www.lastfm.jp";
+ case QLocale::Swedish: return "www.lastfm.se";
+ case QLocale::Chinese: return "cn.last.fm";
+ default: return "www.last.fm";
+ }
+}
+
+
+QUrl //static
+lastfm::UrlBuilder::localize( QUrl url)
+{
+ url.setHost( url.host().replace( QRegExp("^(www.)?last.fm"), host() ) );
+ return url;
+}
+
+
+QUrl //static
+lastfm::UrlBuilder::mobilize( QUrl url )
+{
+ url.setHost( url.host().replace( QRegExp("^(www.)?last"), "m.last" ) );
+ return url;
+}
diff --git a/thirdparty/liblastfm2/src/core/UrlBuilder.h b/thirdparty/liblastfm2/src/core/UrlBuilder.h
new file mode 100644
index 000000000..42014537c
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/UrlBuilder.h
@@ -0,0 +1,69 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef LASTFM_URL_BUILDER_H
+#define LASTFM_URL_BUILDER_H
+
+#include
+#include
+#include
+#include
+
+
+namespace lastfm
+{
+ /** For building www.last.fm urls. We have special rules for encoding and that */
+ class LASTFM_DLLEXPORT UrlBuilder
+ {
+ QByteArray path;
+
+ public:
+ /** Careful, the base is not encoded at all, we assume it is ASCII!
+ * If you need it encoded at all you must use the slash function.
+ * eg. UrlBuilder( "user" ).slash( "mxcl" ) ==> http://last.fm/user/mxcl
+ */
+ UrlBuilder( const QString& base ) : path( '/' + base.toAscii() )
+ {}
+
+ UrlBuilder& slash( const QString& path ) { this->path += '/' + encode( path ); return *this; }
+
+ QUrl url() const;
+
+ /** www.last.fm becomes the local version, eg www.lastfm.de */
+ static QUrl localize( QUrl );
+ /** www.last.fm becomes m.last.fm, localisation is preserved */
+ static QUrl mobilize( QUrl );
+
+ /** Use this to URL encode any database item (artist, track, album). It
+ * internally calls UrlEncodeSpecialChars to double encode some special
+ * symbols according to the same pattern as that used on the website.
+ *
+ * &, /, ;, +, #
+ *
+ * Use for any urls that go to www.last.fm
+ * Do not use for ws.audioscrobbler.com
+ */
+ static QByteArray encode( QString );
+
+ /** returns eg. www.lastfm.de */
+ static QString host( const QLocale& = QLocale() );
+ };
+}
+
+#endif
diff --git a/thirdparty/liblastfm2/src/core/XmlQuery.cpp b/thirdparty/liblastfm2/src/core/XmlQuery.cpp
new file mode 100644
index 000000000..5c68aaa81
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/XmlQuery.cpp
@@ -0,0 +1,64 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include "XmlQuery.h"
+#include
+using lastfm::XmlQuery;
+
+
+XmlQuery::XmlQuery( const QByteArray& bytes )
+{
+ domdoc.setContent(bytes);
+ e = domdoc.documentElement();
+}
+
+
+XmlQuery
+XmlQuery::operator[]( const QString& name ) const
+{
+ QStringList parts = name.split( ' ' );
+ if (parts.size() >= 2)
+ {
+ QString tagName = parts[0];
+ parts = parts[1].split( '=' );
+ QString attributeName = parts.value( 0 );
+ QString attributeValue = parts.value( 1 );
+
+ foreach (XmlQuery e, children( tagName ))
+ if (e.e.attribute( attributeName ) == attributeValue)
+ return e;
+ }
+ XmlQuery xq( e.firstChildElement( name ), name.toUtf8().data() );
+ xq.domdoc = this->domdoc;
+ return xq;
+}
+
+
+QList
+XmlQuery::children( const QString& named ) const
+{
+ QList elements;
+ QDomNodeList nodes = e.elementsByTagName( named );
+ for (int x = 0; x < nodes.count(); ++x) {
+ XmlQuery xq( nodes.at( x ).toElement() );
+ xq.domdoc = this->domdoc;
+ elements += xq;
+ }
+ return elements;
+}
diff --git a/thirdparty/liblastfm2/src/core/XmlQuery.h b/thirdparty/liblastfm2/src/core/XmlQuery.h
new file mode 100644
index 000000000..a3c22d5c2
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/XmlQuery.h
@@ -0,0 +1,77 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef LASTFM_XMLQUERY_H
+#define LASTFM_XMLQUERY_H
+
+#include
+#include
+#include
+
+namespace lastfm
+{
+ /** Qt's XmlQuery implementation is totally unimpressive, so this is a
+ * hack that feels like jQuery */
+ class LASTFM_DLLEXPORT XmlQuery
+ {
+ QDomDocument domdoc;
+ QDomElement e;
+
+ public:
+ /** we assume the bytearray is an XML document, this object will then
+ * represent the documentElement of that document, eg. if this is a
+ * Last.fm webservice response:
+ *
+ * XmlQuery xq = lastfm::ws::parse(response);
+ * qDebug() << xq["artist"].text()
+ *
+ * Notice the lfm node is not referenced, that is because it is the
+ * document-element of the XML document.
+ */
+ XmlQuery( const QByteArray& );
+
+ XmlQuery( const QDomElement& e, const char* name = "" ) : e( e )
+ {
+ if (e.isNull()) qWarning() << "Expected node absent:" << name;
+ }
+
+ /** Selects a DIRECT child element, you can specify attributes like so:
+ *
+ * e["element"]["element attribute=value"].text();
+ */
+ XmlQuery operator[]( const QString& name ) const;
+ QString text() const { return e.text(); }
+ QString attribute( const QString& name ) const{ return e.attribute( name ); }
+
+ /** selects all children with specified name, recursively */
+ QList children( const QString& named ) const;
+
+ operator QDomElement() const { return e; }
+ };
+}
+
+inline QDebug operator<<( QDebug d, const lastfm::XmlQuery& xq )
+{
+ QString s;
+ QTextStream t( &s, QIODevice::WriteOnly );
+ QDomElement(xq).save( t, 2 );
+ return d << s;
+}
+
+#endif
diff --git a/thirdparty/liblastfm2/src/core/misc.cpp b/thirdparty/liblastfm2/src/core/misc.cpp
new file mode 100644
index 000000000..13b5d16af
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/misc.cpp
@@ -0,0 +1,205 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include "misc.h"
+#include
+#ifdef WIN32
+ #include
+#endif
+#ifdef Q_WS_MAC
+ #include
+#endif
+
+
+#ifdef Q_WS_MAC
+QDir
+lastfm::dir::bundle()
+{
+ // Trolltech provided example
+ CFURLRef appUrlRef = CFBundleCopyBundleURL( CFBundleGetMainBundle() );
+ CFStringRef macPath = CFURLCopyFileSystemPath( appUrlRef, kCFURLPOSIXPathStyle );
+ QString path = CFStringToQString( macPath );
+ CFRelease(appUrlRef);
+ CFRelease(macPath);
+ return QDir( path );
+}
+#endif
+
+
+static QDir dataDotDot()
+{
+#ifdef WIN32
+ if ((QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) == 0)
+ {
+ // Use this for non-DOS-based Windowses
+ char path[MAX_PATH];
+ HRESULT h = SHGetFolderPathA( NULL,
+ CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE,
+ NULL,
+ 0,
+ path );
+ if (h == S_OK)
+ return QString::fromLocal8Bit( path );
+ }
+ return QDir::home();
+
+#elif defined(Q_WS_MAC)
+
+ #define EIT( x ) { OSErr err = x; if (err != noErr) throw 1; }
+ try
+ {
+ short vRefNum = 0;
+ long dirId;
+ EIT( ::FindFolder( kOnAppropriateDisk,
+ kApplicationSupportFolderType,
+ kDontCreateFolder,
+ &vRefNum,
+ &dirId ) );
+
+ // Now we have a vRefNum and a dirID - but *not* an Unix-Path as string.
+ // Lets make one based from this:
+ FSSpec fsspec;
+ EIT( ::FSMakeFSSpec( vRefNum, dirId, NULL, &fsspec ) );
+
+ // ...and build an FSRef based on thes FSSpec.
+ FSRef fsref;
+ EIT( ::FSpMakeFSRef( &fsspec, &fsref ) );
+
+ // ...then extract the Unix Path as a C-String from the FSRef
+ unsigned char path[512];
+ EIT( ::FSRefMakePath( &fsref, path, 512 ) );
+
+ return QDir::homePath() + QString::fromUtf8( (char*)path );
+ }
+ catch (int)
+ {
+ return QDir::home().filePath( "Library/Application Support" );
+ }
+
+#elif defined(Q_WS_X11)
+ return QDir::home().filePath( ".local/share" );
+
+#else
+ return QDir::home();
+#endif
+}
+
+
+QDir
+lastfm::dir::runtimeData()
+{
+ return dataDotDot().filePath( "Last.fm" );
+}
+
+
+QDir
+lastfm::dir::logs()
+{
+#ifdef Q_WS_MAC
+ return QDir::home().filePath( "Library/Logs/Last.fm" );
+#else
+ return runtimeData();
+#endif
+}
+
+
+QDir
+lastfm::dir::cache()
+{
+#ifdef Q_WS_MAC
+ return QDir::home().filePath( "Library/Caches/Last.fm" );
+#else
+ return runtimeData().filePath( "cache" );
+#endif
+}
+
+
+#ifdef WIN32
+QDir
+lastfm::dir::programFiles()
+{
+ char path[MAX_PATH];
+
+ // TODO: this call is dependant on a specific version of shell32.dll.
+ // Need to degrade gracefully. Need to bundle SHFolder.exe with installer
+ // and execute it on install for this to work on Win98.
+ HRESULT h = SHGetFolderPathA( NULL,
+ CSIDL_PROGRAM_FILES,
+ NULL,
+ 0, // current path
+ path );
+
+ if (h != S_OK)
+ {
+ qCritical() << "Couldn't get Program Files dir. Possibly Win9x?";
+ return QDir();
+ }
+
+ return QString::fromLocal8Bit( path );
+}
+#endif
+
+#ifdef Q_WS_MAC
+CFStringRef
+lastfm::QStringToCFString( const QString &s )
+{
+ return CFStringCreateWithCharacters( 0, (UniChar*)s.unicode(), s.length() );
+}
+
+QByteArray
+lastfm::CFStringToUtf8( CFStringRef s )
+{
+ QByteArray result;
+
+ if (s != NULL)
+ {
+ CFIndex length;
+ length = CFStringGetLength( s );
+ length = CFStringGetMaximumSizeForEncoding( length, kCFStringEncodingUTF8 ) + 1;
+ char* buffer = new char[length];
+
+ if (CFStringGetCString( s, buffer, length, kCFStringEncodingUTF8 ))
+ result = QByteArray( buffer );
+ else
+ qWarning() << "CFString conversion failed.";
+
+ delete[] buffer;
+ }
+
+ return result;
+}
+#endif
+
+#if 0
+// this is a Qt implementation I found
+QString cfstring2qstring(CFStringRef str)
+{
+ if(!str)
+ return QString();
+
+ CFIndex length = CFStringGetLength(str);
+ if(const UniChar *chars = CFStringGetCharactersPtr(str))
+ return QString((QChar *)chars, length);
+ UniChar *buffer = (UniChar*)malloc(length * sizeof(UniChar));
+ CFStringGetCharacters(str, CFRangeMake(0, length), buffer);
+ QString ret((QChar *)buffer, length);
+ free(buffer);
+ return ret;
+}
+#endif
diff --git a/thirdparty/liblastfm2/src/core/misc.h b/thirdparty/liblastfm2/src/core/misc.h
new file mode 100644
index 000000000..3f41ec534
--- /dev/null
+++ b/thirdparty/liblastfm2/src/core/misc.h
@@ -0,0 +1,111 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef LASTFM_MISC_H
+#define LASTFM_MISC_H
+
+#include
+#include
+#include
+#include
+
+#ifdef Q_WS_MAC
+typedef const struct __CFString* CFStringRef;
+#endif
+
+namespace lastfm
+{
+ namespace dir
+ {
+ #ifdef Q_WS_WIN
+ LASTFM_DLLEXPORT QDir programFiles();
+ #endif
+ #ifdef Q_WS_MAC
+ LASTFM_DLLEXPORT QDir bundle();
+ #endif
+ LASTFM_DLLEXPORT QDir runtimeData();
+ LASTFM_DLLEXPORT QDir cache();
+ LASTFM_DLLEXPORT QDir logs();
+ }
+
+#ifdef Q_WS_MAC
+ LASTFM_DLLEXPORT QByteArray CFStringToUtf8( CFStringRef );
+ LASTFM_DLLEXPORT CFStringRef QStringToCFString( const QString& );
+ inline QString CFStringToQString( CFStringRef s );
+#endif
+
+ inline const char* platform()
+ {
+ #ifdef Q_WS_WIN
+ switch (QSysInfo::WindowsVersion)
+ {
+ case QSysInfo::WV_32s: return "Windows 3.1 with Win32s";
+ case QSysInfo::WV_95: return "Windows 95";
+ case QSysInfo::WV_98: return "Windows 98";
+ case QSysInfo::WV_Me: return "Windows Me";
+ case QSysInfo::WV_DOS_based: return "MS-DOS-based Windows";
+
+ case QSysInfo::WV_NT: return "Windows NT";
+ case QSysInfo::WV_2000: return "Windows 2000";
+ case QSysInfo::WV_XP: return "Windows XP";
+ case QSysInfo::WV_2003: return "Windows Server 2003";
+ case QSysInfo::WV_VISTA: return "Windows Vista";
+ case QSysInfo::WV_NT_based: return "NT-based Windows";
+
+ case QSysInfo::WV_CE: return "Windows CE";
+ case QSysInfo::WV_CENET: return "Windows CE.NET";
+ case QSysInfo::WV_CE_based: return "CE-based Windows";
+
+ default: return "Unknown";
+ }
+ #elif defined Q_WS_MAC
+ switch (QSysInfo::MacintoshVersion)
+ {
+ case QSysInfo::MV_Unknown: return "Unknown Mac";
+ case QSysInfo::MV_9: return "Mac OS 9";
+ case QSysInfo::MV_10_0: return "Mac OS X 10.0";
+ case QSysInfo::MV_10_1: return "Mac OS X 10.1";
+ case QSysInfo::MV_10_2: return "Mac OS X 10.2";
+ case QSysInfo::MV_10_3: return "Mac OS X 10.3";
+ case QSysInfo::MV_10_4: return "Mac OS X 10.4";
+ case QSysInfo::MV_10_5: return "Mac OS X 10.5";
+
+ default: return "Unknown";
+ }
+ #elif defined Q_WS_X11
+ return "UNIX X11";
+ #else
+ return "Unknown";
+ #endif
+ }
+
+ inline QString md5( const QByteArray& src )
+ {
+ QByteArray const digest = QCryptographicHash::hash( src, QCryptographicHash::Md5 );
+ return QString::fromLatin1( digest.toHex() ).rightJustified( 32, '0' ).toLower();
+ }
+}
+
+#ifdef Q_WS_MAC
+inline QString lastfm::CFStringToQString( CFStringRef s )
+{
+ return QString::fromUtf8( CFStringToUtf8( s ) );
+}
+#endif
+#endif //LASTFM_MISC_H
diff --git a/thirdparty/liblastfm2/src/fingerprint/CMakeLists.txt b/thirdparty/liblastfm2/src/fingerprint/CMakeLists.txt
new file mode 100644
index 000000000..eae8397a8
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 2.6)
+
+include_directories(${LIBFFTW3_INCLUDE_DIRS})
+include_directories(${LIBSAMPLERATE_INCLUDE_DIRS})
+include_directories(${QT_INCLUDES})
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
+
+link_directories(${LIBFFTW3_LIBRARY_DIRS})
+link_directories(${LIBSAMPLERATE_LIBRARY_DIRS})
+
+set(SOURCES
+ Collection.cpp
+ Fingerprint.cpp
+ Sha256.cpp
+ fplib/Filter.cpp
+ fplib/FingerprintExtractor.cpp
+ fplib/OptFFT.cpp
+)
+
+add_library(tomahawk_lastfm2_fingerprint SHARED
+ ${SOURCES}
+)
+
+target_link_libraries(tomahawk_lastfm2_fingerprint
+ ${QT_LIBRARIES}
+ ${QT_QTSQL_LIBRARY}
+ ${LIBFFTW3_LIBRARY}
+ ${LIBSAMPLERATE_LIBRARY}
+ tomahawk_lastfm2
+)
+
+set_target_properties(tomahawk_lastfm2_fingerprint PROPERTIES COMPILE_FLAGS "-DLASTFM_FINGERPRINT_OHAI_QMAKE" )
diff --git a/thirdparty/liblastfm2/src/fingerprint/Collection.cpp b/thirdparty/liblastfm2/src/fingerprint/Collection.cpp
new file mode 100644
index 000000000..214e264e2
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/Collection.cpp
@@ -0,0 +1,267 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+#include "Collection.h"
+#include "../core/misc.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const int k_collectionDbVersion = 1;
+
+// Singleton instance needs to be initialised
+Collection* Collection::s_instance = NULL;
+
+
+Collection::Collection()
+{
+ m_db = QSqlDatabase::addDatabase( "QSQLITE", "collection" );
+ m_db.setDatabaseName( lastfm::dir::runtimeData().filePath( "collection.db" ) );
+
+ if (!m_db.open()) {
+ qDebug() << m_db.lastError();
+ return;
+ }
+
+ if (!m_db.isValid()) {
+ qWarning() << "collection.db connection is not valid";
+ return;
+ }
+
+ if (!m_db.tables().contains( "files" ))
+ {
+ qDebug() << "Creating Collection database";
+
+ query( "CREATE TABLE artists ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "serverUid INTEGER,"
+ "lcName TEXT NOT NULL,"
+ "displayName TEXT NOT NULL );" );
+
+ query( "CREATE TABLE albums ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "serverUid INTEGER,"
+ "lcName TEXT NOT NULL,"
+ "displayName TEXT NOT NULL,"
+ "primaryArtist INTEGER NOT NULL );" );
+
+ query( "CREATE UNIQUE INDEX album_names_idx ON albums ( primaryArtist, lcName );" );
+
+ query( "CREATE TABLE tracks ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "lcName TEXT NOT NULL,"
+ "displayName TEXT NOT NULL,"
+ "primaryArtist INTEGER NOT NULL,"
+ "primaryAlbum INTEGER );" );
+
+ query( "CREATE UNIQUE INDEX track_names_idx ON tracks ( primaryArtist, lcName );" );
+
+ query( "CREATE TABLE files ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "uri TEXT NOT NULL,"
+ "track INTEGER NOT NULL,"
+ "bitrate INTEGER,"
+ "samplerate INTEGER,"
+ "duration INTEGER,"
+ "filesize INTEGER,"
+ "source INTEGER,"
+ "modificationDate INTEGER,"
+ "lastPlayDate INTEGER,"
+ "playCounter INTEGER,"
+ "mbId VARCHAR( 36 ),"
+ "fpId INTEGER );" );
+
+ query( "CREATE UNIQUE INDEX files_uri_idx ON files ( uri );" );
+ query( "CREATE INDEX files_track_idx ON files ( track );" );
+ query( "CREATE INDEX files_fpId_idx ON files ( fpId );" );
+ query( "CREATE INDEX files_source_idx ON files ( source );" );
+
+ query( "CREATE TABLE sources ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "name TEXT UNIQUE,"
+ "available INTEGER,"
+ "host TEXT,"
+ "cost INTEGER );" );
+
+ query( "CREATE TABLE genres ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "name TEXT UNIQUE );" );
+
+ query( "CREATE TABLE labels ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "serverUid INTEGER UNIQUE,"
+ "name TEXT );" );
+ }
+
+ int const v = version();
+ if ( v < k_collectionDbVersion )
+ {
+ qDebug() << "Upgrading Collection::db from" << v << "to" << k_collectionDbVersion;
+
+ /**********************************************
+ * README!!!!!!! *
+ * Ensure you use v < x *
+ * Ensure you do upgrades in ascending order! *
+ **********************************************/
+
+ if ( v < 1 )
+ {
+ // Norman discovered that he stored some fpId's wrong prior to 17th December 2007
+ // So we have to wipe the fpIds for databases without the metadata table
+ // we didn't store version information before that, which was a bad decision wasn't it?
+
+ // this will trigger refingerprinting of every track
+ query( "UPDATE files SET fpId = NULL;" );
+
+ query( "CREATE TABLE metadata ("
+ "key TEXT UNIQUE NOT NULL,"
+ "value TEXT );" );
+
+ query( "INSERT INTO metadata (key, value) VALUES ('version', '1');" );
+ }
+
+
+ // do last, update DB version number
+ query( "UPDATE metadata set key='version', value='"
+ + QString::number( k_collectionDbVersion ) + "';" );
+ }
+}
+
+
+Collection& //static
+Collection::instance()
+{
+ static QMutex mutex;
+ QMutexLocker locker( &mutex );
+
+ if ( !s_instance )
+ {
+ s_instance = new Collection;
+ qAddPostRoutine(destroy);
+ }
+
+ return *s_instance;
+}
+
+
+void //static
+Collection::destroy()
+{
+ delete s_instance;
+ QSqlDatabase::removeDatabase( "collection" );
+}
+
+
+int
+Collection::version() const
+{
+ QSqlQuery sql( m_db );
+ sql.exec( "SELECT value FROM metadata WHERE key='version';" );
+
+ if ( sql.next() )
+ {
+ return sql.value( 0 ).toInt();
+ }
+
+ return 0;
+}
+
+
+bool
+Collection::query( const QString& queryToken )
+{
+ QSqlQuery query( m_db );
+ query.exec( queryToken );
+
+ if ( query.lastError().isValid() )
+ {
+ qDebug() << "SQL query failed:" << query.lastQuery() << endl
+ << "SQL error was:" << query.lastError().databaseText() << endl
+ << "SQL error type:" << query.lastError().type();
+
+ return false;
+ }
+
+ return true;
+}
+
+
+QString
+Collection::fileURI( const QString& filePath )
+{
+ QString prefix( "file:/" );
+
+#ifdef WIN32
+ prefix = "file://";
+#endif
+
+ return prefix + QFileInfo( filePath ).absoluteFilePath();
+}
+
+
+QString
+Collection::getFingerprintId( const QString& filePath )
+{
+ QSqlQuery query( m_db );
+ query.prepare( "SELECT fpId FROM files WHERE uri = :uri" );
+ query.bindValue( ":uri", fileURI( filePath ) );
+
+ query.exec();
+ if ( query.lastError().isValid() )
+ {
+ qDebug() << "SQL query failed:" << query.lastQuery() << endl
+ << "SQL error was:" << query.lastError().databaseText() << endl
+ << "SQL error type:" << query.lastError().type();
+ }
+ else if (query.next())
+ return query.value( 0 ).toString();
+
+ return "";
+}
+
+
+bool
+Collection::setFingerprintId( const QString& filePath, QString fpId )
+{
+ bool isNumeric;
+ int intFpId = fpId.toInt( &isNumeric );
+ Q_ASSERT( isNumeric );
+
+ QSqlQuery query( m_db );
+ query.prepare( "REPLACE INTO files ( uri, track, fpId ) VALUES ( :uri, 0, :fpId )" );
+ query.bindValue( ":uri", fileURI( filePath ) );
+ query.bindValue( ":fpId", intFpId );
+ query.exec();
+
+ if ( query.lastError().isValid() )
+ {
+ qDebug() << "SQL query failed:" << query.lastQuery() << endl
+ << "SQL error was:" << query.lastError().databaseText() << endl
+ << "SQL error type:" << query.lastError().type();
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/Collection.h b/thirdparty/liblastfm2/src/fingerprint/Collection.h
new file mode 100644
index 000000000..9a1f3bd17
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/Collection.h
@@ -0,0 +1,59 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+/** Class that we use to store fingerprints, basically
+ */
+
+#ifndef COLLECTION_H
+#define COLLECTION_H
+
+#include
+#include
+
+
+/** @author: */
+class Collection
+{
+public:
+ static Collection& instance();
+
+ /** \brief Temp method: Gets a fingerprint id. Returns "" if none found. */
+ QString getFingerprintId( const QString& filePath );
+
+ /** \brief Temp method: Sets a fingerprint id. */
+ bool setFingerprintId( const QString& filePath, QString fpId );
+
+private:
+ Collection();
+
+ /** the database version
+ * version 0: up until 1.4.1
+ * version 1: from 1.4.2 */
+ int version() const;
+ bool query( const QString& queryToken );
+ QString fileURI( const QString& filePath );
+
+ static void destroy();
+
+ static Collection* s_instance;
+ QSqlDatabase m_db;
+};
+
+#endif // COLLECTION_H
diff --git a/thirdparty/liblastfm2/src/fingerprint/EXAMPLE.cpp b/thirdparty/liblastfm2/src/fingerprint/EXAMPLE.cpp
new file mode 100644
index 000000000..f86d4bbaf
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/EXAMPLE.cpp
@@ -0,0 +1,76 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include
+#include
+#include
+#include
+
+using namespace lastfm;
+
+
+static void finish( QNetworkReply* reply )
+{
+ QEventLoop loop;
+ loop.connect( reply, SIGNAL(finished()), SLOT(quit()) );
+ loop.exec();
+}
+
+
+int main( int argc, char** argv )
+{
+ QCoreApplication app( argc, argv );
+
+ // these fields are required
+ MutableTrack t;
+ t.setArtist( "Air" );
+ t.setTitle( "Redhead Girl" );
+ t.setAlbum( "Pocket Symphony" );
+ t.setUrl( QUrl::fromLocalFile( "/Users/mxcl/Music/iTunes/iTunes Music/Air/Pocket Symphony/1-11 Redhead Girl.mp3") );
+
+ try
+ {
+ Fingerprint fp( t );
+
+ // we cache FingerprintIds in an sqlite3 db, as the generate() function
+ // is expensive
+ if (fp.id().isNull())
+ {
+ // this generates the full fingerprint hash, which is about 20kB
+ fp.generate();
+
+ // this asks Last.fm for a FingerprintId
+ // the finish function is a Qt hack to allow syncronous HTTP
+ finish( fp.submit() );
+
+ // the decode step sets the FingerprintId
+ // the FingerprintId is required to obtain suggestions
+ // id will now be valid, or this function throws
+ fp.decode( reply );
+ }
+
+ finish( fp.id().getSuggestions() );
+
+ qDebug() << FingerprintId::getSuggestions( reply );
+ }
+ catch (Fingerprint::Error e)
+ {
+ qWarning() << e; //TODO enum debug thing
+ }
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/Fingerprint.cpp b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.cpp
new file mode 100644
index 000000000..b23805c7d
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.cpp
@@ -0,0 +1,300 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+#include "Fingerprint.h"
+#include "FingerprintableSource.h"
+#include "Collection.h"
+#include "Sha256.h"
+#include "fplib/FingerprintExtractor.h"
+#include "../ws/ws.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+using lastfm::Track;
+
+static const uint k_bufferSize = 1024 * 8;
+static const int k_minTrackDuration = 30;
+
+
+lastfm::Fingerprint::Fingerprint( const Track& t )
+ : m_track( t )
+ , m_id( -1 ), m_duration( 0 )
+ , m_complete( false )
+{
+ QString id = Collection::instance().getFingerprintId( t.url().toLocalFile() );
+ if (id.size()) {
+ bool b;
+ m_id = id.toInt( &b );
+ if (!b) m_id = -1;
+ }
+}
+
+
+void
+lastfm::Fingerprint::generate( FingerprintableSource* ms ) throw( Error )
+{
+ //TODO throw if we can't get required metadata from the track object
+
+//TODO if (!QFileInfo( path ).isReadable())
+//TODO throw ReadError;
+
+ int sampleRate, bitrate, numChannels;
+
+ if ( !ms )
+ throw ReadError;
+
+ try
+ {
+ ms->init( m_track.url().toLocalFile() );
+ ms->getInfo( m_duration, sampleRate, bitrate, numChannels );
+ }
+ catch (std::exception& e)
+ {
+ qWarning() << e.what();
+ throw HeadersError;
+ }
+
+
+ if (m_duration < k_minTrackDuration)
+ throw TrackTooShortError;
+
+ ms->skipSilence();
+
+ bool fpDone = false;
+ fingerprint::FingerprintExtractor* extractor;
+ try
+ {
+ extractor = new fingerprint::FingerprintExtractor;
+
+ if (m_complete)
+ {
+ extractor->initForFullSubmit( sampleRate, numChannels );
+ }
+ else
+ {
+ extractor->initForQuery( sampleRate, numChannels, m_duration );
+
+ // Skippety skip for as long as the skipper sez (optimisation)
+ ms->skip( extractor->getToSkipMs() );
+ float secsToSkip = extractor->getToSkipMs() / 1000.0f;
+ fpDone = extractor->process( 0,
+ (size_t) sampleRate * numChannels * secsToSkip,
+ false );
+ }
+ }
+ catch (std::exception& e)
+ {
+ qWarning() << e.what();
+ throw DecodeError;
+ }
+
+ const size_t PCMBufSize = 131072;
+ short* pPCMBuffer = new short[PCMBufSize];
+
+ while (!fpDone)
+ {
+ size_t readData = ms->updateBuffer( pPCMBuffer, PCMBufSize );
+ if (readData == 0)
+ break;
+
+ try
+ {
+ fpDone = extractor->process( pPCMBuffer, readData, ms->eof() );
+ }
+ catch ( const std::exception& e )
+ {
+ qWarning() << e.what();
+ delete ms;
+ delete[] pPCMBuffer;
+ throw InternalError;
+ }
+ }
+
+ delete[] pPCMBuffer;
+
+ if (!fpDone)
+ throw InternalError;
+
+ // We succeeded
+ std::pair fpData = extractor->getFingerprint();
+
+ if (fpData.first == NULL || fpData.second == 0)
+ throw InternalError;
+
+ // Make a deep copy before extractor gets deleted
+ m_data = QByteArray( fpData.first, fpData.second );
+ delete extractor;
+}
+
+
+static QString sha256( const QString& path )
+{
+ // no clue why this is static, there was no comment when I refactored it
+ // initially --mxcl
+ static uint8_t pBuffer[SHA_BUFFER_SIZE+7];
+
+ unsigned char hash[SHA256_HASH_SIZE];
+
+ {
+ QByteArray path8 = QFile::encodeName( path );
+ std::ifstream inFile( path8.data(), std::ios::binary);
+
+ SHA256Context sha256;
+ SHA256Init( &sha256 );
+
+ uint8_t* pMovableBuffer = pBuffer;
+
+ // Ensure it is on a 64-bit boundary.
+ INTPTR offs;
+ if ((offs = reinterpret_cast(pBuffer) & 7L))
+ pMovableBuffer += 8 - offs;
+
+ unsigned int len;
+
+ for (;;)
+ {
+ inFile.read( reinterpret_cast(pMovableBuffer), SHA_BUFFER_SIZE );
+ len = inFile.gcount();
+
+ if (len == 0)
+ break;
+
+ SHA256Update( &sha256, pMovableBuffer, len );
+ }
+
+ SHA256Final( &sha256, hash );
+ }
+
+ QString sha;
+ for (int i = 0; i < SHA256_HASH_SIZE; ++i)
+ {
+ QString hex = QString("%1").arg(uchar(hash[i]), 2, 16,
+ QChar('0'));
+ sha.append(hex);
+ }
+
+ return sha;
+}
+
+
+static QByteArray number( uint n )
+{
+ return n ? QByteArray::number( n ) : "";
+}
+
+QNetworkReply*
+lastfm::Fingerprint::submit() const
+{
+ if (m_data.isEmpty())
+ return 0;
+
+ //Parameters understood by the server according to the MIR team:
+ //{ "trackid", "recordingid", "artist", "album", "track", "duration",
+ // "tracknum", "username", "sha256", "ip", "fpversion", "mbid",
+ // "filename", "genre", "year", "samplerate", "noupdate", "fulldump" }
+
+ Track const t = m_track;
+ QString const path = t.url().toLocalFile();
+ QFileInfo const fi( path );
+
+ #define e( x ) QUrl::toPercentEncoding( x )
+ QUrl url( "http://www.last.fm/fingerprint/query/" );
+ url.addEncodedQueryItem( "artist", e(t.artist()) );
+ url.addEncodedQueryItem( "album", e(t.album()) );
+ url.addEncodedQueryItem( "track", e(t.title()) );
+ url.addEncodedQueryItem( "duration", number( m_duration > 0 ? m_duration : t.duration() ) );
+ url.addEncodedQueryItem( "mbid", e(t.mbid()) );
+ url.addEncodedQueryItem( "filename", e(fi.completeBaseName()) );
+ url.addEncodedQueryItem( "fileextension", e(fi.completeSuffix()) );
+ url.addEncodedQueryItem( "tracknum", number( t.trackNumber() ) );
+ url.addEncodedQueryItem( "sha256", sha256( path ).toAscii() );
+ url.addEncodedQueryItem( "time", number(QDateTime::currentDateTime().toTime_t()) );
+ url.addEncodedQueryItem( "fpversion", QByteArray::number((int)fingerprint::FingerprintExtractor::getVersion()) );
+ url.addEncodedQueryItem( "fulldump", m_complete ? "true" : "false" );
+ url.addEncodedQueryItem( "noupdate", "false" );
+ #undef e
+
+ //FIXME: talk to mir about submitting fplibversion
+
+ QNetworkRequest request( url );
+ request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=----------------------------8e61d618ca16" );
+
+ QByteArray bytes;
+ bytes += "------------------------------8e61d618ca16\r\n";
+ bytes += "Content-Disposition: ";
+ bytes += "form-data; name=\"fpdata\"";
+ bytes += "\r\n\r\n";
+ bytes += m_data;
+ bytes += "\r\n";
+ bytes += "------------------------------8e61d618ca16--\r\n";
+
+ qDebug() << url;
+ qDebug() << "Fingerprint size:" << bytes.size() << "bytes";
+
+ return lastfm::nam()->post( request, bytes );
+}
+
+
+void
+lastfm::Fingerprint::decode( QNetworkReply* reply, bool* complete_fingerprint_requested ) throw( Error )
+{
+ // The response data will consist of a number and a string.
+ // The number is the fpid and the string is either FOUND or NEW
+ // (or NOT FOUND when noupdate was used). NEW means we should
+ // schedule a full fingerprint.
+ //
+ // In the case of an error, there will be no initial number, just
+ // an error string.
+
+ QString const response( reply->readAll() );
+ QStringList const list = response.split( ' ' );
+
+ QString const fpid = list.value( 0 );
+ QString const status = list.value( 1 );
+
+ if (response.isEmpty() || list.count() < 2 || response == "No response to client error")
+ goto bad_response;
+ if (list.count() != 2)
+ qWarning() << "Response looks bad but continuing anyway:" << response;
+
+ {
+ // so variables go out of scope before jump to label
+ // otherwise compiler error on GCC 4.2
+ bool b;
+ uint fpid_as_uint = fpid.toUInt( &b );
+ if (!b) goto bad_response;
+
+ Collection::instance().setFingerprintId( m_track.url().toLocalFile(), fpid );
+
+ if (complete_fingerprint_requested)
+ *complete_fingerprint_requested = (status == "NEW");
+
+ m_id = (int)fpid_as_uint;
+ return;
+ }
+
+bad_response:
+ qWarning() << "Response is bad:" << response;
+ throw BadResponseError;
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/Fingerprint.h b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.h
new file mode 100644
index 000000000..b793c4bb9
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/Fingerprint.h
@@ -0,0 +1,116 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ - Primarily authored by Max Howell, Jono Cole and Doug Mansell
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef LASTFM_FINGERPRINT_H
+#define LASTFM_FINGERPRINT_H
+
+#include
+#include
+
+
+namespace lastfm
+{
+ class LASTFM_FINGERPRINT_DLLEXPORT Fingerprint
+ {
+ lastfm::Track m_track;
+ QByteArray m_data;
+ int m_id;
+ int m_duration;
+
+ protected:
+ bool m_complete;
+
+ public:
+ /** represents a partial fingerprint of 20 seconds of music, this is
+ * considered 99.9999...9999% unique and so we use it for most stuff as
+ * it is much quicker than a complete fingerprint, still though, you
+ * should do the generate step in a thread. */
+ Fingerprint( const lastfm::Track& );
+
+ /** if the id isNull(), then you'll need to do generate, submit and decode */
+ FingerprintId id() const { return m_id; }
+
+ /** The actual data that is the fingerprint, this is about 70kB or so,
+ * there isn't anything in it until you call generate. */
+ QByteArray data() const { return m_data; }
+
+ enum Error
+ {
+ ReadError = 0,
+
+ /** failed to extract samplerate, bitrate, channels, duration etc */
+ HeadersError,
+
+ DecodeError,
+
+ /** there is a minimum track duration for fingerprinting */
+ TrackTooShortError,
+
+ /** the fingerprint service went wrong, or we submitted bad data,
+ * or myabe the request failed, whatever, we couldn't parse the
+ * result */
+ BadResponseError,
+
+ /** sorry, liblastfm sucks, report bug with log! */
+ InternalError
+ };
+
+ /** This is CPU intensive, do it in a thread in your GUI application */
+ void generate( FingerprintableSource* ) throw( Error );
+
+ /** Submits the fingerprint data to Last.fm in order to get a FingerprintId
+ * back. You need to wait for the QNetworkReply to finish before you can
+ * pass it to decode clearly. */
+ QNetworkReply* submit() const;
+
+ /** Pass a finished reply from submit(), if the response is sound, id()
+ * will be valid. Otherwise we will throw. You always get a valid id
+ * or a throw.
+ */
+ void decode( QNetworkReply*, bool* lastfm_needs_a_complete_fingerprint = 0 ) throw( Error );
+ };
+
+
+ class CompleteFingerprint : public Fingerprint
+ {
+ public:
+ CompleteFingerprint( const lastfm::Track& t ) : Fingerprint( t )
+ {
+ m_complete = true;
+ }
+ };
+}
+
+
+inline QDebug operator<<( QDebug d, lastfm::Fingerprint::Error e )
+{
+ #define CASE(x) case lastfm::Fingerprint::x: return d << #x;
+ switch (e)
+ {
+ CASE(ReadError)
+ CASE(HeadersError)
+ CASE(DecodeError)
+ CASE(TrackTooShortError)
+ CASE(BadResponseError)
+ CASE(InternalError)
+ }
+ #undef CASE
+}
+
+#endif
diff --git a/thirdparty/liblastfm2/src/fingerprint/FingerprintableSource.h b/thirdparty/liblastfm2/src/fingerprint/FingerprintableSource.h
new file mode 100644
index 000000000..9954fd368
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/FingerprintableSource.h
@@ -0,0 +1,48 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+#ifndef LASTFM_FINGERPRINTABLE_SOURCE_H
+#define LASTFM_FINGERPRINTABLE_SOURCE_H
+
+#include
+#include
+
+namespace lastfm
+{
+ class LASTFM_FINGERPRINT_DLLEXPORT FingerprintableSource
+ {
+ public:
+ /** do all initialisation here and throw if there is problems */
+ virtual void init( const QString& path ) = 0;
+
+ virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) = 0;
+
+ /** put a chunk of PCM data in pBuffer, don't exceed size, return the
+ * number of bytes you put in the buffer */
+ virtual int updateBuffer( signed short* buffer, size_t bufferSize ) = 0;
+
+ virtual void skip( const int mSecs ) = 0;
+ virtual void skipSilence( double silenceThreshold = 0.0001 ) = 0;
+
+ virtual bool eof() const = 0;
+ };
+}
+
+#endif
diff --git a/thirdparty/liblastfm2/src/fingerprint/Sha256.cpp b/thirdparty/liblastfm2/src/fingerprint/Sha256.cpp
new file mode 100644
index 000000000..9be3c18ac
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/Sha256.cpp
@@ -0,0 +1,480 @@
+/*-
+ * Copyright (c) 2001-2003 Allan Saddi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $
+ */
+
+/*
+ * Define WORDS_BIGENDIAN if compiling on a big-endian architecture.
+ *
+ * Define SHA256_TEST to test the implementation using the NIST's
+ * sample messages. The output should be:
+ *
+ * ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad
+ * 248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1
+ * cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif /* HAVE_CONFIG_H */
+
+#if HAVE_INTTYPES_H
+# include
+#else
+# if HAVE_STDINT_H
+# include
+# endif
+#endif
+
+#include
+
+#include "Sha256.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $";
+#endif /* !lint */
+
+#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
+
+#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
+#define SIGMA0(x) (ROTR((x), 2) ^ ROTR((x), 13) ^ ROTR((x), 22))
+#define SIGMA1(x) (ROTR((x), 6) ^ ROTR((x), 11) ^ ROTR((x), 25))
+#define sigma0(x) (ROTR((x), 7) ^ ROTR((x), 18) ^ ((x) >> 3))
+#define sigma1(x) (ROTR((x), 17) ^ ROTR((x), 19) ^ ((x) >> 10))
+
+#define DO_ROUND() { \
+ t1 = h + SIGMA1(e) + Ch(e, f, g) + *(Kp++) + *(W++); \
+ t2 = SIGMA0(a) + Maj(a, b, c); \
+ h = g; \
+ g = f; \
+ f = e; \
+ e = d + t1; \
+ d = c; \
+ c = b; \
+ b = a; \
+ a = t1 + t2; \
+}
+
+static const uint32_t K[64] = {
+ 0x428a2f98L, 0x71374491L, 0xb5c0fbcfL, 0xe9b5dba5L,
+ 0x3956c25bL, 0x59f111f1L, 0x923f82a4L, 0xab1c5ed5L,
+ 0xd807aa98L, 0x12835b01L, 0x243185beL, 0x550c7dc3L,
+ 0x72be5d74L, 0x80deb1feL, 0x9bdc06a7L, 0xc19bf174L,
+ 0xe49b69c1L, 0xefbe4786L, 0x0fc19dc6L, 0x240ca1ccL,
+ 0x2de92c6fL, 0x4a7484aaL, 0x5cb0a9dcL, 0x76f988daL,
+ 0x983e5152L, 0xa831c66dL, 0xb00327c8L, 0xbf597fc7L,
+ 0xc6e00bf3L, 0xd5a79147L, 0x06ca6351L, 0x14292967L,
+ 0x27b70a85L, 0x2e1b2138L, 0x4d2c6dfcL, 0x53380d13L,
+ 0x650a7354L, 0x766a0abbL, 0x81c2c92eL, 0x92722c85L,
+ 0xa2bfe8a1L, 0xa81a664bL, 0xc24b8b70L, 0xc76c51a3L,
+ 0xd192e819L, 0xd6990624L, 0xf40e3585L, 0x106aa070L,
+ 0x19a4c116L, 0x1e376c08L, 0x2748774cL, 0x34b0bcb5L,
+ 0x391c0cb3L, 0x4ed8aa4aL, 0x5b9cca4fL, 0x682e6ff3L,
+ 0x748f82eeL, 0x78a5636fL, 0x84c87814L, 0x8cc70208L,
+ 0x90befffaL, 0xa4506cebL, 0xbef9a3f7L, 0xc67178f2L
+};
+
+#ifndef RUNTIME_ENDIAN
+
+#ifdef WORDS_BIGENDIAN
+
+#define BYTESWAP(x) (x)
+#define BYTESWAP64(x) (x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \
+ (ROTL((x), 8) & 0x00ff00ffL))
+#define BYTESWAP64(x) _byteswap64(x)
+
+static inline uint64_t _byteswap64(uint64_t x)
+{
+ uint32_t a = x >> 32;
+ uint32_t b = (uint32_t) x;
+ return ((uint64_t) BYTESWAP(b) << 32) | (uint64_t) BYTESWAP(a);
+}
+
+#endif /* WORDS_BIGENDIAN */
+
+#else /* !RUNTIME_ENDIAN */
+
+#define BYTESWAP(x) _byteswap(sc->littleEndian, x)
+#define BYTESWAP64(x) _byteswap64(sc->littleEndian, x)
+
+#define _BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \
+ (ROTL((x), 8) & 0x00ff00ffL))
+#define _BYTESWAP64(x) __byteswap64(x)
+
+static inline uint64_t __byteswap64(uint64_t x)
+{
+ uint32_t a = x >> 32;
+ uint32_t b = (uint32_t) x;
+ return ((uint64_t) _BYTESWAP(b) << 32) | (uint64_t) _BYTESWAP(a);
+}
+
+static inline uint32_t _byteswap(int littleEndian, uint32_t x)
+{
+ if (!littleEndian)
+ return x;
+ else
+ return _BYTESWAP(x);
+}
+
+static inline uint64_t _byteswap64(int littleEndian, uint64_t x)
+{
+ if (!littleEndian)
+ return x;
+ else
+ return _BYTESWAP64(x);
+}
+
+static inline void setEndian(int *littleEndianp)
+{
+ union {
+ uint32_t w;
+ uint8_t b[4];
+ } endian;
+
+ endian.w = 1L;
+ *littleEndianp = endian.b[0] != 0;
+}
+
+#endif /* !RUNTIME_ENDIAN */
+
+static const uint8_t padding[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+void
+SHA256Init (SHA256Context *sc)
+{
+#ifdef RUNTIME_ENDIAN
+ setEndian (&sc->littleEndian);
+#endif /* RUNTIME_ENDIAN */
+
+ sc->totalLength = 0LL;
+ sc->hash[0] = 0x6a09e667L;
+ sc->hash[1] = 0xbb67ae85L;
+ sc->hash[2] = 0x3c6ef372L;
+ sc->hash[3] = 0xa54ff53aL;
+ sc->hash[4] = 0x510e527fL;
+ sc->hash[5] = 0x9b05688cL;
+ sc->hash[6] = 0x1f83d9abL;
+ sc->hash[7] = 0x5be0cd19L;
+ sc->bufferLength = 0L;
+}
+
+static void
+burnStack (int size)
+{
+ char buf[128];
+
+ memset (buf, 0, sizeof (buf));
+ size -= sizeof (buf);
+ if (size > 0)
+ burnStack (size);
+}
+
+static void
+SHA256Guts (SHA256Context *sc, const uint32_t *cbuf)
+{
+ uint32_t buf[64];
+ uint32_t *W, *W2, *W7, *W15, *W16;
+ uint32_t a, b, c, d, e, f, g, h;
+ uint32_t t1, t2;
+ const uint32_t *Kp;
+ int i;
+
+ W = buf;
+
+ for (i = 15; i >= 0; i--) {
+ *(W++) = BYTESWAP(*cbuf);
+ cbuf++;
+ }
+
+ W16 = &buf[0];
+ W15 = &buf[1];
+ W7 = &buf[9];
+ W2 = &buf[14];
+
+ for (i = 47; i >= 0; i--) {
+ *(W++) = sigma1(*W2) + *(W7++) + sigma0(*W15) + *(W16++);
+ W2++;
+ W15++;
+ }
+
+ a = sc->hash[0];
+ b = sc->hash[1];
+ c = sc->hash[2];
+ d = sc->hash[3];
+ e = sc->hash[4];
+ f = sc->hash[5];
+ g = sc->hash[6];
+ h = sc->hash[7];
+
+ Kp = K;
+ W = buf;
+
+#ifndef SHA256_UNROLL
+#define SHA256_UNROLL 1
+#endif /* !SHA256_UNROLL */
+
+#if SHA256_UNROLL == 1
+ for (i = 63; i >= 0; i--)
+ DO_ROUND();
+#elif SHA256_UNROLL == 2
+ for (i = 31; i >= 0; i--) {
+ DO_ROUND(); DO_ROUND();
+ }
+#elif SHA256_UNROLL == 4
+ for (i = 15; i >= 0; i--) {
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ }
+#elif SHA256_UNROLL == 8
+ for (i = 7; i >= 0; i--) {
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ }
+#elif SHA256_UNROLL == 16
+ for (i = 3; i >= 0; i--) {
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ }
+#elif SHA256_UNROLL == 32
+ for (i = 1; i >= 0; i--) {
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ }
+#elif SHA256_UNROLL == 64
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+ DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND();
+#else
+#error "SHA256_UNROLL must be 1, 2, 4, 8, 16, 32, or 64!"
+#endif
+
+ sc->hash[0] += a;
+ sc->hash[1] += b;
+ sc->hash[2] += c;
+ sc->hash[3] += d;
+ sc->hash[4] += e;
+ sc->hash[5] += f;
+ sc->hash[6] += g;
+ sc->hash[7] += h;
+}
+
+void
+SHA256Update (SHA256Context *sc, const void *vdata, uint32_t len)
+{
+ const uint8_t *data = (const uint8_t*)vdata;
+ uint32_t bufferBytesLeft;
+ uint32_t bytesToCopy;
+ int needBurn = 0;
+
+#ifdef SHA256_FAST_COPY
+ if (sc->bufferLength) {
+ bufferBytesLeft = 64L - sc->bufferLength;
+
+ bytesToCopy = bufferBytesLeft;
+ if (bytesToCopy > len)
+ bytesToCopy = len;
+
+ memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy);
+
+ sc->totalLength += bytesToCopy * 8L;
+
+ sc->bufferLength += bytesToCopy;
+ data += bytesToCopy;
+ len -= bytesToCopy;
+
+ if (sc->bufferLength == 64L) {
+ SHA256Guts (sc, sc->buffer.words);
+ needBurn = 1;
+ sc->bufferLength = 0L;
+ }
+ }
+
+ while (len > 63L) {
+ sc->totalLength += 512L;
+
+ SHA256Guts (sc, data);
+ needBurn = 1;
+
+ data += 64L;
+ len -= 64L;
+ }
+
+ if (len) {
+ memcpy (&sc->buffer.bytes[sc->bufferLength], data, len);
+
+ sc->totalLength += len * 8L;
+
+ sc->bufferLength += len;
+ }
+#else /* SHA256_FAST_COPY */
+ while (len) {
+ bufferBytesLeft = 64L - sc->bufferLength;
+
+ bytesToCopy = bufferBytesLeft;
+ if (bytesToCopy > len)
+ bytesToCopy = len;
+
+ memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy);
+
+ sc->totalLength += bytesToCopy * 8L;
+
+ sc->bufferLength += bytesToCopy;
+ data += bytesToCopy;
+ len -= bytesToCopy;
+
+ if (sc->bufferLength == 64L) {
+ SHA256Guts (sc, sc->buffer.words);
+ needBurn = 1;
+ sc->bufferLength = 0L;
+ }
+ }
+#endif /* SHA256_FAST_COPY */
+
+ if (needBurn)
+ burnStack (sizeof (uint32_t[74]) + sizeof (uint32_t *[6]) + sizeof (int));
+}
+
+void
+SHA256Final (SHA256Context *sc, uint8_t hash[SHA256_HASH_SIZE])
+{
+ uint32_t bytesToPad;
+ uint64_t lengthPad;
+ int i;
+
+ bytesToPad = 120L - sc->bufferLength;
+ if (bytesToPad > 64L)
+ bytesToPad -= 64L;
+
+ lengthPad = BYTESWAP64(sc->totalLength);
+
+ SHA256Update (sc, padding, bytesToPad);
+ SHA256Update (sc, &lengthPad, 8L);
+
+ if (hash) {
+ for (i = 0; i < SHA256_HASH_WORDS; i++) {
+#ifdef SHA256_FAST_COPY
+ *((uint32_t *) hash) = BYTESWAP(sc->hash[i]);
+#else /* SHA256_FAST_COPY */
+ hash[0] = (uint8_t) (sc->hash[i] >> 24);
+ hash[1] = (uint8_t) (sc->hash[i] >> 16);
+ hash[2] = (uint8_t) (sc->hash[i] >> 8);
+ hash[3] = (uint8_t) sc->hash[i];
+#endif /* SHA256_FAST_COPY */
+ hash += 4;
+ }
+ }
+}
+
+#ifdef SHA256_TEST
+
+#include
+#include
+
+int
+main (int argc, char *argv[])
+{
+ SHA256Context foo;
+ uint8_t hash[SHA256_HASH_SIZE];
+ char buf[1000];
+ int i;
+
+ SHA256Init (&foo);
+ SHA256Update (&foo, "abc", 3);
+ SHA256Final (&foo, hash);
+
+ for (i = 0; i < SHA256_HASH_SIZE;) {
+ printf ("%02x", hash[i++]);
+ if (!(i % 4))
+ printf (" ");
+ }
+ printf ("\n");
+
+ SHA256Init (&foo);
+ SHA256Update (&foo,
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ 56);
+ SHA256Final (&foo, hash);
+
+ for (i = 0; i < SHA256_HASH_SIZE;) {
+ printf ("%02x", hash[i++]);
+ if (!(i % 4))
+ printf (" ");
+ }
+ printf ("\n");
+
+ SHA256Init (&foo);
+ memset (buf, 'a', sizeof (buf));
+ for (i = 0; i < 1000; i++)
+ SHA256Update (&foo, buf, sizeof (buf));
+ SHA256Final (&foo, hash);
+
+ for (i = 0; i < SHA256_HASH_SIZE;) {
+ printf ("%02x", hash[i++]);
+ if (!(i % 4))
+ printf (" ");
+ }
+ printf ("\n");
+
+ exit (0);
+}
+
+#endif /* SHA256_TEST */
diff --git a/thirdparty/liblastfm2/src/fingerprint/Sha256.h b/thirdparty/liblastfm2/src/fingerprint/Sha256.h
new file mode 100644
index 000000000..433c8f9a6
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/Sha256.h
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2001-2003 Allan Saddi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: sha256.h 348 2003-02-23 22:12:06Z asaddi $
+ */
+//
+/////////// EXAMPLE /////////////////////////////////
+//
+// SHA256Context sha256;
+// SHA256Init (&sha256);
+//
+// uint8_t* pBuffer = new uint8_t[SHA_BUFFER_SIZE + 7];
+// // Ensure it is on a 64-bit boundary.
+// INTPTR offs;
+// if ((offs = reinterpret_cast(pBuffer) & 7L))
+// pBuffer += 8 - offs;
+//
+// unsigned int len;
+//
+// ifstream inFile("test.txt", ios::binary);
+//
+// for (;;)
+// {
+// inFile.read( reinterpret_cast(pBuffer), SHA_BUFFER_SIZE );
+// len = inFile.gcount();
+//
+// if ( len == 0)
+// break;
+//
+// SHA256Update (&sha256, pBuffer, len);
+// }
+//
+// uint8_t hash[SHA256_HASH_SIZE];
+// SHA256Final (&sha256, hash);
+//
+// cout << "Hash: ";
+// for (int i = 0; i < SHA256_HASH_SIZE; ++i)
+// printf ("%02x", hash[i]);
+// cout << endl;
+
+
+#ifndef _SHA256_H
+#define _SHA256_H
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+/* Define to 1 if you have the header file. */
+#ifndef WIN32
+#define HAVE_INTTYPES_H 1
+#endif
+
+/* Define to 1 if you have the header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the header file. */
+#ifndef WIN32
+#define HAVE_STDINT_H 1
+#endif
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strerror' function. */
+#ifndef WIN32
+#define HAVE_STRERROR 1
+#endif
+
+/* Define to 1 if you have the header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the header file. */
+#ifndef WIN32
+#define HAVE_SYS_TYPES_H 1
+#endif
+
+/* Define to 1 if you have the header file. */
+#ifndef WIN32
+#define HAVE_UNISTD_H 1
+#endif
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+#ifdef WIN32
+#define inline __inline
+#endif
+
+/* Define to `unsigned' if does not define. */
+/* #undef size_t */
+
+#ifdef WIN32
+#define uint64_t unsigned __int64
+#define uint32_t unsigned int
+#define uint8_t unsigned char
+#endif // WIN32
+
+#ifdef WIN32
+#define INTPTR intptr_t
+#else
+#define INTPTR long
+#endif
+
+#define SHA_BUFFER_SIZE 65536
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+
+#if HAVE_INTTYPES_H
+# include
+#else
+# if HAVE_STDINT_H
+# include
+# endif
+#endif
+
+#define SHA256_HASH_SIZE 32
+
+/* Hash size in 32-bit words */
+#define SHA256_HASH_WORDS 8
+
+struct _SHA256Context {
+ uint64_t totalLength;
+ uint32_t hash[SHA256_HASH_WORDS];
+ uint32_t bufferLength;
+ union {
+ uint32_t words[16];
+ uint8_t bytes[64];
+ } buffer;
+#ifdef RUNTIME_ENDIAN
+ int littleEndian;
+#endif /* RUNTIME_ENDIAN */
+};
+
+typedef struct _SHA256Context SHA256Context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void SHA256Init (SHA256Context *sc);
+void SHA256Update (SHA256Context *sc, const void *data, uint32_t len);
+void SHA256Final (SHA256Context *sc, uint8_t hash[SHA256_HASH_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_SHA256_H */
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.cpp
new file mode 100644
index 000000000..77bacd343
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.cpp
@@ -0,0 +1,953 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+ Portions Copyright 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+ - Adapted from main.c found in the FAAD2 source tarball.
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include "AacSource.h"
+#include "AacSource_p.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// AAC_File
+//
+////////////////////////////////////////////////////////////////////////
+AAC_File::AAC_File(const QString& fileName, int headerType)
+ : m_fileName(fileName)
+ , m_inBuf(NULL)
+ , m_inBufSize(0)
+ , m_decoder(0)
+ , m_overflow(static_cast(malloc( sizeof(unsigned char) * 1024 )))
+ , m_overflowSize(0)
+ , m_header(headerType)
+{
+}
+
+
+AAC_File::~AAC_File()
+{
+ // common
+ if ( m_decoder )
+ {
+ NeAACDecClose( m_decoder );
+ m_decoder = NULL;
+ }
+ if ( m_inBuf )
+ {
+ free( m_inBuf );
+ m_inBufSize = 0;
+ m_inBuf = NULL;
+ }
+ if ( m_overflow )
+ {
+ free( m_overflow );
+ m_overflowSize = 0;
+ m_overflow = NULL;
+ }
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// AAC with ADTS or ADIF headers
+//
+////////////////////////////////////////////////////////////////////////
+
+
+#define MAX_CHANNELS 6 // Output will get mixed down to 2 channels
+#define ADTS_HEADER_SIZE 8
+
+static int adts_sample_rates[] =
+{
+ 96000,
+ 88200,
+ 64000,
+ 48000,
+ 44100,
+ 32000,
+ 24000,
+ 22050,
+ 16000,
+ 12000,
+ 11025,
+ 8000,
+ 7350,
+ 0,
+ 0,
+ 0
+};
+
+AAC_ADTS_File::AAC_ADTS_File( const QString& fileName, int headerType ) : AAC_File(fileName, headerType)
+ , m_file( NULL )
+ , m_adifSamplerate( 0 )
+ , m_adifChannels( 0 )
+{
+}
+
+
+AAC_ADTS_File::~AAC_ADTS_File()
+{
+ if ( m_file )
+ {
+ fclose( m_file );
+ }
+}
+
+
+void AAC_ADTS_File::fillBuffer( FILE*& fp, unsigned char*& buf, size_t& bufSize, const size_t bytesConsumed )
+{
+ size_t bread;
+
+ if ( bytesConsumed > 0 )
+ {
+ if ( bufSize )
+ memmove( (void*)buf, (void*)(buf + bytesConsumed), bufSize*sizeof(unsigned char) );
+
+ bread = fread( (void*)(buf + bufSize), 1, bytesConsumed, fp );
+ bufSize += bread;
+
+ if ( bufSize > 3 )
+ {
+ if ( memcmp( buf, "TAG", 3 ) == 0 )
+ bufSize = 0;
+ }
+ if ( bufSize > 11 )
+ {
+ if ( memcmp( buf, "LYRICSBEGIN", 11 ) == 0 )
+ bufSize = 0;
+ }
+ if ( bufSize > 8 )
+ {
+ if ( memcmp( buf, "APETAGEX", 8 ) == 0 )
+ bufSize = 0;
+ }
+ }
+}
+
+
+void AAC_ADTS_File::parse( FILE*& fp, unsigned char*& buf, size_t& bufSize, int &bitrate, double &length )
+{
+ unsigned int frames, frame_length = 0;
+ int t_framelength = 0;
+ int samplerate = 0;
+ double frames_per_sec, bytes_per_frame;
+
+ // Read all frames to ensure correct time and bitrate
+ for ( frames = 0; /* */; frames++ )
+ {
+ fillBuffer( fp, buf, bufSize, frame_length );
+
+ if ( bufSize > 7 )
+ {
+ /* check syncword */
+ if ( !( (buf[0] == 0xFF) && ((buf[1] & 0xF6) == 0xF0) ) )
+ break;
+
+ if ( frames == 0 )
+ samplerate = adts_sample_rates[ (buf[2] & 0x3c) >> 2 ];
+
+ frame_length = ( ((buf[3] & 0x3) << 11)
+ | ((buf[4]) << 3)
+ | (buf[5] >> 5) );
+
+ t_framelength += frame_length - ADTS_HEADER_SIZE;
+
+ if ( frame_length > bufSize )
+ break;
+
+ bufSize -= frame_length;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ frames_per_sec = samplerate / 1024.0;
+
+ if ( frames != 0 )
+ bytes_per_frame = t_framelength / frames;
+ else
+ bytes_per_frame = 0;
+
+ bitrate = static_cast(8 * bytes_per_frame * frames_per_sec + 0.5);
+
+ if ( frames_per_sec != 0 )
+ length = frames / frames_per_sec;
+ else
+ length = 1;
+}
+
+
+int32_t AAC_ADTS_File::commonSetup( FILE*& fp, NeAACDecHandle& decoder, unsigned char*& buf, size_t& bufSize, uint32_t& samplerate, uint8_t& channels )
+{
+ samplerate = 0;
+ channels = 0;
+
+ fp = fopen(QFile::encodeName(m_fileName), "rb" );
+ if( !fp )
+ {
+ std::cerr << "ERROR: Failed to open " << strerror( errno ) << std::endl;
+ return -1;
+ }
+
+ if ( !(buf = static_cast( malloc(FAAD_MIN_STREAMSIZE*MAX_CHANNELS)) ) )
+ {
+ std::cerr << "Memory allocation error" << std::endl;
+ fclose ( fp );
+ return -1;
+ }
+
+ memset( buf, 0, FAAD_MIN_STREAMSIZE*MAX_CHANNELS );
+
+ bufSize = fread( buf, 1, FAAD_MIN_STREAMSIZE * MAX_CHANNELS, fp );
+
+ int tagsize = 0;
+ if ( !memcmp( buf, "ID3", 3 ) )
+ {
+ /* high bit is not used */
+ tagsize = (buf[6] << 21) | (buf[7] << 14) |
+ (buf[8] << 7) | (buf[9] << 0);
+
+ tagsize += 10;
+ bufSize -= tagsize;
+ fillBuffer( fp, buf, bufSize, tagsize );
+ }
+
+ decoder = NeAACDecOpen();
+
+ /* Set configuration */
+ NeAACDecConfigurationPtr config;
+ config = NeAACDecGetCurrentConfiguration(decoder);
+ config->outputFormat = FAAD_FMT_16BIT;
+ config->downMatrix = 1; // Turn 5.1 channels into 2
+ NeAACDecSetConfiguration( decoder, config);
+
+ int32_t initval = 0;
+ if ((initval = NeAACDecInit(decoder, buf,
+ FAAD_MIN_STREAMSIZE*MAX_CHANNELS, &samplerate, &channels)) < 0)
+ {
+ std::cerr << "Error: could not set up AAC decoder" << std::endl;
+ if ( buf )
+ free( buf );
+ buf = NULL;
+ NeAACDecClose( decoder );
+ decoder = NULL;
+ fclose( fp );
+ fp = NULL;
+ }
+ return initval;
+}
+
+
+bool AAC_ADTS_File::init()
+{
+ uint32_t initSamplerate = 0;
+ uint8_t initChannels = 0;
+ int32_t initval = commonSetup( m_file, m_decoder, m_inBuf, m_inBufSize, initSamplerate, initChannels );
+
+ if ( initval >= 0 )
+ {
+ m_inBufSize -= initval;
+ fillBuffer( m_file, m_inBuf, m_inBufSize, initval );
+
+ // These two only needed for skipping AAC ADIF files
+ m_adifSamplerate = initSamplerate;
+ m_adifChannels = initChannels;
+
+ return true;
+ }
+
+ throw std::runtime_error( "ERROR: Could not initialize AAC file reader!" );
+ return false;
+}
+
+
+/*QString AAC_ADTS_File::getMbid()
+{
+ char out[MBID_BUFFER_SIZE];
+ int const r = getMP3_MBID(QFile::encodeName(m_fileName), out);
+ if ( r == 0 )
+ return QString::fromLatin1( out );
+ return QString();
+}*/
+
+void AAC_ADTS_File::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels )
+{
+ long fileread;
+ uint32_t initSamplerate;
+ uint8_t initChannels;
+ double initLength = 0;
+ unsigned char *tempBuf = NULL;
+ size_t tempBufSize;
+ FILE *fp = NULL;
+ NeAACDecHandle decoder = NULL;
+ commonSetup( fp, decoder, tempBuf, tempBufSize, initSamplerate, initChannels );
+
+ long origpos = ftell( fp );
+ fseek( fp, 0, SEEK_END );
+ fileread = ftell( fp );
+ fseek( fp, origpos, SEEK_SET );
+
+ if ( (tempBuf[0] == 0xFF) && ((tempBuf[1] & 0xF6) == 0xF0) )
+ {
+ parse( fp, tempBuf, tempBufSize, bitrate, initLength );
+ }
+ else if (memcmp(tempBuf, "ADIF", 4) == 0)
+ {
+ int skip_size = (tempBuf[4] & 0x80) ? 9 : 0;
+ bitrate = ((tempBuf[4 + skip_size] & 0x0F)<<19) |
+ (tempBuf[5 + skip_size]<<11) |
+ (tempBuf[6 + skip_size]<<3) |
+ (tempBuf[7 + skip_size] & 0xE0);
+
+ if ( fileread != 0)
+ {
+ initLength = static_cast(fileread) * 8 / bitrate + 0.5;
+ }
+ }
+
+ lengthSecs = static_cast(initLength);
+ nchannels = initChannels;
+ samplerate = initSamplerate;
+
+ if ( decoder )
+ NeAACDecClose( decoder );
+ if ( fp )
+ fclose( fp );
+ if ( tempBuf )
+ free( tempBuf );
+}
+
+
+void AAC_ADTS_File::skip( const int mSecs )
+{
+ if ( m_header == AAC_ADTS )
+ {
+ // As AAC is VBR we need to check all ADTS headers to enable seeking...
+ // There is no other solution
+ unsigned char header[8];
+ unsigned int frameCount, frameLength;
+ double seconds = 0;
+
+ // We need to find the ATDS syncword so rewind to the beginning
+ // of the unprocessed data.
+ if ( m_inBufSize > 0 )
+ {
+ fseek ( m_file, -m_inBufSize, SEEK_CUR );
+ m_inBufSize = 0;
+ }
+
+ for( frameCount = 1; seconds * 1000 < mSecs; frameCount++ )
+ {
+ if ( fread( header, 1, ADTS_HEADER_SIZE, m_file ) != ADTS_HEADER_SIZE )
+ {
+ break;
+ }
+ if ( !strncmp( (char*)header, "ID3", 3 ) )
+ {
+ // high bit is not used
+ unsigned char rest[2];
+ fread( rest, 1, 2, m_file );
+ int tagsize = (header[6] << 21) | (header[7] << 14) |
+ (rest[0] << 7) | (rest[1] << 0);
+
+ fseek( m_file, tagsize, SEEK_CUR );
+ fread( header, 1, ADTS_HEADER_SIZE, m_file );
+ }
+ if ( !((header[0] == 0xFF) && ((header[1] & 0xF6) == 0xF0)) )
+ {
+ std::cerr << "Error: Bad frame header; file may be corrupt!" << std::endl;
+ break;
+ }
+
+ int samplerate = adts_sample_rates[ (header[2] & 0x3c) >> 2 ];
+ frameLength = ( ( header[3] & 0x3 ) << 11 )
+ | ( header[4] << 3 )
+ | ( header[5] >> 5 );
+
+ if ( samplerate > 0 )
+ seconds += 1024.0 / samplerate;
+ else
+ {
+ std::cerr << "Error: Bad frame header; file may be corrupt!" << std::endl;
+ break;
+ }
+
+ if ( fseek( m_file, frameLength - ADTS_HEADER_SIZE, SEEK_CUR ) == -1 )
+ break;
+ }
+ m_inBufSize = fread( m_inBuf, 1, FAAD_MIN_STREAMSIZE * MAX_CHANNELS, m_file );
+ }
+ else if ( m_header == AAC_ADIF )
+ {
+ // AAC ADIF is even worse. There's only the one header at the
+ // beginning of the file. If you want to skip forward, you have to
+ // decode block by block and check how far along you are. Lovely, eh?
+
+ unsigned long totalSamples = 0;
+ void *sampleBuffer = NULL;
+
+ do
+ {
+ NeAACDecFrameInfo frameInfo;
+ sampleBuffer = NeAACDecDecode(m_decoder, &frameInfo, m_inBuf, static_cast(m_inBufSize) );
+ totalSamples += frameInfo.samples;
+ if ( frameInfo.bytesconsumed > 0 )
+ {
+ m_inBufSize -= frameInfo.bytesconsumed;
+ fillBuffer( m_file, m_inBuf, m_inBufSize, frameInfo.bytesconsumed );
+ }
+ if ( totalSamples >= ( mSecs * m_adifSamplerate * m_adifChannels / 1000 ) )
+ break;
+ } while ( sampleBuffer != NULL );
+ }
+}
+
+
+void AAC_ADTS_File::postDecode(unsigned long bytesConsumed)
+{
+ m_inBufSize -= bytesConsumed;
+ fillBuffer( m_file, m_inBuf, m_inBufSize, bytesConsumed );
+}
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// AAC in an MP4 wrapper
+//
+////////////////////////////////////////////////////////////////////////
+
+
+uint32_t read_callback( void *user_data, void *buffer, uint32_t length )
+{
+ return static_cast(fread( buffer, 1, length, static_cast(user_data) ));
+}
+
+
+uint32_t seek_callback( void *user_data, uint64_t position )
+{
+ return fseek( static_cast(user_data), static_cast(position), SEEK_SET );
+}
+
+AAC_MP4_File::AAC_MP4_File( const QString& fileName, int headerType ) : AAC_File(fileName, headerType)
+ , m_mp4AudioTrack( -1 )
+ , m_mp4SampleId( 0 )
+ , m_mp4File ( NULL )
+ , m_mp4cb ( NULL )
+{
+}
+
+int32_t AAC_MP4_File::readSample()
+{
+ unsigned int bsize;
+ int32_t rc = mp4ff_read_sample( m_mp4File, m_mp4AudioTrack, m_mp4SampleId, &m_inBuf, &bsize );
+ m_inBufSize = bsize;
+ // Not necessarily an error. Could just mean end of file.
+ //if ( rc == 0 )
+ // std::cerr << "Reading samples failed." << std::endl;
+ return rc;
+}
+
+
+int32_t AAC_MP4_File::getTrack( const mp4ff_t *f )
+{
+ // find AAC track
+ int32_t numTracks = mp4ff_total_tracks( f );
+
+ for ( int32_t i = 0; i < numTracks; i++ )
+ {
+ unsigned char *buff = NULL;
+ unsigned int buff_size = 0;
+ mp4AudioSpecificConfig mp4ASC;
+
+ mp4ff_get_decoder_config( f, i, &buff, &buff_size );
+
+ if ( buff )
+ {
+ int8_t rc = NeAACDecAudioSpecificConfig( buff, buff_size, &mp4ASC );
+ free( buff );
+
+ if ( rc < 0 )
+ continue;
+ return i;
+ }
+ }
+
+ // can't decode this, probably DRM
+ return -1;
+}
+
+
+bool AAC_MP4_File::commonSetup( NeAACDecHandle& decoder, mp4ff_callback_t*& cb, FILE*& fp, mp4ff_t*& mp4, int32_t& audioTrack )
+{
+ fp = fopen(QFile::encodeName(m_fileName), "rb");
+ if ( !fp )
+ {
+ throw std::runtime_error( "Error: failed to open AAC file!" );
+ return false;
+ }
+
+ decoder = NeAACDecOpen();
+
+ // Set configuration
+ NeAACDecConfigurationPtr config;
+ config = NeAACDecGetCurrentConfiguration( decoder );
+ config->outputFormat = FAAD_FMT_16BIT;
+ config->downMatrix = 1; // Turn 5.1 channels into 2
+ NeAACDecSetConfiguration( decoder, config );
+
+ // initialise the callback structure
+ cb = static_cast( malloc( sizeof(mp4ff_callback_t) ) );
+
+ cb->read = read_callback;
+ cb->seek = seek_callback;
+ cb->user_data = fp;
+
+ mp4 = mp4ff_open_read( cb );
+
+ if ( !mp4 )
+ {
+ // unable to open file
+ free( cb );
+ cb = NULL;
+ NeAACDecClose( decoder );
+ decoder = NULL;
+ fclose( fp );
+ fp = NULL;
+ throw std::runtime_error( "Error: failed to set up AAC decoder!" );
+ return false;
+ }
+
+ if ( ( audioTrack = getTrack( mp4 )) < 0 )
+ {
+ free( cb );
+ cb = NULL;
+ NeAACDecClose( decoder );
+ decoder = NULL;
+ fclose( fp );
+ fp = NULL;
+ mp4ff_close( mp4 );
+ mp4 = NULL;
+ audioTrack = 0;
+ throw std::runtime_error( "Error: Unable to find an audio track. Is the file DRM protected?" );
+ return false;
+ }
+ return true;
+}
+
+
+/*QString AAC_MP4_File::getMbid()
+{
+ int j = mp4ff_meta_get_num_items( m_mp4File );
+ if ( j > 0 )
+ {
+ int k;
+ for ( k = 0; k < j; k++ )
+ {
+ char *tag = NULL, *item = NULL;
+ if ( mp4ff_meta_get_by_index( m_mp4File, k, &item, &tag ) )
+ {
+ if ( item != NULL && tag != NULL )
+ {
+ QString key(item);
+ if ( key.toLower() == "musicbrainz track id" )
+ {
+ QString ret(tag);
+ free( item );
+ free( tag );
+ return ret;
+ }
+ free( item );
+ free( tag );
+ }
+ }
+ }
+ }
+ return QString();
+}*/
+
+
+void AAC_MP4_File::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels )
+{
+ FILE* fp = NULL;
+ mp4ff_callback_t *cb = NULL;
+ NeAACDecHandle decoder = NULL;
+ mp4ff_t* mp4 = NULL;
+ int32_t audioTrack;
+
+ bool success = commonSetup( decoder, cb, fp, mp4, audioTrack );
+
+ if ( success )
+ {
+ // get basic file info
+ mp4AudioSpecificConfig mp4ASC;
+ unsigned char* buffer = NULL;
+ unsigned int buffer_size = 0;
+ double f = 1024.0;
+ unsigned int framesize = 1024;
+
+ int32_t samples = mp4ff_num_samples( mp4, audioTrack );
+
+ if ( buffer )
+ {
+ if ( NeAACDecAudioSpecificConfig(buffer, buffer_size, &mp4ASC) >= 0 )
+ {
+ if ( mp4ASC.frameLengthFlag == 1 )
+ framesize = 960;
+ if ( mp4ASC.sbr_present_flag == 1 )
+ framesize *= 2;
+ if ( mp4ASC.sbr_present_flag == 1 )
+ f = f * 2.0;
+ }
+ free( buffer );
+ }
+
+ samplerate = mp4ff_get_sample_rate( mp4, audioTrack );
+ if ( samplerate > 0 )
+ lengthSecs = static_cast(samples * f / samplerate + 0.5);
+ bitrate = mp4ff_get_avg_bitrate( mp4, audioTrack );
+ nchannels = mp4ff_get_channel_count( mp4, audioTrack );
+
+ mp4ff_close( mp4 );
+ NeAACDecClose( decoder );
+ free( cb );
+ fclose( fp );
+ }
+}
+
+
+bool AAC_MP4_File::init()
+{
+ FILE* fp = NULL;
+
+ bool success = commonSetup( m_decoder, m_mp4cb, fp, m_mp4File, m_mp4AudioTrack );
+ if ( !success )
+ return false;
+
+ unsigned char* buffer = NULL;
+ unsigned int buffer_size = 0;
+ uint32_t samplerate;
+ uint8_t channels;
+
+ mp4ff_get_decoder_config( m_mp4File, m_mp4AudioTrack, &buffer, &buffer_size );
+
+ if( NeAACDecInit2( m_decoder, buffer, buffer_size, &samplerate, &channels) < 0 )
+ {
+ // If some error initializing occured, skip the file
+ if ( fp )
+ fclose( fp );
+ throw std::runtime_error( "Error: unable to initialize AAC decoder library!" );
+ return false;
+ }
+
+ if ( buffer )
+ free( buffer );
+
+ return true;
+}
+
+
+void AAC_MP4_File::postDecode(unsigned long)
+{
+ free( m_inBuf );
+ m_inBuf = NULL;
+ m_mp4SampleId++;
+}
+
+void AAC_MP4_File::skip( const int mSecs )
+{
+ double dur = 0.0;
+ int f = 1;
+ unsigned char *buff = NULL;
+ unsigned int buff_size = 0;
+ uint32_t totalSamples = mp4ff_num_samples( m_mp4File, m_mp4AudioTrack );
+ mp4AudioSpecificConfig mp4ASC;
+
+ mp4ff_get_decoder_config( m_mp4File, m_mp4AudioTrack, &buff, &buff_size );
+
+ if ( buff )
+ {
+ int8_t rc = NeAACDecAudioSpecificConfig( buff, buff_size, &mp4ASC );
+ free( buff );
+ if ( rc >= 0 && mp4ASC.sbr_present_flag == 1 )
+ f = 2;
+
+ // I think the f multiplier is needed here.
+ while ( dur * 1000.0 * f / static_cast(mp4ASC.samplingFrequency) < mSecs && m_mp4SampleId < totalSamples )
+ {
+ dur += mp4ff_get_sample_duration( m_mp4File, m_mp4AudioTrack, m_mp4SampleId );
+ m_mp4SampleId++;
+ }
+ }
+ else
+ std::cerr << "Error: could not skip " << mSecs << " milliseconds" << std::endl;
+}
+
+
+AAC_MP4_File::~AAC_MP4_File()
+{
+ if ( m_mp4File )
+ mp4ff_close( m_mp4File );
+ if ( m_mp4cb )
+ {
+ free( m_mp4cb );
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// AacSource
+//
+////////////////////////////////////////////////////////////////////////
+
+AacSource::AacSource()
+ : m_eof( false )
+ , m_aacFile( NULL )
+{}
+
+
+AacSource::~AacSource()
+{
+ delete m_aacFile;
+}
+
+
+int AacSource::checkHeader()
+{
+ FILE *fp = NULL;
+ unsigned char header[10];
+
+ // check for mp4 file
+ fp = fopen(QFile::encodeName(m_fileName), "rb");
+ if ( !fp )
+ {
+ std::cerr << "Error: failed to open " << strerror( errno ) << std::endl;
+ return AAC_File::AAC_UNKNOWN;
+ }
+
+ fread( header, 1, 10, fp );
+
+ // MP4 headers
+ if ( !memcmp( &header[4], "ftyp", 4 ) )
+ {
+ fclose( fp );
+ return AAC_File::AAC_MP4;
+ }
+
+ // Skip id3 tags
+ int tagsize = 0;
+ if ( !memcmp( header, "ID3", 3 ) )
+ {
+ /* high bit is not used */
+ tagsize = (header[6] << 21) | (header[7] << 14) |
+ (header[8] << 7) | (header[9] << 0);
+
+ tagsize += 10;
+ fseek( fp, tagsize, SEEK_SET );
+ fread( header, 1, 10, fp );
+ }
+
+ // Check for ADTS OR ADIF headers
+ if ( (header[0] == 0xFF) && ((header[1] & 0xF6) == 0xF0) )
+ {
+ fclose( fp );
+ return AAC_File::AAC_ADTS;
+ }
+ else if (memcmp(header, "ADIF", 4) == 0)
+ {
+ fclose( fp );
+ return AAC_File::AAC_ADIF;
+ }
+
+ fclose( fp );
+ return AAC_File::AAC_UNKNOWN;
+}
+
+
+void AacSource::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels )
+{
+ // get the header plus some other stuff..
+
+ m_aacFile->getInfo( lengthSecs, samplerate, bitrate, nchannels );
+}
+
+
+void AacSource::init(const QString& fileName)
+{
+ m_fileName = fileName;
+
+ int headerType = checkHeader();
+ if ( headerType != AAC_File::AAC_UNKNOWN )
+ {
+ if ( headerType == AAC_File::AAC_MP4 )
+ m_aacFile = new AAC_MP4_File(m_fileName, headerType);
+ else
+ m_aacFile = new AAC_ADTS_File( m_fileName, headerType );
+ }
+
+ if ( m_aacFile )
+ m_aacFile->init();
+ else
+ throw std::runtime_error( "ERROR: No suitable AAC decoder found!" );
+}
+
+
+/*QString AacSource::getMbid()
+{
+ QString mbid = m_aacFile->getMbid();
+ return mbid;
+}*/
+
+
+void AacSource::skip( const int mSecs )
+{
+ if ( mSecs < 0 || !m_aacFile->m_decoder )
+ return;
+
+ m_aacFile->skip( mSecs );
+}
+
+
+void AacSource::skipSilence(double silenceThreshold /* = 0.0001 */)
+{
+ if ( !m_aacFile->m_decoder )
+ return;
+
+ silenceThreshold *= static_cast( std::numeric_limits::max() );
+
+ for (;;)
+ {
+ if ( m_aacFile->m_header == AAC_File::AAC_MP4 )
+ {
+ if ( !static_cast(m_aacFile)->readSample() )
+ break;
+ }
+ NeAACDecFrameInfo frameInfo;
+
+ void* sampleBuffer = NeAACDecDecode(m_aacFile->m_decoder, &frameInfo, m_aacFile->m_inBuf, static_cast(m_aacFile->m_inBufSize) );
+
+ m_aacFile->postDecode( frameInfo.bytesconsumed );
+
+ if ( frameInfo.error > 0 )
+ {
+ break;
+ }
+ else if ( frameInfo.samples > 0 )
+ {
+ double sum = 0;
+ int16_t *buf = static_cast(sampleBuffer);
+ switch ( frameInfo.channels )
+ {
+ case 1:
+ for (size_t j = 0; j < frameInfo.samples; ++j)
+ sum += abs( buf[j] );
+ break;
+ case 2:
+ for (size_t j = 0; j < frameInfo.samples; j+=2)
+ sum += abs( (buf[j] >> 1) + (buf[j+1] >> 1) );
+ break;
+ }
+ if ( (sum >= silenceThreshold * static_cast(frameInfo.samples/frameInfo.channels) ) )
+ break;
+ }
+ }
+}
+
+
+int AacSource::updateBuffer( signed short *pBuffer, size_t bufferSize )
+{
+ size_t nwrit = 0; //number of samples written to the output buffer
+
+ if ( m_aacFile->m_overflowSize > 0 )
+ {
+ size_t samples_to_use = bufferSize < m_aacFile->m_overflowSize ? bufferSize : m_aacFile->m_overflowSize;
+ memcpy( pBuffer, m_aacFile->m_overflow, samples_to_use * sizeof(signed short) );
+ nwrit += samples_to_use;
+ m_aacFile->m_overflowSize -= samples_to_use;
+ memmove( (void*)(m_aacFile->m_overflow), (void*)(m_aacFile->m_overflow + samples_to_use*sizeof(signed short)), samples_to_use*sizeof(signed short) );
+ }
+
+ if ( !m_aacFile->m_decoder )
+ return 0;
+
+ for (;;)
+ {
+ signed short* pBufferIt = pBuffer + nwrit;
+ void* sampleBuffer;
+
+ assert( nwrit <= bufferSize );
+
+ if ( m_aacFile->m_header == AAC_File::AAC_MP4 )
+ {
+ if ( !static_cast(m_aacFile)->readSample() )
+ {
+ m_eof = true;
+ return static_cast(nwrit);
+ }
+ }
+ NeAACDecFrameInfo frameInfo;
+
+ sampleBuffer = NeAACDecDecode(m_aacFile->m_decoder, &frameInfo, m_aacFile->m_inBuf, static_cast(m_aacFile->m_inBufSize) );
+ size_t samples_to_use = (bufferSize - nwrit) < frameInfo.samples ? bufferSize-nwrit : frameInfo.samples;
+
+ if ( samples_to_use > 0 && sampleBuffer != NULL )
+ {
+ memcpy( pBufferIt, sampleBuffer, samples_to_use * sizeof(signed short) );
+ nwrit += samples_to_use;
+ }
+
+ if ( samples_to_use < frameInfo.samples )
+ {
+ m_aacFile->m_overflow = static_cast(realloc( m_aacFile->m_overflow, (frameInfo.samples - samples_to_use) * sizeof(signed short) ) );
+ memcpy( m_aacFile->m_overflow, static_cast(sampleBuffer) + samples_to_use, (frameInfo.samples - samples_to_use) * sizeof(signed short) );
+ m_aacFile->m_overflowSize = frameInfo.samples - samples_to_use;
+ }
+
+ m_aacFile->postDecode( frameInfo.bytesconsumed );
+
+ if ( sampleBuffer == NULL )
+ {
+ m_eof = true;
+ break;
+ }
+
+ if ( frameInfo.error > 0 )
+ {
+ std::cerr << "Error: " << NeAACDecGetErrorMessage(frameInfo.error) << std::endl;
+ break;
+ }
+
+ if ( nwrit == bufferSize )
+ break;
+ }
+
+ return static_cast(nwrit);
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.h
new file mode 100644
index 000000000..0fff6f585
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource.h
@@ -0,0 +1,46 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef __AAC_SOURCE_H__
+#define __AAC_SOURCE_H__
+
+#include
+
+class AacSource : public lastfm::FingerprintableSource
+{
+public:
+ AacSource();
+ ~AacSource();
+
+ virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels);
+ virtual void init(const QString& fileName);
+ virtual int updateBuffer(signed short* pBuffer, size_t bufferSize);
+ virtual void skip(const int mSecs);
+ virtual void skipSilence(double silenceThreshold = 0.0001);
+ virtual bool eof() const { return m_eof; }
+
+private:
+ int checkHeader();
+ QString m_fileName;
+ bool m_eof;
+ class AAC_File *m_aacFile;
+};
+
+#endif
+
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource_p.h b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource_p.h
new file mode 100644
index 000000000..870482c1f
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/AacSource_p.h
@@ -0,0 +1,94 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include
+#include
+
+class AAC_File
+{
+public:
+ AAC_File(const QString&, int headerType);
+ virtual ~AAC_File();
+ virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels ) = 0;
+ virtual bool init() = 0;
+ //virtual QString getMbid() = 0;
+ virtual void skip( const int mSecs ) = 0;
+ virtual void postDecode(unsigned long) = 0;
+
+ enum HeaderType
+ {
+ AAC_UNKNOWN = 0,
+ AAC_ADIF,
+ AAC_ADTS,
+ AAC_MP4
+ };
+
+ QString m_fileName;
+ unsigned char *m_inBuf;
+ size_t m_inBufSize;
+ NeAACDecHandle m_decoder;
+ unsigned char *m_overflow;
+ size_t m_overflowSize;
+ int m_header;
+};
+
+
+class AAC_MP4_File : public AAC_File
+{
+public:
+ AAC_MP4_File(const QString&, int headerType = AAC_MP4 );
+ ~AAC_MP4_File();
+ virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels );
+ virtual bool init();
+ //virtual QString getMbid();
+ virtual void skip( const int mSecs );
+ virtual void postDecode(unsigned long);
+ int32_t readSample();
+
+private:
+ bool commonSetup( NeAACDecHandle& handle, mp4ff_callback_t*& cb, FILE*& fp, mp4ff_t*& mp4, int32_t& audioTrack );
+ virtual int32_t getTrack( const mp4ff_t* f );
+ int m_mp4AudioTrack;
+ uint32_t m_mp4SampleId;
+ mp4ff_t *m_mp4File;
+ mp4ff_callback_t *m_mp4cb;
+};
+
+
+class AAC_ADTS_File : public AAC_File
+{
+public:
+ AAC_ADTS_File( const QString& fileName, int headerType );
+ ~AAC_ADTS_File();
+ virtual void getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels );
+ virtual bool init();
+ //virtual QString getMbid();
+ virtual void skip( const int mSecs );
+ virtual void postDecode(unsigned long bytesconsumed );
+
+private:
+ int32_t commonSetup( FILE*& fp, NeAACDecHandle& decoder, unsigned char*& buf, size_t& bufSize, uint32_t& samplerate, uint8_t& channels );
+ void parse( FILE*& fp, unsigned char*& buf, size_t& bufSize, int &bitrate, double &length );
+ void fillBuffer( FILE*& fp, unsigned char*& buf, size_t& bufSize, const size_t m_bytesConsumed );
+
+ FILE* m_file;
+ // These two only needed for skipping AAC ADIF files
+ uint32_t m_adifSamplerate;
+ int m_adifChannels;
+};
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.cpp
new file mode 100644
index 000000000..1e1e29900
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.cpp
@@ -0,0 +1,339 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include "FlacSource.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+
+FLAC__StreamDecoderWriteStatus FlacSource::_write_callback(const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+ assert(client_data != NULL);
+ FlacSource *instance = reinterpret_cast(client_data);
+ assert(instance != NULL);
+ return instance->write_callback(frame, buffer);
+}
+
+FLAC__StreamDecoderWriteStatus FlacSource::write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+{
+ m_outBufLen = 0;
+
+ if ( m_outBuf )
+ {
+ size_t i;
+ for(i = 0; i < frame->header.blocksize; i++)
+ {
+ switch ( m_channels )
+ {
+ case 1:
+ m_outBuf[m_outBufLen] = (FLAC__int16)buffer[0][i]; // mono
+ m_outBufLen++;
+ break;
+ case 2:
+ m_outBuf[m_outBufLen] = (FLAC__int16)buffer[0][i]; // left channel
+ m_outBuf[m_outBufLen+1] = (FLAC__int16)buffer[1][i]; // right channel
+ m_outBufLen += 2;
+ break;
+ }
+ }
+ m_samplePos += frame->header.blocksize;
+ }
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+// ---------------------------------------------------------------------
+
+void FlacSource::_metadata_callback(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ assert(client_data != NULL);
+ FlacSource *instance = reinterpret_cast(client_data);
+ assert(instance != NULL);
+ instance->metadata_callback(metadata);
+}
+
+void FlacSource::metadata_callback( const FLAC__StreamMetadata *metadata )
+{
+ switch ( metadata->type )
+ {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ m_channels = metadata->data.stream_info.channels;
+ m_totalSamples = metadata->data.stream_info.total_samples;
+ m_samplerate = metadata->data.stream_info.sample_rate;
+ m_bps = metadata->data.stream_info.bits_per_sample;
+ m_maxFrameSize = metadata->data.stream_info.max_framesize;
+ break;
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ m_commentData = FLAC__metadata_object_clone(metadata);
+ break;
+ default:
+ break;
+ }
+}
+
+// ---------------------------------------------------------------------
+
+void FlacSource::_error_callback(const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ assert(client_data != NULL);
+ FlacSource *instance = reinterpret_cast(client_data);
+ assert(instance != NULL);
+ instance->error_callback(status);
+}
+
+void FlacSource::error_callback(FLAC__StreamDecoderErrorStatus status)
+{
+ std::cerr << "Got FLAC error: " << FLAC__StreamDecoderErrorStatusString[status] << std::endl;
+}
+
+// ---------------------------------------------------------------------
+
+FlacSource::FlacSource()
+ : m_decoder( 0 )
+ , m_fileSize( 0 )
+ , m_outBuf( 0 )
+ , m_outBufLen( 0 )
+ , m_outBufPos( 0 )
+ , m_samplePos( 0 )
+ , m_maxFrameSize( 0 )
+ , m_commentData( 0 )
+ , m_bps( 0 )
+ , m_channels( 0 )
+ , m_samplerate( 0 )
+ , m_totalSamples( 0 )
+ , m_eof( false )
+{
+}
+
+// ---------------------------------------------------------------------
+
+FlacSource::~FlacSource()
+{
+ if ( m_decoder )
+ {
+ FLAC__stream_decoder_finish( m_decoder );
+ FLAC__stream_decoder_delete( m_decoder );
+ }
+ if ( m_commentData )
+ FLAC__metadata_object_delete( m_commentData );
+ if ( m_outBuf )
+ free( m_outBuf );
+}
+
+// ---------------------------------------------------------------------
+
+void FlacSource::init(const QString& fileName)
+{
+ m_fileName = fileName;
+
+ if ( !m_decoder )
+ {
+ FILE *f = fopen(QFile::encodeName(m_fileName), "rb" );
+ if ( f )
+ {
+ // Need to check which init call to use; flac doesn't do that for us
+ unsigned char header[35];
+ bool isOgg = false;
+ fread( header, 1, 35, f );
+ if ( memcmp(header, "OggS", 4) == 0 &&
+ memcmp(&header[29], "FLAC", 4) == 0 )
+ isOgg = true;
+
+ // getInfo() will need this to calculate bitrate
+ fseek( f, 0, SEEK_END );
+ m_fileSize = ftell(f);
+
+ rewind( f );
+
+ m_decoder = FLAC__stream_decoder_new();
+ FLAC__stream_decoder_set_metadata_respond(m_decoder, FLAC__METADATA_TYPE_STREAMINFO);
+ FLAC__stream_decoder_set_metadata_respond(m_decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+ int init_status;
+ if ( FLAC_API_SUPPORTS_OGG_FLAC && isOgg )
+ init_status = FLAC__stream_decoder_init_ogg_FILE( m_decoder, f, _write_callback, _metadata_callback, _error_callback, this );
+ else
+ init_status = FLAC__stream_decoder_init_FILE( m_decoder, f, _write_callback, _metadata_callback, _error_callback, this );
+
+ if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ return;
+
+ FLAC__stream_decoder_process_until_end_of_metadata( m_decoder );
+ m_outBuf = static_cast(malloc( sizeof(signed short)*m_maxFrameSize));
+
+ if ( m_bps != 16 )
+ {
+ FLAC__stream_decoder_finish( m_decoder );
+ FLAC__stream_decoder_delete( m_decoder );
+ FLAC__metadata_object_delete( m_commentData );
+ m_decoder = 0;
+ m_commentData = 0;
+ throw std::runtime_error( "ERROR: only 16 bit FLAC files are currently supported!" );
+ }
+ }
+ else
+ throw std::runtime_error( "ERROR: cannot load FLAC file!" );
+ }
+}
+
+// ---------------------------------------------------------------------
+
+/*QString FlacSource::getMbid()
+{
+ if ( m_commentData )
+ {
+ FLAC__StreamMetadata_VorbisComment *vc;
+ vc = &m_commentData->data.vorbis_comment;
+ for ( unsigned int i = 0; i < vc->num_comments; ++i )
+ {
+ QByteArray key( (char*)(vc->comments[i].entry), vc->comments[i].length );
+ if ( key.left(20).toLower() == "musicbrainz_trackid=" )
+ {
+ QString val = key.mid(20);
+ return val;
+ }
+ }
+ }
+
+ return QString();
+}*/
+
+// ---------------------------------------------------------------------
+
+void FlacSource::getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels)
+{
+ lengthSecs = 0;
+ samplerate = 0;
+ bitrate = 0;
+ nchannels = 0;
+
+ if ( m_decoder )
+ {
+ samplerate = m_samplerate;
+ nchannels = m_channels;
+ if ( samplerate > 0 )
+ lengthSecs = static_cast( static_cast(m_totalSamples)/m_samplerate + 0.5);
+
+ // Calcuate bitrate
+ if ( lengthSecs > 0 )
+ {
+ FLAC__Metadata_SimpleIterator *it = FLAC__metadata_simple_iterator_new();
+ FLAC__metadata_simple_iterator_init( it, QFile::encodeName(m_fileName), true, true );
+ while( !FLAC__metadata_simple_iterator_is_last( it ) )
+ {
+ FLAC__metadata_simple_iterator_next( it );
+ }
+ off_t audioOffset = FLAC__metadata_simple_iterator_get_block_offset( it ) +
+ FLAC__metadata_simple_iterator_get_block_length( it );
+ FLAC__metadata_simple_iterator_delete( it );
+ bitrate = static_cast( static_cast(m_fileSize - audioOffset) * 8 / lengthSecs + 0.5 );
+ }
+ }
+}
+
+// ---------------------------------------------------------------------
+
+void FlacSource::skip( const int mSecs )
+{
+ FLAC__uint64 absSample = mSecs * m_samplerate / 1000 + m_samplePos;
+ if ( !FLAC__stream_decoder_seek_absolute(m_decoder, absSample) )
+ FLAC__stream_decoder_reset( m_decoder );
+ m_outBufLen = 0;
+}
+
+// ---------------------------------------------------------------------
+
+void FlacSource::skipSilence(double silenceThreshold /* = 0.0001 */)
+{
+ silenceThreshold *= static_cast( std::numeric_limits::max() );
+ for ( ;; )
+ {
+ double sum = 0;
+ bool result = FLAC__stream_decoder_process_single( m_decoder );
+ // there was a fatal read
+ if ( !result )
+ break;
+
+ switch ( m_channels )
+ {
+ case 1:
+ for (size_t j = 0; j < m_outBufLen; ++j)
+ sum += abs( m_outBuf[j] );
+ break;
+ case 2:
+ for ( size_t j = 0; j < m_outBufLen; j+=2 )
+ sum += abs( (m_outBuf[j] >> 1)
+ + (m_outBuf[j+1] >> 1) );
+ break;
+ }
+ if ( (sum >= silenceThreshold * static_cast(m_outBufLen) ) )
+ break;
+ }
+ m_outBufLen = 0;
+}
+
+// ---------------------------------------------------------------------
+
+int FlacSource::updateBuffer( signed short *pBuffer, size_t bufferSize )
+{
+ size_t nwrit = 0;
+
+ for ( ;; )
+ {
+ size_t samples_to_use = std::min (bufferSize - nwrit, m_outBufLen - m_outBufPos);
+ signed short* pBufferIt = pBuffer + nwrit;
+
+ nwrit += samples_to_use;
+ assert( nwrit <= bufferSize );
+ memcpy( pBufferIt, m_outBuf + m_outBufPos, sizeof(signed short)*samples_to_use );
+
+ if ( samples_to_use < m_outBufLen - m_outBufPos )
+ m_outBufPos = samples_to_use;
+ else
+ {
+ m_outBufPos = 0;
+ bool result = FLAC__stream_decoder_process_single( m_decoder );
+ // there was a fatal read
+ if ( !result )
+ {
+ std::cerr << "Fatal error decoding FLAC" << std::endl;
+ return 0;
+ }
+ else if ( FLAC__stream_decoder_get_state( m_decoder ) == FLAC__STREAM_DECODER_END_OF_STREAM )
+ {
+ m_eof = true;
+ break;
+ }
+ }
+
+ if ( nwrit == bufferSize )
+ return static_cast(nwrit);
+ }
+ return static_cast(nwrit);
+}
+
+// -----------------------------------------------------------------------------
+
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.h
new file mode 100644
index 000000000..24dd97f11
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/FlacSource.h
@@ -0,0 +1,74 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef __FLAC_SOURCE_H__
+#define __FLAC_SOURCE_H__
+
+#include
+#include
+#include
+
+
+class FlacSource : public lastfm::FingerprintableSource
+{
+public:
+ FlacSource();
+ virtual ~FlacSource();
+
+ virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels);
+ virtual void init(const QString& fileName);
+
+ // return a chunk of PCM data from the FLAC file
+ virtual int updateBuffer(signed short* pBuffer, size_t bufferSize);
+
+ virtual void skip(const int mSecs);
+ virtual void skipSilence(double silenceThreshold = 0.0001);
+
+ //QString getMbid();
+
+ bool eof() const { return m_eof; }
+
+private:
+ static FLAC__StreamDecoderWriteStatus _write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
+ static void _metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
+ static void _error_callback(const ::FLAC__StreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *client_data);
+
+ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+ void metadata_callback( const FLAC__StreamMetadata *metadata );
+ void error_callback(FLAC__StreamDecoderErrorStatus status);
+
+ FLAC__StreamDecoder *m_decoder;
+ QString m_fileName;
+ size_t m_fileSize;
+ short *m_outBuf;
+ size_t m_outBufLen;
+ size_t m_outBufPos;
+ FLAC__uint64 m_samplePos;
+ unsigned m_maxFrameSize;
+ FLAC__StreamMetadata* m_commentData;
+ unsigned m_bps;
+ unsigned m_channels;
+ unsigned m_samplerate;
+ FLAC__uint64 m_totalSamples;
+
+ bool m_eof;
+};
+
+#endif
+
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.cpp
new file mode 100644
index 000000000..00e725ae6
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.cpp
@@ -0,0 +1,514 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "MadSource.h"
+
+#undef max // was definded in mad
+
+using namespace std;
+
+
+// -----------------------------------------------------------
+
+MadSource::MadSource()
+ : m_pMP3_Buffer ( new unsigned char[m_MP3_BufferSize+MAD_BUFFER_GUARD] )
+{}
+
+// -----------------------------------------------------------
+
+MadSource::~MadSource()
+{
+ if ( m_inputFile.isOpen() )
+ {
+ m_inputFile.close();
+ mad_synth_finish(&m_mad_synth);
+ mad_frame_finish(&m_mad_frame);
+ mad_stream_finish(&m_mad_stream);
+ }
+ if (m_pMP3_Buffer) delete[] m_pMP3_Buffer;
+}
+
+// ---------------------------------------------------------------------
+
+inline short f2s(mad_fixed_t f)
+{
+ /* A fixed point number is formed of the following bit pattern:
+ *
+ * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ * MSB LSB
+ * S ==> Sign (0 is positive, 1 is negative)
+ * W ==> Whole part bits
+ * F ==> Fractional part bits
+ *
+ * This pattern contains MAD_F_FRACBITS fractional bits, one
+ * should alway use this macro when working on the bits of a fixed
+ * point number. It is not guaranteed to be constant over the
+ * different platforms supported by libmad.
+ *
+ * The signed short value is formed, after clipping, by the least
+ * significant whole part bit, followed by the 15 most significant
+ * fractional part bits. Warning: this is a quick and dirty way to
+ * compute the 16-bit number, madplay includes much better
+ * algorithms.
+ */
+
+ /* Clipping */
+ if(f >= MAD_F_ONE)
+ return(SHRT_MAX);
+ if(f <= -MAD_F_ONE)
+ return(-SHRT_MAX);
+
+ /* Conversion. */
+ f = f >> (MAD_F_FRACBITS-15);
+ return (signed short)f;
+}
+
+// ---------------------------------------------------------------------
+
+string MadSource::MadErrorString(const mad_error& error)
+{
+ switch(error)
+ {
+ /* Generic unrecoverable errors. */
+ case MAD_ERROR_BUFLEN:
+ return("input buffer too small (or EOF)");
+ case MAD_ERROR_BUFPTR:
+ return("invalid (null) buffer pointer");
+ case MAD_ERROR_NOMEM:
+ return("not enough memory");
+
+ /* Frame header related unrecoverable errors. */
+ case MAD_ERROR_LOSTSYNC:
+ return("lost synchronization");
+ case MAD_ERROR_BADLAYER:
+ return("reserved header layer value");
+ case MAD_ERROR_BADBITRATE:
+ return("forbidden bitrate value");
+ case MAD_ERROR_BADSAMPLERATE:
+ return("reserved sample frequency value");
+ case MAD_ERROR_BADEMPHASIS:
+ return("reserved emphasis value");
+
+ /* Recoverable errors */
+ case MAD_ERROR_BADCRC:
+ return("CRC check failed");
+ case MAD_ERROR_BADBITALLOC:
+ return("forbidden bit allocation value");
+ case MAD_ERROR_BADSCALEFACTOR:
+ return("bad scalefactor index");
+ case MAD_ERROR_BADFRAMELEN:
+ return("bad frame length");
+ case MAD_ERROR_BADBIGVALUES:
+ return("bad big_values count");
+ case MAD_ERROR_BADBLOCKTYPE:
+ return("reserved block_type");
+ case MAD_ERROR_BADSCFSI:
+ return("bad scalefactor selection info");
+ case MAD_ERROR_BADDATAPTR:
+ return("bad main_data_begin pointer");
+ case MAD_ERROR_BADPART3LEN:
+ return("bad audio data length");
+ case MAD_ERROR_BADHUFFTABLE:
+ return("bad Huffman table select");
+ case MAD_ERROR_BADHUFFDATA:
+ return("Huffman data overrun");
+ case MAD_ERROR_BADSTEREO:
+ return("incompatible block_type for JS");
+
+ /* Unknown error. This switch may be out of sync with libmad's
+ * defined error codes.
+ */
+ default:
+ return("Unknown error code");
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool MadSource::isRecoverable(const mad_error& error, bool log)
+{
+ if (MAD_RECOVERABLE (error))
+ {
+ /* Do not print a message if the error is a loss of
+ * synchronization and this loss is due to the end of
+ * stream guard bytes. (See the comments marked {3}
+ * supra for more informations about guard bytes.)
+ */
+ if (error != MAD_ERROR_LOSTSYNC /*|| mad_stream.this_frame != pGuard */ && log)
+ {
+ cerr << "Recoverable frame level error: "
+ << MadErrorString(error) << endl;
+ }
+
+ return true;
+ }
+ else
+ {
+ if (error == MAD_ERROR_BUFLEN)
+ return true;
+ else
+ {
+ stringstream ss;
+
+ ss << "Unrecoverable frame level error: "
+ << MadErrorString (error) << endl;
+ throw ss.str();
+ }
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------
+
+void MadSource::init(const QString& fileName)
+{
+ m_inputFile.setFileName( m_fileName = fileName );
+ bool fine = m_inputFile.open( QIODevice::ReadOnly );
+
+ if ( !fine )
+ {
+ throw std::runtime_error ("Cannot load mp3 file!");
+ }
+
+ mad_stream_init(&m_mad_stream);
+ mad_frame_init (&m_mad_frame);
+ mad_synth_init (&m_mad_synth);
+ mad_timer_reset(&m_mad_timer);
+
+ m_pcmpos = m_mad_synth.pcm.length;
+}
+
+// -----------------------------------------------------------------------------
+
+/*QString MadSource::getMbid()
+{
+ char out[MBID_BUFFER_SIZE];
+ int const r = getMP3_MBID( QFile::encodeName( m_fileName ), out );
+ if (r == 0)
+ return QString::fromLatin1( out );
+ return QString();
+}*/
+
+void MadSource::getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels )
+{
+ // get the header plus some other stuff..
+ QFile inputFile(m_fileName);
+ bool fine = inputFile.open( QIODevice::ReadOnly );
+
+ if ( !fine )
+ {
+ throw std::runtime_error ("ERROR: Cannot load file for getInfo!");
+ return;
+ }
+
+ unsigned char* pMP3_Buffer = new unsigned char[m_MP3_BufferSize+MAD_BUFFER_GUARD];
+
+ mad_stream madStream;
+ mad_header madHeader;
+ mad_timer_t madTimer;
+
+ mad_stream_init(&madStream);
+ mad_timer_reset(&madTimer);
+
+ double avgSamplerate = 0;
+ double avgBitrate = 0;
+ double avgNChannels = 0;
+ int nFrames = 0;
+
+ while ( fetchData( inputFile, pMP3_Buffer, m_MP3_BufferSize, madStream) )
+ {
+ if ( mad_header_decode(&madHeader, &madStream) != 0 )
+ {
+ if ( isRecoverable(madStream.error) )
+ continue;
+ else
+ break;
+ }
+
+ mad_timer_add(&madTimer, madHeader.duration);
+
+ avgSamplerate += madHeader.samplerate;
+ avgBitrate += madHeader.bitrate;
+
+ if ( madHeader.mode == MAD_MODE_SINGLE_CHANNEL )
+ ++avgNChannels;
+ else
+ avgNChannels += 2;
+
+ ++nFrames;
+ }
+
+ inputFile.close();
+ mad_stream_finish(&madStream);
+ mad_header_finish(&madHeader);
+ delete[] pMP3_Buffer;
+
+
+ lengthSecs = static_cast(madTimer.seconds);
+ samplerate = static_cast( (avgSamplerate/nFrames) + 0.5 );
+ bitrate = static_cast( (avgBitrate/nFrames) + 0.5 );
+ nchannels = static_cast( (avgNChannels/nFrames) + 0.5 );
+}
+
+// -----------------------------------------------------------
+
+
+bool MadSource::fetchData( QFile& mp3File,
+ unsigned char* pMP3_Buffer,
+ const int MP3_BufferSize,
+ mad_stream& madStream )
+{
+ unsigned char *pReadStart = NULL;
+ unsigned char *pGuard = NULL;
+
+ if ( madStream.buffer == NULL ||
+ madStream.error == MAD_ERROR_BUFLEN )
+ {
+
+ size_t readSize;
+ size_t remaining;
+
+ /* {2} libmad may not consume all bytes of the input
+ * buffer. If the last frame in the buffer is not wholly
+ * contained by it, then that frame's start is pointed by
+ * the next_frame member of the Stream structure. This
+ * common situation occurs when mad_frame_decode() fails,
+ * sets the stream error code to MAD_ERROR_BUFLEN, and
+ * sets the next_frame pointer to a non NULL value. (See
+ * also the comment marked {4} bellow.)
+ *
+ * When this occurs, the remaining unused bytes must be
+ * put back at the beginning of the buffer and taken in
+ * account before refilling the buffer. This means that
+ * the input buffer must be large enough to hold a whole
+ * frame at the highest observable bit-rate (currently 448
+ * kb/s). XXX=XXX Is 2016 bytes the size of the largest
+ * frame? (448000*(1152/32000))/8
+ */
+ if (madStream.next_frame != NULL)
+ {
+ remaining = madStream.bufend - madStream.next_frame;
+ memmove (pMP3_Buffer, madStream.next_frame, remaining);
+
+ pReadStart = pMP3_Buffer + remaining;
+ readSize = MP3_BufferSize - remaining;
+ }
+ else
+ {
+ readSize = MP3_BufferSize;
+ pReadStart = pMP3_Buffer;
+ remaining = 0;
+ }
+
+ readSize = mp3File.read( reinterpret_cast(pReadStart), readSize );
+
+ // nothing else to read!
+ if (readSize <= 0)
+ return false;
+
+ if ( mp3File.atEnd() )
+ {
+ pGuard = pReadStart + readSize;
+
+ memset (pGuard, 0, MAD_BUFFER_GUARD);
+ readSize += MAD_BUFFER_GUARD;
+ }
+
+ // Pipe the new buffer content to libmad's stream decoder facility.
+ mad_stream_buffer( &madStream, pMP3_Buffer,
+ static_cast(readSize + remaining));
+
+ madStream.error = MAD_ERROR_NONE;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+
+void MadSource::skipSilence(double silenceThreshold /* = 0.0001 */)
+{
+ mad_frame madFrame;
+ mad_synth madSynth;
+
+ mad_frame_init(&madFrame);
+ mad_synth_init (&madSynth);
+
+ silenceThreshold *= static_cast( numeric_limits::max() );
+
+ for (;;)
+ {
+ if ( !fetchData( m_inputFile, m_pMP3_Buffer, m_MP3_BufferSize, m_mad_stream) )
+ break;
+
+ if ( mad_frame_decode(&madFrame, &m_mad_stream) != 0 )
+ {
+ if ( isRecoverable(m_mad_stream.error) )
+ continue;
+ else
+ break;
+ }
+
+ mad_synth_frame (&madSynth, &madFrame);
+
+ double sum = 0;
+
+ switch (madSynth.pcm.channels)
+ {
+ case 1:
+ for (size_t j = 0; j < madSynth.pcm.length; ++j)
+ sum += abs(f2s(madSynth.pcm.samples[0][j]));
+ break;
+ case 2:
+ for (size_t j = 0; j < madSynth.pcm.length; ++j)
+ sum += abs(f2s(
+ (madSynth.pcm.samples[0][j] >> 1)
+ + (madSynth.pcm.samples[1][j] >> 1)));
+ break;
+ }
+
+ if ( (sum >= silenceThreshold * madSynth.pcm.length) )
+ break;
+ }
+
+ mad_frame_finish(&madFrame);
+}
+
+// -----------------------------------------------------------------------------
+
+void MadSource::skip(const int mSecs)
+{
+ if ( mSecs <= 0 )
+ return;
+
+ mad_header madHeader;
+ mad_header_init(&madHeader);
+
+ for (;;)
+ {
+ if (!fetchData( m_inputFile, m_pMP3_Buffer, m_MP3_BufferSize, m_mad_stream))
+ break;
+
+ if ( mad_header_decode(&madHeader, &m_mad_stream) != 0 )
+ {
+ if ( isRecoverable(m_mad_stream.error) )
+ continue;
+ else
+ break;
+ }
+
+ mad_timer_add(&m_mad_timer, madHeader.duration);
+
+ if ( mad_timer_count(m_mad_timer, MAD_UNITS_MILLISECONDS) >= mSecs )
+ break;
+ }
+
+ mad_header_finish(&madHeader);
+}
+
+// -----------------------------------------------------------
+
+int MadSource::updateBuffer(signed short* pBuffer, size_t bufferSize)
+{
+ size_t nwrit = 0; //number of samples written to the output buffer
+
+ for (;;)
+ {
+ // get a (valid) frame
+ // m_pcmpos == 0 could mean two things
+ // - we have completely decoded a frame, but the output buffer is still
+ // not full (it would make more sense for pcmpos == pcm.length(), but
+ // the loop assigns pcmpos = 0 at the end and does it this way!
+ // - we are starting a stream
+ if ( m_pcmpos == m_mad_synth.pcm.length )
+ {
+ if ( !fetchData( m_inputFile, m_pMP3_Buffer, m_MP3_BufferSize, m_mad_stream) )
+ {
+ break; // nothing else to read
+ }
+
+ // decode the frame
+ if (mad_frame_decode (&m_mad_frame, &m_mad_stream))
+ {
+ if ( isRecoverable(m_mad_stream.error) )
+ continue;
+ else
+ break;
+ } // if (mad_frame_decode (&madFrame, &madStream))
+
+ mad_timer_add (&m_mad_timer, m_mad_frame.header.duration);
+ mad_synth_frame (&m_mad_synth, &m_mad_frame);
+
+ m_pcmpos = 0;
+ }
+
+ size_t samples_for_mp3 = m_mad_synth.pcm.length - m_pcmpos;
+ size_t samples_for_buf = bufferSize - nwrit;
+ signed short* pBufferIt = pBuffer + nwrit;
+ size_t i = 0, j = 0;
+
+ switch( m_mad_synth.pcm.channels )
+ {
+ case 1:
+ {
+ size_t samples_to_use = min (samples_for_mp3, samples_for_buf);
+ for (i = 0; i < samples_to_use; ++i )
+ pBufferIt[i] = f2s( m_mad_synth.pcm.samples[0][i+m_pcmpos] );
+ }
+ j = i;
+ break;
+
+ case 2:
+ for (; i < samples_for_mp3 && j < samples_for_buf ; ++i, j+=2 )
+ {
+ pBufferIt[j] = f2s( m_mad_synth.pcm.samples[0][i+m_pcmpos] );
+ pBufferIt[j+1] = f2s( m_mad_synth.pcm.samples[1][i+m_pcmpos] );
+ }
+ break;
+
+ default:
+ cerr << "wtf kind of mp3 has " << m_mad_synth.pcm.channels << " channels??\n";
+ break;
+ }
+
+ m_pcmpos += i;
+ nwrit += j;
+
+ assert( nwrit <= bufferSize );
+
+ if (nwrit == bufferSize)
+ return static_cast(nwrit);
+ }
+
+ return static_cast(nwrit);
+}
+
+// -----------------------------------------------------------------------------
+
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.h
new file mode 100644
index 000000000..c22cb6d5d
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/MadSource.h
@@ -0,0 +1,69 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+#ifndef __MP3_SOURCE_H__
+#define __MP3_SOURCE_H__
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+class MadSource : public lastfm::FingerprintableSource
+{
+public:
+ MadSource();
+ ~MadSource();
+
+ virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels);
+ virtual void init(const QString& fileName);
+ virtual int updateBuffer(signed short* pBuffer, size_t bufferSize);
+ virtual void skip(const int mSecs);
+ virtual void skipSilence(double silenceThreshold = 0.0001);
+ virtual bool eof() const { return m_inputFile.atEnd(); }
+
+private:
+ static bool fetchData( QFile& mp3File,
+ unsigned char* pMP3_Buffer,
+ const int MP3_BufferSize,
+ mad_stream& madStream );
+
+ static bool isRecoverable(const mad_error& error, bool log = false);
+
+ static std::string MadErrorString(const mad_error& error);
+
+ struct mad_stream m_mad_stream;
+ struct mad_frame m_mad_frame;
+ mad_timer_t m_mad_timer;
+ struct mad_synth m_mad_synth;
+
+ QFile m_inputFile;
+
+ unsigned char* m_pMP3_Buffer;
+ static const int m_MP3_BufferSize = (5*8192);
+ QString m_fileName;
+
+ size_t m_pcmpos;
+};
+
+#endif
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.cpp
new file mode 100644
index 000000000..fd4defb17
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.cpp
@@ -0,0 +1,204 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include "VorbisSource.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// These specify the output format
+static const int wordSize = 2; // 16 bit output
+static const int isSigned = 1;
+#if __BIG_ENDIAN__
+static const int isBigEndian = 1;
+#else
+static const int isBigEndian = 0;
+#endif
+
+
+VorbisSource::VorbisSource()
+ : m_channels( 0 )
+ , m_samplerate( 0 )
+ , m_eof( false )
+{
+ memset( &m_vf, 0, sizeof(m_vf) );
+}
+
+// ---------------------------------------------------------------------
+
+VorbisSource::~VorbisSource()
+{
+ // ov_clear() also closes the file
+ ov_clear( &m_vf );
+}
+
+// ---------------------------------------------------------------------
+
+void VorbisSource::init(const QString& fileName)
+{
+ m_fileName = fileName;
+
+ if ( m_vf.datasource )
+ {
+ std::cerr << "Warning: file already appears to be open";
+ return;
+ }
+
+ FILE *fp = fopen(QFile::encodeName(m_fileName), "rb" );
+ if( !fp )
+ throw std::runtime_error( "ERROR: Cannot open ogg file!" );
+
+ // See the warning about calling ov_open on Windows
+ if ( ov_test_callbacks( fp, &m_vf, NULL, 0, OV_CALLBACKS_DEFAULT ) < 0 )
+ {
+ fclose( fp );
+ throw std::runtime_error( "ERROR: This is not an ogg vorbis file!" );
+ }
+
+ ov_test_open( &m_vf );
+
+ // Don't fingerprint files with more than one logical bitstream
+ // They most likely contain more than one track
+ if ( ov_streams( &m_vf ) != 1 )
+ throw std::runtime_error( "ERROR: ogg file contains multiple bitstreams" );
+
+ m_channels = ov_info( &m_vf, 0 )->channels;
+ m_samplerate = static_cast(ov_info( &m_vf, 0 )->rate);
+ m_eof = false;
+}
+
+void VorbisSource::getInfo( int& lengthSecs, int& samplerate, int& bitrate, int& nchannels)
+{
+ // stream info
+ nchannels = ov_info( &m_vf, -1 )->channels;
+ samplerate = static_cast(ov_info( &m_vf, -1 )->rate);
+ lengthSecs = static_cast(ov_time_total( &m_vf, -1 ) + 0.5);
+ bitrate = static_cast(ov_bitrate( &m_vf, -1 ));
+}
+
+// ---------------------------------------------------------------------
+
+void VorbisSource::skip( const int mSecs )
+{
+ if ( mSecs < 0 )
+ return;
+
+ double ts = mSecs / 1000.0 + ov_time_tell( &m_vf );
+ ov_time_seek( &m_vf, ts );
+}
+
+// ---------------------------------------------------------------------
+
+void VorbisSource::skipSilence(double silenceThreshold /* = 0.0001 */)
+{
+ silenceThreshold *= static_cast( std::numeric_limits::max() );
+
+ char sampleBuffer[4096];
+ int bs = 0;
+ for (;;)
+ {
+ long charReadBytes = ov_read( &m_vf, sampleBuffer, 4096, isBigEndian, wordSize, isSigned, &bs );
+
+ // eof
+ if ( !charReadBytes )
+ {
+ m_eof = true;
+ break;
+ }
+ if ( charReadBytes < 0 )
+ {
+ // a bad bit of data: OV_HOLE || OV_EBADLINK
+ continue;
+ }
+ else if ( charReadBytes > 0 )
+ {
+ double sum = 0;
+ int16_t *buf = reinterpret_cast(sampleBuffer);
+ switch ( m_channels )
+ {
+ case 1:
+ for (long j = 0; j < charReadBytes/wordSize; j++)
+ sum += abs( buf[j] );
+ break;
+ case 2:
+ for (long j = 0; j < charReadBytes/wordSize; j+=2)
+ sum += abs( (buf[j] >> 1) + (buf[j+1] >> 1) );
+ break;
+ }
+ if ( sum >= silenceThreshold * static_cast(charReadBytes/wordSize/m_channels) )
+ break;
+ }
+ }
+}
+
+// ---------------------------------------------------------------------
+
+int VorbisSource::updateBuffer( signed short *pBuffer, size_t bufferSize )
+{
+ char buf[ bufferSize * wordSize ];
+ int bs = 0;
+ size_t charwrit = 0; //number of samples written to the output buffer
+
+ for (;;)
+ {
+ long charReadBytes = ov_read( &m_vf, buf, static_cast(bufferSize * wordSize - charwrit),
+ isBigEndian, wordSize, isSigned, &bs );
+ if ( !charReadBytes )
+ {
+ m_eof = true;
+ break; // nothing else to read
+ }
+
+ // Don't really need this though since we're excluding files that have
+ // more than one logical bitstream
+ if ( bs != 0 )
+ {
+ vorbis_info *vi = ov_info( &m_vf, -1 );
+ if ( m_channels != vi->channels || m_samplerate != vi->rate )
+ {
+ std::cerr << "Files that change channel parameters or samplerate are currently not supported" << std::endl;
+ return 0;
+ }
+ }
+
+ if( charReadBytes < 0 )
+ {
+ std::cerr << "Warning: corrupt section of data, attempting to continue..." << std::endl;
+ continue;
+ }
+
+ char* pBufferIt = reinterpret_cast(pBuffer) + charwrit;
+ charwrit += charReadBytes;
+
+ assert( charwrit <= bufferSize * wordSize );
+ memcpy( pBufferIt, buf, charReadBytes );
+
+ if (charwrit == bufferSize * wordSize)
+ return static_cast(charwrit/wordSize);
+ }
+
+ return static_cast(charwrit/wordSize);
+}
+
+// -----------------------------------------------------------------------------
+
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.h b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.h
new file mode 100644
index 000000000..988ce6239
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/VorbisSource.h
@@ -0,0 +1,47 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef __VORBIS_SOURCE_H__
+#define __VORBIS_SOURCE_H__
+
+#include
+#include
+
+
+class VorbisSource : public lastfm::FingerprintableSource
+{
+public:
+ VorbisSource();
+ ~VorbisSource();
+ virtual void getInfo(int& lengthSecs, int& samplerate, int& bitrate, int& nchannels);
+ virtual void init(const QString& fileName);
+ virtual int updateBuffer(signed short* pBuffer, size_t bufferSize);
+ virtual void skip(const int mSecs);
+ virtual void skipSilence(double silenceThreshold = 0.0001);
+ virtual bool eof() const { return m_eof; }
+
+private:
+ OggVorbis_File m_vf;
+ QString m_fileName;
+ int m_channels;
+ int m_samplerate;
+ bool m_eof;
+};
+
+#endif
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/lastfm-fingerprint.pro b/thirdparty/liblastfm2/src/fingerprint/contrib/lastfm-fingerprint.pro
new file mode 100644
index 000000000..bd615e723
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/lastfm-fingerprint.pro
@@ -0,0 +1,13 @@
+QT = core xml network
+LIBS += -L$$DESTDIR -llastfm -llastfm_fingerprint
+LIBS += -lvorbisfile -lFLAC -lfaad -lmp4ff -lmad
+SOURCES = AacSource.cpp FlacSource.cpp MadSource.cpp VorbisSource.cpp main.cpp
+
+mac {
+ INCLUDEPATH += /opt/local/include
+ LIBS += -L/opt/local/lib
+
+ DEFINES += MACPORTS_SUCKS
+ SOURCES -= AacSource.cpp
+ LIBS -= -lmp4ff
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/contrib/main.cpp b/thirdparty/liblastfm2/src/fingerprint/contrib/main.cpp
new file mode 100644
index 000000000..3b035f2d8
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/contrib/main.cpp
@@ -0,0 +1,173 @@
+/*
+ Copyright 2009 Last.fm Ltd.
+ Copyright 2009 John Stamp
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+
+// ubuntu 9.04: sudo apt-get install libmad0-dev libvorbis-dev libflac-dev libfaac-dev
+// macports: sudo port install libmad libvorbis libflac
+// Windows: lol
+
+#include "MadSource.h"
+#include "VorbisSource.h"
+#include "FlacSource.h"
+#include "AacSource.h"
+#include
+#include
+#include
+#include
+#include
+int typeOf(const QString& path);
+lastfm::FingerprintableSource* factory(int type);
+enum { MP3, OGG, FLAC, AAC, UNKNOWN };
+namespace lastfm { Track taglib(const QString& path); }
+
+
+int main(int argc, char** argv) try
+{
+ if (argc < 2) {
+ std::cerr << "usage: " << argv[0] << " path" << std::endl;
+ return 1;
+ }
+
+ QCoreApplication app(argc, argv);
+ QEventLoop loop;
+
+ QString const path = QFile::decodeName(argv[1]);
+
+ lastfm::Track t = lastfm::taglib(path); //see contrib //TODO mbid
+ lastfm::Fingerprint fp(t);
+ if (fp.id().isNull()) {
+ lastfm::FingerprintableSource* src = factory(typeOf(path));
+ fp.generate(src);
+ QNetworkReply* reply = fp.submit();
+ loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
+ fp.decode(reply);
+ }
+
+ QNetworkReply* reply = fp.id().getSuggestions();
+ loop.connect(reply, SIGNAL(finished()), SLOT(quit()));
+
+ std::cout << reply->readAll().data() << std::endl; //returns XML
+ return 0;
+}
+catch (std::exception& e)
+{
+ std::cerr << e.what() << std::endl;
+}
+
+lastfm::FingerprintableSource* factory(int type)
+{
+ switch (type) {
+ case MP3: return new MadSource;
+ case OGG: return new VorbisSource;
+ case FLAC: return new FlacSource;
+ #ifndef MACPORTS_SUCKS
+ case AAC: return new AacSource;
+ #endif
+ default: throw std::runtime_error("Cannot handle filetype");
+ }
+}
+
+int typeOf(const QString& fileName)
+{
+ QStringList parts = fileName.split( "." );
+ QString extension;
+ if ( parts.size() > 1 )
+ extension = parts.last();
+
+ // Let's be trusting about extensions
+ if ( extension.toLower() == "mp3" )
+ return MP3;
+ else if ( extension.toLower() == "ogg" )
+ return OGG;
+ else if ( extension.toLower() == "oga" )
+ return FLAC;
+ else if ( extension.toLower() == "flac" )
+ return FLAC;
+ else if ( extension.toLower() == "aac" )
+ return AAC;
+ else if ( extension.toLower() == "m4a" )
+ return AAC;
+
+ // So much for relying on extensions. Let's try file magic instead.
+ FILE *fp = NULL;
+ unsigned char header[35];
+
+ fp = fopen(QFile::encodeName(fileName), "rb");
+ if ( !fp )
+ {
+ return UNKNOWN;
+ }
+ int fType = UNKNOWN;
+ fread( header, 1, 35, fp );
+
+ // Some formats can have ID3 tags (or not), so let's just
+ // get them out of the way first before we check what we have.
+ if ( memcmp( header, "ID3", 3) == 0 )
+ {
+ int tagsize = 0;
+ /* high bit is not used */
+ tagsize = (header[6] << 21) | (header[7] << 14) |
+ (header[8] << 7) | (header[9] << 0);
+
+ tagsize += 10;
+ fseek( fp, tagsize, SEEK_SET );
+ fread( header, 1, 35, fp );
+ }
+
+ if ( (header[0] == 0xFF) && ((header[1] & 0xFE) == 0xFA ) )
+ {
+ fType = MP3;
+ }
+ else if ( memcmp(header, "OggS", 4) == 0 )
+ {
+ if ( memcmp(&header[29], "vorbis", 6) == 0 )
+ {
+ // ogg vorbis (.ogg)
+ fType = OGG;
+ }
+ else if ( memcmp(&header[29], "FLAC", 4) == 0 )
+ {
+ // ogg flac (.oga)
+ fType = FLAC;
+ }
+ }
+ else if ( memcmp(header, "fLaC", 4 ) == 0 )
+ {
+ // flac file
+ fType = FLAC;
+ }
+ else if ( (header[0] == 0xFF) && ((header[1] & 0xF6) == 0xF0) )
+ {
+ // aac adts
+ fType = AAC;
+ }
+ else if (memcmp(header, "ADIF", 4) == 0)
+ {
+ // aac adif
+ fType = AAC;
+ }
+ else if ( memcmp( &header[4], "ftyp", 4 ) == 0 )
+ {
+ // mp4 header: aac
+ fType = AAC;
+ }
+
+ fclose(fp);
+ return fType;
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/fingerprint.pro b/thirdparty/liblastfm2/src/fingerprint/fingerprint.pro
new file mode 100644
index 000000000..043ad7bfb
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/fingerprint.pro
@@ -0,0 +1,25 @@
+TEMPLATE = lib
+TARGET = lastfm_fingerprint
+LIBS += -L$$DESTDIR -llastfm
+QT = core xml network sql
+include( _files.qmake )
+DEFINES += LASTFM_FINGERPRINT_LIB
+
+INSTALLS = target
+target.path = /lib
+
+mac:CONFIG( app_bundle ) {
+ LIBS += libfftw3f.a libsamplerate.a -L/opt/local/include
+ INCLUDEPATH += /opt/local/include:/opt/qt/qt-current/lib/QtSql.framework/Include/
+}else{
+ INCLUDEPATH += /opt/qt/qt-current/lib/QtSql.framework/Include/
+ CONFIG += link_pkgconfig
+ PKGCONFIG += samplerate
+ win32 {
+ CONFIG += link_pkgconfig
+ DEFINES += __NO_THREAD_CHECK
+ QMAKE_LFLAGS_DEBUG += /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcmt.lib
+ }
+ PKGCONFIG += fftw3f
+
+}
diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/CircularArray.h b/thirdparty/liblastfm2/src/fingerprint/fplib/CircularArray.h
new file mode 100644
index 000000000..bfec5a8fd
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/fplib/CircularArray.h
@@ -0,0 +1,292 @@
+/*
+ Copyright 2005-2009 Last.fm Ltd.
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef __CIRCULAR_ARRAY_H
+#define __CIRCULAR_ARRAY_H
+
+#include
+#include
+#include
+#include
+#include // for memset
+#include // for max
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+template< typename T >
+class CircularArray
+{
+
+public:
+
+ typedef size_t size_type;
+
+ /////////////////////////////////////////////////////////////
+
+ // IMPORTANT: The distance must be redefined!!
+ // See declaration of iterator from stl_iterator_base_types.h:
+ // template
+ // struct iterator { ...
+
+ // ---------- Forward declarations
+
+ class iterator :
+ public std::iterator
+ {
+ // it should be by default because is an inner class, but I put it just to be sure..
+ friend class CircularArray;
+
+ private:
+ iterator( size_type idx, T* pData, size_type size ) : _idx(idx), _pData(pData), _size(size) {}
+
+ public:
+
+ //typedef random_access_iterator_tag iterator_category;
+
+ iterator() : _idx(0), _pData(NULL) {}
+
+ iterator& operator++()
+ { // preincrement
+ _idx = (_idx + 1) % _size;
+ return (*this);
+ }
+
+ iterator operator++(int)
+ { // postincrement
+ iterator _Tmp = *this;
+ _idx = (_idx + 1) % _size;
+ return (_Tmp);
+ }
+
+ void operator+=(size_type offs)
+ {
+ this->_idx = (_idx + offs) % _size;
+ }
+
+ iterator operator+(size_type offs) const
+ {
+ size_type newIdx = (_idx + offs) % _size;
+ iterator _Tmp(newIdx, _pData, _size);
+ return _Tmp;
+ }
+
+ // return the distance between this iterator and it
+ size_t operator-(const iterator& it) const
+ {
+ if ( this->_idx > it._idx )
+ return this->_idx - it._idx;
+ else
+ return this->_idx + (_size - it._idx);
+ }
+
+ iterator operator-(size_type offs) const
+ {
+ size_type newIdx;
+
+ if ( offs <= _idx )
+ newIdx = _idx - offs;
+ else
+ newIdx = _size - ((_idx - offs) % _size); // note: should be ok, but to be checked better
+
+ iterator _Tmp(newIdx, _pData, _size);
+ return _Tmp;
+ }
+
+ iterator& operator--()
+ { // predecrement
+ if (_idx == 0)
+ _idx = _size - 1;
+ else
+ --_idx;
+ return (*this);
+ }
+
+ iterator operator--(int)
+ { // postdecrement
+ iterator _Tmp = *this;
+ if (_idx == 0)
+ _idx = _size - 1;
+ else
+ --_idx;
+ return (_Tmp);
+ }
+
+ T& operator*() const
+ { // return designated object
+ return _pData[_idx];
+ }
+
+ T* operator->() const
+ { // return pointer to class object
+ return &_pData[_idx];
+ }
+
+ /* T& operator=(const T& right)
+ { // assign reference right to _val
+ return ( this->_idx = right._idx );
+ }*/
+
+ bool operator==(const iterator& right) const
+ { // test for iterator equality
+ return ( this->_idx == right._idx );
+ }
+
+ bool operator!=(const iterator& right) const
+ { // test for iterator inequality
+ return ( this->_idx != right._idx );
+ }
+
+ protected:
+ size_type _idx;
+ T* _pData;
+ size_type _size;
+ };
+
+ /////////////////////////////////////////////////////////////
+
+
+ CircularArray()
+ : _headIdx(0), _pData(NULL), _size(0)
+ { }
+
+ CircularArray( size_type size )
+ : _headIdx(0), _pData(NULL)
+ {
+ this->resize(size);
+ }
+
+ CircularArray( size_type size, const T& init )
+ : _headIdx(0), _pData(NULL)
+ {
+ this->resize(size, init);
+ }
+
+ ~CircularArray()
+ {
+ this->clear();
+ }
+
+ // remember: it is not working (yet!) with negative numbers!
+ T& operator[](size_type offset)
+ {
+ return _pData[ (_headIdx + offset) % _size ];
+ }
+
+ void resize( size_type size )
+ {
+ _headIdx = 0;
+ if ( size == _size )
+ return;
+
+ this->clear();
+ _pData = new T[size];
+ _size = size;
+ }
+
+ void resize( size_type size, const T& init )
+ {
+ this->resize(size, false);
+ this->fill(init);
+ }
+
+ void fill( const T& val )
+ {
+ for (size_type i=0; i<_size; ++i)
+ _pData[i] = val;
+ }
+
+ void zero_fill()
+ {
+ memset( _pData, 0, _size * sizeof(T) );
+ }
+
+ bool empty() const
+ {
+ return ( _pData == NULL );
+ }
+
+ void clear()
+ {
+ if (_pData)
+ delete [] _pData;
+ _pData = NULL;
+ _headIdx = 0;
+ _size = 0;
+ }
+
+ iterator head() const
+ {
+ if (_pData == NULL)
+ std::cerr << "WARNING: iterator in CircularArray points to an empty CircularArray" << std::endl;
+ return iterator(_headIdx, _pData, _size);
+ }
+
+ void shift_head( int offset )
+ {
+ if ( offset < 0)
+ {
+ int mod = (-offset) % (int)_size;
+ mod -= (int)_headIdx;
+ _headIdx = _size - mod;
+ }
+ else
+ _headIdx = (_headIdx + offset) % _size;
+ }
+
+ size_type size() const
+ {
+ return _size;
+ }
+
+ //// to be changed to an input forward iterator
+ //template
+ //void get_data( TIterator toFillIt, size_type size = 0 )
+ //{
+ // if ( size == 0 )
+ // size = _size;
+ // iterator it = head();
+ //
+ // for (size_type i = 0; i < size; ++i)
+ // *(toFillIt++) = *(it++);
+ //}
+
+ // IMPORTANT! Destination buffer MUST be the same size!
+ void copy_buffer( T* pDest )
+ {
+ memcpy( pDest, _pData, sizeof(T)*_size );
+ }
+
+ // returns the buffer
+ T* get_buffer() const
+ {
+ return _pData;
+ }
+
+
+private:
+
+ size_type _headIdx; // index
+ T* _pData; // array of data
+ size_type _size; // size of data
+
+};
+
+#endif // __CIRCULAR_ARRAY_H
diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.cpp b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.cpp
new file mode 100644
index 000000000..eed4ea3a3
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.cpp
@@ -0,0 +1,128 @@
+/*
+ Copyright 2005-2009 Last.fm Ltd.
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include
+#include // for max
+#include
+
+#include "Filter.h"
+#include "fp_helper_fun.h"
+
+using namespace std;
+
+namespace fingerprint
+{
+
+Filter::Filter(unsigned int id, float threshold, float weight)
+: id(id), threshold(threshold), weight(weight)
+{
+ float time_rate = 1.5;
+
+ unsigned int t = 1;
+ vector time_lengths;
+
+ while (t < KEYWIDTH)
+ {
+ time_lengths.push_back(t);
+ t = max( static_cast( round__(time_rate*t) ) +
+ static_cast( round__(time_rate*t) % 2),
+ t+1 );
+ }
+
+ unsigned int filter_count = 0;
+
+ for (wt = 1; wt <= time_lengths.size(); wt++)
+ {
+ for (wb = 1; wb <= NBANDS; wb++)
+ {
+ for (first_band = 1; first_band <= NBANDS - wb + 1;
+ first_band++)
+ {
+ unsigned int time = time_lengths[wt-1];
+ filter_count++;
+
+ if (filter_count == id)
+ {
+ wt = time_lengths[wt-1];
+ filter_type = 1;
+ return;
+ }
+
+ if (time > 1)
+ {
+ filter_count++;
+ if (filter_count == id)
+ {
+ wt = time_lengths[wt-1];
+ filter_type = 2;
+ return;
+ }
+ }
+
+ if (wb > 1)
+ {
+ filter_count++;
+ if (filter_count == id)
+ {
+ wt = time_lengths[wt-1];
+ filter_type = 3;
+ return;
+ }
+ }
+
+ if (time > 1 && wb > 1)
+ {
+ filter_count++;
+ if (filter_count == id)
+ {
+ wt = time_lengths[wt-1];
+ filter_type = 4;
+ return;
+ }
+ }
+
+ if (time > 3)
+ {
+ filter_count++;
+ if (filter_count == id)
+ {
+ wt = time_lengths[wt-1];
+ filter_type = 5;
+ return;
+ }
+ }
+
+ if (wb > 3)
+ {
+ filter_count++;
+ if (filter_count == id)
+ {
+ wt = time_lengths[wt-1];
+ filter_type = 6;
+ return;
+ }
+ }
+
+ } // for first_band
+ } // for wb
+ } // for wt
+}
+
+} // end of namespace fingerprint
+
+// -----------------------------------------------------------------------------
diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.h b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.h
new file mode 100644
index 000000000..04185eec2
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/fplib/Filter.h
@@ -0,0 +1,47 @@
+/*
+ Copyright 2005-2009 Last.fm Ltd.
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#ifndef __FILTER_H
+#define __FILTER_H
+
+namespace fingerprint
+{
+
+struct Filter
+{
+ /// Constructs a new filter with id.
+ Filter(unsigned int id, float threshold, float weight);
+
+ unsigned int id; //< filter id
+ unsigned int wt; //< time width
+ unsigned int first_band; //< first band
+ unsigned int wb; //< band width
+ unsigned int filter_type; //< filter type
+
+ float threshold; //< threshold for filter
+ float weight; //< filter weight
+
+ // number of frames in time
+ static const unsigned int KEYWIDTH = 100;
+ // number of bands to divide the signal (log step)
+ static const unsigned int NBANDS = 33;
+};
+
+}
+
+#endif // __FILTER_H
diff --git a/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.cpp b/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.cpp
new file mode 100644
index 000000000..305aad7b5
--- /dev/null
+++ b/thirdparty/liblastfm2/src/fingerprint/fplib/FingerprintExtractor.cpp
@@ -0,0 +1,786 @@
+/*
+ Copyright 2005-2009 Last.fm Ltd.
+
+ This file is part of liblastfm.
+
+ liblastfm is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ liblastfm 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with liblastfm. If not, see .
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include // libsamplerate
+
+#include "FingerprintExtractor.h"
+#include "fp_helper_fun.h" // for GroupData
+#include "Filter.h"
+#include "FloatingAverage.h"
+#include "OptFFT.h"
+
+//////////////////////////////////////////////////////////////////////////
+
+namespace fingerprint
+{
+
+using namespace std;
+static const int NUM_FRAMES_CLIENT = 32; // ~= 10 secs.
+
+enum eProcessType
+{
+ PT_UNKNOWN,
+ PT_FOR_QUERY,
+ PT_FOR_FULLSUBMIT
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+class PimplData
+{
+
+public:
+
+ PimplData()
+ : m_pDownsampledPCM(NULL), m_pDownsampledCurrIt(NULL),
+ m_normalizedWindowMs(static_cast(NORMALIZATION_SKIP_SECS * 1000 * 2)),
+ m_compensateBufferSize(FRAMESIZE-OVERLAPSAMPLES + Filter::KEYWIDTH * OVERLAPSAMPLES),
+ m_downsampledProcessSize(NUM_FRAMES_CLIENT*FRAMESIZE),
+ // notice that the buffer has extra space on either side for the normalization window
+ m_fullDownsampledBufferSize( m_downsampledProcessSize + // the actual processed part
+ m_compensateBufferSize + // a compensation buffer for the fft
+ ((m_normalizedWindowMs * DFREQ / 1000) / 2) ), // a compensation buffer for the normalization
+ m_normWindow(m_normalizedWindowMs * DFREQ / 1000),
+ m_pFFT(NULL), m_pDownsampleState(NULL), m_processType(PT_UNKNOWN)
+ {
+ m_pFFT = new OptFFT(m_downsampledProcessSize + m_compensateBufferSize);
+ m_pDownsampledPCM = new float[m_fullDownsampledBufferSize];
+
+ // the end of ||-------m_bufferSize-------|-cb-|---norm/2---||
+ // ^-- pEndDownsampledBuf
+ m_pEndDownsampledBuf = m_pDownsampledPCM + m_fullDownsampledBufferSize;
+
+ // loading filters
+ size_t numFilters = sizeof(rFilters) / sizeof(RawFilter) ;
+ for (size_t i = 0; i < numFilters; ++i)
+ m_filters.push_back( Filter( rFilters[i].ftid, rFilters[i].thresh, rFilters[i].weight ) );
+
+ }
+
+ ~PimplData()
+ {
+ if ( m_pFFT )
+ delete m_pFFT;
+ m_pFFT = NULL;
+ if ( m_pDownsampledPCM )
+ delete [] m_pDownsampledPCM;
+ m_pDownsampledPCM = NULL;
+
+ if ( m_pDownsampleState )
+ src_delete(m_pDownsampleState) ;
+
+ }
+
+ float* m_pDownsampledPCM;
+ float* m_pDownsampledCurrIt;
+
+ const unsigned int m_normalizedWindowMs;
+ const size_t m_compensateBufferSize;
+ const size_t m_downsampledProcessSize;
+ const size_t m_fullDownsampledBufferSize;
+
+ FloatingAverage m_normWindow;
+ OptFFT* m_pFFT;
+
+ //////////////////////////////////////////////////////////////////////////
+
+ // libsamplerate
+ SRC_STATE* m_pDownsampleState;
+ SRC_DATA m_downsampleData;
+
+ vector m_floatInData;
+
+ //////////////////////////////////////////////////////////////////////////
+
+
+ bool m_groupsReady;
+ bool m_preBufferPassed;
+
+ eProcessType m_processType;
+
+ size_t m_toSkipSize;
+ size_t m_toSkipMs;
+
+ size_t m_skippedSoFar;
+ bool m_skipPassed;
+
+ float* m_pEndDownsampledBuf;
+
+ int m_freq;
+ int m_nchannels;
+
+ unsigned int m_lengthMs;
+ int m_minUniqueKeys;
+ unsigned int m_uniqueKeyWindowMs;
+
+ unsigned int m_toProcessKeys;
+ unsigned int m_totalWindowKeys;
+
+ vector m_filters;
+
+ deque m_groupWindow;
+ vector m_groups;
+ unsigned int m_processedKeys;
+
+ vector m_partialBits; // here just to avoid reallocation
+
+#if __BIG_ENDIAN__
+
+#define reorderbits(X) ((((unsigned int)(X) & 0xff000000) >> 24) | \
+ (((unsigned int)(X) & 0x00ff0000) >> 8) | \
+ (((unsigned int)(X) & 0x0000ff00) << 8) | \
+ (((unsigned int)(X) & 0x000000ff) << 24))
+
+ vector m_bigEndianGroups;
+#endif
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+void initCustom( PimplData& pd,
+ int freq, int nchannels,
+ unsigned int lengthMs, unsigned int skipMs,
+ int minUniqueKeys, unsigned int uniqueKeyWindowMs, int duration );
+
+inline float getRMS( const FloatingAverage& signal );
+unsigned int processKeys( deque& groups, size_t size, PimplData& pd );
+void integralImage( float** ppFrames, unsigned int nFrames );
+void computeBits( vector& bits,
+ const vector& f,
+ float ** frames, unsigned int nframes );
+
+
+void src_short_to_float_and_mono_array(const short *in, float *out, int srclen, int nchannels);
+
+//////////////////////////////////////////////////////////////////////////
+
+// -----------------------------------------------------------------------------
+
+FingerprintExtractor::FingerprintExtractor()
+: m_pPimplData(NULL)
+{
+ m_pPimplData = new PimplData();
+}
+
+// -----------------------------------------------------------------------------
+
+FingerprintExtractor::~FingerprintExtractor()
+{
+ if ( m_pPimplData )
+ delete m_pPimplData;
+}
+
+// -----------------------------------------------------------------------------
+
+size_t FingerprintExtractor::getToSkipMs()
+{ return m_pPimplData->m_toSkipMs; }
+
+// -----------------------------------------------------------------------------
+
+size_t FingerprintExtractor::getMinimumDurationMs()
+{
+ return static_cast( (QUERY_SIZE_SECS + NORMALIZATION_SKIP_SECS * 2 + GUARD_SIZE_SECS) * 1000 );
+}
+
+// -----------------------------------------------------------------------------
+
+size_t FingerprintExtractor::getVersion()
+{ return FINGERPRINT_LIB_VERSION; }
+
+// -----------------------------------------------------------------------------
+
+void FingerprintExtractor::initForQuery(int freq, int nchannels, int duration )
+{
+ m_pPimplData->m_skipPassed = false;
+ m_pPimplData->m_processType = PT_FOR_QUERY;
+
+ if ( !m_pPimplData )
+ throw std::runtime_error("Not enough RAM to allocate the fingerprinter!");
+
+ initCustom( *m_pPimplData,
+ freq, nchannels,
+ static_cast(QUERY_SIZE_SECS * 1000),
+ static_cast(QUERY_START_SECS * 1000),
+ MIN_UNIQUE_KEYS,
+ static_cast