diff --git a/apf/.gitattributes b/apf/.gitattributes
new file mode 100644
index 00000000..2e51ef09
--- /dev/null
+++ b/apf/.gitattributes
@@ -0,0 +1,4 @@
+*.cpp diff=cpp
+*.h diff=cpp
+/git* export-ignore
+/.* export-ignore
diff --git a/apf/.gitignore b/apf/.gitignore
new file mode 100644
index 00000000..5ac2728d
--- /dev/null
+++ b/apf/.gitignore
@@ -0,0 +1,4 @@
+/doc/html
+/unit_tests/.dep
+/unit_tests/main
+/unit_tests/*.o
diff --git a/apf/AUTHORS b/apf/AUTHORS
new file mode 100644
index 00000000..a28f4797
--- /dev/null
+++ b/apf/AUTHORS
@@ -0,0 +1,9 @@
+Author:
+Matthias Geier
+
+Contributions by:
+Sascha Spors
+Jens Ahrens
+Torben Hohn
+Till Rettberg
+Moritz Heppner
diff --git a/apf/COPYING b/apf/COPYING
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/apf/COPYING
@@ -0,0 +1,674 @@
+ 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
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program 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.
+
+ This program 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 this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/apf/NEWS b/apf/NEWS
new file mode 100644
index 00000000..6d0239fd
--- /dev/null
+++ b/apf/NEWS
@@ -0,0 +1,24 @@
+User-visible changes in the Audio Processing Framework. Recent changes on top.
+
+0.2.0 (03 July 2013)
+
+ - new: Convolver and BlockDelayLine
+
+ - new: query_policy for getting information out of the audio thread
+
+ - new: fixed_vector and fixed_list, changed Matrix to fixed_matrix
+
+ - new: ScopedThread, DetatchedThread, fftw_allocator, PortAudio policy
+
+ - re-design of the crossfade, tools for parameter interpolation were added.
+ see CombineChannels, CombineChannelsCrossfade, CombineChannelsInterpolation
+
+ - posix_thread_policy and posix_sync_policy were combined
+
+ - several bug-fixes and many improvements, more unit tests
+
+ - re-organization of the directory structure, some separate files were combined
+
+0.1.0 (10 April 2012)
+
+ - first release
diff --git a/apf/README b/apf/README
new file mode 100644
index 00000000..37944a28
--- /dev/null
+++ b/apf/README
@@ -0,0 +1,10 @@
+This is the source distribution of the Audio Processing Framework (APF),
+licensed under the GPLv3+. Please consult the file COPYING for more information
+about this license.
+
+Website: http://AudioProcessingFramework.github.com
+
+Copyright (c) 2012-2013 Institut für Nachrichtentechnik, Universität Rostock
+
+Copyright (c) 2006-2012 Quality & Usability Lab
+ Deutsche Telekom Laboratories, TU Berlin
diff --git a/apf/apf/biquad.h b/apf/apf/biquad.h
new file mode 100644
index 00000000..38641cc6
--- /dev/null
+++ b/apf/apf/biquad.h
@@ -0,0 +1,346 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Second order recursive filter and more.
+
+#ifndef APF_BIQUAD_H
+#define APF_BIQUAD_H
+
+#include // for std::ostream
+#include // for std::pow(), std::tan(), std::sqrt(), ...
+#include
+#include
+#include // for assert()
+
+#include "apf/denormalprevention.h"
+#include "apf/math.h"
+
+namespace apf
+{
+
+// TODO: make macros for trivial operators (see iterator.h)
+// TODO: combine SosCoefficients and LaplaceCoefficients in common class
+// template and use typedef with dummy template arguments.
+
+/// Coefficients of digital recursive filter (second order section).
+/// @tparam T Internal data type
+template
+struct SosCoefficients
+{
+ SosCoefficients(T b0_ = T(), T b1_ = T(), T b2_ = T()
+ , T a1_ = T(), T a2_ = T())
+ : b0(b0_), b1(b1_), b2(b2_)
+ , a1(a1_), a2(a2_)
+ {}
+
+ T b0, b1, b2;
+ T a1, a2;
+
+ SosCoefficients& operator+=(const SosCoefficients& rhs)
+ {
+ b0 += rhs.b0; b1 += rhs.b1; b2 += rhs.b2;
+ a1 += rhs.a1; a2 += rhs.a2;
+ return *this;
+ }
+
+ SosCoefficients operator+(const SosCoefficients& rhs) const
+ {
+ auto tmp = SosCoefficients(*this);
+ return tmp += rhs;
+ }
+
+ SosCoefficients& operator*=(T rhs)
+ {
+ b0 *= rhs; b1 *= rhs; b2 *= rhs;
+ a1 *= rhs; a2 *= rhs;
+ return *this;
+ }
+
+ SosCoefficients operator*(T rhs) const
+ {
+ auto tmp = SosCoefficients(*this);
+ return tmp *= rhs;
+ }
+
+ SosCoefficients& operator/=(T rhs)
+ {
+ b0 /= rhs; b1 /= rhs; b2 /= rhs;
+ a1 /= rhs; a2 /= rhs;
+ return *this;
+ }
+
+ SosCoefficients operator/(T rhs) const
+ {
+ auto tmp = SosCoefficients(*this);
+ return tmp /= rhs;
+ }
+
+ friend SosCoefficients operator*(T lhs, const SosCoefficients& rhs)
+ {
+ auto temp = SosCoefficients(rhs);
+ return temp *= lhs;
+ }
+
+ friend SosCoefficients
+ operator-(const SosCoefficients& lhs, const SosCoefficients& rhs)
+ {
+ return {lhs.b0 - rhs.b0, lhs.b1 - rhs.b1, lhs.b2 - rhs.b2
+ , lhs.a1 - rhs.a1, lhs.a2 - rhs.a2};
+ }
+
+ friend std::ostream&
+ operator<<(std::ostream& stream, const SosCoefficients& c)
+ {
+ stream << "b0: " << c.b0 << ", b1: " << c.b1 << ", b2: " << c.b2
+ << ", a1: " << c.a1 << ", a2: " << c.a2;
+ return stream;
+ }
+};
+
+/// Coefficients of analog recursive filter.
+/// @tparam T Internal data type
+template
+struct LaplaceCoefficients
+{
+ LaplaceCoefficients(T b0_ = T(), T b1_ = T(), T b2_ = T()
+ , T a1_ = T(), T a2_ = T())
+ : b0(b0_), b1(b1_), b2(b2_)
+ , a1(a1_), a2(a2_)
+ {}
+
+ T b0, b1, b2;
+ T a1, a2;
+};
+
+/** Direct Form II recursive filter of second order.
+ * @tparam T internal type of states and coefficients
+ * @tparam DenormalPrevention method of denormal prevention (see apf::dp)
+ * @see Cascade, bilinear()
+ **/
+template class DenormalPrevention = apf::dp::ac>
+class BiQuad : public SosCoefficients , private DenormalPrevention
+{
+ public:
+ using argument_type = T;
+ using result_type = T;
+
+ BiQuad() : w0(), w1(), w2() {}
+
+ /// Assignment operator.
+ /// Change coefficients when operator '=' is called with SosCoefficients.
+ /// @param c New set of coefficients
+ /// @note state is unchanged!
+ BiQuad& operator=(const SosCoefficients& c)
+ {
+ this->SosCoefficients::operator=(c);
+ return *this;
+ }
+
+ /// Process filter on single sample.
+ /// @param in input sample
+ /// @return output sample
+ result_type operator()(argument_type in)
+ {
+ w0 = w1;
+ w1 = w2;
+ w2 = in - this->a1*w1 - this->a2*w0;
+
+ this->prevent_denormals(w2);
+
+ in = this->b0*w2 + this->b1*w1 + this->b2*w0;
+
+ return in;
+ }
+
+ T w0, w1, w2;
+};
+
+/// %Cascade of filter sections.
+/// @tparam S section type, e.g. BiQuad
+template>
+class Cascade
+{
+ public:
+ using argument_type = typename S::argument_type;
+ using result_type = typename S::result_type;
+ using size_type = typename Container::size_type;
+
+ /// Constructor.
+ explicit Cascade(size_type n) : _sections(n) {}
+
+ /// Overwrite sections with new content.
+ /// @tparam I Iterator type for arguments
+ /// @param first Begin iterator
+ /// @param last End iterator
+ template
+ void set(I first, I last)
+ {
+ assert(_sections.size() == size_type(std::distance(first, last)));
+
+ std::copy(first, last, _sections.begin());
+ }
+
+ /// Process all sections on single sample.
+ /// @param in Input sample
+ /// @return Output sample
+ result_type operator()(argument_type in)
+ {
+ for (auto& section: _sections)
+ {
+ in = section(in);
+ }
+ return in;
+ }
+
+ /// Process all sections on audio block.
+ /// @tparam In Iterator type for input samples
+ /// @tparam Out Iterator type for output samples
+ /// @param first Iterator to first input sample
+ /// @param last Iterator to (one past) last input sample
+ /// @param result Iterator to first output sample
+ template
+ void execute(In first, In last, Out result)
+ {
+ using out_t = typename std::iterator_traits::value_type;
+
+ while (first != last)
+ {
+ *result++ = static_cast(this->operator()(*first));
+ ++first;
+ }
+ }
+
+ size_type number_of_sections() const { return _sections.size(); }
+
+ private:
+ Container _sections;
+};
+
+namespace internal
+{
+
+/** Roots-to-polynomial conversion.
+ * @tparam T precision of data
+ * @param Roots 2x2 roots matrix
+ * @param poly1 reference to first-order-coefficient of output polynomial
+ * @param poly2 reference to second-order-coefficient of output polynomial
+ * @note zeroth-order is ignored for this special case!
+ **/
+template
+void roots2poly(T Roots[2][2], T& poly1, T& poly2)
+{
+ T two = 2.0;
+
+ std::complex eig[2];
+
+ T tmp_arg = std::pow((Roots[0][0]+Roots[1][1])/two, two)
+ + Roots[0][1]*Roots[1][0] - Roots[0][0]*Roots[1][1];
+
+ if (tmp_arg > 0)
+ {
+ eig[0] = (Roots[0][0]+Roots[1][1])/two + std::sqrt(tmp_arg);
+ eig[1] = (Roots[0][0]+Roots[1][1])/two - std::sqrt(tmp_arg);
+ }
+ else
+ {
+ eig[0] = std::complex((Roots[0][0]+Roots[1][1])/two, std::sqrt(-tmp_arg));
+ eig[1] = std::complex((Roots[0][0]+Roots[1][1])/two, -std::sqrt(-tmp_arg));
+ }
+
+ poly1 = real(-eig[0] - eig[1]);
+ poly2 = real(-eig[1] * -eig[0]);
+}
+
+} // namespace internal
+
+/** Bilinear transform.
+ * @tparam T internal data type
+ * @param coeffs_in coefficients of filter design in Laplace domain
+ * @param fs sampling rate
+ * @param fp prewarping frequency
+ * @return coefficients in z-domain
+ * @see BiQuad
+ **/
+template
+SosCoefficients bilinear(LaplaceCoefficients coeffs_in, int fs, int fp)
+{
+ SosCoefficients coeffs_out;
+
+ T one = 1.0;
+ T two = 2.0;
+
+ // prewarp
+ T fp_temp = static_cast(fp) * (two * apf::math::pi());
+ T lambda = fp_temp / std::tan(fp_temp / static_cast(fs) / two) / two;
+
+ // calculate state space representation
+ T A[2][2] = { { -coeffs_in.a1, -coeffs_in.a2 }, { 1.0, 0.0 } };
+ T B[2] = { 1.0, 0.0 };
+ T C[2] = { coeffs_in.b1-coeffs_in.a1, coeffs_in.b2-coeffs_in.a2 };
+ T D = 1.0;
+
+ T t = one / lambda;
+ T r = std::sqrt(t);
+
+ T T1[2][2] = { { (t/two)*A[0][0] + one, (t/two)*A[0][1] },
+ { (t/two)*A[1][0], (t/two)*A[1][1] + one } };
+ T T2[2][2] = { { -(t/two)*A[0][0] + one, -(t/two)*A[0][1] },
+ { -(t/two)*A[1][0], -(t/two)*A[1][1] + one} };
+
+ // solve linear equation systems
+ T det = T2[0][0]*T2[1][1] - T2[0][1]*T2[1][0];
+ T Ad[2][2] = { { (T1[0][0]*T2[1][1] - T1[1][0]*T2[0][1]) / det,
+ (T1[0][1]*T2[1][1] - T1[1][1]*T2[0][1]) / det },
+ { (T1[1][0]*T2[0][0] - T1[0][0]*T2[1][0]) / det,
+ (T1[1][1]*T2[0][0] - T1[0][1]*T2[1][0]) / det } };
+ T Bd[2] = { (t/r) * (B[0]*T2[1][1] - B[1]*T2[0][1]) / det,
+ (t/r) * (B[1]*T2[0][0] - B[0]*T2[1][0]) / det };
+ T Cd[2] = { (C[0]*T2[1][1] - C[1]*T2[1][0]) / det,
+ (C[1]*T2[0][0] - C[0]*T2[0][1]) / det };
+ T Dd = (B[0]*Cd[0] + B[1]*Cd[1]) * (t/two) + D;
+
+ Cd[0] *= r;
+ Cd[1] *= r;
+
+ // convert roots to polynomial
+ internal::roots2poly(Ad, coeffs_out.a1, coeffs_out.a2);
+
+ T Tmp[2][2] = { { Ad[0][0]-Bd[0]*Cd[0], Ad[0][1]-Bd[0]*Cd[1] },
+ { Ad[1][0]-Bd[1]*Cd[0], Ad[1][1]-Bd[1]*Cd[1] } };
+
+ internal::roots2poly(Tmp, coeffs_out.b1, coeffs_out.b2);
+
+ coeffs_out.b0 = Dd;
+ coeffs_out.b1 += (Dd-one)*coeffs_out.a1;
+ coeffs_out.b2 += (Dd-one)*coeffs_out.a2;
+
+ return coeffs_out;
+}
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
diff --git a/apf/apf/blockdelayline.h b/apf/apf/blockdelayline.h
new file mode 100644
index 00000000..a4dabfd7
--- /dev/null
+++ b/apf/apf/blockdelayline.h
@@ -0,0 +1,281 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Block-based delay line.
+
+#ifndef APF_BLOCKDELAYLINE_H
+#define APF_BLOCKDELAYLINE_H
+
+#include // for std::max()
+#include // default container
+
+#include "apf/iterator.h" // for circular_iterator, stride_iterator
+
+namespace apf
+{
+
+/** Block-based delay line.
+ * This is a "write once, read many times" delay line.
+ * The write operation is simple and fast.
+ * The desired delay is specified at the more flexible read operation.
+ **/
+template>
+class BlockDelayLine
+{
+ public:
+ using size_type = typename Container::size_type;
+ using pointer = typename Container::pointer;
+ using circulator = apf::circular_iterator;
+
+ BlockDelayLine(size_type block_size, size_type max_delay);
+
+ /// Return @b true if @p delay is valid
+ bool delay_is_valid(size_type delay) const
+ {
+ return delay <= _max_delay;
+ }
+
+ /// Advance the internal iterators/pointers to the next block.
+ void advance()
+ {
+ ++_block_circulator;
+ _data_circulator += _block_size;
+ }
+
+ template
+ void write_block(Iterator source);
+
+ template
+ bool read_block(Iterator destination, size_type delay) const;
+
+ template
+ bool read_block(Iterator destination, size_type delay, T weight) const;
+
+ pointer get_write_pointer() const;
+
+ circulator get_read_circulator(size_type delay = 0) const;
+
+ protected:
+ /// Get a circular iterator to the sample with time 0
+ circulator _get_data_circulator() const { return _data_circulator; }
+
+ const size_type _block_size; ///< Size of read/write blocks
+
+ private:
+ const size_type _max_delay; ///< Maximum delay
+
+ const size_type _number_of_blocks; ///< No\. of blocks needed for storage
+
+ Container _data; ///< Internal storage for sample data
+
+ /// Circular iterator which iterates over each sample
+ circulator _data_circulator;
+
+ /// Circular iterator iterating over the block-beginnings.
+ apf::stride_iterator _block_circulator;
+};
+
+/** Constructor.
+ * @param block_size Block size
+ * @param max_delay Maximum delay in samples
+ **/
+template
+BlockDelayLine::BlockDelayLine(size_type block_size
+ , size_type max_delay)
+ : _block_size(block_size)
+ , _max_delay(max_delay)
+ // Minimum number of blocks is 2, even if _max_delay is 0.
+ // With only one block the circular iterators r and (r + _block_size) would be
+ // equal and the read...() functions wouldn't work.
+ // But anyway, who wants a delay line with no delay? Kind of useless ...
+ , _number_of_blocks(
+ std::max(size_type(2), (_max_delay + 2 * _block_size - 1) / _block_size))
+ , _data(_number_of_blocks * _block_size) // initialized with default ctor T()
+ , _data_circulator(_data.begin(), _data.end())
+ , _block_circulator(_data_circulator, _block_size)
+{
+ assert(_block_size >= 1);
+}
+
+/** Write a block of data to the delay line.
+ * Before writing, the read and write pointers are advanced to the next block.
+ * If you don't want to use this function, you can also call advance(), get the
+ * write pointer with get_write_pointer() and write directly to it.
+ * @param source Pointer/iterator where the block of data shall be
+ * read from.
+ * @attention In @p source there must be enough data to read from!
+ * @note @p source must be a random access iterator. If you want to use another
+ * iterator, you'll have to do it on your own (as written above).
+ **/
+template
+template
+void
+BlockDelayLine::write_block(Iterator source)
+{
+ this->advance();
+ // Ignore return value, next time get_write_pointer() has to be used again!
+ std::copy(source, source + _block_size, this->get_write_pointer());
+}
+
+/** Read a block of data from the delay line.
+ * @param destination Iterator to destination
+ * @param delay Delay in samples
+ * @return @b true on success
+ **/
+template
+template
+bool
+BlockDelayLine::read_block(Iterator destination, size_type delay)
+ const
+{
+ // TODO: try to get a more meaningful error message if source is not a random
+ // access iterator (e.g. when using a std::list)
+ if (!this->delay_is_valid(delay)) return false;
+ circulator source = this->get_read_circulator(delay);
+ std::copy(source, source + _block_size, destination);
+ return true;
+}
+
+/// Read from the delay line and multiply each element by a given factor.
+template
+template
+bool
+BlockDelayLine::read_block(Iterator destination
+ , size_type delay, T weight) const
+{
+ if (!this->delay_is_valid(delay)) return false;
+ circulator source = this->get_read_circulator(delay);
+ std::transform(source, source + _block_size, destination
+ , [weight] (T in) { return in * weight; });
+ return true;
+}
+
+/** Get the write pointer.
+ * @attention Before the write operation, advance() must be called to
+ * update read and write pointers.
+ * @attention You must not write more than one block with this pointer! For
+ * the next block, you have to get a new pointer.
+ **/
+template
+typename BlockDelayLine::pointer
+BlockDelayLine::get_write_pointer() const
+{
+ return &*_block_circulator.base().base();
+}
+
+/** Get the read circulator.
+ * @param delay Delay in samples
+ * @attention There is no check if the delay is in the valid range between
+ * 0 and @c max_delay. You are responsible for checking that!
+ **/
+template
+typename BlockDelayLine::circulator
+BlockDelayLine::get_read_circulator(size_type delay) const
+{
+ return _get_data_circulator() - delay;
+}
+
+/** A block-based delay line where negative delay is possible.
+ * This is done by delaying everything by a given initial delay. The (absolute
+ * value of the) negative delay can be at most as large as the initial delay.
+ * @see BlockDelayLine
+ **/
+template>
+class NonCausalBlockDelayLine : private BlockDelayLine
+{
+ private:
+ using _base = BlockDelayLine;
+
+ public:
+ using size_type = typename _base::size_type;
+ using circulator = typename _base::circulator;
+ using difference_type = typename circulator::difference_type;
+
+ /// Constructor. @param initial_delay initial delay
+ /// @param block_size Block size
+ /// @param max_delay Maximum delay in samples
+ /// @param initial_delay Additional delay to achieve negative delay
+ /// @see BlockDelayLine::BlockDelayLine()
+ NonCausalBlockDelayLine(size_type block_size, size_type max_delay
+ , size_type initial_delay)
+ : _base(block_size, max_delay + initial_delay)
+ , _initial_delay(initial_delay)
+ {}
+
+#ifdef APF_DOXYGEN_HACK
+ // This is just for Doxygen documentation:
+ /// @see BlockDelayLine::advance()
+ void advance();
+ /// @see BlockDelayLine::write_block()
+ template void write_block(Iterator source);
+ /// @see BlockDelayLine::get_write_pointer()
+ pointer get_write_pointer() const;
+#else
+ // This is the real thing:
+ using _base::advance;
+ using _base::write_block;
+ using _base::get_write_pointer;
+#endif
+
+ /// @see BlockDelayLine::delay_is_valid()
+ bool delay_is_valid(difference_type delay) const
+ {
+ if (delay < -_initial_delay) return false;
+ if (delay < 0) return true;
+ return _base::delay_is_valid(delay + _initial_delay);
+ }
+
+ /// @see BlockDelayLine::read_block()
+ template
+ bool read_block(Iterator destination, difference_type delay) const
+ {
+ if (delay < -_initial_delay) return false;
+ return _base::read_block(destination, delay + _initial_delay);
+ }
+
+ /// @see BlockDelayLine::read_block()
+ template
+ bool read_block(Iterator destination, difference_type delay, T weight) const
+ {
+ if (delay < -_initial_delay) return false;
+ return _base::read_block(destination, delay + _initial_delay, weight);
+ }
+
+ /// @see BlockDelayLine::get_read_circulator()
+ circulator get_read_circulator(difference_type delay = 0) const
+ {
+ return _base::get_read_circulator(delay + _initial_delay);
+ }
+
+ private:
+ const difference_type _initial_delay;
+};
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
+// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='
diff --git a/apf/apf/combine_channels.h b/apf/apf/combine_channels.h
new file mode 100644
index 00000000..bba2348c
--- /dev/null
+++ b/apf/apf/combine_channels.h
@@ -0,0 +1,505 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Combine channels, interpolate, crossfade.
+
+#ifndef APF_COMBINE_CHANNELS_H
+#define APF_COMBINE_CHANNELS_H
+
+#include
+#include // for assert()
+#include // for std::logic_error
+#include // for std::transform(), std::copy(), std::fill()
+
+#include // for std::bind()
+#include // for std::remove_reference
+
+#include "apf/iterator.h" // for *_iterator, make_*_iterator(), cast_proxy_const
+#include "apf/misc.h" // for CRTP
+
+namespace apf
+{
+
+namespace CombineChannelsResult
+{
+ enum type
+ {
+ nothing = 0,
+ constant = 1,
+ change = 2,
+ fade_in = 3,
+ fade_out = 4
+ };
+}
+
+/** Base class for CombineChannels*.
+ * @tparam Derived Derived class ("Curiously Recurring Template Pattern")
+ * @tparam ListProxy Proxy class for input list. If no proxy is needed, just use
+ * a reference to the list (e.g. std::list&).
+ * @p ListProxy (or the list itself) must have begin() and end() and an inner
+ * type @c value_type which itself must have begin() and end() and an inner
+ * type @c iterator.
+ * @tparam Out Output class. Must have begin() and end() functions.
+ *
+ * @see CombineChannels, CombineChannelsCopy, CombineChannelsCrossfade,
+ * CombineChannelsCrossfadeCopy, CombineChannelsInterpolation
+ **/
+template
+class CombineChannelsBase : public CRTP
+{
+ protected:
+ using T = typename std::iterator_traits::type::value_type::iterator>::value_type;
+
+ public:
+ /// Constructor.
+ /// @param in List of objects to combine
+ /// @param out Target object
+ template
+ CombineChannelsBase(L& in, Out& out)
+ : _in(in)
+ , _out(out)
+ {}
+
+ /// Do the actual combining.
+ /// @param f A "special" function object. It has to have a member function
+ /// @c select() which takes an item of the list as parameter. Depending on
+ /// the derived class, it may also need other member functions.
+ template
+ void process(F f)
+ {
+ // We pass f by value because this is common in STL-like algorithms.
+ // After select() is called, it is passed to case_one() and case_two() as
+ // non-const reference to avoid a further copy.
+
+ _accumulate = false;
+
+ this->derived().before_the_loop();
+
+ for (auto& item: _in)
+ {
+ using namespace CombineChannelsResult;
+
+ switch (_selection = f.select(item))
+ {
+ case nothing:
+ continue; // jump to next list item
+
+ case constant:
+ this->derived().case_one(item, f);
+ break;
+
+ case change:
+ case fade_in:
+ case fade_out:
+ this->derived().case_two(item, f);
+ break;
+
+ default:
+ throw std::runtime_error("Predicate must return 0, 1 or 2!");
+ }
+ }
+
+ this->derived().after_the_loop();
+
+ if (!_accumulate)
+ {
+ std::fill(_out.begin(), _out.end(), T());
+ }
+ }
+
+ void before_the_loop() {}
+
+ template
+ void case_one(const ItemType&, F&)
+ {
+ throw std::logic_error("CombineChannelsBase: case 1 not implemented!");
+ }
+
+ template
+ void case_two(const ItemType&, F&)
+ {
+ throw std::logic_error("CombineChannelsBase: case 2 not implemented!");
+ }
+
+ void after_the_loop() {}
+
+ private:
+ ListProxy _in;
+
+ protected:
+ template
+ void _case_one_copy(const ItemType& item)
+ {
+ if (_accumulate)
+ {
+ std::copy(item.begin(), item.end()
+ , make_accumulating_iterator(_out.begin()));
+ }
+ else
+ {
+ std::copy(item.begin(), item.end(), _out.begin());
+ _accumulate = true;
+ }
+ }
+
+ template
+ void _case_one_transform(const ItemType& item, FunctionType& f)
+ {
+ if (_accumulate)
+ {
+ std::transform(item.begin(), item.end()
+ , make_accumulating_iterator(_out.begin()), f);
+ }
+ else
+ {
+ std::transform(item.begin(), item.end(), _out.begin(), f);
+ _accumulate = true;
+ }
+ }
+
+ Out& _out;
+ CombineChannelsResult::type _selection;
+ bool _accumulate;
+};
+
+/** Combine channels: accumulate.
+ **/
+template
+class CombineChannelsCopy : public CombineChannelsBase<
+ CombineChannelsCopy, L, Out>
+{
+ private:
+ using _base = CombineChannelsBase, L, Out>;
+
+ public:
+ CombineChannelsCopy(const L& in, Out& out) : _base(in, out) {}
+
+ template
+ void case_one(const ItemType& item, F&)
+ {
+ this->_case_one_copy(item);
+ }
+
+ // Case 2 is not implemented and shall not be used!
+};
+
+/** Combine channels: transform and accumulate.
+ **/
+template
+class CombineChannels: public CombineChannelsBase<
+ CombineChannels, L, Out>
+{
+ private:
+ using _base = CombineChannelsBase, L, Out>;
+
+ public:
+ CombineChannels(const L& in, Out& out) : _base(in, out) {}
+
+ template
+ void case_one(const ItemType& item, F& f)
+ {
+ this->_case_one_transform(item, f);
+ }
+
+ // Case 2 is not implemented and shall not be used!
+};
+
+/** Combine channels: interpolate and accumulate.
+ **/
+template
+class CombineChannelsInterpolation: public CombineChannelsBase<
+ CombineChannelsInterpolation, L, Out>
+{
+ private:
+ using _base
+ = CombineChannelsBase, L, Out>;
+ using T = typename _base::T;
+ using _base::_selection;
+ using _base::_accumulate;
+ using _base::_out;
+
+ public:
+ CombineChannelsInterpolation(const L& in, Out& out) : _base(in, out) {}
+
+ template
+ void case_one(const ItemType& item, F& f)
+ {
+ this->_case_one_transform(item, f);
+ }
+
+ template
+ void case_two(const ItemType& item, F& f)
+ {
+ assert(_selection == CombineChannelsResult::change);
+
+ if (_accumulate)
+ {
+ std::transform(item.begin(), item.end(), index_iterator()
+ , make_accumulating_iterator(_out.begin()), f);
+ }
+ else
+ {
+ std::transform(item.begin(), item.end(), index_iterator()
+ , _out.begin(), f);
+ _accumulate = true;
+ }
+ }
+};
+
+struct fade_out_tag {};
+
+/** Base class for CombineChannelsCrossfade*.
+ **/
+template
+class CombineChannelsCrossfadeBase : public CombineChannelsBase
+{
+ private:
+ using _base = CombineChannelsBase;
+ using T = typename _base::T;
+ using _base::_accumulate;
+ using _base::_out;
+
+ public:
+ CombineChannelsCrossfadeBase(const L& in, Out& out, const Crossfade& fade)
+ : _base(in, out)
+ , _fade_out_buffer(fade.size())
+ , _fade_in_buffer(fade.size())
+ , _crossfade_data(fade)
+ {}
+
+ void before_the_loop()
+ {
+ _accumulate_fade_in = _accumulate_fade_out = false;
+ }
+
+ void after_the_loop()
+ {
+ if (_accumulate_fade_out)
+ {
+ if (_accumulate)
+ {
+ std::transform(_fade_out_buffer.begin(), _fade_out_buffer.end()
+ , _crossfade_data.fade_out_begin()
+ , make_accumulating_iterator(_out.begin())
+ , std::multiplies());
+ }
+ else
+ {
+ std::transform(_fade_out_buffer.begin(), _fade_out_buffer.end()
+ , _crossfade_data.fade_out_begin()
+ , _out.begin()
+ , std::multiplies());
+ _accumulate = true;
+ }
+ }
+ if (_accumulate_fade_in)
+ {
+ if (_accumulate)
+ {
+ std::transform(_fade_in_buffer.begin(), _fade_in_buffer.end()
+ , _crossfade_data.fade_in_begin()
+ , make_accumulating_iterator(_out.begin())
+ , std::multiplies());
+ }
+ else
+ {
+ std::transform(_fade_in_buffer.begin(), _fade_in_buffer.end()
+ , _crossfade_data.fade_in_begin()
+ , _out.begin()
+ , std::multiplies());
+ _accumulate = true;
+ }
+ }
+ }
+
+ protected:
+ bool _accumulate_fade_in, _accumulate_fade_out;
+ std::vector _fade_out_buffer, _fade_in_buffer;
+
+ private:
+ const Crossfade& _crossfade_data;
+};
+
+/** Combine channels: crossfade and accumulate.
+ **/
+template
+class CombineChannelsCrossfadeCopy : public CombineChannelsCrossfadeBase<
+ CombineChannelsCrossfadeCopy, L, Out, Crossfade>
+{
+ private:
+ using _base = CombineChannelsCrossfadeBase, L, Out, Crossfade>;
+
+ using _base::_fade_out_buffer;
+ using _base::_fade_in_buffer;
+ using _base::_accumulate_fade_in;
+ using _base::_accumulate_fade_out;
+ using _base::_selection;
+
+ public:
+ CombineChannelsCrossfadeCopy(const L& in, Out& out, const Crossfade& fade)
+ : _base(in, out, fade)
+ {}
+
+ template
+ void case_one(const ItemType& item, F&)
+ {
+ this->_case_one_copy(item);
+ }
+
+ template
+ void case_two(ItemType& item, F& f)
+ {
+ if (_selection != CombineChannelsResult::fade_in)
+ {
+ if (_accumulate_fade_out)
+ {
+ std::copy(item.begin(), item.end()
+ , make_accumulating_iterator(_fade_out_buffer.begin()));
+ }
+ else
+ {
+ std::copy(item.begin(), item.end(), _fade_out_buffer.begin());
+ _accumulate_fade_out = true;
+ }
+ }
+ if (_selection != CombineChannelsResult::fade_out)
+ {
+ f.update();
+
+ if (_accumulate_fade_in)
+ {
+ std::copy(item.begin(), item.end()
+ , make_accumulating_iterator(_fade_in_buffer.begin()));
+ }
+ else
+ {
+ std::copy(item.begin(), item.end(), _fade_in_buffer.begin());
+ _accumulate_fade_in = true;
+ }
+ }
+ }
+};
+
+/** Combine channels: transform, crossfade and accumulate.
+ **/
+template
+class CombineChannelsCrossfade : public CombineChannelsCrossfadeBase<
+ CombineChannelsCrossfade, L, Out, Crossfade>
+{
+ private:
+ using _base = CombineChannelsCrossfadeBase, L, Out, Crossfade>;
+ using _base::_selection;
+ using _base::_accumulate_fade_in;
+ using _base::_accumulate_fade_out;
+
+ public:
+ CombineChannelsCrossfade(const L& in, Out& out, const Crossfade& fade)
+ : _base(in, out, fade)
+ {}
+
+ template
+ void case_one(const ItemType& item, F& f)
+ {
+ this->_case_one_transform(item, f);
+ }
+
+ template
+ void case_two(ItemType& item, F& f)
+ {
+ if (_selection != CombineChannelsResult::fade_in)
+ {
+ if (_accumulate_fade_out)
+ {
+ std::transform(item.begin(), item.end()
+ , make_accumulating_iterator(this->_fade_out_buffer.begin())
+ , std::bind(f, std::placeholders::_1, fade_out_tag()));
+ }
+ else
+ {
+ std::transform(item.begin(), item.end()
+ , this->_fade_out_buffer.begin()
+ , std::bind(f, std::placeholders::_1, fade_out_tag()));
+ _accumulate_fade_out = true;
+ }
+ }
+ if (_selection != CombineChannelsResult::fade_out)
+ {
+ f.update();
+
+ if (_accumulate_fade_in)
+ {
+ std::transform(item.begin(), item.end()
+ , make_accumulating_iterator(this->_fade_in_buffer.begin()), f);
+ }
+ else
+ {
+ std::transform(item.begin(), item.end()
+ , this->_fade_in_buffer.begin(), f);
+ _accumulate_fade_in = true;
+ }
+ }
+ }
+};
+
+/** Crossfade using a raised cosine.
+ **/
+template
+class raised_cosine_fade
+{
+ private:
+ using iterator_type
+ = transform_iterator, math::raised_cosine>;
+
+ public:
+ using iterator = typename std::vector::const_iterator;
+ using reverse_iterator = typename std::vector::const_reverse_iterator;
+
+ raised_cosine_fade(size_t block_size)
+ : _crossfade_data(
+ iterator_type(index_iterator()
+ , math::raised_cosine(static_cast(2 * block_size))),
+ // block_size + 1 because we also use it in reverse order
+ iterator_type(index_iterator(static_cast(block_size + 1))))
+ , _size(block_size)
+ {}
+
+ iterator fade_out_begin() const { return _crossfade_data.begin(); }
+ reverse_iterator fade_in_begin() const { return _crossfade_data.rbegin(); }
+ size_t size() const { return _size; }
+
+ private:
+ const std::vector _crossfade_data;
+ const size_t _size;
+};
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
+// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='
diff --git a/apf/apf/commandqueue.h b/apf/apf/commandqueue.h
new file mode 100644
index 00000000..66d35d3e
--- /dev/null
+++ b/apf/apf/commandqueue.h
@@ -0,0 +1,235 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Command queue.
+
+#ifndef APF_COMMANDQUEUE_H
+#define APF_COMMANDQUEUE_H
+
+#include // for usleep()
+#include // for assert()
+
+#include "apf/lockfreefifo.h"
+
+namespace apf
+{
+
+/** Manage command queue from non-realtime thread to realtime thread.
+ * Commands can be added in the non-realtime thread with push().
+ *
+ * Commands are executed when process_commands() is called from the realtime
+ * thread.
+ **/
+class CommandQueue : NonCopyable
+{
+ public:
+ /// Abstract base class for realtime commands.
+ /// These commands are passed through queues into the realtime thread and
+ /// after execution back to the non-realtime thread for cleanup.
+ struct Command : NonCopyable
+ {
+ /// Empty virtual destructor.
+ virtual ~Command() {}
+
+ /// The actual implementation of the command. This is called from the
+ /// realtime thread. Overwritten in the derived class.
+ virtual void execute() = 0;
+
+ /// Cleanup of resources. This is called from the non-realtime thread.
+ /// Overwritten in the derived class.
+ virtual void cleanup() = 0;
+ };
+
+ /// Dummy command to synchronize with non-realtime thread.
+ class WaitCommand : public Command
+ {
+ public:
+ /// Constructor. @param done is set to @b true when cleanup() is called.
+ WaitCommand(bool& done) : _done(done) {}
+
+ private:
+ virtual void execute() { }
+ virtual void cleanup() { _done = true; }
+
+ bool& _done;
+ };
+
+ /// @name Functions to be called from the non-realtime thread
+ /// If there are multiple non-realtime threads, access has to be locked!
+ //@{
+
+ /// Constructor.
+ /// @param size maximum number of commands in queue.
+ explicit CommandQueue(size_t size)
+ : _in_fifo(size)
+ , _out_fifo(size)
+ , _active(true)
+ {}
+
+ /// Destructor.
+ /// @attention Commands in the cleanup queue are cleaned up, but commands in
+ /// the process queue are ignored and their memory is not freed!
+ ~CommandQueue()
+ {
+ this->cleanup_commands();
+ // TODO: warning if process queue is not empty?
+ // TODO: if inactive -> process commands (if active -> ???)
+ }
+
+ inline void push(Command* cmd);
+
+ inline void wait();
+
+ /// Clean up all commands in the cleanup-queue.
+ /// @note This function must be called from the non-realtime thread.
+ void cleanup_commands()
+ {
+ Command* cmd;
+ while ((cmd = _out_fifo.pop()) != nullptr) { _cleanup(cmd); }
+ }
+
+ // TODO: avoid return value?
+ /// Deactivate queue; process following commands in the non-realtime thread.
+ /// @return @b true on success
+ /// @note The queue must be empty. If not, the queue is @em not deactivated
+ /// and @b false is returned.
+ inline bool deactivate()
+ {
+ this->cleanup_commands();
+ if (_in_fifo.empty()) _active = false;
+ return !_active;
+ }
+
+ /// Re-activate queue. @see deactivate().
+ inline void reactivate()
+ {
+ this->cleanup_commands();
+ assert(_in_fifo.empty());
+ _active = true;
+ }
+
+ //@}
+
+ /// @name Functions to be called from the realtime thread
+ //@{
+
+ /// Execute all commands in the queue.
+ /// After execution, the commands are queued for cleanup in the non-realtime
+ /// thread.
+ /// @note This function must be called from the realtime thread.
+ void process_commands()
+ {
+ Command* cmd;
+ while ((cmd = _in_fifo.pop()) != nullptr)
+ {
+ cmd->execute();
+ bool result = _out_fifo.push(cmd);
+ // If _out_fifo is full, cmd is not cleaned up!
+ // This is very unlikely to happen (if not impossible).
+ assert(result && "Error in _out_fifo.push()!");
+ (void)result; // avoid "unused-but-set-variable" warning
+ }
+ }
+
+ /// Check if commands are available.
+ /// @return @b true if commands are available.
+ bool commands_available() const
+ {
+ return !_in_fifo.empty();
+ }
+
+ //@}
+
+ private:
+ /// Clean up and delete a command @p cmd
+ void _cleanup(Command* cmd)
+ {
+ assert(cmd != nullptr);
+ cmd->cleanup();
+ delete cmd;
+ }
+
+ /// Queue of commands to execute in realtime thread
+ LockFreeFifo _in_fifo;
+ /// Queue of executed commands to delete in non-realtime thread
+ LockFreeFifo _out_fifo;
+
+ bool _active; ///< default: true
+};
+
+/** Push a command to be executed in the realtime thread.
+ * The command will be cleaned up when it comes back from the
+ * realtime thread.
+ * If the CommandQueue is inactive, the command is not queued but executed and
+ * cleaned up immediately.
+ * @param cmd The command to be executed.
+ **/
+void CommandQueue::push(Command* cmd)
+{
+ if (!_active)
+ {
+ cmd->execute();
+ _cleanup(cmd);
+ return;
+ }
+
+ // First remove all commands from _out_fifo.
+ // This ensures that it's not going to be full which would block
+ // process_commands() and its calling realtime thread.
+ this->cleanup_commands();
+
+ // Now push the command on _in_fifo; if the FIFO is full: retry, retry, ...
+ while (!_in_fifo.push(cmd))
+ {
+ // We don't really know if that ever happens, so we abort in debug-mode:
+ assert(false && "Error in _in_fifo.push()!");
+ // TODO: avoid this usleep()?
+ usleep(50);
+ }
+}
+
+/** Wait for realtime thread.
+ * Push an empty command and wait for its return.
+ **/
+void CommandQueue::wait()
+{
+ bool done = false;
+ this->push(new WaitCommand(done));
+
+ this->cleanup_commands();
+ while (!done)
+ {
+ // TODO: avoid this usleep()?
+ usleep(50);
+ this->cleanup_commands();
+ }
+}
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
+// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='
diff --git a/apf/apf/container.h b/apf/apf/container.h
new file mode 100644
index 00000000..d04da895
--- /dev/null
+++ b/apf/apf/container.h
@@ -0,0 +1,661 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Some containers.
+
+#ifndef APF_CONTAINER_H
+#define APF_CONTAINER_H
+
+#include // for std::allocator
+#include
+#include
+#include // for std::logic_error
+#include // for std::find
+
+#include "apf/iterator.h" // for stride_iterator, ...
+
+namespace apf
+{
+
+// TODO: move metaprogramming stuff into separate file?
+namespace internal
+{
+ template struct first { using type = T1; };
+
+ // This didn't work with GCC 4.8.2 (segmentation fault during compilation)
+ //template using first = T1;
+
+ template struct last : last {};
+ template struct last { using type = T1; };
+
+ template using if_first_not_integral
+ = typename std::enable_if<
+ !std::is_integral::type>::value>::type;
+ template using if_integral
+ = typename std::enable_if::value>::type;
+
+ template using if_last_not_convertible
+ = typename std::enable_if<
+ !std::is_convertible::type, Arg>::value>::type;
+}
+
+/** Derived from @c std::vector, but without memory re-allocations.
+ * Non-copyable types can be used as long as they are movable.
+ * Normally, the size is specified in the constructor and doesn't change ever.
+ * If you need to initialize the fixed_vector before you know its final size,
+ * there is one exception: You can initialize the fixed_vector with the default
+ * constructor, at a later time you can call reserve() and afterwards
+ * emplace_back(). In this case the size grows, but the memory is still never
+ * re-allocated.
+ * @par Differences to @c std::vector:
+ * - There are slightly different constructors, especially one with a
+ * size-argument and further arbitrary arguments which are forwarded to the
+ * constructor of each element.
+ * - reserve() and emplace_back() have different semantics.
+ * - all other functions which (potentially) change size are disabled.
+ **/
+template>
+class fixed_vector : public std::vector
+{
+ private:
+ using _base = typename std::vector;
+
+ public:
+ using value_type = typename _base::value_type;
+ using size_type = typename _base::size_type;
+
+ fixed_vector() = default;
+ fixed_vector(fixed_vector&&) = default;
+ fixed_vector(const fixed_vector&) = delete;
+ fixed_vector& operator=(const fixed_vector&) = delete;
+ fixed_vector& operator=(fixed_vector&&) = delete;
+
+ /// Constructor that forwards everything except if first type is integral.
+ template>
+ explicit fixed_vector(Args&&... args)
+ : _base(std::forward(args)...)
+ {}
+
+ template>
+ fixed_vector(Size n, const Allocator& a = Allocator())
+ : _base(n, a)
+ {}
+
+ template>
+ fixed_vector(Size n, Arg&& arg, const Allocator& a)
+ : _base(n, std::forward(arg), a)
+ {}
+
+ /// Constructor from size and initialization arguments.
+ /// This can be used for initializing nested containers, for example.
+ template
+ , typename = internal::if_last_not_convertible>
+ explicit fixed_vector(Size n, Args&&... args)
+ : _base()
+ {
+ _base::reserve(static_cast(n));
+ for (Size i = 0; i < n; ++i)
+ {
+ // Note: std::forward is not used here, because it's called repeatedly
+ _base::emplace_back(args...);
+ }
+ }
+
+ // Perfect forwarding doesn't cover initializer lists:
+ explicit fixed_vector(std::initializer_list il
+ , const Allocator& a = Allocator())
+ : _base(il, a)
+ {}
+
+ /** Reserve space for new elements and default-construct them.
+ * In contrast to @c std::vector::resize(), this can only be called @e once
+ * and only on an empty fixed_vector (i.e. iff capacity == 0).
+ * Thus, resize() will allocate memory, but never @e re-allocate.
+ * @throw std::logic_error if capacity != 0
+ **/
+ void resize(size_type n)
+ {
+ if (this->capacity() == 0)
+ {
+ _base::resize(n);
+ }
+ else
+ {
+ throw std::logic_error(
+ "Bug: fixed_vector::resize() is only allowed if capacity == 0!");
+ }
+ }
+
+ /** Reserve space for new elements.
+ * In contrast to @c std::vector::reserve(), this can only be called @e once
+ * and only on an empty fixed_vector (i.e. iff capacity == 0).
+ * Thus, reserve() will allocate memory, but never @e re-allocate.
+ * @throw std::logic_error if capacity != 0
+ **/
+ void reserve(size_type n)
+ {
+ if (this->capacity() == 0)
+ {
+ _base::reserve(n);
+ }
+ else
+ {
+ throw std::logic_error(
+ "Bug: fixed_vector::reserve() is only allowed if capacity == 0!");
+ }
+ }
+
+ /** Construct element at the end.
+ * In contrast to @c std::vector::emplace_back() this can @e only be called
+ * after reserve() and at most as many times as specified in reserve() (and
+ * is typically called @e exactly as many times).
+ * Thus, memory will never be allocated.
+ * @throw std::logic_error if capacity would be exceeded
+ **/
+ template
+ void emplace_back(Args&&... args)
+ {
+ if (this->size() < this->capacity())
+ {
+ _base::emplace_back(std::forward(args)...);
+ }
+ else
+ {
+ throw std::logic_error(
+ "Bug: fixed_vector::emplace_back() "
+ "is only allowed if size < capacity!");
+ }
+ }
+
+ private:
+ // Hide all base class functions which would change size:
+ void resize();
+ void assign();
+ void push_back();
+ void pop_back();
+ void insert();
+ void erase();
+ void swap();
+ void clear();
+ void emplace();
+};
+
+/** Derived from std::list, but without re-sizing.
+ * Items cannot be added/removed, but they can be re-ordered with move().
+ **/
+template>
+class fixed_list : public std::list
+{
+ private:
+ using _base = typename std::list;
+
+ public:
+ using value_type = typename _base::value_type;
+ using iterator = typename _base::iterator;
+
+ fixed_list() = default;
+ fixed_list(fixed_list&&) = default;
+ fixed_list(const fixed_list&) = delete;
+ fixed_list& operator=(const fixed_list&) = delete;
+ fixed_list& operator=(fixed_list&&) = delete;
+
+ /// Constructor that forwards everything except if first type is integral.
+ template>
+ explicit fixed_list(Args&&... args)
+ : _base(std::forward(args)...)
+ {}
+
+ /// Constructor from size and initialization arguments.
+ template>
+ explicit fixed_list(Size n, Args&&... args)
+ : _base()
+ {
+ for (Size i = 0; i < n; ++i)
+ {
+ // Note: std::forward is not used here, because it's called repeatedly
+ _base::emplace_back(args...);
+ }
+ }
+
+ explicit fixed_list(std::initializer_list il
+ , const Allocator& a = Allocator())
+ : _base(il, a)
+ {}
+
+ /// Move list element @p from one place @p to another.
+ /// @p from is placed in front of @p to.
+ /// No memory is allocated/deallocated, no content is copied.
+ void move(iterator from, iterator to)
+ {
+ _base::splice(to, *this, from);
+ }
+
+ /// Move range (from @p first to @p last) to @p target.
+ /// The range is placed in front of @p target.
+ /// No memory is allocated/deallocated, no content is copied.
+ void move(iterator first, iterator last, iterator target)
+ {
+ _base::splice(target, *this, first, last);
+ }
+
+ private:
+ // Hide all base class functions which would change size:
+ void assign();
+ void emplace_front();
+ void emplace_back();
+ void push_front();
+ void pop_front();
+ void push_back();
+ void pop_back();
+ void emplace();
+ void insert();
+ void erase();
+ void swap();
+ void resize();
+ void clear();
+ void splice();
+ void remove();
+ void remove_if();
+ void unique();
+ void merge();
+};
+
+/** Two-dimensional data storage for row- and column-wise access.
+ * The two dimensions have following properties:
+ * -# Channel
+ * - stored in contiguous memory
+ * - fixed_matrix can be iterated from channels.begin() to channels.end()
+ * (using fixed_matrix::channels_iterator)
+ * - resulting channel can be iterated from .begin() to .end()
+ * (using fixed_matrix::channel_iterator)
+ * -# Slice
+ * - stored in memory locations with constant step size
+ * - fixed_matrix can be iterated from slices.begin() to slices.end()
+ * (using fixed_matrix::slices_iterator)
+ * - resulting slice can be iterated from .begin() to .end()
+ * (using fixed_matrix::slice_iterator)
+ *
+ * @tparam T Type of stored data
+ **/
+template>
+class fixed_matrix : public fixed_vector
+{
+ private:
+ using _base = fixed_vector;
+
+ public:
+ using pointer = typename _base::pointer;
+ using size_type = typename _base::size_type;
+
+ /// Proxy class for returning one channel of the fixed_matrix
+ using Channel = has_begin_and_end;
+ /// Iterator within a Channel
+ using channel_iterator = typename Channel::iterator;
+
+ /// Proxy class for returning one slice of the fixed_matrix
+ using Slice = has_begin_and_end>;
+ /// Iterator within a Slice
+ using slice_iterator = typename Slice::iterator;
+
+ class channels_iterator;
+ class slices_iterator;
+
+ /// Default constructor.
+ /// Only initialize() makes sense after this.
+ explicit fixed_matrix(const Allocator& a = Allocator())
+ : _base(a)
+ {
+ this->initialize(0, 0);
+ }
+
+ fixed_matrix(fixed_matrix&&) = default;
+ fixed_matrix(const fixed_matrix&) = delete;
+ fixed_matrix& operator=(const fixed_matrix&) = delete;
+ fixed_matrix& operator=(fixed_matrix&&) = delete;
+
+ /** Constructor.
+ * @param max_channels Number of Channels
+ * @param max_slices Number of Slices
+ * @param a Optional allocator
+ **/
+ fixed_matrix(size_type max_channels, size_type max_slices
+ , const Allocator& a = Allocator())
+ : fixed_matrix(a)
+ {
+ this->initialize(max_channels, max_slices);
+ }
+
+ /// Allocate memory for @p max_channels x @p max_slices elements and
+ /// default-construct them.
+ /// @pre empty() == true
+ void initialize(size_type max_channels, size_type max_slices)
+ {
+ _base::resize(max_channels * max_slices);
+
+ this->channels = make_begin_and_end(
+ channels_iterator(_base::data(), max_slices), max_channels);
+ this->slices = make_begin_and_end(
+ slices_iterator(_base::data(), max_channels, max_slices), max_slices);
+
+ _channel_ptrs.reserve(max_channels);
+ for (const auto& channel: this->channels)
+ {
+ _channel_ptrs.emplace_back(&*channel.begin());
+ }
+ assert(_channel_ptrs.size() == max_channels);
+ }
+
+ template
+ void set_channels(const Ch& ch);
+
+ /// Get array of pointers to the channels. This can be useful to interact
+ /// with functions which use plain pointers instead of iterators.
+ pointer const* get_channel_ptrs() const { return _channel_ptrs.data(); }
+
+ /// Access to Channels; use channels.begin() and channels.end()
+ has_begin_and_end channels;
+ /// Access to Slices; use slices.begin() and slices.end()
+ has_begin_and_end slices;
+
+ private:
+ // Hide functions from fixed_vector:
+ void emplace_back();
+ void reserve();
+ void resize();
+
+ fixed_vector _channel_ptrs;
+};
+
+/// Iterator over fixed_matrix::Channel%s.
+template
+class fixed_matrix::channels_iterator
+{
+ private:
+ using self = channels_iterator;
+ using _base_type = stride_iterator;
+
+ /// Helper class for operator->()
+ struct ChannelArrowProxy : Channel
+ {
+ ChannelArrowProxy(const Channel& ch) : Channel(ch) {}
+ Channel* operator->() { return this; }
+ };
+
+ public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = Channel;
+ using reference = Channel;
+ using difference_type = typename _base_type::difference_type;
+ using pointer = ChannelArrowProxy;
+
+ /// Default constructor.
+ /// @note This constructor creates a singular iterator. Another
+ /// channels_iterator can be assigned to it, but nothing else works.
+ channels_iterator()
+ : _size(0)
+ {}
+
+ /// Constructor.
+ channels_iterator(channel_iterator base_iterator, size_type step)
+ : _base_iterator(base_iterator, step)
+ , _size(step)
+ {}
+
+ /// Dereference operator.
+ /// @return a proxy object of type fixed_matrix::Channel
+ reference operator*() const
+ {
+ auto temp = _base_iterator.base();
+ assert(apf::no_nullptr(temp));
+ return Channel(temp, temp + _size);
+ }
+
+ /// Arrow operator.
+ /// @return a proxy object of type fixed_matrix::ChannelArrowProxy
+ pointer operator->() const
+ {
+ return this->operator*();
+ }
+
+ APF_ITERATOR_RANDOMACCESS_EQUAL(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_PREINCREMENT(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_PREDECREMENT(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_ADDITION_ASSIGNMENT(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_DIFFERENCE(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_SUBSCRIPT
+ APF_ITERATOR_RANDOMACCESS_LESS(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_UNEQUAL
+ APF_ITERATOR_RANDOMACCESS_OTHER_COMPARISONS
+ APF_ITERATOR_RANDOMACCESS_POSTINCREMENT
+ APF_ITERATOR_RANDOMACCESS_POSTDECREMENT
+ APF_ITERATOR_RANDOMACCESS_THE_REST
+
+ APF_ITERATOR_BASE(_base_type, _base_iterator)
+
+ private:
+ _base_type _base_iterator;
+ size_type _size;
+};
+
+/// Iterator over fixed_matrix::Slice%s.
+template
+class fixed_matrix::slices_iterator
+{
+ private:
+ using self = slices_iterator;
+
+ /// Helper class for operator->()
+ struct SliceArrowProxy : Slice
+ {
+ SliceArrowProxy(const Slice& sl) : Slice(sl) {}
+ Slice* operator->() { return this; }
+ };
+
+ public:
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = Slice;
+ using reference = Slice;
+ using pointer = SliceArrowProxy;
+ using difference_type
+ = typename std::iterator_traits::difference_type;
+
+ /// Default constructor.
+ /// @note This constructor creates a singular iterator. Another
+ /// slices_iterator can be assigned to it, but nothing else works.
+ slices_iterator()
+ : _max_channels(0)
+ , _max_slices(0)
+ {}
+
+ /// Constructor.
+ slices_iterator(channel_iterator base_iterator
+ , size_type max_channels, size_type max_slices)
+ : _base_iterator(base_iterator)
+ , _max_channels(max_channels)
+ , _max_slices(max_slices)
+ {}
+
+ /// Dereference operator.
+ /// @return a proxy object of type fixed_matrix::Slice
+ reference operator*() const
+ {
+ assert(apf::no_nullptr(_base_iterator));
+ slice_iterator temp(_base_iterator, _max_slices);
+ return Slice(temp, temp + _max_channels);
+ }
+
+ /// Arrow operator.
+ /// @return a proxy object of type fixed_matrix::SliceArrowProxy
+ pointer operator->() const
+ {
+ return this->operator*();
+ }
+
+ APF_ITERATOR_RANDOMACCESS_EQUAL(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_PREINCREMENT(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_PREDECREMENT(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_ADDITION_ASSIGNMENT(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_DIFFERENCE(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_SUBSCRIPT
+ APF_ITERATOR_RANDOMACCESS_LESS(_base_iterator)
+ APF_ITERATOR_RANDOMACCESS_UNEQUAL
+ APF_ITERATOR_RANDOMACCESS_OTHER_COMPARISONS
+ APF_ITERATOR_RANDOMACCESS_POSTINCREMENT
+ APF_ITERATOR_RANDOMACCESS_POSTDECREMENT
+ APF_ITERATOR_RANDOMACCESS_THE_REST
+
+ APF_ITERATOR_BASE(channel_iterator, _base_iterator)
+
+ private:
+ channel_iterator _base_iterator;
+ size_type _max_channels;
+ size_type _max_slices;
+};
+
+/** Copy channels from another matrix.
+ * @param ch channels (or slices) to copy from another fixed_matrix
+ * @note A plain copy may be faster with @c std::copy() from
+ * fixed_matrix::begin() to fixed_matrix::end().
+ * @note Anyway, a plain copy of a fixed_matrix is rarely needed, the main
+ * reason for this function is that if you use slices instead of channels,
+ * you'll get a transposed matrix.
+ * @pre The dimensions must be correct beforehand!
+ * @warning If the dimensions are not correct, bad things will happen!
+ **/
+template
+template
+void
+fixed_matrix::set_channels(const Ch& ch)
+{
+ assert(std::distance(ch.begin(), ch.end())
+ == std::distance(this->channels.begin(), this->channels.end()));
+ assert((ch.begin() == ch.end()) ? true :
+ std::distance(ch.begin()->begin(), ch.begin()->end()) ==
+ std::distance(this->channels.begin()->begin()
+ , this->channels.begin()->end()));
+
+ auto target = this->channels.begin();
+
+ for (const auto& i: ch)
+ {
+ std::copy(i.begin(), i.end(), target->begin());
+ ++target;
+ }
+}
+
+/// Append pointers to the elements of the first list to the second list.
+/// @note @c L2::value_type must be a pointer to @c L1::value_type!
+template
+void append_pointers(L1& source, L2& target)
+{
+ for (auto& i: source)
+ {
+ target.push_back(&i);
+ }
+}
+
+/// Const-version of append_pointers()
+/// @note @c L2::value_type must be a pointer to @b const @c L1::value_type!
+template
+void append_pointers(const L1& source, L2& target)
+{
+ for (const auto& i: source)
+ {
+ target.push_back(&i);
+ }
+}
+
+/// Splice list elements from @p source to member lists of @p target.
+/// @param source Elements of this list are distributed to the corresponding
+/// @p member lists of @p target. This must have the same type as @p member.
+/// @param target Each element of this list receives one element of @p source.
+/// @param member The distributed elements are appended at @c member.end().
+/// @note Lists must have the same size. If not, an exception is thrown.
+/// @note There is no const version, both lists are modified.
+/// @post @p source will be empty.
+/// @post The @p member of each @p target element will have one more element.
+template
+void distribute_list(L1& source, L2& target, DataMember member)
+{
+ if (source.size() != target.size())
+ {
+ throw std::logic_error("distribute_list: Different sizes!");
+ }
+
+ auto in = source.begin();
+
+ for (auto& out: target)
+ {
+ (out.*member).splice((out.*member).end(), source, in++);
+ }
+}
+
+/// The opposite of distribute_list() -- sorry for the strange name!
+/// @param source Container of items which will be removed from @p member of the
+/// corresponding @p target elements.
+/// @param target Container of elements which have a @p member.
+/// @param member Member container from which elements will be removed. Must
+/// have a splice() member function (like @c std::list).
+/// @param garbage Removed elements are appended to this list. Must have the
+/// same type as @p member.
+/// @throw std::logic_error If any element isn't found in the corresponding
+/// @p member.
+/// @attention If a list element is not found, an exception is thrown and the
+/// original state is @b not restored!
+// TODO: better name?
+template
+void
+undistribute_list(const L1& source, L2& target, DataMember member, L3& garbage)
+{
+ if (source.size() != target.size())
+ {
+ throw std::logic_error("undistribute_list(): Different sizes!");
+ }
+
+ auto in = source.begin();
+
+ for (auto& out: target)
+ {
+ auto delinquent
+ = std::find((out.*member).begin(), (out.*member).end(), *in++);
+ if (delinquent == (out.*member).end())
+ {
+ throw std::logic_error("undistribute_list(): Element not found!");
+ }
+ garbage.splice(garbage.end(), out.*member, delinquent);
+ }
+}
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
+// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='
diff --git a/apf/apf/convolver.h b/apf/apf/convolver.h
new file mode 100644
index 00000000..091e2948
--- /dev/null
+++ b/apf/apf/convolver.h
@@ -0,0 +1,827 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Convolution engine.
+
+#ifndef APF_CONVOLVER_H
+#define APF_CONVOLVER_H
+
+#include // for std::transform()
+#include
+
+#ifdef __SSE__
+#include // for SSE instrinsics
+#endif
+
+#include "apf/math.h"
+#include "apf/fftwtools.h" // for fftw_allocator and fftw traits
+#include "apf/container.h" // for fixed_vector, fixed_list
+#include "apf/iterator.h" // for make_*_iterator()
+
+namespace apf
+{
+
+/** Convolution engine.
+ * A convolution engine normally consists of an Input, a Filter and
+ * an Output / StaticOutput.
+ * There are also combinations:
+ * Input + Output = Convolver; Input + StaticOutput = StaticConvolver
+ *
+ * Uses (uniformly) partitioned convolution.
+ *
+ * TODO: describe thread (un)safety
+ **/
+namespace conv
+{
+
+/// Calculate necessary number of partitions for a given filter length
+static size_t min_partitions(size_t block_size, size_t filter_size)
+{
+ return (filter_size + block_size - 1) / block_size;
+}
+
+/// Two blocks of time-domain or FFT (half-complex) data.
+struct fft_node : fixed_vector>
+{
+ explicit fft_node(size_t n)
+ : fixed_vector>(n)
+ , zero(true)
+ {}
+
+ fft_node(const fft_node&) = delete;
+ fft_node(fft_node&&) = default;
+
+ fft_node& operator=(const fft_node& rhs)
+ {
+ assert(this->size() == rhs.size());
+
+ if (rhs.zero)
+ {
+ this->zero = true;
+ }
+ else
+ {
+ std::copy(rhs.begin(), rhs.end(), this->begin());
+ this->zero = false;
+ }
+ return *this;
+ }
+
+ // WARNING: The 'zero' flag allows saving computation power, but it also
+ // raises the risk of programming errors! Handle with care!
+
+ /// To avoid unnecessary FFTs and filling buffers with zeros.
+ /// @note If zero == true, the buffer itself is not necessarily all zeros!
+ bool zero;
+};
+
+/// Container holding a number of FFT blocks.
+struct Filter : fixed_vector
+{
+ /// Constructor; create empty filter.
+ Filter(size_t block_size_, size_t partitions_)
+ : fixed_vector(partitions_, block_size_ * 2)
+ {
+ assert(this->partitions() > 0);
+ }
+
+ /// Constructor from time domain coefficients.
+ template
+ Filter(size_t block_size_, In first, In last, size_t partitions_ = 0);
+ // Implementation below, after definition of Transform
+
+ size_t block_size() const { return this->front().size() / 2; }
+ size_t partition_size() const { return this->front().size(); }
+ size_t partitions() const { return this->size(); }
+};
+
+/// Forward-FFT-related functions
+class TransformBase
+{
+ public:
+ template
+ void prepare_filter(In first, In last, Filter& filter) const;
+
+ size_t block_size() const { return _block_size; }
+ size_t partition_size() const { return _partition_size; }
+
+ template
+ In prepare_partition(In first, In last, fft_node& partition) const;
+
+ protected:
+ explicit TransformBase(size_t block_size_);
+
+ TransformBase(TransformBase&&) = default;
+ ~TransformBase() = default;
+
+ using scoped_plan = fftw::scoped_plan;
+ using plan_ptr = std::unique_ptr;
+
+ plan_ptr _create_plan(float* array) const;
+
+ /// In-place FFT
+ void _fft(float* first) const
+ {
+ fftw::execute_r2r(*_fft_plan, first, first);
+ _sort_coefficients(first);
+ }
+
+ plan_ptr _fft_plan;
+
+ private:
+ void _sort_coefficients(float* first) const;
+
+ const size_t _block_size;
+ const size_t _partition_size;
+};
+
+TransformBase::TransformBase(size_t block_size_)
+ : _block_size(block_size_)
+ , _partition_size(2 * _block_size)
+{
+ if (_block_size % 8 != 0)
+ {
+ throw std::logic_error("Convolver: block size must be a multiple of 8!");
+ }
+}
+
+/** Create in-place FFT plan for halfcomplex data format.
+ * @note FFT plans are not re-entrant except when using FFTW_THREADSAFE!
+ * @note Once a plan of a certain size exists, creating further plans
+ * is very fast because "wisdom" is shared (and therefore the creation of
+ * plans is not thread-safe).
+ * It is not necessary to re-use plans in other convolver instances.
+ **/
+TransformBase::plan_ptr
+TransformBase::_create_plan(float* array) const
+{
+ return plan_ptr(new scoped_plan(fftw::plan_r2r_1d, int(_partition_size)
+ , array, array, FFTW_R2HC, FFTW_PATIENT));
+}
+
+/** %Transform time-domain samples.
+ * If there are too few input samples, the rest is zero-padded, if there are
+ * too few blocks in the container @p c, the rest of the samples is ignored.
+ * @param first Iterator to first time-domain sample
+ * @param last Past-the-end iterator
+ * @param[out] filter Target container
+ **/
+template
+void
+TransformBase::prepare_filter(In first, In last, Filter& filter) const
+{
+ for (auto& partition: filter)
+ {
+ first = this->prepare_partition(first, last, partition);
+ }
+}
+
+/** FFT of one block.
+ * If there are too few coefficients, the rest is zero-padded.
+ * @param first Iterator to first coefficient
+ * @param last Past-the-end iterator
+ * @param[out] partition Target partition
+ * @tparam In Forward iterator
+ * @return Iterator to the first coefficient of the next block (for the next
+ * iteration, if needed)
+ **/
+template
+In
+TransformBase::prepare_partition(In first, In last, fft_node& partition) const
+{
+ assert(size_t(std::distance(partition.begin(), partition.end()))
+ == _partition_size);
+
+ auto chunk = std::min(_block_size, size_t(std::distance(first, last)));
+
+ if (chunk == 0)
+ {
+ partition.zero = true;
+ // No FFT has to be done (FFT of zero is also zero)
+ }
+ else
+ {
+ std::copy(first, first + chunk, partition.begin());
+ std::fill(partition.begin() + chunk, partition.end(), 0.0f); // zero padding
+ _fft(partition.data());
+ partition.zero = false;
+ }
+ return first + chunk;
+}
+
+/** Sort the FFT coefficients to be in proper place for the efficient
+ * multiplication of the spectra.
+ **/
+void
+TransformBase::_sort_coefficients(float* data) const
+{
+ auto buffer = fixed_vector(_partition_size);
+
+ size_t base = 8;
+
+ buffer[0] = data[0];
+ buffer[1] = data[1];
+ buffer[2] = data[2];
+ buffer[3] = data[3];
+ buffer[4] = data[_block_size];
+ buffer[5] = data[_partition_size - 1];
+ buffer[6] = data[_partition_size - 2];
+ buffer[7] = data[_partition_size - 3];
+
+ for (size_t i = 0; i < (_partition_size / 8-1); i++)
+ {
+ for (size_t ii = 0; ii < 4; ii++)
+ {
+ buffer[base+ii] = data[base/2+ii];
+ }
+
+ for (size_t ii = 0; ii < 4; ii++)
+ {
+ buffer[base+4+ii] = data[_partition_size-base/2-ii];
+ }
+
+ base += 8;
+ }
+
+ std::copy(buffer.begin(), buffer.end(), data);
+}
+
+/// Helper class to prepare filters
+struct Transform : TransformBase
+{
+ Transform(size_t block_size_)
+ : TransformBase(block_size_)
+ {
+ // Temporary memory area for FFTW planning routines
+ fft_node planning_space(this->partition_size());
+ _fft_plan = _create_plan(planning_space.data());
+ }
+};
+
+template
+Filter::Filter(size_t block_size_, In first, In last, size_t partitions_)
+ : fixed_vector(partitions_ ? partitions_
+ : min_partitions(block_size_, std::distance(first, last))
+ , block_size_ * 2)
+{
+ assert(this->partitions() > 0);
+
+ Transform(block_size_).prepare_filter(first, last, *this);
+}
+
+/** %Input stage of convolution.
+ * New audio data is fed in here, further processing happens in Output.
+ **/
+struct Input : TransformBase
+{
+ /// @param block_size_ audio block size
+ /// @param partitions_ number of partitions
+ Input(size_t block_size_, size_t partitions_)
+ : TransformBase(block_size_)
+ // One additional list element for preparing the upcoming partition:
+ , spectra(partitions_ + 1, this->partition_size())
+ {
+ assert(partitions_ > 0);
+
+ _fft_plan = _create_plan(spectra.front().data());
+ }
+
+ template
+ void add_block(In first);
+
+ size_t partitions() const { return spectra.size() - 1; }
+
+ /// Spectra of the partitions (double-blocks) of the input signal to be
+ /// convolved. The first element is the most recent signal chunk.
+ fixed_list spectra;
+};
+
+/** Add a block of time-domain input samples.
+ * @param first Iterator to first sample.
+ * @tparam In Forward iterator
+ **/
+template
+void
+Input::add_block(In first)
+{
+ In last = first;
+ std::advance(last, this->block_size());
+
+ // rotate buffers (this->spectra.size() is always at least 2)
+ this->spectra.move(--this->spectra.end(), this->spectra.begin());
+
+ auto& current = this->spectra.front();
+ auto& next = this->spectra.back();
+
+ if (math::has_only_zeros(first, last))
+ {
+ next.zero = true;
+
+ if (current.zero)
+ {
+ // Nothing to be done, actual data is ignored
+ }
+ else
+ {
+ // If first half is not zero, second half must be filled with zeros
+ std::fill(current.begin() + this->block_size(), current.end(), 0.0f);
+ }
+ }
+ else
+ {
+ if (current.zero)
+ {
+ // First half must be actually filled with zeros
+ std::fill(current.begin(), current.begin() + this->block_size(), 0.0f);
+ }
+
+ // Copy data to second half of the current partition
+ std::copy(first, last, current.begin() + this->block_size());
+ current.zero = false;
+ // Copy data to first half of the upcoming partition
+ std::copy(first, last, next.begin());
+ next.zero = false;
+ }
+
+ if (current.zero)
+ {
+ // Nothing to be done, FFT of zero is also zero
+ }
+ else
+ {
+ _fft(current.data());
+ }
+}
+
+/// Base class for Output and StaticOutput
+class OutputBase
+{
+ public:
+ float* convolve(float weight = 1.0f);
+
+ size_t block_size() const { return _input.block_size(); }
+ size_t partitions() const { return _filter_ptrs.size(); }
+
+ protected:
+ explicit OutputBase(const Input& input);
+
+ // This is non-const to allow automatic move-constructor:
+ fft_node _empty_partition;
+
+ using filter_ptrs_t = fixed_vector;
+ filter_ptrs_t _filter_ptrs;
+
+ private:
+ void _multiply_spectra();
+ void _multiply_partition_cpp(const float* signal, const float* filter);
+#ifdef __SSE__
+ void _multiply_partition_simd(const float* signal, const float* filter);
+#endif
+
+ void _unsort_coefficients();
+
+ void _ifft();
+
+ const Input& _input;
+
+ const size_t _partition_size;
+
+ fft_node _output_buffer;
+ fftw::scoped_plan _ifft_plan;
+};
+
+OutputBase::OutputBase(const Input& input)
+ : _empty_partition(0)
+ // Initialize with empty partition
+ , _filter_ptrs(input.partitions(), &_empty_partition)
+ , _input(input)
+ , _partition_size(input.partition_size())
+ , _output_buffer(_partition_size)
+ , _ifft_plan(fftw::plan_r2r_1d, int(_partition_size)
+ , _output_buffer.data()
+ , _output_buffer.data(), FFTW_HC2R, FFTW_PATIENT)
+{
+ assert(_filter_ptrs.size() > 0);
+}
+
+/** Fast convolution of one audio block.
+ * %Input data has to be supplied with Input::add_block().
+ * @param weight amplitude weighting factor for current audio block.
+ * The filter has to be set in the constructor of StaticOutput or via
+ * Output::set_filter().
+ * @return pointer to the first sample of the convolved (and weighted) signal
+ **/
+float*
+OutputBase::convolve(float weight)
+{
+ _multiply_spectra();
+
+ // The first half will be discarded
+ auto second_half = make_begin_and_end(
+ _output_buffer.begin() + _input.block_size(), _output_buffer.end());
+
+ assert(static_cast(
+ std::distance(second_half.begin(), second_half.end()))
+ == _input.block_size());
+
+ if (_output_buffer.zero)
+ {
+ // Nothing to be done, IFFT of zero is also zero.
+ // _output_buffer was already reset to zero in _multiply_spectra().
+ }
+ else
+ {
+ _ifft();
+
+ // normalize buffer (fftw3 does not do this)
+ const auto norm = weight / float(_partition_size);
+ for (auto& x: second_half)
+ {
+ x *= norm;
+ }
+ }
+ return &second_half[0];
+}
+
+void
+OutputBase::_multiply_partition_cpp(const float* signal, const float* filter)
+{
+ // see http://www.ludd.luth.se/~torger/brutefir.html#bruteconv_4
+
+ auto d1s = _output_buffer[0] + signal[0] * filter[0];
+ auto d2s = _output_buffer[4] + signal[4] * filter[4];
+
+ for (size_t nn = 0; nn < _partition_size; nn += 8)
+ {
+ // real parts
+ _output_buffer[nn+0] += signal[nn+0] * filter[nn + 0] -
+ signal[nn+4] * filter[nn + 4];
+ _output_buffer[nn+1] += signal[nn+1] * filter[nn + 1] -
+ signal[nn+5] * filter[nn + 5];
+ _output_buffer[nn+2] += signal[nn+2] * filter[nn + 2] -
+ signal[nn+6] * filter[nn + 6];
+ _output_buffer[nn+3] += signal[nn+3] * filter[nn + 3] -
+ signal[nn+7] * filter[nn + 7];
+
+ // imaginary parts
+ _output_buffer[nn+4] += signal[nn+0] * filter[nn + 4] +
+ signal[nn+4] * filter[nn + 0];
+ _output_buffer[nn+5] += signal[nn+1] * filter[nn + 5] +
+ signal[nn+5] * filter[nn + 1];
+ _output_buffer[nn+6] += signal[nn+2] * filter[nn + 6] +
+ signal[nn+6] * filter[nn + 2];
+ _output_buffer[nn+7] += signal[nn+3] * filter[nn + 7] +
+ signal[nn+7] * filter[nn + 3];
+
+ } // for
+
+ _output_buffer[0] = d1s;
+ _output_buffer[4] = d2s;
+}
+
+#ifdef __SSE__
+void
+OutputBase::_multiply_partition_simd(const float* signal, const float* filter)
+{
+ // 16 byte alignment is needed for _mm_load_ps()!
+ // This should be the case anyway because fftwf_malloc() is used.
+
+ auto dc = _output_buffer[0] + signal[0] * filter[0];
+ auto ny = _output_buffer[4] + signal[4] * filter[4];
+
+ for(size_t i = 0; i < _partition_size; i += 8)
+ {
+ // load real and imaginary parts of signal and filter
+ __m128 sigr = _mm_load_ps(signal + i);
+ __m128 sigi = _mm_load_ps(signal + i + 4);
+ __m128 filtr = _mm_load_ps(filter + i);
+ __m128 filti = _mm_load_ps(filter + i + 4);
+
+ // multiply and subtract
+ __m128 res1 = _mm_sub_ps(_mm_mul_ps(sigr, filtr), _mm_mul_ps(sigi, filti));
+
+ // multiply and add
+ __m128 res2 = _mm_add_ps(_mm_mul_ps(sigr, filti), _mm_mul_ps(sigi, filtr));
+
+ // load output data for accumulation
+ __m128 acc1 = _mm_load_ps(&_output_buffer[i]);
+ __m128 acc2 = _mm_load_ps(&_output_buffer[i + 4]);
+
+ // accumulate
+ acc1 = _mm_add_ps(acc1, res1);
+ acc2 = _mm_add_ps(acc2, res2);
+
+ // store output data
+ _mm_store_ps(&_output_buffer[i], acc1);
+ _mm_store_ps(&_output_buffer[i + 4], acc2);
+ }
+
+ _output_buffer[0] = dc;
+ _output_buffer[4] = ny;
+}
+#endif
+
+/// Complex multiplication of input and filter spectra
+void
+OutputBase::_multiply_spectra()
+{
+ // Clear IFFT buffer
+ std::fill(_output_buffer.begin(), _output_buffer.end(), 0.0f);
+ _output_buffer.zero = true;
+
+ assert(_filter_ptrs.size() == _input.partitions());
+
+ auto input = _input.spectra.begin();
+
+ for (const auto filter: _filter_ptrs)
+ {
+ assert(filter != nullptr);
+
+ if (input->zero || filter->zero) continue;
+
+#ifdef __SSE__
+ _multiply_partition_simd(input->data(), filter->data());
+#else
+ _multiply_partition_cpp(input->data(), filter->data());
+#endif
+
+ _output_buffer.zero = false;
+ ++input;
+ }
+}
+
+void
+OutputBase::_unsort_coefficients()
+{
+ fixed_vector buffer(_partition_size);
+
+ size_t base = 8;
+
+ buffer[0] = _output_buffer[0];
+ buffer[1] = _output_buffer[1];
+ buffer[2] = _output_buffer[2];
+ buffer[3] = _output_buffer[3];
+ buffer[_input.block_size()] = _output_buffer[4];
+ buffer[_partition_size-1] = _output_buffer[5];
+ buffer[_partition_size-2] = _output_buffer[6];
+ buffer[_partition_size-3] = _output_buffer[7];
+
+ for (size_t i=0; i < (_partition_size / 8-1); i++)
+ {
+ for (size_t ii = 0; ii < 4; ii++)
+ {
+ buffer[base/2+ii] = _output_buffer[base+ii];
+ }
+
+ for (size_t ii = 0; ii < 4; ii++)
+ {
+ buffer[_partition_size-base/2-ii] = _output_buffer[base+4+ii];
+ }
+
+ base += 8;
+ }
+
+ std::copy(buffer.begin(), buffer.end(), _output_buffer.begin());
+}
+
+void
+OutputBase::_ifft()
+{
+ _unsort_coefficients();
+ fftw::execute(_ifft_plan);
+}
+
+/** Convolution engine (output part).
+ * @see Input, StaticOutput
+ **/
+class Output : public OutputBase
+{
+ public:
+ Output(const Input& input)
+ : OutputBase(input)
+ , _queues(apf::make_index_iterator(size_t(1))
+ , apf::make_index_iterator(input.partitions()))
+ {}
+
+ void set_filter(const Filter& filter);
+
+ bool queues_empty() const;
+ void rotate_queues();
+
+ private:
+ fixed_vector _queues;
+};
+
+/** Set a new filter.
+ * The first filter partition is updated immediately, the later partitions are
+ * updated with rotate_queues().
+ * @param filter Container with filter partitions. If too few partitions are
+ * given, the rest is set to zero, if too many are given, the rest is ignored.
+ **/
+void
+Output::set_filter(const Filter& filter)
+{
+ auto partition = filter.begin();
+
+ // First partition has no queue and is updated immediately
+ if (partition != filter.end())
+ {
+ _filter_ptrs.front() = &*partition++;
+ }
+
+ for (size_t i = 0; i < _queues.size(); ++i)
+ {
+ _queues[i][i]
+ = (partition == filter.end()) ? &_empty_partition : &*partition++;
+ }
+}
+
+/** Check if there are still valid partitions in the queues.
+ * If this function returns @b false, rotate_queues() should be called.
+ * @note This is important for crossfades: even if set_filter() wasn't used,
+ * older partitions may still change! If the queues are empty, no crossfade is
+ * necessary (except @p weight was changed in convolve()).
+ **/
+bool
+Output::queues_empty() const
+{
+ if (_queues.empty()) return true;
+
+ // It may not be obvious, but that's what the following code does:
+ // If some non-null pointer is found in the last queue, return false
+
+ auto first = _queues.rbegin()->begin();
+ auto last = _queues.rbegin()->end();
+ return std::find_if(first, last, math::identity()) == last;
+}
+
+/** Update filter queues.
+ * If queues_empty() returns @b true, calling this function is unnecessary.
+ * @note This can lead to artifacts, so a crossfade is recommended.
+ **/
+void
+Output::rotate_queues()
+{
+ auto target = _filter_ptrs.begin();
+ // Skip first element, it doesn't have a queue
+ ++target;
+
+ for (auto& queue: _queues)
+ {
+ // If first element is valid, use it
+ if (queue.front()) *target = queue.front();
+
+ std::copy(queue.begin() + 1, queue.end(), queue.begin());
+ *queue.rbegin() = nullptr;
+ ++target;
+ }
+}
+
+/** %Convolver output stage with static filter.
+ * The filter coefficients are set in the constructor(s) and cannot be changed.
+ * @see Output
+ **/
+class StaticOutput : public OutputBase
+{
+ public:
+ /// Constructor from time domain samples
+ template
+ StaticOutput(const Input& input, In first, In last)
+ : OutputBase(input)
+ {
+ _filter.reset(new Filter(input.block_size(), first, last
+ , input.partitions()));
+
+ _set_filter(*_filter);
+ }
+
+ /// Constructor from existing frequency domain filter coefficients.
+ /// @attention The filter coefficients are not copied, their lifetime must
+ /// exceed that of the StaticOutput!
+ StaticOutput(const Input& input, const Filter& filter)
+ : OutputBase(input)
+ {
+ _set_filter(filter);
+ }
+
+ private:
+ void _set_filter(const Filter& filter)
+ {
+ auto from = filter.begin();
+
+ for (auto& to: _filter_ptrs)
+ {
+ // If less partitions are given, the rest is set to zero
+ to = (from == filter.end()) ? &_empty_partition : &*from++;
+ }
+ // If further partitions are available, they are ignored
+ }
+
+ // This is only used for the first constructor!
+ std::unique_ptr _filter;
+};
+
+/// Combination of Input and Output
+struct Convolver : Input, Output
+{
+ Convolver(size_t block_size_, size_t partitions_)
+ : Input(block_size_, partitions_)
+ // static_cast to resolve ambiguity
+ , Output(*static_cast(this))
+ {}
+};
+
+/// Combination of Input and StaticOutput
+struct StaticConvolver : Input, StaticOutput
+{
+ template
+ StaticConvolver(size_t block_size_, In first, In last, size_t partitions_ = 0)
+ : Input(block_size_, partitions_ ? partitions_
+ : min_partitions(block_size_, std::distance(first, last)))
+ , StaticOutput(*this, first, last)
+ {}
+
+ StaticConvolver(const Filter& filter, size_t partitions_ = 0)
+ : Input(filter.block_size()
+ , partitions_ ? partitions_ : filter.partitions())
+ , StaticOutput(*this, filter)
+ {}
+};
+
+/// Apply @c std::transform to a container of fft_node%s
+template
+void transform_nested(const Filter& in1, const Filter& in2, Filter& out
+ , BinaryFunction f)
+{
+ auto it1 = in1.begin();
+ auto it2 = in2.begin();
+
+ for (auto& result: out)
+ {
+ if (it1 == in1.end() || it1->zero)
+ {
+ if (it2 == in2.end() || it2->zero)
+ {
+ result.zero = true;
+ }
+ else
+ {
+ assert(it2->size() == result.size());
+ std::transform(it2->begin(), it2->end(), result.begin()
+ , std::bind(f, 0, std::placeholders::_1));
+ result.zero = false;
+ }
+ }
+ else
+ {
+ if (it2 == in2.end() || it2->zero)
+ {
+ assert(it1->size() == result.size());
+ std::transform(it1->begin(), it1->end(), result.begin()
+ , std::bind(f, std::placeholders::_1, 0));
+ result.zero = false;
+ }
+ else
+ {
+ assert(it1->size() == it2->size());
+ assert(it1->size() == result.size());
+ std::transform(it1->begin(), it1->end(), it2->begin(), result.begin()
+ , f);
+ result.zero = false;
+ }
+ }
+ if (it1 != in1.end()) ++it1;
+ if (it2 != in2.end()) ++it2;
+ }
+}
+
+} // namespace conv
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
+// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='
diff --git a/apf/apf/denormalprevention.h b/apf/apf/denormalprevention.h
new file mode 100644
index 00000000..0449389f
--- /dev/null
+++ b/apf/apf/denormalprevention.h
@@ -0,0 +1,239 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Different methods to prevent denormal numbers.
+
+#ifndef APF_DENORMALPREVENTION_H
+#define APF_DENORMALPREVENTION_H
+
+#include // for std::numeric_limits()
+#include // for std::abs()
+
+#ifdef __SSE__
+#include // for SSE intrinsics
+#endif
+#ifdef __SSE3__
+#include // for SSE3 intrinsics
+#endif
+
+namespace apf
+{
+
+/// Denormal prevention
+/// @see Laurent de Soras, "Denormal numbers in floating point signal processing
+/// applications": http://ldesoras.free.fr/doc/articles/denormal-en.pdf
+namespace dp
+{
+
+/// Disable denormal prevention.
+template
+struct none
+{
+ void prevent_denormals(T&) {}
+};
+
+template struct dc; // default case not implemented!
+
+/// Add DC signal (float specialization).
+template<>
+struct dc
+{
+ static void prevent_denormals(float& val) { val += 1e-18f; }
+};
+
+/// Add DC signal (double specialization).
+template<>
+struct dc
+{
+ static void prevent_denormals(double& val) { val += 1e-30; }
+};
+
+template struct ac; // default case not implemented!
+
+/// Add sine component at nyquist frequency (float specialization).
+template<>
+struct ac
+{
+ public:
+ ac() : _anti_denorm(1e-18f) {}
+
+ void prevent_denormals(float& val)
+ {
+ _anti_denorm = -_anti_denorm;
+ val += _anti_denorm;
+ }
+
+ private:
+ float _anti_denorm;
+};
+
+/// Add sine component at nyquist frequency (double specialization).
+template<>
+struct ac
+{
+ public:
+ ac() : _anti_denorm(1e-30) {}
+
+ void prevent_denormals(double& val)
+ {
+ _anti_denorm = -_anti_denorm;
+ val += _anti_denorm;
+ }
+
+ private:
+ double _anti_denorm;
+};
+
+template struct quantization; // default case not implemented!
+
+/// Quantize denormal numbers (float specialization).
+template<>
+struct quantization
+{
+ static void prevent_denormals(float& val)
+ {
+ val += 1e-18f;
+ val -= 1e-18f;
+ }
+};
+
+/// Quantize denormal numbers (double specialization).
+template<>
+struct quantization
+{
+ static void prevent_denormals(double& val)
+ {
+ val += 1e-30;
+ val -= 1e-30;
+ }
+};
+
+/// Detect denormals and set 0.
+template
+struct set_zero_1
+{
+ static void prevent_denormals(T& val)
+ {
+ if (std::abs(val) < std::numeric_limits::min() && (val != 0)) val = 0;
+ }
+};
+
+/// Detect denormals and set 0.
+template
+struct set_zero_2
+{
+ static void prevent_denormals(T& val)
+ {
+ if ((val != 0) && std::abs(val) < std::numeric_limits::min()) val = 0;
+ }
+};
+
+/// Detect denormals and set 0.
+template
+struct set_zero_3
+{
+ static void prevent_denormals(T& val)
+ {
+ if (std::abs(val) < std::numeric_limits::min()) val = 0;
+ }
+};
+
+#if 0
+// add noise component; equally distributed spectrum
+// NOTE: noise appears to be kind of deterministic
+// - temporarily deactivated due to warnings
+
+template struct NoisePrevention;
+
+template<>
+struct NoisePrevention
+{
+ public:
+ NoisePrevention() : _rand_state(1) {}
+
+ void prevent_denormals(float& val)
+ {
+ _rand_state = _rand_state * 1234567UL + 890123UL;
+ int mantissa = _rand_state & 0x807F0000; // Keep only most significant bits
+ int flt_rnd = mantissa | 0x1E000000; // Set exponent
+ val += *reinterpret_cast(&flt_rnd);
+ }
+
+ private:
+ unsigned int _rand_state;
+};
+#endif
+
+#ifdef __SSE__
+// The compiler must be set to generate SSE instructions automatically!
+// In GCC, this is done with -mfpmath=sse (which is on by default on amd64)
+
+/// Set Flush-To-Zero (FTZ).
+/// @note requires SSE support
+inline void ftz_on()
+{
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+}
+
+/// Unset Flush-To-Zero (FTZ).
+/// @note requires SSE support
+inline void ftz_off()
+{
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF);
+}
+
+#ifdef __SSE3__
+/// Set Denormals-Are-Zero (DAZ).
+/// @note requires SSE3 support
+///
+/// From http://softpixel.com/~cwright/programming/simd/sse.php:
+///
+/// DAZ wasn't available in the first version of SSE. Since setting a reserved
+/// bit in MXCSR causes a general protection fault, we need to be able to check
+/// the availability of this feature without causing problems. To do this, one
+/// needs to set up a 512-byte area of memory to save the SSE state to, using
+/// fxsave, and then one needs to inspect bytes 28 through 31 for the MXCSR_MASK
+/// value. If bit 6 is set, DAZ is supported, otherwise, it isn't.
+void daz_on()
+{
+ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+}
+
+/// Unset Denormals-Are-Zero (DAZ).
+/// @note requires SSE3 support
+void daz_off()
+{
+ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF);
+}
+#endif
+#endif
+
+} // namespace dp
+
+} // namespace apf
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
diff --git a/apf/apf/dummy_thread_policy.h b/apf/apf/dummy_thread_policy.h
new file mode 100644
index 00000000..75458225
--- /dev/null
+++ b/apf/apf/dummy_thread_policy.h
@@ -0,0 +1,154 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Dummy thread policy class.
+
+#ifndef APF_DUMMY_THREAD_POLICY_H
+#define APF_DUMMY_THREAD_POLICY_H
+
+#include // for std::logic_error
+
+#ifndef APF_MIMOPROCESSOR_THREAD_POLICY
+#define APF_MIMOPROCESSOR_THREAD_POLICY apf::dummy_thread_policy
+#endif
+
+#define APF_DUMMY_THREAD_POLICY_ERROR throw std::logic_error( \
+ "'dummy_thread_policy' can only be used with a single thread!")
+
+namespace apf
+{
+
+/// @c thread_policy without functionality. Can only be used for single-threaded
+/// processing.
+/// @see MimoProcessor
+/// @ingroup apf_policies
+class dummy_thread_policy
+{
+ public:
+ using useconds_type = int;
+
+ class Thread;
+ template struct ScopedThread;
+ template struct DetachedThread;
+ class Lock;
+ class Semaphore;
+
+ protected:
+ dummy_thread_policy() = default; ///< Protected ctor.
+ ~dummy_thread_policy() = default; ///< Protected dtor.
+};
+
+class dummy_thread_policy::Thread
+{
+ public:
+ using native_handle_type = int;
+
+ void create(void* (*f)(void*), void* data)
+ {
+ (void)f; // avoid "unused parameter" warning
+ (void)data;
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ }
+
+ bool join()
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ return false;
+ }
+
+ native_handle_type native_handle() const
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ return -1;
+ }
+};
+
+template
+struct dummy_thread_policy::ScopedThread : Thread
+{
+ ScopedThread(F, useconds_type)
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ }
+};
+
+template
+struct dummy_thread_policy::DetachedThread : Thread
+{
+ explicit DetachedThread(F)
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ }
+};
+
+class dummy_thread_policy::Lock
+{
+ public:
+ Lock() {}
+
+ int lock()
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ return -1;
+ }
+
+ int unlock()
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ return -1;
+ }
+};
+
+class dummy_thread_policy::Semaphore
+{
+ public:
+ using value_type = unsigned int;
+
+ explicit Semaphore(value_type = 0)
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ }
+
+ bool post()
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ return false;
+ }
+
+ bool wait()
+ {
+ APF_DUMMY_THREAD_POLICY_ERROR;
+ return false;
+ }
+};
+
+} // namespace apf
+
+#undef APF_DUMMY_THREAD_POLICY_ERROR
+
+#endif
+
+// Settings for Vim (http://www.vim.org/), please do not remove:
+// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
+// vim:fdm=expr:foldexpr=getline(v\:lnum)=~'/\\*\\*'&&getline(v\:lnum)!~'\\*\\*/'?'a1'\:getline(v\:lnum)=~'\\*\\*/'&&getline(v\:lnum)!~'/\\*\\*'?'s1'\:'='
diff --git a/apf/apf/fftwtools.h b/apf/apf/fftwtools.h
new file mode 100644
index 00000000..cd7ef161
--- /dev/null
+++ b/apf/apf/fftwtools.h
@@ -0,0 +1,137 @@
+/******************************************************************************
+ * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
+ * Copyright © 2006-2012 Quality & Usability Lab, *
+ * Telekom Innovation Laboratories, TU Berlin *
+ * *
+ * This file is part of the Audio Processing Framework (APF). *
+ * *
+ * The APF 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. *
+ * *
+ * The APF 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 this program. If not, see . *
+ * *
+ * http://AudioProcessingFramework.github.com *
+ ******************************************************************************/
+
+/// @file
+/// Some tools for the use with the FFTW library.
+
+#ifndef APF_FFTWTOOLS_H
+#define APF_FFTWTOOLS_H
+
+#include
+
+#include // for std::forward
+#include