diff --git a/aubio/COPYING b/aubio/COPYING new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/aubio/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/aubio/src/aubio.h b/aubio/src/aubio.h new file mode 100644 index 00000000..3c9216d0 --- /dev/null +++ b/aubio/src/aubio.h @@ -0,0 +1,233 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \mainpage + + \section introduction Introduction + + aubio is a library to extract annotations from audio signals: it provides a + set of functions that take an input audio signal, and output pitch estimates, + attack times (onset), beat location estimates, and other annotation tasks. + + \section basics Basics + + All object structures in aubio share the same function prefixes and suffixes: + + - \p new_aubio_foo creates the object \p foo + - \p aubio_foo_do executes the object \p foo + - \p del_aubio_foo destroys the object \p foo + + All memory allocation and deallocation take place in the \p new_ and \p del_ + functions. Optionally, more than one \p _do methods are available. + Additional parameters can be adjusted and observed using: + + - \p aubio_foo_get_param, getter function, gets the value of a parameter + - \p aubio_foo_set_param, setter function, changes the value of a parameter + + Unless specified in its documentation, no memory operations take place in the + getter functions. However, memory resizing can take place in setter + functions. + + \subsection vectors Vectors + + Two basic structures are being used in aubio: ::fvec_t and ::cvec_t. The + ::fvec_t structures are used to store vectors of floating pointer number. + ::cvec_t are used to store complex number, as two vectors of norm and phase + elements. + + Additionally, the ::lvec_t structure can be used to store floating point + numbers in double precision. They are mostly used to store filter + coefficients, to avoid instability. + + \subsection objects Available objects + + Here is a list of some of the most common objects for aubio: + + \code + + // fast Fourier transform (FFT) + aubio_fft_t *fft = new_aubio_fft (winsize); + // phase vocoder + aubio_pvoc_t *pv = new_aubio_pvoc (winsize, stepsize); + // onset detection + aubio_onset_t *onset = new_aubio_onset (method, winsize, stepsize, samplerate); + // pitch detection + aubio_pitch_t *pitch = new_aubio_pitch (method, winsize, stepsize, samplerate); + // beat tracking + aubio_tempo_t *tempo = new_aubio_tempo (method, winsize, stepsize, samplerate); + + \endcode + + See the list of typedefs for a complete list. + + \subsection example Example + + Here is a simple example that creates an A-Weighting filter and applies it to a + vector. + + \code + + // set window size, and sampling rate + uint_t winsize = 1024, sr = 44100; + // create a vector + fvec_t *this_buffer = new_fvec (winsize); + // create the a-weighting filter + aubio_filter_t *this_filter = new_aubio_filter_a_weighting (sr); + + while (running) { + // here some code to put some data in this_buffer + // ... + + // apply the filter, in place + aubio_filter_do (this_filter, this_buffer); + + // here some code to get some data from this_buffer + // ... + } + + // and free the structures + del_aubio_filter (this_filter); + del_fvec (this_buffer); + + \endcode + + Several examples of C programs are available in the \p examples/ and \p tests/src + directories of the source tree. + + Some examples: + - @ref spectral/test-fft.c + - @ref spectral/test-phasevoc.c + - @ref onset/test-onset.c + - @ref pitch/test-pitch.c + - @ref tempo/test-tempo.c + - @ref test-fvec.c + - @ref test-cvec.c + + \subsection unstable_api Unstable API + + Several more functions are available and used within aubio, but not + documented here, either because they are not considered useful to the user, + or because they may need to be changed in the future. However, they can still + be used by defining AUBIO_UNSTABLE to 1 before including the aubio header: + + \code + #define AUBIO_UNSTABLE 1 + #include + \endcode + + Future versions of aubio could break API compatibility with these functions + without warning. If you choose to use functions in AUBIO_UNSTABLE, you are on + your own. + + \section download Download + + Latest versions, further documentation, examples, wiki, and mailing lists can + be found at https://aubio.org . + + */ + +#ifndef AUBIO_H +#define AUBIO_H + +/** @file aubio.h Global aubio include file. + + You will want to include this file as: + + @code + #include + @endcode + + To access headers with unstable prototypes, use: + + @code + #define AUBIO_UNSTABLE 1 + #include + @endcode + + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* in this order */ +#include "types.h" +#include "fvec.h" +#include "cvec.h" +#include "lvec.h" +#include "fmat.h" +#include "musicutils.h" +#include "vecutils.h" +#include "temporal/resampler.h" +#include "temporal/filter.h" +#include "temporal/biquad.h" +#include "temporal/a_weighting.h" +#include "temporal/c_weighting.h" +#include "spectral/fft.h" +#include "spectral/dct.h" +#include "spectral/phasevoc.h" +#include "spectral/filterbank.h" +#include "spectral/filterbank_mel.h" +#include "spectral/mfcc.h" +#include "spectral/specdesc.h" +#include "spectral/awhitening.h" +#include "spectral/tss.h" +#include "pitch/pitch.h" +#include "onset/onset.h" +#include "tempo/tempo.h" +#include "notes/notes.h" +#include "io/source.h" +#include "io/sink.h" +#include "synth/sampler.h" +#include "synth/wavetable.h" +#include "utils/parameter.h" +#include "utils/log.h" + +#if AUBIO_UNSTABLE +#include "mathutils.h" +#include "io/source_sndfile.h" +#include "io/source_apple_audio.h" +#include "io/source_avcodec.h" +#include "io/source_wavread.h" +#include "io/sink_sndfile.h" +#include "io/sink_apple_audio.h" +#include "io/sink_wavwrite.h" +#include "io/audio_unit.h" +#include "onset/peakpicker.h" +#include "pitch/pitchmcomb.h" +#include "pitch/pitchyin.h" +#include "pitch/pitchyinfft.h" +#include "pitch/pitchyinfast.h" +#include "pitch/pitchschmitt.h" +#include "pitch/pitchfcomb.h" +#include "pitch/pitchspecacf.h" +#include "tempo/beattracking.h" +#include "effects/pitchshift.h" +#include "effects/timestretch.h" +#include "utils/scale.h" +#include "utils/hist.h" +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/aubio/src/aubio_priv.h b/aubio/src/aubio_priv.h new file mode 100644 index 00000000..955f63c6 --- /dev/null +++ b/aubio/src/aubio_priv.h @@ -0,0 +1,414 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** @file + * Private include file + * + * This file is for inclusion from _within_ the library only. + */ + +#ifndef AUBIO_PRIV_H +#define AUBIO_PRIV_H + +/********************* + * + * External includes + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STDIO_H +#include +#endif + +/* must be included before fftw3.h */ +#ifdef HAVE_COMPLEX_H +#include +#endif + +#if defined(HAVE_FFTW3) || defined(HAVE_FFTW3F) +#include +#endif + +#ifdef HAVE_MATH_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_LIMITS_H +#include // for CHAR_BIT, in C99 standard +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#if defined(HAVE_BLAS) // --enable-blas=true +// check which cblas header we found +#if defined(HAVE_ATLAS_CBLAS_H) +#define HAVE_ATLAS 1 +#include +#elif defined(HAVE_OPENBLAS_CBLAS_H) +#include +#elif defined(HAVE_CBLAS_H) +#include +#elif !defined(HAVE_ACCELERATE) +#error "HAVE_BLAS was defined, but no blas header was found" +#endif /* end of cblas includes */ +#endif + +#if defined(HAVE_ACCELERATE) +// include accelerate framework after blas +#define HAVE_ATLAS 1 +#define HAVE_BLAS 1 +#define ACCELERATE_NEW_LAPACK 1 +#define ACCELERATE_LAPACK_ILP6 1 +#include + +#ifndef HAVE_AUBIO_DOUBLE +#define aubio_vDSP_mmov vDSP_mmov +#define aubio_vDSP_vmul vDSP_vmul +#define aubio_vDSP_vsmul vDSP_vsmul +#define aubio_vDSP_vsadd vDSP_vsadd +#define aubio_vDSP_vfill vDSP_vfill +#define aubio_vDSP_meanv vDSP_meanv +#define aubio_vDSP_sve vDSP_sve +#define aubio_vDSP_maxv vDSP_maxv +#define aubio_vDSP_maxvi vDSP_maxvi +#define aubio_vDSP_minv vDSP_minv +#define aubio_vDSP_minvi vDSP_minvi +#define aubio_vDSP_dotpr vDSP_dotpr +#define aubio_vDSP_vclr vDSP_vclr +#else /* HAVE_AUBIO_DOUBLE */ +#define aubio_vDSP_mmov vDSP_mmovD +#define aubio_vDSP_vmul vDSP_vmulD +#define aubio_vDSP_vsmul vDSP_vsmulD +#define aubio_vDSP_vsadd vDSP_vsaddD +#define aubio_vDSP_vfill vDSP_vfillD +#define aubio_vDSP_meanv vDSP_meanvD +#define aubio_vDSP_sve vDSP_sveD +#define aubio_vDSP_maxv vDSP_maxvD +#define aubio_vDSP_maxvi vDSP_maxviD +#define aubio_vDSP_minv vDSP_minvD +#define aubio_vDSP_minvi vDSP_minviD +#define aubio_vDSP_dotpr vDSP_dotprD +#define aubio_vDSP_vclr vDSP_vclrD +#endif /* HAVE_AUBIO_DOUBLE */ +#endif /* HAVE_ACCELERATE */ + +#if defined(HAVE_BLAS) +#ifndef HAVE_AUBIO_DOUBLE +#ifdef HAVE_ATLAS +#define aubio_catlas_set catlas_sset +#endif /* HAVE_ATLAS */ +#define aubio_cblas_copy cblas_scopy +#define aubio_cblas_swap cblas_sswap +#define aubio_cblas_dot cblas_sdot +#else /* HAVE_AUBIO_DOUBLE */ +#ifdef HAVE_ATLAS +#define aubio_catlas_set catlas_dset +#endif /* HAVE_ATLAS */ +#define aubio_cblas_copy cblas_dcopy +#define aubio_cblas_swap cblas_dswap +#define aubio_cblas_dot cblas_ddot +#endif /* HAVE_AUBIO_DOUBLE */ +#endif /* HAVE_BLAS */ + +#if defined HAVE_INTEL_IPP +#include +#include +#include +#ifndef HAVE_AUBIO_DOUBLE +#define aubio_ippsSet ippsSet_32f +#define aubio_ippsZero ippsZero_32f +#define aubio_ippsCopy ippsCopy_32f +#define aubio_ippsMul ippsMul_32f +#define aubio_ippsMulC ippsMulC_32f +#define aubio_ippsAddC ippsAddC_32f +#define aubio_ippsLn ippsLn_32f_A21 +#define aubio_ippsMean(a,b,c) ippsMean_32f(a, b, c, ippAlgHintFast) +#define aubio_ippsSum(a,b,c) ippsSum_32f(a, b, c, ippAlgHintFast) +#define aubio_ippsMax ippsMax_32f +#define aubio_ippsMin ippsMin_32f +#else /* HAVE_AUBIO_DOUBLE */ +#define aubio_ippsSet ippsSet_64f +#define aubio_ippsZero ippsZero_64f +#define aubio_ippsCopy ippsCopy_64f +#define aubio_ippsMul ippsMul_64f +#define aubio_ippsMulC ippsMulC_64f +#define aubio_ippsAddC ippsAddC_64f +#define aubio_ippsLn ippsLn_64f_A26 +#define aubio_ippsMean ippsMean_64f +#define aubio_ippsSum ippsSum_64f +#define aubio_ippsMax ippsMax_64f +#define aubio_ippsMin ippsMin_64f +#endif /* HAVE_AUBIO_DOUBLE */ +#endif + +#if !defined(HAVE_MEMCPY_HACKS) && !defined(HAVE_ACCELERATE) && !defined(HAVE_ATLAS) && !defined(HAVE_INTEL_IPP) +#define HAVE_NOOPT 1 +#endif + +#include "types.h" + +#define AUBIO_UNSTABLE 1 + +#include "mathutils.h" + +/**** + * + * SYSTEM INTERFACE + * + */ + +/* Memory management */ +#define AUBIO_MALLOC(_n) malloc(_n) +#define AUBIO_REALLOC(_p,_n) realloc(_p,_n) +#define AUBIO_NEW(_t) (_t*)calloc(sizeof(_t), 1) +#define AUBIO_ARRAY(_t,_n) (_t*)calloc((_n)*sizeof(_t), 1) +#define AUBIO_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n) +#define AUBIO_MEMSET(_dst,_src,_t) memset(_dst,_src,_t) +#define AUBIO_FREE(_p) free(_p) + + +/* file interface */ +#define AUBIO_FOPEN(_f,_m) fopen(_f,_m) +#define AUBIO_FCLOSE(_f) fclose(_f) +#define AUBIO_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f) +#define AUBIO_FSEEK(_f,_n,_set) fseek(_f,_n,_set) + +/* strings */ +#define AUBIO_STRLEN(_s) strlen(_s) +#define AUBIO_STRCMP(_s,_t) strcmp(_s,_t) +#define AUBIO_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n) +#define AUBIO_STRCPY(_dst,_src) strcpy(_dst,_src) +#define AUBIO_STRCHR(_s,_c) strchr(_s,_c) +#ifdef strdup +#define AUBIO_STRDUP(s) strdup(s) +#else +#define AUBIO_STRDUP(s) AUBIO_STRCPY(AUBIO_MALLOC(AUBIO_STRLEN(s) + 1), s) +#endif + + +/* Error reporting */ +typedef enum { + AUBIO_OK = 0, + AUBIO_FAIL = 1 +} aubio_status; + +/* Logging */ + +#include "utils/log.h" + +/** internal logging function, defined in utils/log.c */ +uint_t aubio_log(sint_t level, const char_t *fmt, ...); + +#ifdef HAVE_C99_VARARGS_MACROS +#define AUBIO_ERR(...) aubio_log(AUBIO_LOG_ERR, "AUBIO ERROR: " __VA_ARGS__) +#define AUBIO_INF(...) aubio_log(AUBIO_LOG_INF, "AUBIO INFO: " __VA_ARGS__) +#define AUBIO_MSG(...) aubio_log(AUBIO_LOG_MSG, __VA_ARGS__) +#define _AUBIO_DBG(...) aubio_log(AUBIO_LOG_DBG, __VA_ARGS__) +#define AUBIO_WRN(...) aubio_log(AUBIO_LOG_WRN, "AUBIO WARNING: " __VA_ARGS__) +#else +#define AUBIO_ERR(format, args...) aubio_log(AUBIO_LOG_ERR, "AUBIO ERROR: " format , ##args) +#define AUBIO_INF(format, args...) aubio_log(AUBIO_LOG_INF, "AUBIO INFO: " format , ##args) +#define AUBIO_MSG(format, args...) aubio_log(AUBIO_LOG_MSG, format , ##args) +#define _AUBIO_DBG(format, args...) aubio_log(AUBIO_LOG_DBG, format , ##args) +#define AUBIO_WRN(format, args...) aubio_log(AUBIO_LOG_WRN, "AUBIO WARNING: " format, ##args) +#endif + +#ifdef DEBUG +#define AUBIO_DBG _AUBIO_DBG +#else +// disable debug output +#ifdef HAVE_C99_VARARGS_MACROS +#define AUBIO_DBG(...) {} +#else +#define AUBIO_DBG(format, args...) {} +#endif +#endif + +#define AUBIO_ERROR AUBIO_ERR + +#define AUBIO_QUIT(_s) exit(_s) +#define AUBIO_SNPRINTF snprintf + +#define AUBIO_MAX_SAMPLERATE (192000*8) +#define AUBIO_MAX_CHANNELS 1024 + +/* pi and 2*pi */ +#ifndef M_PI +#define PI (3.14159265358979323846) +#else +#define PI (M_PI) +#endif +#define TWO_PI (PI*2.) + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +/* aliases to math.h functions */ +#if !HAVE_AUBIO_DOUBLE +#define EXP expf +#define COS cosf +#define SIN sinf +#define ABS fabsf +#define POW powf +#define SQRT sqrtf +#define LOG10 log10f +#define LOG logf +#define FLOOR floorf +#define CEIL ceilf +#define ATAN atanf +#define ATAN2 atan2f +#else +#define EXP exp +#define COS cos +#define SIN sin +#define ABS fabs +#define POW pow +#define SQRT sqrt +#define LOG10 log10 +#define LOG log +#define FLOOR floor +#define CEIL ceil +#define ATAN atan +#define ATAN2 atan2 +#endif +#define ROUND(x) FLOOR(x+.5) + +/* aliases to complex.h functions */ +#if HAVE_AUBIO_DOUBLE || !defined(HAVE_COMPLEX_H) || defined(WIN32) +/* mingw32 does not know about c*f functions */ +#define EXPC cexp +/** complex = CEXPC(complex) */ +#define CEXPC cexp +/** sample = ARGC(complex) */ +#define ARGC carg +/** sample = ABSC(complex) norm */ +#define ABSC cabs +/** sample = REAL(complex) */ +#define REAL creal +/** sample = IMAG(complex) */ +#define IMAG cimag +#else +/** sample = EXPC(complex) */ +#define EXPC cexpf +/** complex = CEXPC(complex) */ +#define CEXPC cexp +/** sample = ARGC(complex) */ +#define ARGC cargf +/** sample = ABSC(complex) norm */ +#define ABSC cabsf +/** sample = REAL(complex) */ +#define REAL crealf +/** sample = IMAG(complex) */ +#define IMAG cimagf +#endif + +/* avoid unresolved symbol with msvc 9 */ +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define isnan _isnan +#endif + +#if !defined(_WIN32) +#define AUBIO_STRERROR(errno,buf,len) strerror_r(errno, buf, len) +#else +#define AUBIO_STRERROR(errno,buf,len) strerror_s(buf, len, errno) +#endif + +#ifdef HAVE_C99_VARARGS_MACROS +#define AUBIO_STRERR(...) \ + char errorstr[256]; \ + AUBIO_STRERROR(errno, errorstr, sizeof(errorstr)); \ + AUBIO_ERR(__VA_ARGS__) +#else +#define AUBIO_STRERR(format, args...) \ + char errorstr[256]; \ + AUBIO_STRERROR(errno, errorstr, sizeof(errorstr)); \ + AUBIO_ERR(format, ##args) +#endif + +/* handy shortcuts */ +#define DB2LIN(g) (POW(10.0,(g)*0.05f)) +#define LIN2DB(v) (20.0*LOG10(v)) +#define SQR(_a) ((_a)*(_a)) + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif /* MAX */ +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif /* MIN */ + +#define ELEM_SWAP(a,b) { register smpl_t t=(a);(a)=(b);(b)=t; } + +#define VERY_SMALL_NUMBER 2.e-42 //1.e-37 + +/** if ABS(f) < VERY_SMALL_NUMBER, returns 1, else 0 */ +#define IS_DENORMAL(f) ABS(f) < VERY_SMALL_NUMBER + +/** if ABS(f) < VERY_SMALL_NUMBER, returns 0., else f */ +#define KILL_DENORMAL(f) IS_DENORMAL(f) ? 0. : f + +/** if f > VERY_SMALL_NUMBER, returns f, else returns VERY_SMALL_NUMBER */ +#define CEIL_DENORMAL(f) f < VERY_SMALL_NUMBER ? VERY_SMALL_NUMBER : f + +#define SAFE_LOG10(f) LOG10(CEIL_DENORMAL(f)) +#define SAFE_LOG(f) LOG(CEIL_DENORMAL(f)) + +/** silence unused parameter warning by adding an attribute */ +#if defined(__GNUC__) +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif + +/* are we using gcc -std=c99 ? */ +#if defined(__STRICT_ANSI__) +#define strnlen(a,b) MIN(strlen(a),b) +#if !HAVE_AUBIO_DOUBLE +#define floorf floor +#endif +#endif /* __STRICT_ANSI__ */ + +#if defined(DEBUG) +#include +#define AUBIO_ASSERT(x) assert(x) +#else +#define AUBIO_ASSERT(x) +#endif /* DEBUG */ + +#endif /* AUBIO_PRIV_H */ diff --git a/aubio/src/config.h b/aubio/src/config.h new file mode 100644 index 00000000..827971b9 --- /dev/null +++ b/aubio/src/config.h @@ -0,0 +1,22 @@ +#ifndef CMAKE_CONFIG_H +#define CMAKE_CONFIG_H + +#define HAVE_C99_VARARGS_MACROS 1 + +#define HAVE_STDLIB_H 1 +#define HAVE_STDIO_H 1 +#define HAVE_MATH_H 1 +#define HAVE_STRING_H 1 +#define HAVE_ERRNO_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_STDARG_H 1 + +/* no FFTW3, no Accelerate, no Intel IPP — uses Ooura FFT */ +/* no SNDFILE, no SAMPLERATE, no libav — we supply our own samples */ + +/* MSVC compatibility */ +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#endif + +#endif /* CMAKE_CONFIG_H */ diff --git a/aubio/src/cvec.c b/aubio/src/cvec.c new file mode 100644 index 00000000..00c43bee --- /dev/null +++ b/aubio/src/cvec.c @@ -0,0 +1,169 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "cvec.h" + +cvec_t * new_cvec(uint_t length) { + cvec_t * s; + if ((sint_t)length <= 0) { + return NULL; + } + s = AUBIO_NEW(cvec_t); + s->length = length/2 + 1; + s->norm = AUBIO_ARRAY(smpl_t,s->length); + s->phas = AUBIO_ARRAY(smpl_t,s->length); + return s; +} + +void del_cvec(cvec_t *s) { + AUBIO_FREE(s->norm); + AUBIO_FREE(s->phas); + AUBIO_FREE(s); +} + +void cvec_norm_set_sample (cvec_t *s, smpl_t data, uint_t position) { + s->norm[position] = data; +} + +void cvec_phas_set_sample (cvec_t *s, smpl_t data, uint_t position) { + s->phas[position] = data; +} + +smpl_t cvec_norm_get_sample (cvec_t *s, uint_t position) { + return s->norm[position]; +} + +smpl_t cvec_phas_get_sample (cvec_t *s, uint_t position) { + return s->phas[position]; +} + +smpl_t * cvec_norm_get_data (const cvec_t *s) { + return s->norm; +} + +smpl_t * cvec_phas_get_data (const cvec_t *s) { + return s->phas; +} + +/* helper functions */ + +void cvec_print(const cvec_t *s) { + uint_t j; + AUBIO_MSG("norm: "); + for (j=0; j< s->length; j++) { + AUBIO_MSG(AUBIO_SMPL_FMT " ", s->norm[j]); + } + AUBIO_MSG("\n"); + AUBIO_MSG("phas: "); + for (j=0; j< s->length; j++) { + AUBIO_MSG(AUBIO_SMPL_FMT " ", s->phas[j]); + } + AUBIO_MSG("\n"); +} + +void cvec_copy(const cvec_t *s, cvec_t *t) { + if (s->length != t->length) { + AUBIO_ERR("trying to copy %d elements to %d elements \n", + s->length, t->length); + return; + } +#if defined(HAVE_INTEL_IPP) + aubio_ippsCopy(s->phas, t->phas, (int)s->length); + aubio_ippsCopy(s->norm, t->norm, (int)s->length); +#elif defined(HAVE_MEMCPY_HACKS) + memcpy(t->norm, s->norm, t->length * sizeof(smpl_t)); + memcpy(t->phas, s->phas, t->length * sizeof(smpl_t)); +#else + uint_t j; + for (j=0; j< t->length; j++) { + t->norm[j] = s->norm[j]; + t->phas[j] = s->phas[j]; + } +#endif +} + +void cvec_norm_set_all(cvec_t *s, smpl_t val) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsSet(val, s->norm, (int)s->length); +#else + uint_t j; + for (j=0; j< s->length; j++) { + s->norm[j] = val; + } +#endif +} + +void cvec_norm_zeros(cvec_t *s) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsZero(s->norm, (int)s->length); +#elif defined(HAVE_MEMCPY_HACKS) + memset(s->norm, 0, s->length * sizeof(smpl_t)); +#else + cvec_norm_set_all (s, 0.); +#endif +} + +void cvec_norm_ones(cvec_t *s) { + cvec_norm_set_all (s, 1.); +} + +void cvec_phas_set_all (cvec_t *s, smpl_t val) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsSet(val, s->phas, (int)s->length); +#else + uint_t j; + for (j=0; j< s->length; j++) { + s->phas[j] = val; + } +#endif +} + +void cvec_phas_zeros(cvec_t *s) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsZero(s->phas, (int)s->length); +#elif defined(HAVE_MEMCPY_HACKS) + memset(s->phas, 0, s->length * sizeof(smpl_t)); +#else + cvec_phas_set_all (s, 0.); +#endif +} + +void cvec_phas_ones(cvec_t *s) { + cvec_phas_set_all (s, 1.); +} + +void cvec_zeros(cvec_t *s) { + cvec_norm_zeros(s); + cvec_phas_zeros(s); +} + +void cvec_logmag(cvec_t *s, smpl_t lambda) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsMulC(s->norm, lambda, s->norm, (int)s->length); + aubio_ippsAddC(s->norm, 1.0, s->norm, (int)s->length); + aubio_ippsLn(s->norm, s->norm, (int)s->length); +#else + uint_t j; + for (j=0; j< s->length; j++) { + s->norm[j] = LOG(lambda * s->norm[j] + 1); + } +#endif +} diff --git a/aubio/src/cvec.h b/aubio/src/cvec.h new file mode 100644 index 00000000..7c826b68 --- /dev/null +++ b/aubio/src/cvec.h @@ -0,0 +1,247 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_CVEC_H +#define AUBIO_CVEC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Vector of complex-valued data, stored in polar coordinates + + This file specifies the ::cvec_t buffer type, which is used throughout aubio + to store complex data. Complex values are stored in terms of ::cvec_t.phas + and norm, within 2 vectors of ::smpl_t of size (size/2+1) each. + + \example test-cvec.c + +*/ + +/** Vector of real-valued phase and spectrum data + + \code + + uint_t buffer_size = 1024; + + // create a complex vector of 512 values + cvec_t * input = new_cvec (buffer_size); + + // set some values of the vector + input->norm[23] = 2.; + input->phas[23] = M_PI; + // .. + + // compute the mean of the vector + mean = cvec_mean(input); + + // destroy the vector + del_cvec (input); + + \endcode + + */ +typedef struct { + uint_t length; /**< length of buffer = (requested length)/2 + 1 */ + smpl_t *norm; /**< norm array of size ::cvec_t.length */ + smpl_t *phas; /**< phase array of size ::cvec_t.length */ +} cvec_t; + +/** cvec_t buffer creation function + + This function creates a cvec_t structure holding two arrays of size + [length/2+1], corresponding to the norm and phase values of the + spectral frame. The length stored in the structure is the actual size of both + arrays, not the length of the complex and symmetrical vector, specified as + creation argument. + + \param length the length of the buffer to create + +*/ +cvec_t * new_cvec(uint_t length); + +/** cvec_t buffer deletion function + + \param s buffer to delete as returned by new_cvec() + +*/ +void del_cvec(cvec_t *s); + +/** write norm value in a complex buffer + + This is equivalent to: + \code + s->norm[position] = val; + \endcode + + \param s vector to write to + \param val norm value to write in s->norm[position] + \param position sample position to write to + +*/ +void cvec_norm_set_sample (cvec_t *s, smpl_t val, uint_t position); + +/** write phase value in a complex buffer + + This is equivalent to: + \code + s->phas[position] = val; + \endcode + + \param s vector to write to + \param val phase value to write in s->phas[position] + \param position sample position to write to + +*/ +void cvec_phas_set_sample (cvec_t *s, smpl_t val, uint_t position); + +/** read norm value from a complex buffer + + This is equivalent to: + \code + smpl_t foo = s->norm[position]; + \endcode + + \param s vector to read from + \param position sample position to read from + +*/ +smpl_t cvec_norm_get_sample (cvec_t *s, uint_t position); + +/** read phase value from a complex buffer + + This is equivalent to: + \code + smpl_t foo = s->phas[position]; + \endcode + + \param s vector to read from + \param position sample position to read from + \returns the value of the sample at position + +*/ +smpl_t cvec_phas_get_sample (cvec_t *s, uint_t position); + +/** read norm data from a complex buffer + + \code + smpl_t *data = s->norm; + \endcode + + \param s vector to read from + +*/ +smpl_t * cvec_norm_get_data (const cvec_t *s); + +/** read phase data from a complex buffer + + This is equivalent to: + \code + smpl_t *data = s->phas; + \endcode + + \param s vector to read from + +*/ +smpl_t * cvec_phas_get_data (const cvec_t *s); + +/** print out cvec data + + \param s vector to print out + +*/ +void cvec_print(const cvec_t *s); + +/** make a copy of a vector + + \param s source vector + \param t vector to copy to + +*/ +void cvec_copy(const cvec_t *s, cvec_t *t); + +/** set all norm elements to a given value + + \param s vector to modify + \param val value to set elements to + +*/ +void cvec_norm_set_all (cvec_t *s, smpl_t val); + +/** set all norm elements to zero + + \param s vector to modify + +*/ +void cvec_norm_zeros(cvec_t *s); + +/** set all norm elements to one + + \param s vector to modify + +*/ +void cvec_norm_ones(cvec_t *s); + +/** set all phase elements to a given value + + \param s vector to modify + \param val value to set elements to + +*/ +void cvec_phas_set_all (cvec_t *s, smpl_t val); + +/** set all phase elements to zero + + \param s vector to modify + +*/ +void cvec_phas_zeros(cvec_t *s); + +/** set all phase elements to one + + \param s vector to modify + +*/ +void cvec_phas_ones(cvec_t *s); + +/** set all norm and phas elements to zero + + \param s vector to modify + +*/ +void cvec_zeros(cvec_t *s); + +/** take logarithmic magnitude + + \param s input cvec to compress + \param lambda value to use for normalisation + + \f$ S_k = log( \lambda * S_k + 1 ) \f$ + +*/ +void cvec_logmag(cvec_t *s, smpl_t lambda); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_CVEC_H */ diff --git a/aubio/src/effects/pitchshift.h b/aubio/src/effects/pitchshift.h new file mode 100644 index 00000000..a67a6dcc --- /dev/null +++ b/aubio/src/effects/pitchshift.h @@ -0,0 +1,127 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_PITCHSHIFT_H +#define AUBIO_PITCHSHIFT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Pitch shifting object + + ::aubio_pitchshift_t can be used to transpose a stream of blocks of frames. + + \example effects/test-pitchshift.c + +*/ + +/** pitch shifting object */ +typedef struct _aubio_pitchshift_t aubio_pitchshift_t; + +/** execute pitch shifting on an input signal frame + + \param o pitch shifting object as returned by new_aubio_pitchshift() + \param in input signal of size [hop_size] + \param out output pitch candidates of size [1] + +*/ +void aubio_pitchshift_do (aubio_pitchshift_t * o, const fvec_t * in, + fvec_t * out); + +/** deletion of the pitch shifting object + + \param o pitch shifting object as returned by new_aubio_pitchshift() + +*/ +void del_aubio_pitchshift (aubio_pitchshift_t * o); + +/** creation of the pitch shifting object + + \param method set pitch shifting algorithm ("default") + \param transpose initial pitch transposition + \param hop_size step size between two consecutive analysis instant + \param samplerate sampling rate of the signal + + \return newly created ::aubio_pitchshift_t + +*/ +aubio_pitchshift_t *new_aubio_pitchshift (const char_t * method, + smpl_t transpose, uint_t hop_size, uint_t samplerate); + +/** get the latency of the pitch shifting object, in samples + + \param o pitch shifting object as returned by ::new_aubio_pitchshift() + + \return latency of the pitch shifting object in samples + +*/ +uint_t aubio_pitchshift_get_latency (aubio_pitchshift_t * o); + +/** set the pitch scale of the pitch shifting object + + \param o pitch shifting object as returned by new_aubio_pitchshift() + \param pitchscale new pitch scale of the pitch shifting object + + pitchscale is a frequency ratio. It should be in the range [0.25, 4]. + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_pitchshift_set_pitchscale (aubio_pitchshift_t * o, + smpl_t pitchscale); + +/** get the pitchscale of the pitch shifting object + + \param o pitch shifting object as returned by ::new_aubio_pitchshift() + + \return pitchscale of the pitch shifting object + +*/ +smpl_t aubio_pitchshift_get_pitchscale (aubio_pitchshift_t * o); + +/** set the transposition of the pitch shifting object, in semitones + + \param o pitch shifting object as returned by new_aubio_pitchshift() + \param transpose new pitch transposition of the pitch shifting object, + expressed in semitones (should be in the range [-24;+24]) + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_pitchshift_set_transpose (aubio_pitchshift_t * o, + smpl_t transpose); + +/** get the transposition of the pitch shifting object, in semitones + + \param o pitch shifting object as returned by ::new_aubio_pitchshift() + + \return transposition of the pitch shifting object, in semitones + +*/ +smpl_t aubio_pitchshift_get_transpose (aubio_pitchshift_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHSHIFT_H */ diff --git a/aubio/src/effects/timestretch.h b/aubio/src/effects/timestretch.h new file mode 100644 index 00000000..52861c1c --- /dev/null +++ b/aubio/src/effects/timestretch.h @@ -0,0 +1,198 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_TIMESTRETCH_H +#define AUBIO_TIMESTRETCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + time stretching object + + ::aubio_timestretch_t can be used to open a source file, read samples from + it, time-stretch them, and write out the modified samples. + + The time-stretching factor can be changed at any time using + aubio_timestretch_set_stretch(). + + A transposition can also be applied and changed at any time with + aubio_timestretch_set_transpose(). + + \example effects/test-timestretch.c + +*/ + +/** time stretching object */ +typedef struct _aubio_timestretch_t aubio_timestretch_t; + +/** execute time stretching on an input signal frame + + \param o time stretching object as returned by new_aubio_timestretch() + \param out timestretched output of size [hop_size] + \param read number of frames actually wrote out + +*/ +void aubio_timestretch_do (aubio_timestretch_t * o, fvec_t * out, + uint_t * read); + +/** deletion of the time stretching object + + \param o time stretching object as returned by new_aubio_timestretch() + +*/ +void del_aubio_timestretch (aubio_timestretch_t * o); + +/** creation of the time stretching object + + \param method time stretching algorithm ("default") + \param stretch initial time stretching factor + \param hop_size block size at which the frames should be produced + \param samplerate sampling rate of the signal + + \return newly created ::aubio_timestretch_t + +*/ +aubio_timestretch_t *new_aubio_timestretch (const char_t * method, + smpl_t stretch, uint_t hop_size, uint_t samplerate); + +/** push length samples from in to time stretching object + + \param o time stretching object as returned by ::new_aubio_timestretch() + \param in input vector of new samples to push to time stretching object + \param length number of new samples to push from input vector + + \return number of currently available samples + + */ +sint_t aubio_timestretch_push(aubio_timestretch_t * o, fvec_t *in, + uint_t length); + +/** get number of currently available samples from time stretching object + + \param o time stretching object as returned by ::new_aubio_timestretch() + + \return number of currently available samples + + */ +sint_t aubio_timestretch_get_available(aubio_timestretch_t * o); + +/** get the latency of the time stretching object, in samples + + \param o time stretching object as returned by ::new_aubio_timestretch() + + \return latency of the time stretching object in samples + +*/ +uint_t aubio_timestretch_get_latency (aubio_timestretch_t * o); + +/** get the samplerate of the time stretching object + + Call after new_aubio_timestretch() was called with 0 to match the original + samplerate of the input file. + + \param o time stretching object as returned by new_aubio_timestretch() + + \return samplerate of the time stretching object + + */ +uint_t aubio_timestretch_get_samplerate (aubio_timestretch_t * o); + +/** set the stretching ratio of the time stretching object + + \param o time stretching object as returned by new_aubio_timestretch() + \param stretch new time stretching ratio of the time stretching object + (should be in the range [0.025; 10.]) + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_timestretch_set_stretch (aubio_timestretch_t * o, smpl_t stretch); + +/** get the transposition of the time stretching object, in semitones + + \param o time stretching object as returned by ::new_aubio_timestretch() + + \return time stretching ratio of the time stretching object, in the range + [0.025; 10.] + +*/ +smpl_t aubio_timestretch_get_stretch (aubio_timestretch_t * o); + +/** set the pitch scale of the time stretching object + + \param o time stretching object as returned by new_aubio_timestretch() + \param pitchscale new pitch scale of the time stretching object + + pitchscale is a frequency ratio. It should be in the range [0.25, 4]. + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_timestretch_set_pitchscale (aubio_timestretch_t * o, + smpl_t pitchscale); + +/** get the pitchscale of the time stretching object + + \param o time stretching object as returned by ::new_aubio_timestretch() + + \return pitchscale of the time stretching object + +*/ +smpl_t aubio_timestretch_get_pitchscale (aubio_timestretch_t * o); + +/** set the transposition of the time stretching object, in semitones + + \param o time stretching object as returned by new_aubio_timestretch() + + \param transpose new pitch transposition of the time stretching object, + expressed in semitones (should be in the range [-24;+24]) + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_timestretch_set_transpose (aubio_timestretch_t * o, + smpl_t transpose); + +/** get the transposition of the time stretching object, in semitones + + \param o time stretching object as returned by ::new_aubio_timestretch() + + \return transposition of the time stretching object, in semitones + +*/ +smpl_t aubio_timestretch_get_transpose (aubio_timestretch_t * o); + +/** reset the time stretching object + + \param o time stretching object as returned by ::new_aubio_timestretch() + + \return 0 on success, non-zero otherwise + +*/ +uint_t aubio_timestretch_reset(aubio_timestretch_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_TIMESTRETCH_H */ diff --git a/aubio/src/fmat.c b/aubio/src/fmat.c new file mode 100644 index 00000000..8dde20ce --- /dev/null +++ b/aubio/src/fmat.c @@ -0,0 +1,186 @@ +/* + Copyright (C) 2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fmat.h" + +fmat_t * new_fmat (uint_t height, uint_t length) { + fmat_t * s; + uint_t i,j; + if ((sint_t)length <= 0 || (sint_t)height <= 0 ) { + return NULL; + } + s = AUBIO_NEW(fmat_t); + s->height = height; + s->length = length; + s->data = AUBIO_ARRAY(smpl_t*,s->height); + for (i=0; i< s->height; i++) { + s->data[i] = AUBIO_ARRAY(smpl_t, s->length); + for (j=0; j< s->length; j++) { + s->data[i][j]=0.; + } + } + return s; +} + +void del_fmat (fmat_t *s) { + uint_t i; + for (i=0; iheight; i++) { + AUBIO_FREE(s->data[i]); + } + AUBIO_FREE(s->data); + AUBIO_FREE(s); +} + +void fmat_set_sample(fmat_t *s, smpl_t data, uint_t channel, uint_t position) { + s->data[channel][position] = data; +} + +smpl_t fmat_get_sample(const fmat_t *s, uint_t channel, uint_t position) { + return s->data[channel][position]; +} + +void fmat_get_channel(const fmat_t *s, uint_t channel, fvec_t *output) { + output->data = s->data[channel]; + output->length = s->length; + return; +} + +smpl_t * fmat_get_channel_data(const fmat_t *s, uint_t channel) { + return s->data[channel]; +} + +smpl_t ** fmat_get_data(const fmat_t *s) { + return s->data; +} + +/* helper functions */ + +void fmat_print(const fmat_t *s) { + uint_t i,j; + for (i=0; i< s->height; i++) { + for (j=0; j< s->length; j++) { + AUBIO_MSG(AUBIO_SMPL_FMT " ", s->data[i][j]); + } + AUBIO_MSG("\n"); + } +} + +void fmat_set(fmat_t *s, smpl_t val) { + uint_t i,j; + for (i=0; i< s->height; i++) { + for (j=0; j< s->length; j++) { + s->data[i][j] = val; + } + } +} + +void fmat_zeros(fmat_t *s) { +#ifdef HAVE_MEMCPY_HACKS + uint_t i; + for (i=0; i< s->height; i++) { + memset(s->data[i], 0, s->length * sizeof(smpl_t)); + } +#else /* HAVE_MEMCPY_HACKS */ + fmat_set(s, 0.); +#endif /* HAVE_MEMCPY_HACKS */ +} + +void fmat_ones(fmat_t *s) { + fmat_set(s, 1.); +} + +void fmat_rev(fmat_t *s) { + uint_t i,j; + for (i=0; i< s->height; i++) { + for (j=0; j< FLOOR((smpl_t)s->length/2); j++) { + ELEM_SWAP(s->data[i][j], s->data[i][s->length-1-j]); + } + } +} + +void fmat_weight(fmat_t *s, const fmat_t *weight) { + uint_t i,j; + uint_t length = MIN(s->length, weight->length); + for (i=0; i< s->height; i++) { + for (j=0; j< length; j++) { + s->data[i][j] *= weight->data[0][j]; + } + } +} + +void fmat_copy(const fmat_t *s, fmat_t *t) { + uint_t i; +#ifndef HAVE_MEMCPY_HACKS + uint_t j; +#endif /* HAVE_MEMCPY_HACKS */ + if (s->height != t->height) { + AUBIO_ERR("trying to copy %d rows to %d rows \n", + s->height, t->height); + return; + } + if (s->length != t->length) { + AUBIO_ERR("trying to copy %d columns to %d columns\n", + s->length, t->length); + return; + } +#ifdef HAVE_MEMCPY_HACKS + for (i=0; i< s->height; i++) { + memcpy(t->data[i], s->data[i], t->length * sizeof(smpl_t)); + } +#else /* HAVE_MEMCPY_HACKS */ + for (i=0; i< t->height; i++) { + for (j=0; j< t->length; j++) { + t->data[i][j] = s->data[i][j]; + } + } +#endif /* HAVE_MEMCPY_HACKS */ +} + +void fmat_vecmul(const fmat_t *s, const fvec_t *scale, fvec_t *output) { + uint_t k; +#if 0 + assert(s->height == output->length); + assert(s->length == scale->length); +#endif +#if !defined(HAVE_ACCELERATE) && !defined(HAVE_BLAS) + uint_t j; + fvec_zeros(output); + for (j = 0; j < s->length; j++) { + for (k = 0; k < s->height; k++) { + output->data[k] += scale->data[j] + * s->data[k][j]; + } + } +#elif defined(HAVE_ACCELERATE) +#if 0 + // seems slower and less precise (and dangerous?) + vDSP_mmul (s->data[0], 1, scale->data, 1, output->data, 1, s->height, 1, s->length); +#else + for (k = 0; k < s->height; k++) { + aubio_vDSP_dotpr( scale->data, 1, s->data[k], 1, &(output->data[k]), s->length); + } +#endif +#elif defined(HAVE_BLAS) + for (k = 0; k < s->height; k++) { + output->data[k] = aubio_cblas_dot( s->length, scale->data, 1, s->data[k], 1); + } +#endif +} diff --git a/aubio/src/fmat.h b/aubio/src/fmat.h new file mode 100644 index 00000000..8e65d110 --- /dev/null +++ b/aubio/src/fmat.h @@ -0,0 +1,172 @@ +/* + Copyright (C) 2009-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_FMAT_H +#define AUBIO_FMAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Matrix of real valued data + + This file specifies the fmat_t type, which is used in aubio to store arrays + of floating point values. + + \example test-fmat.c + +*/ + +/** Buffer for real data */ +typedef struct { + uint_t length; /**< length of matrix */ + uint_t height; /**< height of matrix */ + smpl_t **data; /**< data array of size [length] * [height] */ +} fmat_t; + +/** fmat_t buffer creation function + + \param length the length of the matrix to create + \param height the height of the matrix to create + +*/ +fmat_t * new_fmat(uint_t height, uint_t length); + +/** fmat_t buffer deletion function + + \param s buffer to delete as returned by new_fmat() + +*/ +void del_fmat(fmat_t *s); + +/** read sample value in a buffer + + \param s vector to read from + \param channel channel to read from + \param position sample position to read from + +*/ +smpl_t fmat_get_sample(const fmat_t *s, uint_t channel, uint_t position); + +/** write sample value in a buffer + + \param s vector to write to + \param data value to write in s->data[channel][position] + \param channel channel to write to + \param position sample position to write to + +*/ +void fmat_set_sample(fmat_t *s, smpl_t data, uint_t channel, uint_t position); + +/** read channel vector from a buffer + + \param s vector to read from + \param channel channel to read from + \param output ::fvec_t to output to + +*/ +void fmat_get_channel (const fmat_t *s, uint_t channel, fvec_t *output); + +/** get vector buffer from an fmat data + + \param s vector to read from + \param channel channel to read from + +*/ +smpl_t * fmat_get_channel_data (const fmat_t *s, uint_t channel); + +/** read data from a buffer + + \param s vector to read from + +*/ +smpl_t ** fmat_get_data(const fmat_t *s); + +/** print out fmat data + + \param s vector to print out + +*/ +void fmat_print(const fmat_t *s); + +/** set all elements to a given value + + \param s vector to modify + \param val value to set elements to + +*/ +void fmat_set(fmat_t *s, smpl_t val); + +/** set all elements to zero + + \param s vector to modify + +*/ +void fmat_zeros(fmat_t *s); + +/** set all elements to ones + + \param s vector to modify + +*/ +void fmat_ones(fmat_t *s); + +/** revert order of vector elements + + \param s vector to revert + +*/ +void fmat_rev(fmat_t *s); + +/** apply weight to vector + + If the weight vector is longer than s, only the first elements are used. If + the weight vector is shorter than s, the last elements of s are not weighted. + + \param s vector to weight + \param weight weighting coefficients + +*/ +void fmat_weight(fmat_t *s, const fmat_t *weight); + +/** make a copy of a matrix + + \param s source vector + \param t vector to copy to + +*/ +void fmat_copy(const fmat_t *s, fmat_t *t); + +/** compute the product of a matrix by a vector + + \param s matrix to compute product with + \param scale vector to compute product with + \param output vector to store restults in + +*/ +void fmat_vecmul(const fmat_t *s, const fvec_t *scale, fvec_t *output); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FMAT_H */ diff --git a/aubio/src/fvec.c b/aubio/src/fvec.c new file mode 100644 index 00000000..6b5681f5 --- /dev/null +++ b/aubio/src/fvec.c @@ -0,0 +1,149 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" + +fvec_t * new_fvec(uint_t length) { + fvec_t * s; + if ((sint_t)length <= 0) { + return NULL; + } + s = AUBIO_NEW(fvec_t); + s->length = length; + s->data = AUBIO_ARRAY(smpl_t, s->length); + return s; +} + +void del_fvec(fvec_t *s) { + AUBIO_FREE(s->data); + AUBIO_FREE(s); +} + +void fvec_set_sample(fvec_t *s, smpl_t data, uint_t position) { + s->data[position] = data; +} + +smpl_t fvec_get_sample(const fvec_t *s, uint_t position) { + return s->data[position]; +} + +smpl_t * fvec_get_data(const fvec_t *s) { + return s->data; +} + +/* helper functions */ + +void fvec_print(const fvec_t *s) { + uint_t j; + for (j=0; j< s->length; j++) { + AUBIO_MSG(AUBIO_SMPL_FMT " ", s->data[j]); + } + AUBIO_MSG("\n"); +} + +void fvec_set_all (fvec_t *s, smpl_t val) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsSet(val, s->data, (int)s->length); +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_vfill(&val, s->data, 1, s->length); +#elif defined(HAVE_ATLAS) + aubio_catlas_set(s->length, val, s->data, 1); +#else + uint_t j; + for ( j = 0; j< s->length; j++ ) + { + s->data[j] = val; + } +#endif +} + +void fvec_zeros(fvec_t *s) { +#if defined(HAVE_INTEL_IPP) + aubio_ippsZero(s->data, (int)s->length); +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_vclr(s->data, 1, s->length); +#elif defined(HAVE_MEMCPY_HACKS) + memset(s->data, 0, s->length * sizeof(smpl_t)); +#else + fvec_set_all(s, 0.); +#endif +} + +void fvec_ones(fvec_t *s) { + fvec_set_all (s, 1.); +} + +void fvec_rev(fvec_t *s) { + uint_t j; + for (j=0; j< FLOOR((smpl_t)s->length/2); j++) { + ELEM_SWAP(s->data[j], s->data[s->length-1-j]); + } +} + +void fvec_weight(fvec_t *s, const fvec_t *weight) { + uint_t length = MIN(s->length, weight->length); +#if defined(HAVE_INTEL_IPP) + aubio_ippsMul(s->data, weight->data, s->data, (int)length); +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_vmul( s->data, 1, weight->data, 1, s->data, 1, length ); +#else + uint_t j; + for (j = 0; j < length; j++) { + s->data[j] *= weight->data[j]; + } +#endif /* HAVE_ACCELERATE */ +} + +void fvec_weighted_copy(const fvec_t *in, const fvec_t *weight, fvec_t *out) { + uint_t length = MIN(in->length, MIN(out->length, weight->length)); +#if defined(HAVE_INTEL_IPP) + aubio_ippsMul(in->data, weight->data, out->data, (int)length); +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_vmul(in->data, 1, weight->data, 1, out->data, 1, length); +#else + uint_t j; + for (j = 0; j < length; j++) { + out->data[j] = in->data[j] * weight->data[j]; + } +#endif +} + +void fvec_copy(const fvec_t *s, fvec_t *t) { + if (s->length != t->length) { + AUBIO_ERR("trying to copy %d elements to %d elements \n", + s->length, t->length); + return; + } +#if defined(HAVE_INTEL_IPP) + aubio_ippsCopy(s->data, t->data, (int)s->length); +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_mmov(s->data, t->data, 1, s->length, 1, 1); +#elif defined(HAVE_BLAS) + aubio_cblas_copy(s->length, s->data, 1, t->data, 1); +#elif defined(HAVE_MEMCPY_HACKS) + memcpy(t->data, s->data, t->length * sizeof(smpl_t)); +#else + uint_t j; + for (j = 0; j < t->length; j++) { + t->data[j] = s->data[j]; + } +#endif +} diff --git a/aubio/src/fvec.h b/aubio/src/fvec.h new file mode 100644 index 00000000..bd8c5a60 --- /dev/null +++ b/aubio/src/fvec.h @@ -0,0 +1,178 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_FVEC_H +#define AUBIO_FVEC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Vector of real-valued data + + This file specifies the ::fvec_t buffer type, which is used throughout aubio + to store vector of real-valued ::smpl_t. + + \example test-fvec.c + +*/ + +/** Buffer for real data + + Vector of real-valued data + + ::fvec_t is is the structure used to store vector of real-valued data, ::smpl_t . + + \code + + uint_t buffer_size = 1024; + + // create a vector of 512 values + fvec_t * input = new_fvec (buffer_size); + + // set some values of the vector + input->data[23] = 2.; + // .. + + // compute the mean of the vector + mean = fvec_mean(a_vector); + + // destroy the vector + del_fvec(a_vector); + + \endcode + + See `examples/` and `tests/src` directories for more examples. + + */ +typedef struct { + uint_t length; /**< length of buffer */ + smpl_t *data; /**< data vector of length ::fvec_t.length */ +} fvec_t; + +/** fvec_t buffer creation function + + \param length the length of the buffer to create + +*/ +fvec_t * new_fvec(uint_t length); + +/** fvec_t buffer deletion function + + \param s buffer to delete as returned by new_fvec() + +*/ +void del_fvec(fvec_t *s); + +/** read sample value in a buffer + + \param s vector to read from + \param position sample position to read from + +*/ +smpl_t fvec_get_sample(const fvec_t *s, uint_t position); + +/** write sample value in a buffer + + \param s vector to write to + \param data value to write in s->data[position] + \param position sample position to write to + +*/ +void fvec_set_sample(fvec_t *s, smpl_t data, uint_t position); + +/** read data from a buffer + + \param s vector to read from + +*/ +smpl_t * fvec_get_data(const fvec_t *s); + +/** print out fvec data + + \param s vector to print out + +*/ +void fvec_print(const fvec_t *s); + +/** set all elements to a given value + + \param s vector to modify + \param val value to set elements to + +*/ +void fvec_set_all (fvec_t *s, smpl_t val); + +/** set all elements to zero + + \param s vector to modify + +*/ +void fvec_zeros(fvec_t *s); + +/** set all elements to ones + + \param s vector to modify + +*/ +void fvec_ones(fvec_t *s); + +/** revert order of vector elements + + \param s vector to revert + +*/ +void fvec_rev(fvec_t *s); + +/** apply weight to vector + + If the weight vector is longer than s, only the first elements are used. If + the weight vector is shorter than s, the last elements of s are not weighted. + + \param s vector to weight + \param weight weighting coefficients + +*/ +void fvec_weight(fvec_t *s, const fvec_t *weight); + +/** make a copy of a vector + + \param s source vector + \param t vector to copy to + +*/ +void fvec_copy(const fvec_t *s, fvec_t *t); + +/** make a copy of a vector, applying weights to each element + + \param in input vector + \param weight weights vector + \param out output vector + +*/ +void fvec_weighted_copy(const fvec_t *in, const fvec_t *weight, fvec_t *out); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FVEC_H */ diff --git a/aubio/src/io/audio_unit.h b/aubio/src/io/audio_unit.h new file mode 100644 index 00000000..46f86b1e --- /dev/null +++ b/aubio/src/io/audio_unit.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_AUDIO_UNIT_H +#define AUBIO_AUDIO_UNIT_H + +/** \file + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _aubio_audio_unit_t aubio_audio_unit_t; + +aubio_audio_unit_t * new_aubio_audio_unit(uint_t samplerate, uint_t inchannels, + uint_t outchannels, uint_t blocksize); + +typedef uint_t (*aubio_device_callback_t) (void * closure, fmat_t *ibuf, fmat_t *obuf); + +uint_t aubio_audio_unit_set_callback(aubio_audio_unit_t *o, + aubio_device_callback_t callback, void *closure); + +sint_t aubio_audio_unit_set_verbose (aubio_audio_unit_t *o, uint_t verbose); +sint_t aubio_audio_unit_set_preferred_latency (aubio_audio_unit_t *o, smpl_t + latency); +sint_t aubio_audio_unit_set_prevent_feedback (aubio_audio_unit_t *o, uint_t + prevent_feedback); + +sint_t aubio_audio_unit_get_info (aubio_audio_unit_t *o); + +sint_t aubio_audio_unit_init (aubio_audio_unit_t *o); + +sint_t aubio_audio_unit_start (aubio_audio_unit_t *o); +sint_t aubio_audio_unit_stop (aubio_audio_unit_t *o); + +uint_t del_aubio_audio_unit(aubio_audio_unit_t *o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_AUDIO_UNIT_H */ diff --git a/aubio/src/io/ioutils.h b/aubio/src/io/ioutils.h new file mode 100644 index 00000000..f3cdf927 --- /dev/null +++ b/aubio/src/io/ioutils.h @@ -0,0 +1,133 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_IOUTILS_H +#define AUBIO_IOUTILS_H + +/** \file + + Simple utility functions to validate input parameters. + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** validate samplerate + + \param kind the object kind to report on + \param path the object properties to report on + \param samplerate the object properties to report on + \return 0 if ok, non-zero if validation failed + + */ +uint_t aubio_io_validate_samplerate(const char_t *kind, const char_t *path, + uint_t samplerate); + +/** validate number of channels + + \param kind the object kind to report on + \param path the object properties to report on + \param channels the object properties to report on + \return 0 if ok, non-zero if validation failed + + */ +uint_t aubio_io_validate_channels(const char_t *kind, const char_t *path, + uint_t channels); + +/** validate length of source output + + \param kind the object kind to report on + \param path the path to report on + \param hop_size number of frames to be read + \param read_data_length actual length of input + + \return hop_size or the maximum number of frames that can be written +*/ +uint_t +aubio_source_validate_input_length(const char_t *kind, const char_t *path, + uint_t hop_size, uint_t read_data_length); + +/** validate height of source output + + \param kind the object kind to report on + \param path the path to report on + \param source_channels maximum number of channels that can be written + \param read_data_height actual height of input + + \return write_data_height or the maximum number of channels +*/ +uint_t +aubio_source_validate_input_channels(const char_t *kind, const char_t *path, + uint_t source_channels, uint_t read_data_height); + +/** pad end of source output vector with zeroes + + \param read_data output vector to pad + \param source_read number of frames read + +*/ +void +aubio_source_pad_output (fvec_t *read_data, uint_t source_read); + +/** pad end of source output matrix with zeroes + + \param read_data output matrix to pad + \param source_channels number of channels in the source + \param source_read number of frames read + +*/ +void +aubio_source_pad_multi_output (fmat_t *read_data, uint_t source_channels, + uint_t source_read); + +/** validate length of sink input + + \param kind the object kind to report on + \param path the path to report on + \param max_size maximum number of frames that can be written + \param write_data_length actual length of input + \param write number of samples asked + + \return write or the maximum number of frames that can be written +*/ +uint_t +aubio_sink_validate_input_length(const char_t *kind, const char_t *path, + uint_t max_size, uint_t write_data_length, uint_t write); + +/** validate height of sink input + + \param kind the object kind to report on + \param path the path to report on + \param sink_channels maximum number of channels that can be written + \param write_data_height actual height of input matrix + + \return write_data_height or the maximum number of channels +*/ +uint_t +aubio_sink_validate_input_channels(const char_t *kind, const char_t *path, + uint_t sink_channels, uint_t write_data_height); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_IOUTILS_H */ diff --git a/aubio/src/io/sink.h b/aubio/src/io/sink.h new file mode 100644 index 00000000..ee0f5cf8 --- /dev/null +++ b/aubio/src/io/sink.h @@ -0,0 +1,181 @@ +/* + Copyright (C) 2012-2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SINK_H +#define AUBIO_SINK_H + +/** \file + + Media sink to write blocks of consecutive audio samples to file. + + To read from file, use ::aubio_source_t. + + Depending on how aubio was compiled, the following sinks will be available. + + When creating a new sink using ::new_aubio_sink, the new function of each of + the compiled-in sinks will be attempted, in the following order, until one of + them gets successfully created. If all sinks returned NULL, ::new_aubio_sink + will return NULL. + + \b \p sink_apple_audio : ExtAudioFileRef + + This sink uses CoreAudio [Extended Audio File Services] + (https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/ExtendedAudioFileServicesReference/Reference/reference.html) + to write 16-bits encoded WAV files. + + \b \p sink_sndfile : libsndfile + + This sink uses [libsndfile](http://www.mega-nerd.com/libsndfile/) to write + 16-bits encoded WAV files. + + \b \p sink_wavwrite : native WAV write + + A simple sink to write 16-bits PCM RIFF encoded WAV files. + + \example io/test-sink.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** media sink object */ +typedef struct _aubio_sink_t aubio_sink_t; + +/** + + create new ::aubio_sink_t + + \param uri the file path or uri to write to + \param samplerate sample rate to write the file at + + \return newly created ::aubio_sink_t + + Creates a new sink object. + + If samplerate is set to 0, the creation of the file will be delayed until + both ::aubio_sink_preset_samplerate and ::aubio_sink_preset_channels have + been called. + +*/ +aubio_sink_t * new_aubio_sink(const char_t * uri, uint_t samplerate); + +/** + + preset sink samplerate + + \param s sink, created with ::new_aubio_sink + \param samplerate samplerate to preset the sink to, in Hz + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_preset_samplerate(aubio_sink_t *s, uint_t samplerate); + +/** + + preset sink channels + + \param s sink, created with ::new_aubio_sink + \param channels number of channels to preset the sink to + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_preset_channels(aubio_sink_t *s, uint_t channels); + +/** + + get samplerate of sink object + + \param s sink object, created with ::new_aubio_sink + \return samplerate, in Hz + +*/ +uint_t aubio_sink_get_samplerate(const aubio_sink_t *s); + +/** + + get channels of sink object + + \param s sink object, created with ::new_aubio_sink + \return number of channels + +*/ +uint_t aubio_sink_get_channels(const aubio_sink_t *s); + +/** + + write monophonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink + \param write_data ::fvec_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_do(aubio_sink_t * s, fvec_t * write_data, uint_t write); + +/** + + write polyphonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink + \param write_data ::fmat_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_do_multi(aubio_sink_t * s, fmat_t * write_data, uint_t write); + +/** + + close sink + + \param s sink object, created with ::new_aubio_sink + + \return 0 on success, non-zero on failure + +*/ +uint_t aubio_sink_close(aubio_sink_t * s); + +/** + + close sink and cleanup memory + + \param s sink object, created with ::new_aubio_sink + +*/ +void del_aubio_sink(aubio_sink_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SINK_H */ diff --git a/aubio/src/io/sink_apple_audio.h b/aubio/src/io/sink_apple_audio.h new file mode 100644 index 00000000..1e228bcf --- /dev/null +++ b/aubio/src/io/sink_apple_audio.h @@ -0,0 +1,186 @@ +/* + Copyright (C) 2012-2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SINK_APPLE_AUDIO_H +#define AUBIO_SINK_APPLE_AUDIO_H + +/** \file + + Write to file using Apple AudioToolbox's + [ExtAudioFileRef](https://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/ExtendedAudioFileServicesReference/Reference/reference.html) + + Avoid including this file directly! Prefer using ::aubio_sink_t instead to + make your code portable. + + To read from file, use ::aubio_source_t. + + \example io/test-sink_apple_audio.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** sink_apple_audio object */ +typedef struct _aubio_sink_apple_audio_t aubio_sink_apple_audio_t; + +/** + + create new ::aubio_sink_apple_audio_t + + \param uri the file path or uri to write to + \param samplerate sample rate to write the file at + + \return newly created ::aubio_sink_apple_audio_t + + Creates a new sink object. + + If samplerate is set to 0, the creation of the file will be delayed until + both ::aubio_sink_preset_samplerate and ::aubio_sink_preset_channels have + been called. + +*/ +aubio_sink_apple_audio_t * new_aubio_sink_apple_audio(const char_t * uri, uint_t samplerate); + +/** + + preset sink samplerate + + \param s sink, created with ::new_aubio_sink_apple_audio + \param samplerate samplerate to preset the sink to, in Hz + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_apple_audio_preset_samplerate(aubio_sink_apple_audio_t *s, uint_t samplerate); + +/** + + preset sink channels + + \param s sink, created with ::new_aubio_sink_apple_audio + \param channels number of channels to preset the sink to + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_apple_audio_preset_channels(aubio_sink_apple_audio_t *s, uint_t channels); + +/** + + preset sink format + + \param s sink, created with ::new_aubio_sink_apple_audio + \param fmt format of the file to create + + \return 0 on success, 1 on error + + Preset the format of the sink. Supported format strings: + - "wav": WAVE, 16 bit (default) + - "aiff": AIFF, 16 bit + - "m4a" or "mp4": Apple Audio Lossless Codec (ALAC) + - "aac": Audio Advanced Codec, lossy + + Full list of supported encoding format is available in Table 1-2 of + `Multimedia Programming Guide + `_. + + */ +uint_t aubio_sink_apple_audio_preset_format(aubio_sink_apple_audio_t *s, + const char_t *fmt); + +/** + + get samplerate of sink object + + \param s sink object, created with ::new_aubio_sink_apple_audio + \return samplerate, in Hz + +*/ +uint_t aubio_sink_apple_audio_get_samplerate(const aubio_sink_apple_audio_t *s); + +/** + + get channels of sink object + + \param s sink object, created with ::new_aubio_sink_apple_audio + \return number of channels + +*/ +uint_t aubio_sink_apple_audio_get_channels(const aubio_sink_apple_audio_t *s); + +/** + + write monophonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink_apple_audio + \param write_data ::fvec_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_apple_audio_do(aubio_sink_apple_audio_t * s, fvec_t * write_data, uint_t write); + +/** + + write polyphonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink_apple_audio + \param write_data ::fmat_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_apple_audio_do_multi(aubio_sink_apple_audio_t * s, fmat_t * write_data, uint_t write); + +/** + + close sink + + \param s sink_apple_audio object, created with ::new_aubio_sink_apple_audio + + \return 0 on success, non-zero on failure + +*/ +uint_t aubio_sink_apple_audio_close(aubio_sink_apple_audio_t * s); + +/** + + close sink and cleanup memory + + \param s sink, created with ::new_aubio_sink_apple_audio + +*/ +void del_aubio_sink_apple_audio(aubio_sink_apple_audio_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SINK_APPLE_AUDIO_H */ diff --git a/aubio/src/io/sink_sndfile.h b/aubio/src/io/sink_sndfile.h new file mode 100644 index 00000000..d12ff55c --- /dev/null +++ b/aubio/src/io/sink_sndfile.h @@ -0,0 +1,195 @@ +/* + Copyright (C) 2012-2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SINK_SNDFILE_H +#define AUBIO_SINK_SNDFILE_H + +/** \file + + Write to file using [libsndfile](http://www.mega-nerd.com/libsndfile/) + + Avoid including this file directly! Prefer using ::aubio_sink_t instead to + make your code portable. + + To read from file, use ::aubio_source_t. + + \example io/test-sink_sndfile.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** sink_sndfile object */ +typedef struct _aubio_sink_sndfile_t aubio_sink_sndfile_t; + +/** + + create new ::aubio_sink_sndfile_t + + \param uri the file path or uri to write to + \param samplerate sample rate to write the file at + + \return newly created ::aubio_sink_sndfile_t + + Creates a new sink object. + + If samplerate is set to 0, the creation of the file will be delayed until + both ::aubio_sink_preset_samplerate and ::aubio_sink_preset_channels have + been called. + +*/ +aubio_sink_sndfile_t * new_aubio_sink_sndfile(const char_t * uri, uint_t samplerate); + +/** + + preset sink samplerate + + \param s sink, created with ::new_aubio_sink_sndfile + \param samplerate samplerate to preset the sink to, in Hz + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_sndfile_preset_samplerate(aubio_sink_sndfile_t *s, uint_t samplerate); + +/** + + preset sink channels + + \param s sink, created with ::new_aubio_sink_sndfile + \param channels number of channels to preset the sink to + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_sndfile_preset_channels(aubio_sink_sndfile_t *s, uint_t channels); + +/** + + preset sink format + + \param s sink, created with ::new_aubio_sink_sndfile + \param fmt format of the file to create + + \return 0 on success, 1 on error + + Preset the format of the sink. Supported format strings: + - "wav": 16 bit (default) + - "aiff": aiff, 16 bit + - "flac": flac, 16 bit + - "ogg": ogg vorbis stream + + Alternatively, any sndfile format can be set by passing the corresponding + integer as a string: + + \code{.c} + char_t fmt[10]; + snprintf(fmt, sizeof(fmt), "%d", SF_FORMAT_FLAC | SF_FORMAT_PCM_24); + aubio_sink_sndfile_preset_format(s, fmt); + \endcode + + The file should have been created using a samplerate of 0. + + This function should be called before aubio_sink_sndfile_preset_samplerate() + and aubio_sink_sndfile_preset_channels(). + +*/ +uint_t aubio_sink_sndfile_preset_format(aubio_sink_sndfile_t *s, + const char_t* fmt); + +/** + + get samplerate of sink object + + \param s sink object, created with ::new_aubio_sink_sndfile + \return samplerate, in Hz + +*/ +uint_t aubio_sink_sndfile_get_samplerate(const aubio_sink_sndfile_t *s); + +/** + + get channels of sink object + + \param s sink object, created with ::new_aubio_sink_sndfile + \return number of channels + +*/ +uint_t aubio_sink_sndfile_get_channels(const aubio_sink_sndfile_t *s); + +/** + + write monophonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink_sndfile + \param write_data ::fvec_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_sndfile_do(aubio_sink_sndfile_t * s, fvec_t * write_data, uint_t write); + +/** + + write polyphonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink_sndfile + \param write_data ::fmat_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_sndfile_do_multi(aubio_sink_sndfile_t * s, fmat_t * write_data, uint_t write); + +/** + + close sink + + \param s sink_sndfile object, created with ::new_aubio_sink_sndfile + + \return 0 on success, non-zero on failure + +*/ +uint_t aubio_sink_sndfile_close(aubio_sink_sndfile_t * s); + +/** + + close sink and cleanup memory + + \param s sink, created with ::new_aubio_sink_sndfile + +*/ +void del_aubio_sink_sndfile(aubio_sink_sndfile_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SINK_SNDFILE_H */ diff --git a/aubio/src/io/sink_wavwrite.h b/aubio/src/io/sink_wavwrite.h new file mode 100644 index 00000000..bb479e43 --- /dev/null +++ b/aubio/src/io/sink_wavwrite.h @@ -0,0 +1,162 @@ +/* + Copyright (C) 2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SINK_WAVWRITE_H +#define AUBIO_SINK_WAVWRITE_H + +/** \file + + Write to file using native file writer. + + Avoid including this file directly! Prefer using ::aubio_sink_t instead to + make your code portable. + + To read from file, use ::aubio_source_t. + + \example io/test-sink_wavwrite.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** sink_wavwrite object */ +typedef struct _aubio_sink_wavwrite_t aubio_sink_wavwrite_t; + +/** + + create new ::aubio_sink_wavwrite_t + + \param uri the file path or uri to write to + \param samplerate sample rate to write the file at + + \return newly created ::aubio_sink_wavwrite_t + + Creates a new sink object. + + If samplerate is set to 0, the creation of the file will be delayed until + both ::aubio_sink_preset_samplerate and ::aubio_sink_preset_channels have + been called. + +*/ +aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(const char_t * uri, uint_t samplerate); + +/** + + preset sink samplerate + + \param s sink, created with ::new_aubio_sink_wavwrite + \param samplerate samplerate to preset the sink to, in Hz + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_wavwrite_preset_samplerate(aubio_sink_wavwrite_t *s, uint_t samplerate); + +/** + + preset sink channels + + \param s sink, created with ::new_aubio_sink_wavwrite + \param channels number of channels to preset the sink to + + \return 0 on success, 1 on error + + Preset the samplerate of the sink. The file should have been created using a + samplerate of 0. + + The file will be opened only when both samplerate and channels have been set. + +*/ +uint_t aubio_sink_wavwrite_preset_channels(aubio_sink_wavwrite_t *s, uint_t channels); + +/** + + get samplerate of sink object + + \param s sink object, created with ::new_aubio_sink_wavwrite + \return samplerate, in Hz + +*/ +uint_t aubio_sink_wavwrite_get_samplerate(const aubio_sink_wavwrite_t *s); + +/** + + get channels of sink object + + \param s sink object, created with ::new_aubio_sink_wavwrite + \return number of channels + +*/ +uint_t aubio_sink_wavwrite_get_channels(const aubio_sink_wavwrite_t *s); + +/** + + write monophonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink_wavwrite + \param write_data ::fvec_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_wavwrite_do(aubio_sink_wavwrite_t * s, fvec_t * write_data, uint_t write); + +/** + + write polyphonic vector of length hop_size to sink + + \param s sink, created with ::new_aubio_sink_wavwrite + \param write_data ::fmat_t samples to write to sink + \param write number of frames to write + +*/ +void aubio_sink_wavwrite_do_multi(aubio_sink_wavwrite_t * s, fmat_t * write_data, uint_t write); + +/** + + close sink + + \param s sink_wavwrite object, create with ::new_aubio_sink_wavwrite + + \return 0 on success, non-zero on failure + +*/ +uint_t aubio_sink_wavwrite_close(aubio_sink_wavwrite_t * s); + +/** + + close sink and cleanup memory + + \param s sink, created with ::new_aubio_sink_wavwrite + +*/ +void del_aubio_sink_wavwrite(aubio_sink_wavwrite_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SINK_WAVWRITE_H */ diff --git a/aubio/src/io/source.h b/aubio/src/io/source.h new file mode 100644 index 00000000..ff9b8be9 --- /dev/null +++ b/aubio/src/io/source.h @@ -0,0 +1,183 @@ +/* + Copyright (C) 2012-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SOURCE_H +#define AUBIO_SOURCE_H + +/** \file + + Media source to read blocks of consecutive audio samples from file. + + To write to file, use ::aubio_sink_t. + + Depending on how aubio was compiled, the following sources will be available. + + When creating a new source using ::new_aubio_source, the new function of each + of the compiled-in sources will be used, in the following order, until one of + them gets successfully created. If all sources returned NULL, + ::new_aubio_source will return NULL. + + \b \p source_avcodec : libav + + aubio can be optionally compiled with [libav](http://libav.org), which can + read from a very large number of audio and video formats, including over + different network protocols such as HTTP. + + \b \p source_apple_audio : ExtAudioFileRef + + On Mac and iOS platforms, aubio should be compiled with CoreAudio [Extended + Audio File Services] + (https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/ExtendedAudioFileServicesReference/Reference/reference.html). + This provides access to most common audio file formats, including compressed + ones. + + \b \p source_sndfile : libsndfile + + Also optional, aubio can be built against + [libsndfile](http://www.mega-nerd.com/libsndfile/), which can read [most + uncompressed formats](http://www.mega-nerd.com/libsndfile/#Features). + + \b \p source_wavread : native WAV reader + + A simple source to read from 16-bits PCM RIFF encoded WAV files. + + \example io/test-source.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** media source object */ +typedef struct _aubio_source_t aubio_source_t; + +/** + + create new ::aubio_source_t + + \param uri the file path or uri to read from + \param samplerate sampling rate to view the fie at + \param hop_size the size of the blocks to read from + + Creates a new source object. If `0` is passed as `samplerate`, the sample + rate of the original file is used. + + The samplerate of newly created source can be obtained using + ::aubio_source_get_samplerate. + +*/ +aubio_source_t * new_aubio_source(const char_t * uri, uint_t samplerate, uint_t hop_size); + +/** + + read monophonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source + \param read_to ::fvec_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_do(aubio_source_t * s, fvec_t * read_to, uint_t * read); + +/** + + read polyphonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source + \param read_to ::fmat_t of data to read to + \param[out] read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_do_multi(aubio_source_t * s, fmat_t * read_to, uint_t * read); + +/** + + get samplerate of source object + + \param s source object, created with ::new_aubio_source + \return samplerate, in Hz + +*/ +uint_t aubio_source_get_samplerate(aubio_source_t * s); + +/** + + get channels of source object + + \param s source object, created with ::new_aubio_source + \return channels + +*/ +uint_t aubio_source_get_channels (aubio_source_t * s); + +/** + + seek source object + + \param s source object, created with ::new_aubio_source + \param pos position to seek to, in frames + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_seek (aubio_source_t * s, uint_t pos); + +/** + + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source + \return number of frames in file + +*/ +uint_t aubio_source_get_duration (aubio_source_t * s); + +/** + + close source object + + \param s source object, created with ::new_aubio_source + + \return 0 if sucessful, non-zero on failure + + */ +uint_t aubio_source_close (aubio_source_t *s); + +/** + + close source and cleanup memory + + \param s source object, created with ::new_aubio_source + +*/ +void del_aubio_source(aubio_source_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SOURCE_H */ diff --git a/aubio/src/io/source_apple_audio.h b/aubio/src/io/source_apple_audio.h new file mode 100644 index 00000000..0ad55694 --- /dev/null +++ b/aubio/src/io/source_apple_audio.h @@ -0,0 +1,156 @@ +/* + Copyright (C) 2012-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SOURCE_APPLE_AUDIO_H +#define AUBIO_SOURCE_APPLE_AUDIO_H + +/** \file + + Read from file using CoreAudio Extended Audio File Services + [ExtAudioFileRef](https://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/ExtendedAudioFileServicesReference/Reference/reference.html) + + Avoid including this file directly! Prefer using ::aubio_source_t instead to + make your code portable. + + To write to file, use ::aubio_sink_t. + + \example io/test-source_apple_audio.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** apple audio media source object */ +typedef struct _aubio_source_apple_audio_t aubio_source_apple_audio_t; + +/** + + create new ::aubio_source_apple_audio_t + + \param uri the file path or uri to read from + \param samplerate sampling rate to view the fie at + \param hop_size the size of the blocks to read from + + Creates a new source object. If `0` is passed as `samplerate`, the sample + rate of the original file is used. + + The samplerate of newly created source can be obtained using + ::aubio_source_apple_audio_get_samplerate. + +*/ +aubio_source_apple_audio_t * new_aubio_source_apple_audio(const char_t * uri, uint_t samplerate, uint_t hop_size); + +/** + + read monophonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_apple_audio + \param read_to ::fvec_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_apple_audio_do(aubio_source_apple_audio_t * s, fvec_t * read_to, uint_t * read); + +/** + + read polyphonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_apple_audio + \param read_to ::fmat_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_apple_audio_do_multi(aubio_source_apple_audio_t * s, fmat_t * read_to, uint_t * read); + +/** + + get samplerate of source object + + \param s source object, created with ::new_aubio_source_apple_audio + \return samplerate, in Hz + +*/ +uint_t aubio_source_apple_audio_get_samplerate(const aubio_source_apple_audio_t * s); + +/** + + get channels of source object + + \param s source object, created with ::new_aubio_source_apple_audio + \return number of channels + +*/ +uint_t aubio_source_apple_audio_get_channels(const aubio_source_apple_audio_t * s); + +/** + + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_apple_audio + \return number of frames in file + +*/ +uint_t aubio_source_apple_audio_get_duration(const aubio_source_apple_audio_t * s); + +/** + + seek source object + + \param s source object, created with ::new_aubio_source + \param pos position to seek to, in frames + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_apple_audio_seek (aubio_source_apple_audio_t * s, uint_t pos); + +/** + + close source + + \param s source object, created with ::new_aubio_source_apple_audio + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_apple_audio_close(aubio_source_apple_audio_t * s); + +/** + + close source and cleanup memory + + \param s source object, created with ::new_aubio_source_apple_audio + +*/ +void del_aubio_source_apple_audio(aubio_source_apple_audio_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SOURCE_APPLE_AUDIO_H */ diff --git a/aubio/src/io/source_avcodec.h b/aubio/src/io/source_avcodec.h new file mode 100644 index 00000000..81f657d6 --- /dev/null +++ b/aubio/src/io/source_avcodec.h @@ -0,0 +1,155 @@ +/* + Copyright (C) 2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SOURCE_AVCODEC_H +#define AUBIO_SOURCE_AVCODEC_H + +/** \file + + Read from file using [libavcodec](http://libav.org/) + + Avoid including this file directly! Prefer using ::aubio_source_t instead to + make your code portable. + + To write to file, use ::aubio_sink_t. + + \example io/test-source_avcodec.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** avcodec media source object */ +typedef struct _aubio_source_avcodec_t aubio_source_avcodec_t; + +/** + + create new ::aubio_source_avcodec_t + + \param uri the file path or uri to read from + \param samplerate sampling rate to view the fie at + \param hop_size the size of the blocks to read from + + Creates a new source object. If `0` is passed as `samplerate`, the sample + rate of the original file is used. + + The samplerate of newly created source can be obtained using + ::aubio_source_avcodec_get_samplerate. + +*/ +aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * uri, uint_t samplerate, uint_t hop_size); + +/** + + read monophonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_avcodec + \param read_to ::fvec_t of data to read to + \param[out] read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_to, uint_t * read); + +/** + + read polyphonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_avcodec + \param read_to ::fmat_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_avcodec_do_multi(aubio_source_avcodec_t * s, fmat_t * read_to, uint_t * read); + +/** + + get samplerate of source object + + \param s source object, created with ::new_aubio_source_avcodec + \return samplerate, in Hz + +*/ +uint_t aubio_source_avcodec_get_samplerate(const aubio_source_avcodec_t * s); + +/** + + get number of channels of source object + + \param s source object, created with ::new_aubio_source_avcodec + \return number of channels + +*/ +uint_t aubio_source_avcodec_get_channels (const aubio_source_avcodec_t * s); + +/** + + seek source object + + \param s source object, created with ::new_aubio_source_avcodec + \param pos position to seek to, in frames + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_avcodec_seek (aubio_source_avcodec_t *s, uint_t pos); + +/** + + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_avcodec + \return number of frames in file + +*/ +uint_t aubio_source_avcodec_get_duration (aubio_source_avcodec_t * s); + +/** + + close source + + \param s source object, created with ::new_aubio_source_avcodec + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_avcodec_close (aubio_source_avcodec_t *s); + +/** + + close source and cleanup memory + + \param s source object, created with ::new_aubio_source_avcodec + +*/ +void del_aubio_source_avcodec(aubio_source_avcodec_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SOURCE_AVCODEC_H */ diff --git a/aubio/src/io/source_sndfile.h b/aubio/src/io/source_sndfile.h new file mode 100644 index 00000000..210a40d2 --- /dev/null +++ b/aubio/src/io/source_sndfile.h @@ -0,0 +1,155 @@ +/* + Copyright (C) 2012-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SOURCE_SNDFILE_H +#define AUBIO_SOURCE_SNDFILE_H + +/** \file + + Read from file using [libsndfile](http://www.mega-nerd.com/libsndfile/) + + Avoid including this file directly! Prefer using ::aubio_source_t instead to + make your code portable. + + To write to file, use ::aubio_sink_t. + + \example io/test-source_sndfile.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** sndfile media source object */ +typedef struct _aubio_source_sndfile_t aubio_source_sndfile_t; + +/** + + create new ::aubio_source_sndfile_t + + \param uri the file path or uri to read from + \param samplerate sampling rate to view the fie at + \param hop_size the size of the blocks to read from + + Creates a new source object. If `0` is passed as `samplerate`, the sample + rate of the original file is used. + + The samplerate of newly created source can be obtained using + ::aubio_source_sndfile_get_samplerate. + +*/ +aubio_source_sndfile_t * new_aubio_source_sndfile(const char_t * uri, uint_t samplerate, uint_t hop_size); + +/** + + read monophonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_sndfile + \param read_to ::fvec_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_sndfile_do(aubio_source_sndfile_t * s, fvec_t * read_to, uint_t * read); + +/** + + read polyphonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_sndfile + \param read_to ::fmat_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_sndfile_do_multi(aubio_source_sndfile_t * s, fmat_t * read_to, uint_t * read); + +/** + + get samplerate of source object + + \param s source object, created with ::new_aubio_source_sndfile + \return samplerate, in Hz + +*/ +uint_t aubio_source_sndfile_get_samplerate(aubio_source_sndfile_t * s); + +/** + + get number of channels of source object + + \param s source object, created with ::new_aubio_source_sndfile + \return number of channels + +*/ +uint_t aubio_source_sndfile_get_channels (aubio_source_sndfile_t * s); + +/** + + seek source object + + \param s source object, created with ::new_aubio_source_sndfile + \param pos position to seek to, in frames + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_sndfile_seek (aubio_source_sndfile_t *s, uint_t pos); + +/** + + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_sndfile + \return number of frames in file + +*/ +uint_t aubio_source_sndfile_get_duration (const aubio_source_sndfile_t *s); + +/** + + close source + + \param s source object, created with ::new_aubio_source_sndfile + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_sndfile_close (aubio_source_sndfile_t *s); + +/** + + close source and cleanup memory + + \param s source object, created with ::new_aubio_source_sndfile + +*/ +void del_aubio_source_sndfile(aubio_source_sndfile_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SOURCE_SNDFILE_H */ diff --git a/aubio/src/io/source_wavread.h b/aubio/src/io/source_wavread.h new file mode 100644 index 00000000..65f752f9 --- /dev/null +++ b/aubio/src/io/source_wavread.h @@ -0,0 +1,160 @@ +/* + Copyright (C) 2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SOURCE_WAVREAD_H +#define AUBIO_SOURCE_WAVREAD_H + +/** \file + + Read from file using custom wav reading routines. + + Avoid including this file directly! Prefer using ::aubio_source_t instead to + make your code portable. + + To write to file, use ::aubio_sink_t. + + References: + + - http://netghost.narod.ru/gff/graphics/summary/micriff.htm + - https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ + + \example io/test-source_wavread.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** wavread media source object */ +typedef struct _aubio_source_wavread_t aubio_source_wavread_t; + +/** + + create new ::aubio_source_wavread_t + + \param uri the file path or uri to read from + \param samplerate sampling rate to view the fie at + \param hop_size the size of the blocks to read from + + Creates a new source object. If `0` is passed as `samplerate`, the sample + rate of the original file is used. + + The samplerate of newly created source can be obtained using + ::aubio_source_wavread_get_samplerate. + +*/ +aubio_source_wavread_t * new_aubio_source_wavread(const char_t * uri, uint_t samplerate, uint_t hop_size); + +/** + + read monophonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_wavread + \param read_to ::fvec_t of data to read to + \param[out] read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_wavread_do(aubio_source_wavread_t * s, fvec_t * read_to, uint_t * read); + +/** + + read polyphonic vector of length hop_size from source object + + \param s source object, created with ::new_aubio_source_wavread + \param read_to ::fmat_t of data to read to + \param read upon returns, equals to number of frames actually read + + Upon returns, `read` contains the number of frames actually read from the + source. `hop_size` if enough frames could be read, less otherwise. + +*/ +void aubio_source_wavread_do_multi(aubio_source_wavread_t * s, fmat_t * read_to, uint_t * read); + +/** + + get samplerate of source object + + \param s source object, created with ::new_aubio_source_wavread + \return samplerate, in Hz + +*/ +uint_t aubio_source_wavread_get_samplerate(aubio_source_wavread_t * s); + +/** + + get number of channels of source object + + \param s source object, created with ::new_aubio_source_wavread + \return number of channels + +*/ +uint_t aubio_source_wavread_get_channels (aubio_source_wavread_t * s); + +/** + + seek source object + + \param s source object, created with ::new_aubio_source_wavread + \param pos position to seek to, in frames + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_wavread_seek (aubio_source_wavread_t *s, uint_t pos); + +/** + + get the duration of source object, in frames + + \param s source object, created with ::new_aubio_source_sndfile + \return number of frames in file + +*/ +uint_t aubio_source_wavread_get_duration (const aubio_source_wavread_t *s); + +/** + + close source + + \param s source object, created with ::new_aubio_source_wavread + + \return 0 if sucessful, non-zero on failure + +*/ +uint_t aubio_source_wavread_close (aubio_source_wavread_t *s); + +/** + + close source and cleanup memory + + \param s source object, created with ::new_aubio_source_wavread + +*/ +void del_aubio_source_wavread(aubio_source_wavread_t * s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SOURCE_WAVREAD_H */ diff --git a/aubio/src/lvec.c b/aubio/src/lvec.c new file mode 100644 index 00000000..dd63bf42 --- /dev/null +++ b/aubio/src/lvec.c @@ -0,0 +1,80 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "lvec.h" + +lvec_t * new_lvec(uint_t length) { + lvec_t * s; + if ((sint_t)length <= 0) { + return NULL; + } + s = AUBIO_NEW(lvec_t); + s->length = length; + s->data = AUBIO_ARRAY(lsmp_t, s->length); + return s; +} + +void del_lvec(lvec_t *s) { + AUBIO_FREE(s->data); + AUBIO_FREE(s); +} + +void lvec_set_sample(lvec_t *s, lsmp_t data, uint_t position) { + s->data[position] = data; +} + +lsmp_t lvec_get_sample(lvec_t *s, uint_t position) { + return s->data[position]; +} + +lsmp_t * lvec_get_data(const lvec_t *s) { + return s->data; +} + +/* helper functions */ + +void lvec_print(const lvec_t *s) { + uint_t j; + for (j=0; j< s->length; j++) { + AUBIO_MSG(AUBIO_LSMP_FMT " ", s->data[j]); + } + AUBIO_MSG("\n"); +} + +void lvec_set_all (lvec_t *s, smpl_t val) { + uint_t j; + for (j=0; j< s->length; j++) { + s->data[j] = val; + } +} + +void lvec_zeros(lvec_t *s) { +#if HAVE_MEMCPY_HACKS + memset(s->data, 0, s->length * sizeof(lsmp_t)); +#else + lvec_set_all (s, 0.); +#endif +} + +void lvec_ones(lvec_t *s) { + lvec_set_all (s, 1.); +} + diff --git a/aubio/src/lvec.h b/aubio/src/lvec.h new file mode 100644 index 00000000..402ba0ff --- /dev/null +++ b/aubio/src/lvec.h @@ -0,0 +1,118 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_LVEC_H +#define AUBIO_LVEC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Vector of real-valued data in double precision + + This file specifies the ::lvec_t buffer type, which is used in some places in + aubio to store a vector of ::lsmp_t. + + Note: the lvec_t data type is required in some algorithms such as IIR filters + (see temporal/filter.h). + + \example test-lvec.c + +*/ + +/** Buffer for real data in double precision */ +typedef struct { + uint_t length; /**< length of buffer */ + lsmp_t *data; /**< data array of size [length] */ +} lvec_t; + +/** lvec_t buffer creation function + + \param length the length of the buffer to create + +*/ +lvec_t * new_lvec(uint_t length); +/** lvec_t buffer deletion function + + \param s buffer to delete as returned by new_lvec() + +*/ +void del_lvec(lvec_t *s); + +/** read sample value in a buffer + + \param s vector to read from + \param position sample position to read from + +*/ +lsmp_t lvec_get_sample(lvec_t *s, uint_t position); + +/** write sample value in a buffer + + \param s vector to write to + \param data value to write in s->data[position] + \param position sample position to write to + +*/ +void lvec_set_sample(lvec_t *s, lsmp_t data, uint_t position); + +/** read data from a buffer + + \param s vector to read from + +*/ +lsmp_t * lvec_get_data(const lvec_t *s); + +/** print out lvec data + + \param s vector to print out + +*/ +void lvec_print(const lvec_t *s); + +/** set all elements to a given value + + \param s vector to modify + \param val value to set elements to + +*/ +void lvec_set_all(lvec_t *s, smpl_t val); + +/** set all elements to zero + + \param s vector to modify + +*/ +void lvec_zeros(lvec_t *s); + +/** set all elements to ones + + \param s vector to modify + +*/ +void lvec_ones(lvec_t *s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_LVEC_H */ diff --git a/aubio/src/mathutils.c b/aubio/src/mathutils.c new file mode 100644 index 00000000..35755fe4 --- /dev/null +++ b/aubio/src/mathutils.c @@ -0,0 +1,681 @@ +/* + Copyright (C) 2003-2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/* see in mathutils.h for doc */ + +#include "aubio_priv.h" +#include "fvec.h" +#include "mathutils.h" +#include "musicutils.h" + +/** Window types */ +typedef enum +{ + aubio_win_ones, + aubio_win_rectangle, + aubio_win_hamming, + aubio_win_hanning, + aubio_win_hanningz, + aubio_win_blackman, + aubio_win_blackman_harris, + aubio_win_gaussian, + aubio_win_welch, + aubio_win_parzen, + aubio_win_default = aubio_win_hanningz, +} aubio_window_type; + +fvec_t * +new_aubio_window (char_t * window_type, uint_t length) +{ + fvec_t * win = new_fvec (length); + uint_t err; + if (win == NULL) { + return NULL; + } + err = fvec_set_window (win, window_type); + if (err != 0) { + del_fvec(win); + return NULL; + } + return win; +} + +uint_t fvec_set_window (fvec_t *win, char_t *window_type) { + smpl_t * w = win->data; + uint_t i, size = win->length; + aubio_window_type wintype; + if (window_type == NULL) { + AUBIO_ERR ("window type can not be null.\n"); + return 1; + } else if (strcmp (window_type, "ones") == 0) + wintype = aubio_win_ones; + else if (strcmp (window_type, "rectangle") == 0) + wintype = aubio_win_rectangle; + else if (strcmp (window_type, "hamming") == 0) + wintype = aubio_win_hamming; + else if (strcmp (window_type, "hanning") == 0) + wintype = aubio_win_hanning; + else if (strcmp (window_type, "hanningz") == 0) + wintype = aubio_win_hanningz; + else if (strcmp (window_type, "blackman") == 0) + wintype = aubio_win_blackman; + else if (strcmp (window_type, "blackman_harris") == 0) + wintype = aubio_win_blackman_harris; + else if (strcmp (window_type, "gaussian") == 0) + wintype = aubio_win_gaussian; + else if (strcmp (window_type, "welch") == 0) + wintype = aubio_win_welch; + else if (strcmp (window_type, "parzen") == 0) + wintype = aubio_win_parzen; + else if (strcmp (window_type, "default") == 0) + wintype = aubio_win_default; + else { + AUBIO_ERR ("unknown window type `%s`.\n", window_type); + return 1; + } + switch(wintype) { + case aubio_win_ones: + fvec_ones(win); + break; + case aubio_win_rectangle: + fvec_set_all(win, .5); + break; + case aubio_win_hamming: + for (i=0;idata, (int)s->length, &tmp); + return tmp; +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_meanv(s->data, 1, &tmp, s->length); + return tmp; +#else + uint_t j; + for (j = 0; j < s->length; j++) { + tmp += s->data[j]; + } + return tmp / (smpl_t)(s->length); +#endif +} + +smpl_t +fvec_sum (fvec_t * s) +{ + smpl_t tmp = 0.0; +#if defined(HAVE_INTEL_IPP) + aubio_ippsSum(s->data, (int)s->length, &tmp); +#elif defined(HAVE_ACCELERATE) + aubio_vDSP_sve(s->data, 1, &tmp, s->length); +#else + uint_t j; + for (j = 0; j < s->length; j++) { + tmp += s->data[j]; + } +#endif + return tmp; +} + +smpl_t +fvec_max (fvec_t * s) +{ +#if defined(HAVE_INTEL_IPP) + smpl_t tmp = 0.; + aubio_ippsMax( s->data, (int)s->length, &tmp); +#elif defined(HAVE_ACCELERATE) + smpl_t tmp = 0.; + aubio_vDSP_maxv( s->data, 1, &tmp, s->length ); +#else + uint_t j; + smpl_t tmp = s->data[0]; + for (j = 1; j < s->length; j++) { + tmp = (tmp > s->data[j]) ? tmp : s->data[j]; + } +#endif + return tmp; +} + +smpl_t +fvec_min (fvec_t * s) +{ +#if defined(HAVE_INTEL_IPP) + smpl_t tmp = 0.; + aubio_ippsMin(s->data, (int)s->length, &tmp); +#elif defined(HAVE_ACCELERATE) + smpl_t tmp = 0.; + aubio_vDSP_minv(s->data, 1, &tmp, s->length); +#else + uint_t j; + smpl_t tmp = s->data[0]; + for (j = 1; j < s->length; j++) { + tmp = (tmp < s->data[j]) ? tmp : s->data[j]; + } +#endif + return tmp; +} + +uint_t +fvec_min_elem (fvec_t * s) +{ +#ifndef HAVE_ACCELERATE + uint_t j, pos = 0.; + smpl_t tmp = s->data[0]; + for (j = 0; j < s->length; j++) { + pos = (tmp < s->data[j]) ? pos : j; + tmp = (tmp < s->data[j]) ? tmp : s->data[j]; + } +#else + smpl_t tmp = 0.; + vDSP_Length pos = 0; + aubio_vDSP_minvi(s->data, 1, &tmp, &pos, s->length); +#endif + return (uint_t)pos; +} + +uint_t +fvec_max_elem (fvec_t * s) +{ +#ifndef HAVE_ACCELERATE + uint_t j, pos = 0; + smpl_t tmp = 0.0; + for (j = 0; j < s->length; j++) { + pos = (tmp > s->data[j]) ? pos : j; + tmp = (tmp > s->data[j]) ? tmp : s->data[j]; + } +#else + smpl_t tmp = 0.; + vDSP_Length pos = 0; + aubio_vDSP_maxvi(s->data, 1, &tmp, &pos, s->length); +#endif + return (uint_t)pos; +} + +void +fvec_shift (fvec_t * s) +{ + uint_t half = s->length / 2, start = half, j; + // if length is odd, middle element is moved to the end + if (2 * half < s->length) start ++; +#ifndef HAVE_BLAS + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[j], s->data[j + start]); + } +#else + aubio_cblas_swap(half, s->data, 1, s->data + start, 1); +#endif + if (start != half) { + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[j + start - 1], s->data[j + start]); + } + } +} + +void +fvec_ishift (fvec_t * s) +{ + uint_t half = s->length / 2, start = half, j; + // if length is odd, middle element is moved to the beginning + if (2 * half < s->length) start ++; +#ifndef HAVE_BLAS + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[j], s->data[j + start]); + } +#else + aubio_cblas_swap(half, s->data, 1, s->data + start, 1); +#endif + if (start != half) { + for (j = 0; j < half; j++) { + ELEM_SWAP (s->data[half], s->data[j]); + } + } +} + +void fvec_push(fvec_t *in, smpl_t new_elem) { + uint_t i; + for (i = 0; i < in->length - 1; i++) { + in->data[i] = in->data[i + 1]; + } + in->data[in->length - 1] = new_elem; +} + +void fvec_clamp(fvec_t *in, smpl_t absmax) { + uint_t i; + for (i = 0; i < in->length; i++) { + if (in->data[i] > 0 && in->data[i] > ABS(absmax)) { + in->data[i] = absmax; + } else if (in->data[i] < 0 && in->data[i] < -ABS(absmax)) { + in->data[i] = -absmax; + } + } +} + +smpl_t +aubio_level_lin (const fvec_t * f) +{ + smpl_t energy = 0.; +#ifndef HAVE_BLAS + uint_t j; + for (j = 0; j < f->length; j++) { + energy += SQR (f->data[j]); + } +#else + energy = aubio_cblas_dot(f->length, f->data, 1, f->data, 1); +#endif + return energy / f->length; +} + +smpl_t +fvec_local_hfc (fvec_t * v) +{ + smpl_t hfc = 0.; + uint_t j; + for (j = 0; j < v->length; j++) { + hfc += (j + 1) * v->data[j]; + } + return hfc; +} + +void +fvec_min_removal (fvec_t * v) +{ + smpl_t v_min = fvec_min (v); + fvec_add (v, - v_min ); +} + +smpl_t +fvec_alpha_norm (fvec_t * o, smpl_t alpha) +{ + uint_t j; + smpl_t tmp = 0.; + for (j = 0; j < o->length; j++) { + tmp += POW (ABS (o->data[j]), alpha); + } + return POW (tmp / o->length, 1. / alpha); +} + +void +fvec_alpha_normalise (fvec_t * o, smpl_t alpha) +{ + uint_t j; + smpl_t norm = fvec_alpha_norm (o, alpha); + for (j = 0; j < o->length; j++) { + o->data[j] /= norm; + } +} + +void +fvec_add (fvec_t * o, smpl_t val) +{ + uint_t j; + for (j = 0; j < o->length; j++) { + o->data[j] += val; + } +} + +void +fvec_mul (fvec_t *o, smpl_t val) +{ + uint_t j; + for (j = 0; j < o->length; j++) { + o->data[j] *= val; + } +} + +void fvec_adapt_thres(fvec_t * vec, fvec_t * tmp, + uint_t post, uint_t pre) { + uint_t length = vec->length, j; + for (j=0;jdata[j] -= fvec_moving_thres(vec, tmp, post, pre, j); + } +} + +smpl_t +fvec_moving_thres (fvec_t * vec, fvec_t * tmpvec, + uint_t post, uint_t pre, uint_t pos) +{ + uint_t k; + smpl_t *medar = (smpl_t *) tmpvec->data; + uint_t win_length = post + pre + 1; + uint_t length = vec->length; + /* post part of the buffer does not exist */ + if (pos < post + 1) { + for (k = 0; k < post + 1 - pos; k++) + medar[k] = 0.; /* 0-padding at the beginning */ + for (k = post + 1 - pos; k < win_length; k++) + medar[k] = vec->data[k + pos - post]; + /* the buffer is fully defined */ + } else if (pos + pre < length) { + for (k = 0; k < win_length; k++) + medar[k] = vec->data[k + pos - post]; + /* pre part of the buffer does not exist */ + } else { + for (k = 0; k < length - pos + post; k++) + medar[k] = vec->data[k + pos - post]; + for (k = length - pos + post; k < win_length; k++) + medar[k] = 0.; /* 0-padding at the end */ + } + return fvec_median (tmpvec); +} + +smpl_t fvec_median (fvec_t * input) { + uint_t n = input->length; + smpl_t * arr = (smpl_t *) input->data; + uint_t low, high ; + uint_t median; + uint_t middle, ll, hh; + + low = 0 ; high = n-1 ; median = (low + high) / 2; + for (;;) { + if (high <= low) /* One element only */ + return arr[median] ; + + if (high == low + 1) { /* Two elements only */ + if (arr[low] > arr[high]) + ELEM_SWAP(arr[low], arr[high]) ; + return arr[median] ; + } + + /* Find median of low, middle and high items; swap into position low */ + middle = (low + high) / 2; + if (arr[middle] > arr[high]) ELEM_SWAP(arr[middle], arr[high]); + if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]); + if (arr[middle] > arr[low]) ELEM_SWAP(arr[middle], arr[low]) ; + + /* Swap low item (now in position middle) into position (low+1) */ + ELEM_SWAP(arr[middle], arr[low+1]) ; + + /* Nibble from each end towards middle, swapping items when stuck */ + ll = low + 1; + hh = high; + for (;;) { + do ll++; while (arr[low] > arr[ll]) ; + do hh--; while (arr[hh] > arr[low]) ; + + if (hh < ll) + break; + + ELEM_SWAP(arr[ll], arr[hh]) ; + } + + /* Swap middle item (in position low) back into correct position */ + ELEM_SWAP(arr[low], arr[hh]) ; + + /* Re-set active partition */ + if (hh <= median) + low = ll; + if (hh >= median) + high = hh - 1; + } +} + +smpl_t fvec_quadratic_peak_pos (const fvec_t * x, uint_t pos) { + smpl_t s0, s1, s2; uint_t x0, x2; + smpl_t half = .5, two = 2.; + if (pos == 0 || pos == x->length - 1) return pos; + x0 = (pos < 1) ? pos : pos - 1; + x2 = (pos + 1 < x->length) ? pos + 1 : pos; + if (x0 == pos) return (x->data[pos] <= x->data[x2]) ? pos : x2; + if (x2 == pos) return (x->data[pos] <= x->data[x0]) ? pos : x0; + s0 = x->data[x0]; + s1 = x->data[pos]; + s2 = x->data[x2]; + return pos + half * (s0 - s2 ) / (s0 - two * s1 + s2); +} + +smpl_t fvec_quadratic_peak_mag (fvec_t *x, smpl_t pos) { + smpl_t x0, x1, x2; + uint_t index = (uint_t)(pos - .5) + 1; + if (pos >= x->length || pos < 0.) return 0.; + if ((smpl_t)index == pos) return x->data[index]; + x0 = x->data[index - 1]; + x1 = x->data[index]; + x2 = x->data[index + 1]; + return x1 - .25 * (x0 - x2) * (pos - index); +} + +uint_t fvec_peakpick(const fvec_t * onset, uint_t pos) { + uint_t tmp=0; + tmp = (onset->data[pos] > onset->data[pos-1] + && onset->data[pos] > onset->data[pos+1] + && onset->data[pos] > 0.); + return tmp; +} + +smpl_t +aubio_quadfrac (smpl_t s0, smpl_t s1, smpl_t s2, smpl_t pf) +{ + smpl_t tmp = + s0 + (pf / 2.) * (pf * (s0 - 2. * s1 + s2) - 3. * s0 + 4. * s1 - s2); + return tmp; +} + +smpl_t +aubio_freqtomidi (smpl_t freq) +{ + smpl_t midi; + if (freq < 2. || freq > 100000.) return 0.; // avoid nans and infs + /* log(freq/A-2)/log(2) */ + midi = freq / 6.875; + midi = LOG (midi) / 0.6931471805599453; + midi *= 12; + midi -= 3; + return midi; +} + +smpl_t +aubio_miditofreq (smpl_t midi) +{ + smpl_t freq; + if (midi > 140.) return 0.; // avoid infs + freq = (midi + 3.) / 12.; + freq = EXP (freq * 0.6931471805599453); + freq *= 6.875; + return freq; +} + +smpl_t +aubio_bintofreq (smpl_t bin, smpl_t samplerate, smpl_t fftsize) +{ + smpl_t freq = samplerate / fftsize; + return freq * MAX(bin, 0); +} + +smpl_t +aubio_bintomidi (smpl_t bin, smpl_t samplerate, smpl_t fftsize) +{ + smpl_t midi = aubio_bintofreq (bin, samplerate, fftsize); + return aubio_freqtomidi (midi); +} + +smpl_t +aubio_freqtobin (smpl_t freq, smpl_t samplerate, smpl_t fftsize) +{ + smpl_t bin = fftsize / samplerate; + return MAX(freq, 0) * bin; +} + +smpl_t +aubio_miditobin (smpl_t midi, smpl_t samplerate, smpl_t fftsize) +{ + smpl_t freq = aubio_miditofreq (midi); + return aubio_freqtobin (freq, samplerate, fftsize); +} + +uint_t +aubio_is_power_of_two (uint_t a) +{ + if ((a & (a - 1)) == 0) { + return 1; + } else { + return 0; + } +} + +uint_t +aubio_next_power_of_two (uint_t a) +{ + uint_t i = 1; + while (i < a) i <<= 1; + return i; +} + +uint_t +aubio_power_of_two_order (uint_t a) +{ + int order = 0; + int temp = aubio_next_power_of_two(a); + while (temp >>= 1) { + ++order; + } + return order; +} + +smpl_t +aubio_db_spl (const fvec_t * o) +{ + return 10. * LOG10 (aubio_level_lin (o)); +} + +uint_t +aubio_silence_detection (const fvec_t * o, smpl_t threshold) +{ + return (aubio_db_spl (o) < threshold); +} + +smpl_t +aubio_level_detection (const fvec_t * o, smpl_t threshold) +{ + smpl_t db_spl = aubio_db_spl (o); + if (db_spl < threshold) { + return 1.; + } else { + return db_spl; + } +} + +smpl_t +aubio_zero_crossing_rate (fvec_t * input) +{ + uint_t j; + uint_t zcr = 0; + for (j = 1; j < input->length; j++) { + // previous was strictly negative + if (input->data[j - 1] < 0.) { + // current is positive or null + if (input->data[j] >= 0.) { + zcr += 1; + } + // previous was positive or null + } else { + // current is strictly negative + if (input->data[j] < 0.) { + zcr += 1; + } + } + } + return zcr / (smpl_t) input->length; +} + +void +aubio_autocorr (const fvec_t * input, fvec_t * output) +{ + uint_t i, j, length = input->length; + smpl_t *data, *acf; + smpl_t tmp = 0; + data = input->data; + acf = output->data; + for (i = 0; i < length; i++) { + tmp = 0.; + for (j = i; j < length; j++) { + tmp += data[j - i] * data[j]; + } + acf[i] = tmp / (smpl_t) (length - i); + } +} + +void +aubio_cleanup (void) +{ +#ifdef HAVE_FFTW3F + fftwf_cleanup (); +#else +#ifdef HAVE_FFTW3 + fftw_cleanup (); +#endif +#endif +} diff --git a/aubio/src/mathutils.h b/aubio/src/mathutils.h new file mode 100644 index 00000000..4336d7ec --- /dev/null +++ b/aubio/src/mathutils.h @@ -0,0 +1,338 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Various math functions + + \example test-mathutils.c + \example test-mathutils-window.c + + */ + +#ifndef AUBIO_MATHUTILS_H +#define AUBIO_MATHUTILS_H + +#include "fvec.h" +#include "musicutils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** compute the mean of a vector + + \param s vector to compute mean from + \return the mean of `v` + +*/ +smpl_t fvec_mean (fvec_t * s); + +/** find the max of a vector + + \param s vector to get the max from + + \return the value of the minimum of v + +*/ +smpl_t fvec_max (fvec_t * s); + +/** find the min of a vector + + \param s vector to get the min from + + \return the value of the maximum of v + +*/ +smpl_t fvec_min (fvec_t * s); + +/** find the index of the min of a vector + + \param s vector to get the index from + + \return the index of the minimum element of v + +*/ +uint_t fvec_min_elem (fvec_t * s); + +/** find the index of the max of a vector + + \param s vector to get the index from + + \return the index of the maximum element of v + +*/ +uint_t fvec_max_elem (fvec_t * s); + +/** swap the left and right halves of a vector + + This function swaps the left part of the signal with the right part of the +signal. Therefore + + \f$ a[0], a[1], ..., a[\frac{N}{2}], a[\frac{N}{2}+1], ..., a[N-1], a[N] \f$ + + becomes + + \f$ a[\frac{N}{2}+1], ..., a[N-1], a[N], a[0], a[1], ..., a[\frac{N}{2}] \f$ + + This operation, known as 'fftshift' in the Matlab Signal Processing Toolbox, +can be used before computing the FFT to simplify the phase relationship of the +resulting spectrum. See Amalia de Götzen's paper referred to above. + +*/ +void fvec_shift (fvec_t * v); + +/** swap the left and right halves of a vector + + This function swaps the left part of the signal with the right part of the +signal. Therefore + + \f$ a[0], a[1], ..., a[\frac{N}{2}], a[\frac{N}{2}+1], ..., a[N-1], a[N] \f$ + + becomes + + \f$ a[\frac{N}{2}+1], ..., a[N-1], a[N], a[0], a[1], ..., a[\frac{N}{2}] \f$ + + This operation, known as 'ifftshift' in the Matlab Signal Processing Toolbox, +can be used after computing the inverse FFT to simplify the phase relationship +of the resulting spectrum. See Amalia de Götzen's paper referred to above. + +*/ +void fvec_ishift (fvec_t * v); + +/** push a new element to the end of a vector, erasing the first element and + * sliding all others + + \param in vector to push to + \param new_elem new_element to add at the end of the vector + + In numpy words, this is equivalent to: in = np.concatenate([in, [new_elem]])[1:] + +*/ +void fvec_push(fvec_t *in, smpl_t new_elem); + +/** compute the sum of all elements of a vector + + \param v vector to compute the sum of + + \return the sum of v + +*/ +smpl_t fvec_sum (fvec_t * v); + +/** compute the High Frequency Content of a vector + + The High Frequency Content is defined as \f$ \sum_0^{N-1} (k+1) v[k] \f$. + + \param v vector to get the energy from + + \return the HFC of v + +*/ +smpl_t fvec_local_hfc (fvec_t * v); + +/** computes the p-norm of a vector + + Computes the p-norm of a vector for \f$ p = \alpha \f$ + + \f$ L^p = ||x||_p = (|x_1|^p + |x_2|^p + ... + |x_n|^p ) ^ \frac{1}{p} \f$ + + If p = 1, the result is the Manhattan distance. + + If p = 2, the result is the Euclidean distance. + + As p tends towards large values, \f$ L^p \f$ tends towards the maximum of the +input vector. + + References: + + - \f$L^p\f$ space on + Wikipedia + + \param v vector to compute norm from + \param p order of the computed norm + + \return the p-norm of v + +*/ +smpl_t fvec_alpha_norm (fvec_t * v, smpl_t p); + +/** alpha normalisation + + This function divides all elements of a vector by the p-norm as computed by +fvec_alpha_norm(). + + \param v vector to compute norm from + \param p order of the computed norm + +*/ +void fvec_alpha_normalise (fvec_t * v, smpl_t p); + +/** add a constant to each elements of a vector + + \param v vector to add constant to + \param c constant to add to v + +*/ +void fvec_add (fvec_t * v, smpl_t c); + +/** multiply each elements of a vector by a scalar + + \param v vector to add constant to + \param s constant to scale v with + +*/ +void fvec_mul (fvec_t * v, smpl_t s); + +/** remove the minimum value of the vector to each elements + + \param v vector to remove minimum from + +*/ +void fvec_min_removal (fvec_t * v); + +/** compute moving median threshold of a vector + + This function computes the moving median threshold value of at the given +position of a vector, taking the median among post elements before and up to +pre elements after pos. + + \param v input vector + \param tmp temporary vector of length post+1+pre + \param post length of causal part to take before pos + \param pre length of anti-causal part to take after pos + \param pos index to compute threshold for + + \return moving median threshold value + +*/ +smpl_t fvec_moving_thres (fvec_t * v, fvec_t * tmp, uint_t post, uint_t pre, + uint_t pos); + +/** apply adaptive threshold to a vector + + For each points at position p of an input vector, this function remove the +moving median threshold computed at p. + + \param v input vector + \param tmp temporary vector of length post+1+pre + \param post length of causal part to take before pos + \param pre length of anti-causal part to take after pos + +*/ +void fvec_adapt_thres (fvec_t * v, fvec_t * tmp, uint_t post, uint_t pre); + +/** returns the median of a vector + + The QuickSelect routine is based on the algorithm described in "Numerical +recipes in C", Second Edition, Cambridge University Press, 1992, Section 8.5, +ISBN 0-521-43108-5 + + This implementation of the QuickSelect routine is based on Nicolas +Devillard's implementation, available at http://ndevilla.free.fr/median/median/ +and in the Public Domain. + + \param v vector to get median from + + \return the median of v + +*/ +smpl_t fvec_median (fvec_t * v); + +/** finds exact peak index by quadratic interpolation + + See [Quadratic Interpolation of Spectral + Peaks](https://ccrma.stanford.edu/~jos/sasp/Quadratic_Peak_Interpolation.html), + by Julius O. Smith III + + \f$ p_{frac} = \frac{1}{2} \frac {x[p-1] - x[p+1]} {x[p-1] - 2 x[p] + x[p+1]} \in [ -.5, .5] \f$ + + \param x vector to get the interpolated peak position from + \param p index of the peak in vector `x` + \return \f$ p + p_{frac} \f$ exact peak position of interpolated maximum or minimum + +*/ +smpl_t fvec_quadratic_peak_pos (const fvec_t * x, uint_t p); + +/** finds magnitude of peak by quadratic interpolation + + See [Quadratic Interpolation of Spectral + Peaks](https://ccrma.stanford.edu/~jos/sasp/Quadratic_Peak_Interpolation.html), + by Julius O. Smith III + + \param x vector to get the magnitude of the interpolated peak position from + \param p index of the peak in vector `x` + \return magnitude of interpolated peak + +*/ +smpl_t fvec_quadratic_peak_mag (fvec_t * x, smpl_t p); + +/** Quadratic interpolation using Lagrange polynomial. + + Inspired from ``Comparison of interpolation algorithms in real-time sound +processing'', Vladimir Arnost, + + \param s0,s1,s2 are 3 consecutive samples of a curve + \param pf is the floating point index [0;2] + + \return \f$ s0 + (pf/2.)*((pf-3.)*s0-2.*(pf-2.)*s1+(pf-1.)*s2); \f$ + +*/ +smpl_t aubio_quadfrac (smpl_t s0, smpl_t s1, smpl_t s2, smpl_t pf); + +/** return 1 if v[p] is a peak and positive, 0 otherwise + + This function returns 1 if a peak is found at index p in the vector v. The +peak is defined as follows: + + - v[p] is positive + - v[p-1] < v[p] + - v[p] > v[p+1] + + \param v input vector + \param p position of supposed for peak + + \return 1 if a peak is found, 0 otherwise + +*/ +uint_t fvec_peakpick (const fvec_t * v, uint_t p); + +/** return 1 if a is a power of 2, 0 otherwise */ +uint_t aubio_is_power_of_two(uint_t a); + +/** return the next power of power of 2 greater than a */ +uint_t aubio_next_power_of_two(uint_t a); + +/** return the log2 factor of the given power of 2 value a */ +uint_t aubio_power_of_two_order(uint_t a); + +/** compute normalised autocorrelation function + + \param input vector to compute autocorrelation from + \param output vector to store autocorrelation function to + +*/ +void aubio_autocorr (const fvec_t * input, fvec_t * output); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_MATHUTILS_H */ diff --git a/aubio/src/musicutils.c b/aubio/src/musicutils.c new file mode 100644 index 00000000..14ef849e --- /dev/null +++ b/aubio/src/musicutils.c @@ -0,0 +1,85 @@ +/* + Copyright (C) 2018 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "musicutils.h" + +smpl_t +aubio_hztomel (smpl_t freq) +{ + const smpl_t lin_space = 3./200.; + const smpl_t split_hz = 1000.; + const smpl_t split_mel = split_hz * lin_space; + const smpl_t log_space = 27./LOG(6400/1000.); + if (freq < 0) { + AUBIO_WRN("hztomel: input frequency should be >= 0\n"); + return 0; + } + if (freq < split_hz) + { + return freq * lin_space; + } else { + return split_mel + log_space * LOG (freq / split_hz); + } + +} + +smpl_t +aubio_meltohz (smpl_t mel) +{ + const smpl_t lin_space = 200./3.; + const smpl_t split_hz = 1000.; + const smpl_t split_mel = split_hz / lin_space; + const smpl_t logSpacing = POW(6400/1000., 1/27.); + if (mel < 0) { + AUBIO_WRN("meltohz: input mel should be >= 0\n"); + return 0; + } + if (mel < split_mel) { + return lin_space * mel; + } else { + return split_hz * POW(logSpacing, mel - split_mel); + } +} + +smpl_t +aubio_hztomel_htk (smpl_t freq) +{ + const smpl_t split_hz = 700.; + const smpl_t log_space = 1127.; + if (freq < 0) { + AUBIO_WRN("hztomel_htk: input frequency should be >= 0\n"); + return 0; + } + return log_space * LOG (1 + freq / split_hz); +} + +smpl_t +aubio_meltohz_htk (smpl_t mel) +{ + const smpl_t split_hz = 700.; + const smpl_t log_space = 1./1127.; + if (mel < 0) { + AUBIO_WRN("meltohz_htk: input frequency should be >= 0\n"); + return 0; + } + return split_hz * ( EXP ( mel * log_space) - 1.); +} + diff --git a/aubio/src/musicutils.h b/aubio/src/musicutils.h new file mode 100644 index 00000000..af222e5b --- /dev/null +++ b/aubio/src/musicutils.h @@ -0,0 +1,270 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** @file + * various functions useful in audio signal processing + */ + +#ifndef AUBIO_MUSICUTILS_H +#define AUBIO_MUSICUTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** create window + + \param window_type type of the window to create + \param size length of the window to create (see fvec_set_window()) + +*/ +fvec_t *new_aubio_window (char_t * window_type, uint_t size); + +/** set elements of a vector to window coefficients + + \param window exsting ::fvec_t to use + \param window_type type of the window to create + + List of available window types: "rectangle", "hamming", "hanning", + "hanningz", "blackman", "blackman_harris", "gaussian", "welch", "parzen", + "default". + + "default" is equivalent to "hanningz". + + References: + + - Window +function on Wikipedia + - Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional (?) +implementations of a phase vocoder: the tricks of the trade. In Proceedings of +the International Conference on Digital Audio Effects (DAFx-00), pages 37–44, +Uni- versity of Verona, Italy, 2000. + ( + pdf) + + */ +uint_t fvec_set_window (fvec_t * window, char_t * window_type); + +/** compute the principal argument + + This function maps the input phase to its corresponding value wrapped in the +range \f$ [-\pi, \pi] \f$. + + \param phase unwrapped phase to map to the unit circle + + \return equivalent phase wrapped to the unit circle + +*/ +smpl_t aubio_unwrap2pi (smpl_t phase); + +/** convert frequency bin to midi value */ +smpl_t aubio_bintomidi (smpl_t bin, smpl_t samplerate, smpl_t fftsize); + +/** convert midi value to frequency bin */ +smpl_t aubio_miditobin (smpl_t midi, smpl_t samplerate, smpl_t fftsize); + +/** convert frequency bin to frequency (Hz) */ +smpl_t aubio_bintofreq (smpl_t bin, smpl_t samplerate, smpl_t fftsize); + +/** convert frequency (Hz) to frequency bin */ +smpl_t aubio_freqtobin (smpl_t freq, smpl_t samplerate, smpl_t fftsize); + +/** convert frequency (Hz) to mel + + \param freq input frequency, in Hz + + \return output mel + + Converts a scalar from the frequency domain to the mel scale using Slaney + Auditory Toolbox's implementation: + + If \f$ f < 1000 \f$, \f$ m = 3 f / 200 \f$. + + If \f$ f >= 1000 \f$, \f$ m = 1000 + 27 \frac{{ln}(f) - ln(1000))} + {{ln}(6400) - ln(1000)} + \f$ + + See also + -------- + + aubio_meltohz(), aubio_hztomel_htk(). + +*/ +smpl_t aubio_hztomel (smpl_t freq); + +/** convert mel to frequency (Hz) + + \param mel input mel + + \return output frequency, in Hz + + Converts a scalar from the mel scale to the frequency domain using Slaney + Auditory Toolbox's implementation: + + If \f$ f < 1000 \f$, \f$ f = 200 m/3 \f$. + + If \f$ f \geq 1000 \f$, \f$ f = 1000 + \left(\frac{6400}{1000}\right) + ^{\frac{m - 1000}{27}} \f$ + + See also + -------- + + aubio_hztomel(), aubio_meltohz_htk(). + + References + ---------- + + Malcolm Slaney, *Auditory Toolbox Version 2, Technical Report #1998-010* + https://engineering.purdue.edu/~malcolm/interval/1998-010/ + +*/ +smpl_t aubio_meltohz (smpl_t mel); + +/** convert frequency (Hz) to mel + + \param freq input frequency, in Hz + + \return output mel + + Converts a scalar from the frequency domain to the mel scale, using the + equation defined by O'Shaughnessy, as implemented in the HTK speech + recognition toolkit: + + \f$ m = 1127 + ln(1 + \frac{f}{700}) \f$ + + See also + -------- + + aubio_meltohz_htk(), aubio_hztomel(). + + References + ---------- + + Douglas O'Shaughnessy (1987). *Speech communication: human and machine*. + Addison-Wesley. p. 150. ISBN 978-0-201-16520-3. + + HTK Speech Recognition Toolkit: http://htk.eng.cam.ac.uk/ + + */ +smpl_t aubio_hztomel_htk (smpl_t freq); + +/** convert mel to frequency (Hz) + + \param mel input mel + + \return output frequency, in Hz + + Converts a scalar from the mel scale to the frequency domain, using the + equation defined by O'Shaughnessy, as implemented in the HTK speech + recognition toolkit: + + \f$ f = 700 * {e}^\left(\frac{f}{1127} - 1\right) \f$ + + See also + -------- + + aubio_hztomel_htk(), aubio_meltohz(). + +*/ +smpl_t aubio_meltohz_htk (smpl_t mel); + +/** convert frequency (Hz) to midi value (0-128) */ +smpl_t aubio_freqtomidi (smpl_t freq); + +/** convert midi value (0-128) to frequency (Hz) */ +smpl_t aubio_miditofreq (smpl_t midi); + +/** clean up cached memory at the end of program + + This function should be used at the end of programs to purge all cached + memory. So far it is only useful to clean FFTW's cache. + +*/ +void aubio_cleanup (void); + +/** zero-crossing rate (ZCR) + + The zero-crossing rate is the number of times a signal changes sign, + divided by the length of this signal. + + \param v vector to compute ZCR from + + \return zero-crossing rate of v + +*/ +smpl_t aubio_zero_crossing_rate (fvec_t * v); + +/** compute sound level on a linear scale + + This gives the average of the square amplitudes. + + \param v vector to compute level from + + \return level of v + +*/ +smpl_t aubio_level_lin (const fvec_t * v); + +/** compute sound pressure level (SPL) in dB + + This quantity is often wrongly called 'loudness'. + + This gives ten times the log10 of the average of the square amplitudes. + + \param v vector to compute dB SPL from + + \return level of v in dB SPL + +*/ +smpl_t aubio_db_spl (const fvec_t * v); + +/** check if buffer level in dB SPL is under a given threshold + + \param v vector to get level from + \param threshold threshold in dB SPL + + \return 1 if level is under the given threshold, 0 otherwise + +*/ +uint_t aubio_silence_detection (const fvec_t * v, smpl_t threshold); + +/** get buffer level if level >= threshold, 1. otherwise + + \param v vector to get level from + \param threshold threshold in dB SPL + + \return level in dB SPL if level >= threshold, 1. otherwise + +*/ +smpl_t aubio_level_detection (const fvec_t * v, smpl_t threshold); + +/** clamp the values of a vector within the range [-abs(max), abs(max)] + + \param in vector to clamp + \param absmax maximum value over which input vector elements should be clamped + +*/ +void fvec_clamp(fvec_t *in, smpl_t absmax); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_MUSICUTILS_H */ diff --git a/aubio/src/notes/notes.h b/aubio/src/notes/notes.h new file mode 100644 index 00000000..f256be31 --- /dev/null +++ b/aubio/src/notes/notes.h @@ -0,0 +1,143 @@ +/* + Copyright (C) 2003-2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Note detection object + +*/ + +#ifndef _AUBIO_NOTES_H +#define _AUBIO_NOTES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** notes detection object */ +typedef struct _aubio_notes_t aubio_notes_t; + +/** create notes detection object + + \param method notes detection type as specified in specdesc.h + \param buf_size buffer size for phase vocoder + \param hop_size hop size for phase vocoder + \param samplerate sampling rate of the input signal + + \return newly created ::aubio_notes_t + +*/ +aubio_notes_t * new_aubio_notes (const char_t * method, + uint_t buf_size, uint_t hop_size, uint_t samplerate); + +/** delete notes detection object + + \param o notes detection object to delete + +*/ +void del_aubio_notes(aubio_notes_t * o); + +/** execute note detection on an input signal frame + + \param o note detection object as returned by new_aubio_notes() + \param input input signal of size [hop_size] + \param output output notes, fvec of length 3 + + The notes output is a vector of length 3 containing: + - 0. the midi note value, or 0 if no note was found + - 1. the note velocity + - 2. the midi note to turn off + +*/ +void aubio_notes_do (aubio_notes_t *o, const fvec_t * input, fvec_t * output); + +/** set notes detection silence threshold + + \param o notes detection object as returned by new_aubio_notes() + \param silence new silence detection threshold + + \return 0 on success, non-zero otherwise + +*/ +uint_t aubio_notes_set_silence(aubio_notes_t * o, smpl_t silence); + +/** get notes detection silence threshold + + \param o notes detection object as returned by new_aubio_notes() + + \return current silence threshold + +*/ +smpl_t aubio_notes_get_silence(const aubio_notes_t * o); + +/** get notes detection minimum inter-onset interval, in millisecond + + \param o notes detection object as returned by new_aubio_notes() + + \return current minimum inter onset interval + + */ +smpl_t aubio_notes_get_minioi_ms(const aubio_notes_t *o); + +/** set notes detection minimum inter-onset interval, in millisecond + + \param o notes detection object as returned by new_aubio_notes() + \param minioi_ms new inter-onset interval + + \return 0 on success, non-zero otherwise + +*/ +uint_t aubio_notes_set_minioi_ms (aubio_notes_t *o, smpl_t minioi_ms); + +/** get notes object release drop level, in dB + + \param o notes detection object as returned by new_aubio_notes() + + \return current release drop level, in dB + + */ +smpl_t aubio_notes_get_release_drop (const aubio_notes_t *o); + +/** set note release drop level, in dB + + This function sets the release_drop_level parameter, in dB. When a new note + is found, the current level in dB is measured. If the measured level drops + under that initial level - release_drop_level, then a note-off will be + emitted. + + Defaults to `10`, in dB. + + \note This parameter was added in version `0.4.8`. Results obtained with + earlier versions can be reproduced by setting this value to `100`, so that + note-off will not be played until the next note. + + \param o notes detection object as returned by new_aubio_notes() + \param release_drop new release drop level, in dB + + \return 0 on success, non-zero otherwise + +*/ +uint_t aubio_notes_set_release_drop (aubio_notes_t *o, smpl_t release_drop); + +#ifdef __cplusplus +} +#endif + +#endif /* _AUBIO_NOTES_H */ diff --git a/aubio/src/onset/onset.c b/aubio/src/onset/onset.c new file mode 100644 index 00000000..6123d51a --- /dev/null +++ b/aubio/src/onset/onset.c @@ -0,0 +1,355 @@ +/* + Copyright (C) 2006-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "cvec.h" +#include "spectral/specdesc.h" +#include "spectral/phasevoc.h" +#include "spectral/awhitening.h" +#include "onset/peakpicker.h" +#include "mathutils.h" +#include "onset/onset.h" + +void aubio_onset_default_parameters (aubio_onset_t *o, const char_t * method); + +/** structure to store object state */ +struct _aubio_onset_t { + aubio_pvoc_t * pv; /**< phase vocoder */ + aubio_specdesc_t * od; /**< spectral descriptor */ + aubio_peakpicker_t * pp; /**< peak picker */ + cvec_t * fftgrain; /**< phase vocoder output */ + fvec_t * desc; /**< spectral description */ + smpl_t silence; /**< silence threhsold */ + uint_t minioi; /**< minimum inter onset interval */ + uint_t delay; /**< constant delay, in samples, removed from detected onset times */ + uint_t samplerate; /**< sampling rate of the input signal */ + uint_t hop_size; /**< number of samples between two runs */ + + uint_t total_frames; /**< total number of frames processed since the beginning */ + uint_t last_onset; /**< last detected onset location, in frames */ + + uint_t apply_compression; + smpl_t lambda_compression; + uint_t apply_awhitening; /**< apply adaptive spectral whitening */ + aubio_spectral_whitening_t *spectral_whitening; +}; + +/* execute onset detection function on iput buffer */ +void aubio_onset_do (aubio_onset_t *o, const fvec_t * input, fvec_t * onset) +{ + smpl_t isonset = 0; + aubio_pvoc_do (o->pv,input, o->fftgrain); + /* + if (apply_filtering) { + } + */ + if (o->apply_awhitening) { + aubio_spectral_whitening_do(o->spectral_whitening, o->fftgrain); + } + if (o->apply_compression) { + cvec_logmag(o->fftgrain, o->lambda_compression); + } + aubio_specdesc_do (o->od, o->fftgrain, o->desc); + aubio_peakpicker_do(o->pp, o->desc, onset); + isonset = onset->data[0]; + if (isonset > 0.) { + if (aubio_silence_detection(input, o->silence)==1) { + //AUBIO_DBG ("silent onset, not marking as onset\n"); + isonset = 0; + } else { + // we have an onset + uint_t new_onset = o->total_frames + (uint_t)ROUND(isonset * o->hop_size); + // check if last onset time was more than minioi ago + if (o->last_onset + o->minioi < new_onset) { + // start of file: make sure (new_onset - delay) >= 0 + if (o->last_onset > 0 && o->delay > new_onset) { + isonset = 0; + } else { + //AUBIO_DBG ("accepted detection, marking as onset\n"); + o->last_onset = MAX(o->delay, new_onset); + } + } else { + //AUBIO_DBG ("doubled onset, not marking as onset\n"); + isonset = 0; + } + } + } else { + // we are at the beginning of the file + if (o->total_frames <= o->delay) { + // and we don't find silence + if (aubio_silence_detection(input, o->silence) == 0) { + uint_t new_onset = o->total_frames; + if (o->total_frames == 0 || o->last_onset + o->minioi < new_onset) { + isonset = o->delay / o->hop_size; + o->last_onset = o->total_frames + o->delay; + } + } + } + } + onset->data[0] = isonset; + o->total_frames += o->hop_size; + return; +} + +uint_t aubio_onset_get_last (const aubio_onset_t *o) +{ + return o->last_onset - o->delay; +} + +smpl_t aubio_onset_get_last_s (const aubio_onset_t *o) +{ + return aubio_onset_get_last (o) / (smpl_t) (o->samplerate); +} + +smpl_t aubio_onset_get_last_ms (const aubio_onset_t *o) +{ + return aubio_onset_get_last_s (o) * 1000.; +} + +uint_t aubio_onset_set_awhitening (aubio_onset_t *o, uint_t enable) +{ + o->apply_awhitening = enable == 1 ? 1 : 0; + return AUBIO_OK; +} + +smpl_t aubio_onset_get_awhitening (aubio_onset_t *o) +{ + return o->apply_awhitening; +} + +uint_t aubio_onset_set_compression (aubio_onset_t *o, smpl_t lambda) +{ + if (lambda < 0.) { + return AUBIO_FAIL; + } + o->lambda_compression = lambda; + o->apply_compression = (o->lambda_compression > 0.) ? 1 : 0; + return AUBIO_OK; +} + +smpl_t aubio_onset_get_compression (aubio_onset_t *o) +{ + return o->apply_compression ? o->lambda_compression : 0; +} + +uint_t aubio_onset_set_silence(aubio_onset_t * o, smpl_t silence) { + o->silence = silence; + return AUBIO_OK; +} + +smpl_t aubio_onset_get_silence(const aubio_onset_t * o) { + return o->silence; +} + +uint_t aubio_onset_set_threshold(aubio_onset_t * o, smpl_t threshold) { + aubio_peakpicker_set_threshold(o->pp, threshold); + return AUBIO_OK; +} + +smpl_t aubio_onset_get_threshold(const aubio_onset_t * o) { + return aubio_peakpicker_get_threshold(o->pp); +} + +uint_t aubio_onset_set_minioi(aubio_onset_t * o, uint_t minioi) { + o->minioi = minioi; + return AUBIO_OK; +} + +uint_t aubio_onset_get_minioi(const aubio_onset_t * o) { + return o->minioi; +} + +uint_t aubio_onset_set_minioi_s(aubio_onset_t * o, smpl_t minioi) { + return aubio_onset_set_minioi (o, (uint_t)ROUND(minioi * o->samplerate)); +} + +smpl_t aubio_onset_get_minioi_s(const aubio_onset_t * o) { + return aubio_onset_get_minioi (o) / (smpl_t) o->samplerate; +} + +uint_t aubio_onset_set_minioi_ms(aubio_onset_t * o, smpl_t minioi) { + return aubio_onset_set_minioi_s (o, minioi / 1000.); +} + +smpl_t aubio_onset_get_minioi_ms(const aubio_onset_t * o) { + return aubio_onset_get_minioi_s (o) * 1000.; +} + +uint_t aubio_onset_set_delay(aubio_onset_t * o, uint_t delay) { + o->delay = delay; + return AUBIO_OK; +} + +uint_t aubio_onset_get_delay(const aubio_onset_t * o) { + return o->delay; +} + +uint_t aubio_onset_set_delay_s(aubio_onset_t * o, smpl_t delay) { + return aubio_onset_set_delay (o, delay * o->samplerate); +} + +smpl_t aubio_onset_get_delay_s(const aubio_onset_t * o) { + return aubio_onset_get_delay (o) / (smpl_t) o->samplerate; +} + +uint_t aubio_onset_set_delay_ms(aubio_onset_t * o, smpl_t delay) { + return aubio_onset_set_delay_s (o, delay / 1000.); +} + +smpl_t aubio_onset_get_delay_ms(const aubio_onset_t * o) { + return aubio_onset_get_delay_s (o) * 1000.; +} + +smpl_t aubio_onset_get_descriptor(const aubio_onset_t * o) { + return o->desc->data[0]; +} + +smpl_t aubio_onset_get_thresholded_descriptor(const aubio_onset_t * o) { + fvec_t * thresholded = aubio_peakpicker_get_thresholded_input(o->pp); + return thresholded->data[0]; +} + +/* Allocate memory for an onset detection */ +aubio_onset_t * new_aubio_onset (const char_t * onset_mode, + uint_t buf_size, uint_t hop_size, uint_t samplerate) +{ + aubio_onset_t * o = AUBIO_NEW(aubio_onset_t); + + /* check parameters are valid */ + if ((sint_t)hop_size < 1) { + AUBIO_ERR("onset: got hop_size %d, but can not be < 1\n", hop_size); + goto beach; + } else if ((sint_t)buf_size < 2) { + AUBIO_ERR("onset: got buffer_size %d, but can not be < 2\n", buf_size); + goto beach; + } else if (buf_size < hop_size) { + AUBIO_ERR("onset: hop size (%d) is larger than win size (%d)\n", hop_size, buf_size); + goto beach; + } else if ((sint_t)samplerate < 1) { + AUBIO_ERR("onset: samplerate (%d) can not be < 1\n", samplerate); + goto beach; + } + + /* store creation parameters */ + o->samplerate = samplerate; + o->hop_size = hop_size; + + /* allocate memory */ + o->pv = new_aubio_pvoc(buf_size, o->hop_size); + o->pp = new_aubio_peakpicker(); + o->od = new_aubio_specdesc(onset_mode,buf_size); + o->fftgrain = new_cvec(buf_size); + o->desc = new_fvec(1); + o->spectral_whitening = new_aubio_spectral_whitening(buf_size, hop_size, samplerate); + + if (!o->pv || !o->pp || !o->od || !o->fftgrain + || !o->desc || !o->spectral_whitening) + goto beach; + + /* initialize internal variables */ + aubio_onset_set_default_parameters (o, onset_mode); + + aubio_onset_reset(o); + return o; + +beach: + del_aubio_onset(o); + return NULL; +} + +void aubio_onset_reset (aubio_onset_t *o) { + o->last_onset = 0; + o->total_frames = 0; +} + +uint_t aubio_onset_set_default_parameters (aubio_onset_t * o, const char_t * onset_mode) +{ + uint_t ret = AUBIO_OK; + /* set some default parameter */ + aubio_onset_set_threshold (o, 0.3); + aubio_onset_set_delay (o, 4.3 * o->hop_size); + aubio_onset_set_minioi_ms (o, 50.); + aubio_onset_set_silence (o, -70.); + // disable spectral whitening + aubio_onset_set_awhitening (o, 0); + // disable logarithmic magnitude + aubio_onset_set_compression (o, 0.); + + /* method specific optimisations */ + if (strcmp (onset_mode, "energy") == 0) { + } else if (strcmp (onset_mode, "hfc") == 0 || strcmp (onset_mode, "default") == 0) { + aubio_onset_set_threshold (o, 0.058); + aubio_onset_set_compression (o, 1.); + } else if (strcmp (onset_mode, "complexdomain") == 0 + || strcmp (onset_mode, "complex") == 0) { + aubio_onset_set_delay (o, 4.6 * o->hop_size); + aubio_onset_set_threshold (o, 0.15); + aubio_onset_set_awhitening(o, 1); + aubio_onset_set_compression (o, 1.); + } else if (strcmp (onset_mode, "phase") == 0) { + o->apply_compression = 0; + aubio_onset_set_awhitening (o, 0); + } else if (strcmp (onset_mode, "wphase") == 0) { + // use defaults for now + } else if (strcmp (onset_mode, "mkl") == 0) { + aubio_onset_set_threshold (o, 0.05); + aubio_onset_set_awhitening(o, 1); + aubio_onset_set_compression (o, 0.02); + } else if (strcmp (onset_mode, "kl") == 0) { + aubio_onset_set_threshold (o, 0.35); + aubio_onset_set_awhitening(o, 1); + aubio_onset_set_compression (o, 0.02); + } else if (strcmp (onset_mode, "specflux") == 0) { + aubio_onset_set_threshold (o, 0.18); + aubio_onset_set_awhitening(o, 1); + aubio_spectral_whitening_set_relax_time(o->spectral_whitening, 100); + aubio_spectral_whitening_set_floor(o->spectral_whitening, 1.); + aubio_onset_set_compression (o, 10.); + } else if (strcmp (onset_mode, "specdiff") == 0) { + } else if (strcmp (onset_mode, "old_default") == 0) { + // used to reproduce results obtained with the previous version + aubio_onset_set_threshold (o, 0.3); + aubio_onset_set_minioi_ms (o, 20.); + aubio_onset_set_compression (o, 0.); + } else { + AUBIO_WRN("onset: unknown spectral descriptor type %s, " + "using default parameters.\n", onset_mode); + ret = AUBIO_FAIL; + } + return ret; +} + +void del_aubio_onset (aubio_onset_t *o) +{ + if (o->spectral_whitening) + del_aubio_spectral_whitening(o->spectral_whitening); + if (o->od) + del_aubio_specdesc(o->od); + if (o->pp) + del_aubio_peakpicker(o->pp); + if (o->pv) + del_aubio_pvoc(o->pv); + if (o->desc) + del_fvec(o->desc); + if (o->fftgrain) + del_cvec(o->fftgrain); + AUBIO_FREE(o); +} diff --git a/aubio/src/onset/onset.h b/aubio/src/onset/onset.h new file mode 100644 index 00000000..b115e6aa --- /dev/null +++ b/aubio/src/onset/onset.h @@ -0,0 +1,347 @@ +/* + Copyright (C) 2006-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Onset detection object + + The following routines compute the onset detection function and detect peaks + in these functions. When onsets are found above a given silence threshold, + and after a minimum inter-onset interval, the output vector returned by + aubio_onset_do() is filled with `1`. Otherwise, the output vector remains + `0`. + + The peak-picking threshold, the silence threshold, and the minimum + inter-onset interval can be adjusted during the execution of the + aubio_onset_do routine using the corresponding functions. + + \example onset/test-onset.c + \example examples/aubioonset.c + \example examples/aubionotes.c + +*/ + + +#ifndef AUBIO_ONSET_H +#define AUBIO_ONSET_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** onset detection object */ +typedef struct _aubio_onset_t aubio_onset_t; + +/** create onset detection object + + \param method onset detection type as specified in specdesc.h + \param buf_size buffer size for phase vocoder + \param hop_size hop size for phase vocoder + \param samplerate sampling rate of the input signal + + \return newly created ::aubio_onset_t + +*/ +aubio_onset_t * new_aubio_onset (const char_t * method, + uint_t buf_size, uint_t hop_size, uint_t samplerate); + +/** execute onset detection + + \param o onset detection object as returned by new_aubio_onset() + \param input new audio vector of length hop_size + \param onset output vector of length 1, containing 0 if no onset was found, + and a value equal or greater than 1 otherwise + + When no onset was detected, the first element of the output vector `onset` + is set to 0. + + When an onset is found, the first element of the output vector `onset` is set + to `offset = 1 + a` where `a` is a number in the range`[0, 1]`. + + The final onset detection time, in samples, can be obtained with + aubio_onset_get_last(). It can also be derived from `offset` as + follows: + + \code + t = total_frames + offset * hop_size - delay + \endcode + + where `total_frames` is the total number of frames processed so far, and + `delay` is the current delay of the onset object, as returned by + aubio_onset_get_delay(). + +*/ +void aubio_onset_do (aubio_onset_t *o, const fvec_t * input, fvec_t * onset); + +/** get the time of the latest onset detected, in samples + + \param o onset detection object as returned by new_aubio_onset() + + \return onset detection timestamps (in samples) + +*/ +uint_t aubio_onset_get_last (const aubio_onset_t *o); + +/** get the time of the latest onset detected, in seconds + + \param o onset detection object as returned by new_aubio_onset() + + \return onset detection timestamps (in seconds) + +*/ +smpl_t aubio_onset_get_last_s (const aubio_onset_t *o); + +/** get the time of the latest onset detected, in milliseconds + + \param o onset detection object as returned by new_aubio_onset() + + \return onset detection timestamps (in milliseconds) + +*/ +smpl_t aubio_onset_get_last_ms (const aubio_onset_t *o); + +/** set onset detection adaptive whitening + + \param o onset detection object as returned by new_aubio_onset() + \param enable 1 to enable, 0 to disable + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_onset_set_awhitening(aubio_onset_t * o, uint_t enable); + +/** get onset detection adaptive whitening + + \param o onset detection object as returned by new_aubio_onset() + + \return 1 if enabled, 0 otherwise + +*/ +smpl_t aubio_onset_get_awhitening(aubio_onset_t * o); + +/** set or disable log compression + + \param o onset detection object as returned by new_aubio_onset() + \param lambda logarithmic compression factor, 0 to disable + + \return 0 if successful, 1 otherwise + + */ +uint_t aubio_onset_set_compression(aubio_onset_t *o, smpl_t lambda); + +/** get onset detection log compression + + \param o onset detection object as returned by new_aubio_onset() + + \returns 0 if disabled, compression factor otherwise + + */ +smpl_t aubio_onset_get_compression(aubio_onset_t *o); + +/** set onset detection silence threshold + + \param o onset detection object as returned by new_aubio_onset() + \param silence new silence detection threshold + +*/ +uint_t aubio_onset_set_silence(aubio_onset_t * o, smpl_t silence); + +/** get onset detection silence threshold + + \param o onset detection object as returned by new_aubio_onset() + + \return current silence threshold + +*/ +smpl_t aubio_onset_get_silence(const aubio_onset_t * o); + +/** get onset detection function + + \param o onset detection object as returned by new_aubio_onset() + \return the current value of the descriptor + +*/ +smpl_t aubio_onset_get_descriptor (const aubio_onset_t *o); + +/** get thresholded onset detection function + + \param o onset detection object as returned by new_aubio_onset() + \return the value of the thresholded descriptor + +*/ +smpl_t aubio_onset_get_thresholded_descriptor (const aubio_onset_t *o); + +/** set onset detection peak picking threshold + + \param o onset detection object as returned by new_aubio_onset() + \param threshold new peak-picking threshold + +*/ +uint_t aubio_onset_set_threshold(aubio_onset_t * o, smpl_t threshold); + +/** set minimum inter onset interval in samples + + \param o onset detection object as returned by new_aubio_onset() + \param minioi minimum interval between two consecutive onsets (in + samples) + +*/ +uint_t aubio_onset_set_minioi(aubio_onset_t * o, uint_t minioi); + +/** set minimum inter onset interval in seconds + + \param o onset detection object as returned by new_aubio_onset() + \param minioi minimum interval between two consecutive onsets (in + seconds) + +*/ +uint_t aubio_onset_set_minioi_s(aubio_onset_t * o, smpl_t minioi); + +/** set minimum inter onset interval in milliseconds + + \param o onset detection object as returned by new_aubio_onset() + \param minioi minimum interval between two consecutive onsets (in + milliseconds) + +*/ +uint_t aubio_onset_set_minioi_ms(aubio_onset_t * o, smpl_t minioi); + +/** set delay in samples + + \param o onset detection object as returned by new_aubio_onset() + \param delay constant system delay to take back from detection time + (in samples) + +*/ +uint_t aubio_onset_set_delay(aubio_onset_t * o, uint_t delay); + +/** set delay in seconds + + \param o onset detection object as returned by new_aubio_onset() + \param delay constant system delay to take back from detection time + (in seconds) + +*/ +uint_t aubio_onset_set_delay_s(aubio_onset_t * o, smpl_t delay); + +/** set delay in milliseconds + + \param o onset detection object as returned by new_aubio_onset() + \param delay constant system delay to take back from detection time + (in milliseconds) + +*/ +uint_t aubio_onset_set_delay_ms(aubio_onset_t * o, smpl_t delay); + +/** get minimum inter onset interval in samples + + \param o onset detection object as returned by new_aubio_onset() + \return minimum interval between two consecutive onsets (in + samples) + +*/ +uint_t aubio_onset_get_minioi(const aubio_onset_t * o); + +/** get minimum inter onset interval in seconds + + \param o onset detection object as returned by new_aubio_onset() + \return minimum interval between two consecutive onsets (in + seconds) + +*/ +smpl_t aubio_onset_get_minioi_s(const aubio_onset_t * o); + +/** get minimum inter onset interval in milliseconds + + \param o onset detection object as returned by new_aubio_onset() + \return minimum interval between two consecutive onsets (in + milliseconds) + +*/ +smpl_t aubio_onset_get_minioi_ms(const aubio_onset_t * o); + +/** get delay in samples + + \param o onset detection object as returned by new_aubio_onset() + \return constant system delay to take back from detection time + (in samples) + +*/ +uint_t aubio_onset_get_delay(const aubio_onset_t * o); + +/** get delay in seconds + + \param o onset detection object as returned by new_aubio_onset() + \return constant system delay to take back from detection time + (in seconds) + +*/ +smpl_t aubio_onset_get_delay_s(const aubio_onset_t * o); + +/** get delay in milliseconds + + \param o onset detection object as returned by new_aubio_onset() + \return constant system delay to take back from detection time + (in milliseconds) + +*/ +smpl_t aubio_onset_get_delay_ms(const aubio_onset_t * o); + +/** get onset peak picking threshold + + \param o onset detection object as returned by new_aubio_onset() + \return current onset detection threshold + +*/ +smpl_t aubio_onset_get_threshold(const aubio_onset_t * o); + +/** set default parameters + + \param o onset detection object as returned by new_aubio_onset() + \param onset_mode detection mode to adjust + + This function is called at the end of new_aubio_onset(). + + */ +uint_t aubio_onset_set_default_parameters (aubio_onset_t * o, const char_t * onset_mode); + +/** reset onset detection + + \param o onset detection object as returned by new_aubio_onset() + + Reset current time and last onset to 0. + + This function is called at the end of new_aubio_onset(). + + */ +void aubio_onset_reset(aubio_onset_t * o); + +/** delete onset detection object + + \param o onset detection object to delete + +*/ +void del_aubio_onset(aubio_onset_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_ONSET_H */ diff --git a/aubio/src/onset/peakpicker.c b/aubio/src/onset/peakpicker.c new file mode 100644 index 00000000..fa1ee38e --- /dev/null +++ b/aubio/src/onset/peakpicker.c @@ -0,0 +1,199 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "mathutils.h" +#include "lvec.h" +#include "temporal/filter.h" +#include "temporal/biquad.h" +#include "onset/peakpicker.h" + +/** function pointer to thresholding function */ +typedef smpl_t (*aubio_thresholdfn_t)(fvec_t *input); +/** function pointer to peak-picking function */ +typedef uint_t (*aubio_pickerfn_t)(fvec_t *input, uint_t pos); + +/** set peak picker thresholding function */ +uint_t aubio_peakpicker_set_thresholdfn(aubio_peakpicker_t * p, aubio_thresholdfn_t thresholdfn); +/** get peak picker thresholding function */ +aubio_thresholdfn_t aubio_peakpicker_get_thresholdfn(aubio_peakpicker_t * p); + +/* peak picking parameters, default values in brackets + * + * [<----post----|--pre-->] + * .................|............. + * time-> ^now + */ +struct _aubio_peakpicker_t +{ + /** thresh: offset threshold [0.033 or 0.01] */ + smpl_t threshold; + /** win_post: median filter window length (causal part) [8] */ + uint_t win_post; + /** pre: median filter window (anti-causal part) [post-1] */ + uint_t win_pre; + /** threshfn: name or handle of fn for computing adaptive threshold [median] */ + aubio_thresholdfn_t thresholdfn; + /** picker: name or handle of fn for picking event times [peakpick] */ + aubio_pickerfn_t pickerfn; + + /** biquad lowpass filter */ + aubio_filter_t *biquad; + /** original onsets */ + fvec_t *onset_keep; + /** modified onsets */ + fvec_t *onset_proc; + /** peak picked window [3] */ + fvec_t *onset_peek; + /** thresholded function */ + fvec_t *thresholded; + /** scratch pad for biquad and median */ + fvec_t *scratch; + + /** \bug should be used to calculate filter coefficients */ + /* cutoff: low-pass filter cutoff [0.34, 1] */ + /* smpl_t cutoff; */ + + /* not used anymore */ + /* time precision [512/44100 winlength/samplerate, fs/buffer_size */ + /* smpl_t tau; */ + /* alpha: normalisation exponent [9] */ + /* smpl_t alpha; */ +}; + + +/** modified version for real time, moving mean adaptive threshold this method + * is slightly more permissive than the offline one, and yelds to an increase + * of false positives. best */ +void +aubio_peakpicker_do (aubio_peakpicker_t * p, fvec_t * onset, fvec_t * out) +{ + fvec_t *onset_keep = p->onset_keep; + fvec_t *onset_proc = p->onset_proc; + fvec_t *onset_peek = p->onset_peek; + fvec_t *thresholded = p->thresholded; + fvec_t *scratch = p->scratch; + smpl_t mean = 0., median = 0.; + uint_t j = 0; + + /* push new novelty to the end */ + fvec_push(onset_keep, onset->data[0]); + /* store a copy */ + fvec_copy(onset_keep, onset_proc); + + /* filter this copy */ + aubio_filter_do_filtfilt (p->biquad, onset_proc, scratch); + + /* calculate mean and median for onset_proc */ + mean = fvec_mean (onset_proc); + + /* copy to scratch and compute its median */ + fvec_copy(onset_proc, scratch); + median = p->thresholdfn (scratch); + + /* shift peek array */ + for (j = 0; j < 3 - 1; j++) + onset_peek->data[j] = onset_peek->data[j + 1]; + /* calculate new tresholded value */ + thresholded->data[0] = + onset_proc->data[p->win_post] - median - mean * p->threshold; + onset_peek->data[2] = thresholded->data[0]; + out->data[0] = (p->pickerfn) (onset_peek, 1); + if (out->data[0]) { + out->data[0] = fvec_quadratic_peak_pos (onset_peek, 1); + } +} + +/** this method returns the current value in the pick peaking buffer + * after smoothing + */ +fvec_t * +aubio_peakpicker_get_thresholded_input (aubio_peakpicker_t * p) +{ + return p->thresholded; +} + +uint_t +aubio_peakpicker_set_threshold (aubio_peakpicker_t * p, smpl_t threshold) +{ + p->threshold = threshold; + return AUBIO_OK; +} + +smpl_t +aubio_peakpicker_get_threshold (aubio_peakpicker_t * p) +{ + return p->threshold; +} + +uint_t +aubio_peakpicker_set_thresholdfn (aubio_peakpicker_t * p, + aubio_thresholdfn_t thresholdfn) +{ + p->thresholdfn = thresholdfn; + return AUBIO_OK; +} + +aubio_thresholdfn_t +aubio_peakpicker_get_thresholdfn (aubio_peakpicker_t * p) +{ + return (aubio_thresholdfn_t) (p->thresholdfn); +} + +aubio_peakpicker_t * +new_aubio_peakpicker (void) +{ + aubio_peakpicker_t *t = AUBIO_NEW (aubio_peakpicker_t); + t->threshold = 0.1; /* 0.0668; 0.33; 0.082; 0.033; */ + t->win_post = 5; + t->win_pre = 1; + + t->thresholdfn = (aubio_thresholdfn_t) (fvec_median); /* (fvec_mean); */ + t->pickerfn = (aubio_pickerfn_t) (fvec_peakpick); + + t->scratch = new_fvec (t->win_post + t->win_pre + 1); + t->onset_keep = new_fvec (t->win_post + t->win_pre + 1); + t->onset_proc = new_fvec (t->win_post + t->win_pre + 1); + t->onset_peek = new_fvec (3); + t->thresholded = new_fvec (1); + + /* cutoff: low-pass filter with cutoff reduced frequency at 0.34 + generated with octave butter function: [b,a] = butter(2, 0.34); + */ + t->biquad = new_aubio_filter_biquad (0.15998789, 0.31997577, 0.15998789, + // FIXME: broken since c9e20ca, revert for now + //-0.59488894, 0.23484048); + 0.23484048, 0); + + return t; +} + +void +del_aubio_peakpicker (aubio_peakpicker_t * p) +{ + del_aubio_filter (p->biquad); + del_fvec (p->onset_keep); + del_fvec (p->onset_proc); + del_fvec (p->onset_peek); + del_fvec (p->thresholded); + del_fvec (p->scratch); + AUBIO_FREE (p); +} diff --git a/aubio/src/onset/peakpicker.h b/aubio/src/onset/peakpicker.h new file mode 100644 index 00000000..dafaf915 --- /dev/null +++ b/aubio/src/onset/peakpicker.h @@ -0,0 +1,57 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Peak picking utilities function + + \example onset/test-peakpicker.c + +*/ + +#ifndef AUBIO_PEAKPICK_H +#define AUBIO_PEAKPICK_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** peak-picker structure */ +typedef struct _aubio_peakpicker_t aubio_peakpicker_t; + +/** peak-picker creation function */ +aubio_peakpicker_t * new_aubio_peakpicker(void); +/** real time peak picking function */ +void aubio_peakpicker_do(aubio_peakpicker_t * p, fvec_t * in, fvec_t * out); +/** destroy peak picker structure */ +void del_aubio_peakpicker(aubio_peakpicker_t * p); + +/** get current peak value */ +fvec_t *aubio_peakpicker_get_thresholded_input (aubio_peakpicker_t * p); +/** set peak picking threshold */ +uint_t aubio_peakpicker_set_threshold(aubio_peakpicker_t * p, smpl_t threshold); +/** get peak picking threshold */ +smpl_t aubio_peakpicker_get_threshold(aubio_peakpicker_t * p); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PEAKPICK_H */ diff --git a/aubio/src/pitch/pitch.h b/aubio/src/pitch/pitch.h new file mode 100644 index 00000000..92a05f77 --- /dev/null +++ b/aubio/src/pitch/pitch.h @@ -0,0 +1,197 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_PITCH_H +#define AUBIO_PITCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Pitch detection object + + This file creates the objects required for the computation of the selected + pitch detection algorithm and output the results, in midi note or Hz. + + \section pitch Pitch detection methods + + A list of the pitch detection methods currently available follows. + + \b \p default : use the default method + + Currently, the default method is set to \p yinfft . + + \b \p schmitt : Schmitt trigger + + This pitch extraction method implements a Schmitt trigger to estimate the + period of a signal. + + This file was derived from the tuneit project, written by Mario Lang to + detect the fundamental frequency of a sound. + + See http://delysid.org/tuneit.html + + \b \p fcomb : a fast harmonic comb filter + + This pitch extraction method implements a fast harmonic comb filter to + determine the fundamental frequency of a harmonic sound. + + This file was derived from the tuneit project, written by Mario Lang to + detect the fundamental frequency of a sound. + + See http://delysid.org/tuneit.html + + \b \p mcomb : multiple-comb filter + + This fundamental frequency estimation algorithm implements spectral + flattening, multi-comb filtering and peak histogramming. + + This method was designed by Juan P. Bello and described in: + + Juan-Pablo Bello. ``Towards the Automated Analysis of Simple Polyphonic + Music''. PhD thesis, Centre for Digital Music, Queen Mary University of + London, London, UK, 2003. + + \b \p yin : YIN algorithm + + This algorithm was developed by A. de Cheveigne and H. Kawahara and + published in: + + De Cheveigné, A., Kawahara, H. (2002) "YIN, a fundamental frequency + estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930. + + see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html + + \b \p yinfast : Yinfast algorithm + + This algorithm is equivalent to the YIN algorithm, but computed in the + spectral domain for efficiency. See also `python/demos/demo_yin_compare.py`. + + \b \p yinfft : Yinfft algorithm + + This algorithm was derived from the YIN algorithm. In this implementation, a + Fourier transform is used to compute a tapered square difference function, + which allows spectral weighting. Because the difference function is tapered, + the selection of the period is simplified. + + Paul Brossier, [Automatic annotation of musical audio for interactive + systems](http://aubio.org/phd/), Chapter 3, Pitch Analysis, PhD thesis, + Centre for Digital music, Queen Mary University of London, London, UK, 2006. + + \example pitch/test-pitch.c + \example examples/aubiopitch.c + +*/ + +/** pitch detection object */ +typedef struct _aubio_pitch_t aubio_pitch_t; + +/** execute pitch detection on an input signal frame + + \param o pitch detection object as returned by new_aubio_pitch() + \param in input signal of size [hop_size] + \param out output pitch candidates of size [1] + +*/ +void aubio_pitch_do (aubio_pitch_t * o, const fvec_t * in, fvec_t * out); + +/** change yin or yinfft tolerance threshold + + \param o pitch detection object as returned by new_aubio_pitch() + \param tol tolerance default is 0.15 for yin and 0.85 for yinfft + +*/ +uint_t aubio_pitch_set_tolerance (aubio_pitch_t * o, smpl_t tol); + +/** get yin or yinfft tolerance threshold + + \param o pitch detection object as returned by new_aubio_pitch() + \return tolerance (default is 0.15 for yin and 0.85 for yinfft) + +*/ +smpl_t aubio_pitch_get_tolerance (aubio_pitch_t * o); + +/** deletion of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitch() + +*/ +void del_aubio_pitch (aubio_pitch_t * o); + +/** creation of the pitch detection object + + \param method set pitch detection algorithm + \param buf_size size of the input buffer to analyse + \param hop_size step size between two consecutive analysis instant + \param samplerate sampling rate of the signal + + \return newly created ::aubio_pitch_t + +*/ +aubio_pitch_t *new_aubio_pitch (const char_t * method, + uint_t buf_size, uint_t hop_size, uint_t samplerate); + +/** set the output unit of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitch() + \param mode set pitch units for output + + mode can be one of "Hz", "midi", "cent", or "bin". Defaults to "Hz". + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_pitch_set_unit (aubio_pitch_t * o, const char_t * mode); + +/** set the silence threshold of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitch() + \param silence level threshold under which pitch should be ignored, in dB + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_pitch_set_silence (aubio_pitch_t * o, smpl_t silence); + +/** set the silence threshold of the pitch detection object + + \param o pitch detection object as returned by ::new_aubio_pitch() + + \return level threshold under which pitch should be ignored, in dB + +*/ +smpl_t aubio_pitch_get_silence (aubio_pitch_t * o); + +/** get the current confidence + + \param o pitch detection object as returned by new_aubio_pitch() + + \return the current confidence of the pitch algorithm + +*/ +smpl_t aubio_pitch_get_confidence (aubio_pitch_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCH_H */ diff --git a/aubio/src/pitch/pitchfcomb.h b/aubio/src/pitch/pitchfcomb.h new file mode 100644 index 00000000..50167088 --- /dev/null +++ b/aubio/src/pitch/pitchfcomb.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using a fast harmonic comb filter + + This pitch extraction method implements a fast harmonic comb filter to + determine the fundamental frequency of a harmonic sound. + + This file was derived from the tuneit project, written by Mario Lang to + detect the fundamental frequency of a sound. + + See http://delysid.org/tuneit.html + + \example pitch/test-pitchfcomb.c + +*/ + +#ifndef AUBIO_PITCHFCOMB_H +#define AUBIO_PITCHFCOMB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchfcomb_t aubio_pitchfcomb_t; + +/** execute pitch detection on an input buffer + + \param p pitch detection object as returned by new_aubio_pitchfcomb + \param input input signal window (length as specified at creation time) + \param output pitch candidates in bins + +*/ +void aubio_pitchfcomb_do (aubio_pitchfcomb_t * p, const fvec_t * input, + fvec_t * output); + +/** creation of the pitch detection object + + \param buf_size size of the input buffer to analyse + \param hop_size step size between two consecutive analysis instant + +*/ +aubio_pitchfcomb_t *new_aubio_pitchfcomb (uint_t buf_size, uint_t hop_size); + +/** deletion of the pitch detection object + + \param p pitch detection object as returned by new_aubio_pitchfcomb + +*/ +void del_aubio_pitchfcomb (aubio_pitchfcomb_t * p); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHFCOMB_H */ diff --git a/aubio/src/pitch/pitchmcomb.h b/aubio/src/pitch/pitchmcomb.h new file mode 100644 index 00000000..88f117f2 --- /dev/null +++ b/aubio/src/pitch/pitchmcomb.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using multiple-comb filter + + This fundamental frequency estimation algorithm implements spectral + flattening, multi-comb filtering and peak histogramming. + + This method was designed by Juan P. Bello and described in: + + Juan-Pablo Bello. ``Towards the Automated Analysis of Simple Polyphonic + Music''. PhD thesis, Centre for Digital Music, Queen Mary University of + London, London, UK, 2003. + + \example pitch/test-pitchmcomb.c + +*/ + +#ifndef AUBIO_PITCHMCOMB_H +#define AUBIO_PITCHMCOMB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchmcomb_t aubio_pitchmcomb_t; + +/** execute pitch detection on an input spectral frame + + \param p pitch detection object as returned by new_aubio_pitchmcomb + \param in_fftgrain input signal spectrum as computed by aubio_pvoc_do + \param out_cands pitch candidate frequenciess, in bins + +*/ +void aubio_pitchmcomb_do (aubio_pitchmcomb_t * p, const cvec_t * in_fftgrain, + fvec_t * out_cands); + +/** creation of the pitch detection object + + \param buf_size size of the input buffer to analyse + \param hop_size step size between two consecutive analysis instant + +*/ +aubio_pitchmcomb_t *new_aubio_pitchmcomb (uint_t buf_size, uint_t hop_size); + +/** deletion of the pitch detection object + + \param p pitch detection object as returned by new_aubio_pitchfcomb + +*/ +void del_aubio_pitchmcomb (aubio_pitchmcomb_t * p); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHMCOMB_H */ diff --git a/aubio/src/pitch/pitchschmitt.h b/aubio/src/pitch/pitchschmitt.h new file mode 100644 index 00000000..db952c34 --- /dev/null +++ b/aubio/src/pitch/pitchschmitt.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using a Schmitt trigger + + This pitch extraction method implements a Schmitt trigger to estimate the + period of a signal. + + This file was derived from the tuneit project, written by Mario Lang to + detect the fundamental frequency of a sound. + + See http://delysid.org/tuneit.html + + \example pitch/test-pitchschmitt.c + +*/ + +#ifndef AUBIO_PITCHSCHMITT_H +#define AUBIO_PITCHSCHMITT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchschmitt_t aubio_pitchschmitt_t; + +/** execute pitch detection on an input buffer + + \param p pitch detection object as returned by new_aubio_pitchschmitt + \param samples_in input signal vector (length as specified at creation time) + \param cands_out pitch period estimates, in samples + +*/ +void aubio_pitchschmitt_do (aubio_pitchschmitt_t * p, const fvec_t * samples_in, + fvec_t * cands_out); + +/** creation of the pitch detection object + + \param buf_size size of the input buffer to analyse + +*/ +aubio_pitchschmitt_t *new_aubio_pitchschmitt (uint_t buf_size); + +/** deletion of the pitch detection object + + \param p pitch detection object as returned by new_aubio_pitchschmitt + +*/ +void del_aubio_pitchschmitt (aubio_pitchschmitt_t * p); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHSCHMITT_H */ diff --git a/aubio/src/pitch/pitchspecacf.h b/aubio/src/pitch/pitchspecacf.h new file mode 100644 index 00000000..e3c9c4c1 --- /dev/null +++ b/aubio/src/pitch/pitchspecacf.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using spectral auto correlation + + This algorithm implements pitch detection by computing the autocorrelation + function as the cosine transform of the square spectral magnitudes. + + Anssi Klapuri. Qualitative and quantitative aspects in the design of + periodicity esti- mation algorithms. In Proceedings of the European Signal + Processing Conference (EUSIPCO), 2000. + + Paul Brossier, [Automatic annotation of musical audio for interactive + systems](http://aubio.org/phd/), Chapter 3, Pitch Analysis, Autocorrelation, + pp. 75-77, PhD thesis, Centre for Digital music, Queen Mary University of + London, London, UK, 2006. + + \example pitch/test-pitchspecacf.c + +*/ + +#ifndef AUBIO_PITCHSPECACF_H +#define AUBIO_PITCHSPECACF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchspecacf_t aubio_pitchspecacf_t; + +/** execute pitch detection on an input buffer + + \param o pitch detection object as returned by new_aubio_pitchspecacf + \param samples_in input signal vector (length as specified at creation time) + \param cands_out pitch period candidates, in samples + +*/ +void aubio_pitchspecacf_do (aubio_pitchspecacf_t * o, const fvec_t * samples_in, fvec_t * cands_out); +/** creation of the pitch detection object + + \param buf_size size of the input buffer to analyse + +*/ +aubio_pitchspecacf_t *new_aubio_pitchspecacf (uint_t buf_size); +/** deletion of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitchspecacf() + +*/ +void del_aubio_pitchspecacf (aubio_pitchspecacf_t * o); + +/** get tolerance parameter for `specacf` pitch detection object + + \param o pitch detection object + + \return tolerance parameter for minima selection [default 1.] + +*/ +smpl_t aubio_pitchspecacf_get_tolerance (const aubio_pitchspecacf_t * o); + +/** set tolerance parameter for `specacf` pitch detection object + + \param o pitch detection object + \param tol tolerance parameter for minima selection [default 1.] + + \return `1` on error, `0` on success + +*/ +uint_t aubio_pitchspecacf_set_tolerance (aubio_pitchspecacf_t * o, smpl_t tol); + +/** get currenct confidence for `specacf` pitch detection object + + \param o pitch detection object + \return confidence parameter + +*/ +smpl_t aubio_pitchspecacf_get_confidence (const aubio_pitchspecacf_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHSPECACF_H */ diff --git a/aubio/src/pitch/pitchyin.h b/aubio/src/pitch/pitchyin.h new file mode 100644 index 00000000..fa32b8da --- /dev/null +++ b/aubio/src/pitch/pitchyin.h @@ -0,0 +1,100 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using the YIN algorithm + + This algorithm was developed by A. de Cheveigne and H. Kawahara and + published in: + + De Cheveigné, A., Kawahara, H. (2002) "YIN, a fundamental frequency + estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930. + + see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html + http://recherche.ircam.fr/equipes/pcm/cheveign/ps/2002_JASA_YIN_proof.pdf + + \example pitch/test-pitchyin.c + +*/ + +#ifndef AUBIO_PITCHYIN_H +#define AUBIO_PITCHYIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchyin_t aubio_pitchyin_t; + +/** creation of the pitch detection object + + \param buf_size size of the input buffer to analyse + +*/ +aubio_pitchyin_t *new_aubio_pitchyin (uint_t buf_size); + +/** deletion of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitchyin() + +*/ +void del_aubio_pitchyin (aubio_pitchyin_t * o); + +/** execute pitch detection an input buffer + + \param o pitch detection object as returned by new_aubio_pitchyin() + \param samples_in input signal vector (length as specified at creation time) + \param cands_out pitch period candidates, in samples + +*/ +void aubio_pitchyin_do (aubio_pitchyin_t * o, const fvec_t * samples_in, fvec_t * cands_out); + + +/** set tolerance parameter for YIN algorithm + + \param o YIN pitch detection object + \param tol tolerance parameter for minima selection [default 0.15] + +*/ +uint_t aubio_pitchyin_set_tolerance (aubio_pitchyin_t * o, smpl_t tol); + +/** get tolerance parameter for YIN algorithm + + \param o YIN pitch detection object + \return tolerance parameter for minima selection [default 0.15] + +*/ +smpl_t aubio_pitchyin_get_tolerance (aubio_pitchyin_t * o); + +/** get current confidence of YIN algorithm + + \param o YIN pitch detection object + \return confidence parameter + +*/ +smpl_t aubio_pitchyin_get_confidence (aubio_pitchyin_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHYIN_H */ diff --git a/aubio/src/pitch/pitchyinfast.h b/aubio/src/pitch/pitchyinfast.h new file mode 100644 index 00000000..abb81393 --- /dev/null +++ b/aubio/src/pitch/pitchyinfast.h @@ -0,0 +1,102 @@ +/* + Copyright (C) 2003-2017 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using YIN algorithm (fast implementation) + + This algorithm was developed by A. de Cheveigne and H. Kawahara and + published in: + + De Cheveigné, A., Kawahara, H. (2002) "YIN, a fundamental frequency + estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930. + + This implementation compute the autocorrelation function using time domain + convolution computed in the spectral domain. + + see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html + http://recherche.ircam.fr/equipes/pcm/cheveign/ps/2002_JASA_YIN_proof.pdf + +*/ + +#ifndef AUBIO_PITCHYINFAST_H +#define AUBIO_PITCHYINFAST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchyinfast_t aubio_pitchyinfast_t; + +/** creation of the pitch detection object + + \param buf_size size of the input buffer to analyse + +*/ +aubio_pitchyinfast_t *new_aubio_pitchyinfast (uint_t buf_size); + +/** deletion of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitchyin() + +*/ +void del_aubio_pitchyinfast (aubio_pitchyinfast_t * o); + +/** execute pitch detection an input buffer + + \param o pitch detection object as returned by new_aubio_pitchyin() + \param samples_in input signal vector (length as specified at creation time) + \param cands_out pitch period candidates, in samples + +*/ +void aubio_pitchyinfast_do (aubio_pitchyinfast_t * o, const fvec_t * samples_in, fvec_t * cands_out); + + +/** set tolerance parameter for YIN algorithm + + \param o YIN pitch detection object + \param tol tolerance parameter for minima selection [default 0.15] + +*/ +uint_t aubio_pitchyinfast_set_tolerance (aubio_pitchyinfast_t * o, smpl_t tol); + +/** get tolerance parameter for YIN algorithm + + \param o YIN pitch detection object + \return tolerance parameter for minima selection [default 0.15] + +*/ +smpl_t aubio_pitchyinfast_get_tolerance (aubio_pitchyinfast_t * o); + +/** get current confidence of YIN algorithm + + \param o YIN pitch detection object + \return confidence parameter + +*/ +smpl_t aubio_pitchyinfast_get_confidence (aubio_pitchyinfast_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHYINFAST_H */ + diff --git a/aubio/src/pitch/pitchyinfft.h b/aubio/src/pitch/pitchyinfft.h new file mode 100644 index 00000000..ccd4e518 --- /dev/null +++ b/aubio/src/pitch/pitchyinfft.h @@ -0,0 +1,99 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Pitch detection using a spectral implementation of the YIN algorithm + + This algorithm was derived from the YIN algorithm. In this implementation, a + Fourier transform is used to compute a tapered square difference function, + which allows spectral weighting. Because the difference function is tapered, + the selection of the period is simplified. + + Paul Brossier, [Automatic annotation of musical audio for interactive + systems](http://aubio.org/phd/), Chapter 3, Pitch Analysis, PhD thesis, + Centre for Digital music, Queen Mary University of London, London, UK, 2006. + + \example pitch/test-pitchyinfft.c + +*/ + +#ifndef AUBIO_PITCHYINFFT_H +#define AUBIO_PITCHYINFFT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** pitch detection object */ +typedef struct _aubio_pitchyinfft_t aubio_pitchyinfft_t; + +/** execute pitch detection on an input buffer + + \param o pitch detection object as returned by new_aubio_pitchyinfft + \param samples_in input signal vector (length as specified at creation time) + \param cands_out pitch period candidates, in samples + +*/ +void aubio_pitchyinfft_do (aubio_pitchyinfft_t * o, const fvec_t * samples_in, fvec_t * cands_out); +/** creation of the pitch detection object + + \param samplerate samplerate of the input signal + \param buf_size size of the input buffer to analyse + +*/ +aubio_pitchyinfft_t *new_aubio_pitchyinfft (uint_t samplerate, uint_t buf_size); +/** deletion of the pitch detection object + + \param o pitch detection object as returned by new_aubio_pitchyinfft() + +*/ +void del_aubio_pitchyinfft (aubio_pitchyinfft_t * o); + +/** get tolerance parameter for YIN algorithm + + \param o YIN pitch detection object + + \return tolerance parameter for minima selection [default 0.15] + +*/ +smpl_t aubio_pitchyinfft_get_tolerance (aubio_pitchyinfft_t * o); + +/** set tolerance parameter for YIN algorithm + + \param o YIN pitch detection object + \param tol tolerance parameter for minima selection [default 0.15] + +*/ +uint_t aubio_pitchyinfft_set_tolerance (aubio_pitchyinfft_t * o, smpl_t tol); + +/** get current confidence of YIN algorithm + + \param o YIN pitch detection object + \return confidence parameter + +*/ +smpl_t aubio_pitchyinfft_get_confidence (aubio_pitchyinfft_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PITCHYINFFT_H */ diff --git a/aubio/src/spectral/awhitening.c b/aubio/src/spectral/awhitening.c new file mode 100644 index 00000000..1543544c --- /dev/null +++ b/aubio/src/spectral/awhitening.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2003-2015 Paul Brossier + * + * This file is part of aubio. + * + * aubio 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. + * + * aubio 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 + * aubio. If not, see . + * + */ + +#include "aubio_priv.h" +#include "fvec.h" +#include "cvec.h" +#include "mathutils.h" +#include "spectral/awhitening.h" + +#define aubio_spectral_whitening_default_relax_time 250 // in seconds, between 22 and 446 +#define aubio_spectral_whitening_default_decay 0.001 // -60dB attenuation +#define aubio_spectral_whitening_default_floor 1.e-4 // from 1.e-6 to .2 + +/** structure to store object state */ +struct _aubio_spectral_whitening_t { + uint_t buf_size; + uint_t hop_size; + uint_t samplerate; + smpl_t relax_time; + smpl_t r_decay; + smpl_t floor; + fvec_t *peak_values; +}; + +void +aubio_spectral_whitening_do (aubio_spectral_whitening_t * o, cvec_t * fftgrain) +{ + uint_t i = 0; + uint_t length = MIN(fftgrain->length, o->peak_values->length); + for (i = 0; i < length; i++) { + smpl_t tmp = MAX(o->r_decay * o->peak_values->data[i], o->floor); + o->peak_values->data[i] = MAX(fftgrain->norm[i], tmp); + fftgrain->norm[i] /= o->peak_values->data[i]; + } +} + +aubio_spectral_whitening_t * +new_aubio_spectral_whitening (uint_t buf_size, uint_t hop_size, uint_t samplerate) +{ + aubio_spectral_whitening_t *o = AUBIO_NEW (aubio_spectral_whitening_t); + if ((sint_t)buf_size < 1) { + AUBIO_ERR("spectral_whitening: got buffer_size %d, but can not be < 1\n", buf_size); + goto beach; + } else if ((sint_t)hop_size < 1) { + AUBIO_ERR("spectral_whitening: got hop_size %d, but can not be < 1\n", hop_size); + goto beach; + } else if ((sint_t)samplerate < 1) { + AUBIO_ERR("spectral_whitening: got samplerate %d, but can not be < 1\n", samplerate); + goto beach; + } + o->peak_values = new_fvec (buf_size / 2 + 1); + o->buf_size = buf_size; + o->hop_size = hop_size; + o->samplerate = samplerate; + o->floor = aubio_spectral_whitening_default_floor; + aubio_spectral_whitening_set_relax_time (o, aubio_spectral_whitening_default_relax_time); + aubio_spectral_whitening_reset (o); + return o; + +beach: + AUBIO_FREE(o); + return NULL; +} + +uint_t +aubio_spectral_whitening_set_relax_time (aubio_spectral_whitening_t * o, smpl_t relax_time) +{ + o->relax_time = relax_time; + o->r_decay = POW (aubio_spectral_whitening_default_decay, + (o->hop_size / (float) o->samplerate) / o->relax_time); + return AUBIO_OK; +} + +smpl_t +aubio_spectral_whitening_get_relax_time (aubio_spectral_whitening_t * o) +{ + return o->relax_time; +} + +uint_t +aubio_spectral_whitening_set_floor (aubio_spectral_whitening_t *o, smpl_t floor) +{ + o->floor = floor; + return AUBIO_OK; +} + +smpl_t aubio_spectral_whitening_get_floor (aubio_spectral_whitening_t *o) +{ + return o->floor; +} + +void +aubio_spectral_whitening_reset (aubio_spectral_whitening_t * o) +{ + /* cover the case n == 0. */ + fvec_set_all (o->peak_values, o->floor); +} + +void +del_aubio_spectral_whitening (aubio_spectral_whitening_t * o) +{ + del_fvec (o->peak_values); + AUBIO_FREE (o); +} diff --git a/aubio/src/spectral/awhitening.h b/aubio/src/spectral/awhitening.h new file mode 100644 index 00000000..64150e7f --- /dev/null +++ b/aubio/src/spectral/awhitening.h @@ -0,0 +1,125 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Spectral adaptive whitening + + References: + + D. Stowell and M. D. Plumbley. Adaptive whitening for improved real-time + audio onset detection. In Proceedings of the International Computer Music + Conference (ICMC), 2007, Copenhagen, Denmark. + + http://www.eecs.qmul.ac.uk/~markp/2007/StowellPlumbley07-icmc.pdf + + S. Böck,, F. Krebs, and M. Schedl. Evaluating the Online Capabilities of + Onset Detection Methods. In Proceedings of the 13th International Society for + Music Information Retrieval Conference (ISMIR), 2012, Porto, Portugal. + + http://ismir2012.ismir.net/event/papers/049_ISMIR_2012.pdf + http://www.cp.jku.at/research/papers/Boeck_etal_ISMIR_2012.pdf + +*/ + + +#ifndef _AUBIO_SPECTRAL_WHITENING_H +#define _AUBIO_SPECTRAL_WHITENING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** spectral whitening structure */ +typedef struct _aubio_spectral_whitening_t aubio_spectral_whitening_t; + +/** execute spectral adaptive whitening, in-place + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + \param fftgrain input signal spectrum as computed by aubio_pvoc_do() or aubio_fft_do() + +*/ +void aubio_spectral_whitening_do (aubio_spectral_whitening_t * o, + cvec_t * fftgrain); + +/** creation of a spectral whitening object + + \param buf_size window size of input grains + \param hop_size number of samples between two consecutive input grains + \param samplerate sampling rate of the input signal + +*/ +aubio_spectral_whitening_t *new_aubio_spectral_whitening (uint_t buf_size, + uint_t hop_size, + uint_t samplerate); + +/** reset spectral whitening object + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + + */ +void aubio_spectral_whitening_reset (aubio_spectral_whitening_t * o); + +/** set relaxation time for spectral whitening + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + \param relax_time relaxation time in seconds between 20 and 500, defaults 250 + + */ +uint_t aubio_spectral_whitening_set_relax_time (aubio_spectral_whitening_t * o, + smpl_t relax_time); + +/** get relaxation time of spectral whitening + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + \return relaxation time in seconds + +*/ +smpl_t aubio_spectral_whitening_get_relax_time (aubio_spectral_whitening_t * o); + +/** set floor for spectral whitening + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + \param floor value (typically between 1.e-6 and .2, defaults to 1.e-4) + + */ +uint_t aubio_spectral_whitening_set_floor (aubio_spectral_whitening_t * o, + smpl_t floor); + +/** get floor of spectral whitening + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + \return floor value + +*/ +smpl_t aubio_spectral_whitening_get_floor (aubio_spectral_whitening_t * o); + +/** deletion of a spectral whitening + + \param o spectral whitening object as returned by new_aubio_spectral_whitening() + +*/ +void del_aubio_spectral_whitening (aubio_spectral_whitening_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* _AUBIO_SPECTRAL_WHITENING_H */ diff --git a/aubio/src/spectral/dct.h b/aubio/src/spectral/dct.h new file mode 100644 index 00000000..6ceb006f --- /dev/null +++ b/aubio/src/spectral/dct.h @@ -0,0 +1,85 @@ +/* + Copyright (C) 2017 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Discrete Cosine Transform + + Functions aubio_dct_do() and aubio_dct_rdo() are equivalent to MATLAB/Octave + dct() and idct() functions, as well as scipy.fftpack.dct(x, norm='ortho') and + scipy.fftpack.idct(x, norm='ortho') + + \example spectral/test-dct.c + +*/ + +#ifndef AUBIO_DCT_H +#define AUBIO_DCT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** DCT object + + This object computes forward and backward DCT type 2 with orthonormal + scaling. + +*/ +typedef struct _aubio_dct_t aubio_dct_t; + +/** create new DCT computation object + + \param size length of the DCT + +*/ +aubio_dct_t * new_aubio_dct(uint_t size); + +/** compute forward DCT + + \param s dct object as returned by new_aubio_dct + \param input input signal + \param dct_output transformed input array + +*/ +void aubio_dct_do (aubio_dct_t *s, const fvec_t * input, fvec_t * dct_output); + +/** compute backward DCT + + \param s dct object as returned by new_aubio_dct + \param input input signal + \param idct_output transformed input array + +*/ +void aubio_dct_rdo (aubio_dct_t *s, const fvec_t * input, fvec_t * idct_output); + + +/** delete DCT object + + \param s dct object as returned by new_aubio_dct + +*/ +void del_aubio_dct (aubio_dct_t *s); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_DCT_H */ diff --git a/aubio/src/spectral/fft.c b/aubio/src/spectral/fft.c new file mode 100644 index 00000000..598fe22d --- /dev/null +++ b/aubio/src/spectral/fft.c @@ -0,0 +1,572 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "cvec.h" +#include "mathutils.h" +#include "spectral/fft.h" + +#ifdef HAVE_FFTW3 // using FFTW3 +/* note that is not included here but only in aubio_priv.h, so that + * c++ projects can still use their own complex definition. */ +#include +#include + +#ifdef HAVE_COMPLEX_H +#ifdef HAVE_FFTW3F +/** fft data type with complex.h and fftw3f */ +#define FFTW_TYPE fftwf_complex +#else +/** fft data type with complex.h and fftw3 */ +#define FFTW_TYPE fftw_complex +#endif +#else +#ifdef HAVE_FFTW3F +/** fft data type without complex.h and with fftw3f */ +#define FFTW_TYPE float +#else +/** fft data type without complex.h and with fftw */ +#define FFTW_TYPE double +#endif +#endif + +/** fft data type */ +typedef FFTW_TYPE fft_data_t; + +#ifdef HAVE_FFTW3F +#define fftw_malloc fftwf_malloc +#define fftw_free fftwf_free +#define fftw_execute fftwf_execute +#define fftw_plan_dft_r2c_1d fftwf_plan_dft_r2c_1d +#define fftw_plan_dft_c2r_1d fftwf_plan_dft_c2r_1d +#define fftw_plan_r2r_1d fftwf_plan_r2r_1d +#define fftw_plan fftwf_plan +#define fftw_destroy_plan fftwf_destroy_plan +#endif + +#ifdef HAVE_FFTW3F +#if HAVE_AUBIO_DOUBLE +#error "Using aubio in double precision with fftw3 in single precision" +#endif /* HAVE_AUBIO_DOUBLE */ +#define real_t float +#elif defined (HAVE_FFTW3) /* HAVE_FFTW3F */ +#if !HAVE_AUBIO_DOUBLE +#error "Using aubio in single precision with fftw3 in double precision" +#endif /* HAVE_AUBIO_DOUBLE */ +#define real_t double +#endif /* HAVE_FFTW3F */ + +// a global mutex for FFTW thread safety +pthread_mutex_t aubio_fftw_mutex = PTHREAD_MUTEX_INITIALIZER; + +#elif defined HAVE_ACCELERATE // using ACCELERATE +// https://developer.apple.com/library/mac/#documentation/Accelerate/Reference/vDSPRef/Reference/reference.html +#include + +#if !HAVE_AUBIO_DOUBLE +#define aubio_vDSP_ctoz vDSP_ctoz +#define aubio_vDSP_fft_zrip vDSP_fft_zrip +#define aubio_vDSP_ztoc vDSP_ztoc +#define aubio_vDSP_zvmags vDSP_zvmags +#define aubio_vDSP_zvphas vDSP_zvphas +#define aubio_vDSP_vsadd vDSP_vsadd +#define aubio_vDSP_vsmul vDSP_vsmul +#define aubio_DSPComplex DSPComplex +#define aubio_DSPSplitComplex DSPSplitComplex +#define aubio_vDSP_DFT_Setup vDSP_DFT_Setup +#define aubio_vDSP_DFT_zrop_CreateSetup vDSP_DFT_zrop_CreateSetup +#define aubio_vDSP_DFT_Execute vDSP_DFT_Execute +#define aubio_vDSP_DFT_DestroySetup vDSP_DFT_DestroySetup +#define aubio_vvsqrt vvsqrtf +#else +#define aubio_vDSP_ctoz vDSP_ctozD +#define aubio_vDSP_fft_zrip vDSP_fft_zripD +#define aubio_vDSP_ztoc vDSP_ztocD +#define aubio_vDSP_zvmags vDSP_zvmagsD +#define aubio_vDSP_zvphas vDSP_zvphasD +#define aubio_vDSP_vsadd vDSP_vsaddD +#define aubio_vDSP_vsmul vDSP_vsmulD +#define aubio_DSPComplex DSPDoubleComplex +#define aubio_DSPSplitComplex DSPDoubleSplitComplex +#define aubio_vDSP_DFT_Setup vDSP_DFT_SetupD +#define aubio_vDSP_DFT_zrop_CreateSetup vDSP_DFT_zrop_CreateSetupD +#define aubio_vDSP_DFT_Execute vDSP_DFT_ExecuteD +#define aubio_vDSP_DFT_DestroySetup vDSP_DFT_DestroySetupD +#define aubio_vvsqrt vvsqrt +#endif /* HAVE_AUBIO_DOUBLE */ + +#elif defined HAVE_INTEL_IPP // using INTEL IPP + +#if !HAVE_AUBIO_DOUBLE +#define aubio_IppFloat Ipp32f +#define aubio_IppComplex Ipp32fc +#define aubio_FFTSpec FFTSpec_R_32f +#define aubio_ippsMalloc_complex ippsMalloc_32fc +#define aubio_ippsFFTInit_R ippsFFTInit_R_32f +#define aubio_ippsFFTGetSize_R ippsFFTGetSize_R_32f +#define aubio_ippsFFTInv_CCSToR ippsFFTInv_CCSToR_32f +#define aubio_ippsFFTFwd_RToCCS ippsFFTFwd_RToCCS_32f +#define aubio_ippsAtan2 ippsAtan2_32f_A21 +#else /* HAVE_AUBIO_DOUBLE */ +#define aubio_IppFloat Ipp64f +#define aubio_IppComplex Ipp64fc +#define aubio_FFTSpec FFTSpec_R_64f +#define aubio_ippsMalloc_complex ippsMalloc_64fc +#define aubio_ippsFFTInit_R ippsFFTInit_R_64f +#define aubio_ippsFFTGetSize_R ippsFFTGetSize_R_64f +#define aubio_ippsFFTInv_CCSToR ippsFFTInv_CCSToR_64f +#define aubio_ippsFFTFwd_RToCCS ippsFFTFwd_RToCCS_64f +#define aubio_ippsAtan2 ippsAtan2_64f_A50 +#endif + + +#else // using OOURA +// let's use ooura instead +extern void aubio_ooura_rdft(int, int, smpl_t *, int *, smpl_t *); + +#endif + +struct _aubio_fft_t { + uint_t winsize; + uint_t fft_size; + +#ifdef HAVE_FFTW3 // using FFTW3 + real_t *in, *out; + fftw_plan pfw, pbw; + fft_data_t * specdata; /* complex spectral data */ + +#elif defined HAVE_ACCELERATE // using ACCELERATE + aubio_vDSP_DFT_Setup fftSetupFwd; + aubio_vDSP_DFT_Setup fftSetupBwd; + aubio_DSPSplitComplex spec; + smpl_t *in, *out; + +#elif defined HAVE_INTEL_IPP // using Intel IPP + smpl_t *in, *out; + Ipp8u* memSpec; + Ipp8u* memInit; + Ipp8u* memBuffer; + struct aubio_FFTSpec* fftSpec; + aubio_IppComplex* complexOut; +#else // using OOURA + smpl_t *in, *out; + smpl_t *w; + int *ip; +#endif /* using OOURA */ + + fvec_t * compspec; +}; + +aubio_fft_t * new_aubio_fft (uint_t winsize) { + aubio_fft_t * s = AUBIO_NEW(aubio_fft_t); + if ((sint_t)winsize < 2) { + AUBIO_ERR("fft: got winsize %d, but can not be < 2\n", winsize); + goto beach; + } + +#ifdef HAVE_FFTW3 + uint_t i; + s->winsize = winsize; + /* allocate memory */ + s->in = AUBIO_ARRAY(real_t,winsize); + s->out = AUBIO_ARRAY(real_t,winsize); + s->compspec = new_fvec(winsize); + /* create plans */ + pthread_mutex_lock(&aubio_fftw_mutex); +#ifdef HAVE_COMPLEX_H + s->fft_size = winsize/2 + 1; + s->specdata = (fft_data_t*)fftw_malloc(sizeof(fft_data_t)*s->fft_size); + s->pfw = fftw_plan_dft_r2c_1d(winsize, s->in, s->specdata, FFTW_ESTIMATE); + s->pbw = fftw_plan_dft_c2r_1d(winsize, s->specdata, s->out, FFTW_ESTIMATE); +#else + s->fft_size = winsize; + s->specdata = (fft_data_t*)fftw_malloc(sizeof(fft_data_t)*s->fft_size); + s->pfw = fftw_plan_r2r_1d(winsize, s->in, s->specdata, FFTW_R2HC, FFTW_ESTIMATE); + s->pbw = fftw_plan_r2r_1d(winsize, s->specdata, s->out, FFTW_HC2R, FFTW_ESTIMATE); +#endif + pthread_mutex_unlock(&aubio_fftw_mutex); + for (i = 0; i < s->winsize; i++) { + s->in[i] = 0.; + s->out[i] = 0.; + } + for (i = 0; i < s->fft_size; i++) { + s->specdata[i] = 0.; + } + +#elif defined HAVE_ACCELERATE // using ACCELERATE + { + uint_t radix = winsize; + uint_t order = 0; + while ((radix / 2) * 2 == radix) { + radix /= 2; + order++; + } + if (order < 4 || (radix != 1 && radix != 3 && radix != 5 && radix != 15)) { + AUBIO_ERR("fft: vDSP/Accelerate supports FFT with sizes = " + "f * 2 ** n, where n > 4 and f in [1, 3, 5, 15], but requested %d. " + "Use the closest power of two, or try recompiling aubio with " + "--enable-fftw3.\n", winsize); + goto beach; + } + } + s->winsize = winsize; + s->fft_size = winsize; + s->compspec = new_fvec(winsize); + s->in = AUBIO_ARRAY(smpl_t, s->fft_size); + s->out = AUBIO_ARRAY(smpl_t, s->fft_size); + s->spec.realp = AUBIO_ARRAY(smpl_t, s->fft_size/2); + s->spec.imagp = AUBIO_ARRAY(smpl_t, s->fft_size/2); + s->fftSetupFwd = aubio_vDSP_DFT_zrop_CreateSetup(NULL, + s->fft_size, vDSP_DFT_FORWARD); + s->fftSetupBwd = aubio_vDSP_DFT_zrop_CreateSetup(s->fftSetupFwd, + s->fft_size, vDSP_DFT_INVERSE); + +#elif defined HAVE_INTEL_IPP // using Intel IPP + const IppHintAlgorithm qualityHint = ippAlgHintAccurate; // OR ippAlgHintFast; + const int flags = IPP_FFT_NODIV_BY_ANY; // we're scaling manually afterwards + int order = aubio_power_of_two_order(winsize); + int sizeSpec, sizeInit, sizeBuffer; + IppStatus status; + + if (winsize <= 4 || aubio_is_power_of_two(winsize) != 1) + { + AUBIO_ERR("intel IPP fft: can only create with sizes > 4 and power of two, requested %d," + " try recompiling aubio with --enable-fftw3\n", winsize); + goto beach; + } + + status = aubio_ippsFFTGetSize_R(order, flags, qualityHint, + &sizeSpec, &sizeInit, &sizeBuffer); + if (status != ippStsNoErr) { + AUBIO_ERR("fft: failed to initialize fft. IPP error: %d\n", status); + goto beach; + } + s->fft_size = s->winsize = winsize; + s->compspec = new_fvec(winsize); + s->in = AUBIO_ARRAY(smpl_t, s->winsize); + s->out = AUBIO_ARRAY(smpl_t, s->winsize); + s->memSpec = ippsMalloc_8u(sizeSpec); + s->memBuffer = ippsMalloc_8u(sizeBuffer); + if (sizeInit > 0 ) { + s->memInit = ippsMalloc_8u(sizeInit); + } + s->complexOut = aubio_ippsMalloc_complex(s->fft_size / 2 + 1); + status = aubio_ippsFFTInit_R( + &s->fftSpec, order, flags, qualityHint, s->memSpec, s->memInit); + if (status != ippStsNoErr) { + AUBIO_ERR("fft: failed to initialize. IPP error: %d\n", status); + goto beach; + } + +#else // using OOURA + if (aubio_is_power_of_two(winsize) != 1) { + AUBIO_ERR("fft: can only create with sizes power of two, requested %d," + " try recompiling aubio with --enable-fftw3\n", winsize); + goto beach; + } + s->winsize = winsize; + s->fft_size = winsize / 2 + 1; + s->compspec = new_fvec(winsize); + s->in = AUBIO_ARRAY(smpl_t, s->winsize); + s->out = AUBIO_ARRAY(smpl_t, s->winsize); + s->ip = AUBIO_ARRAY(int , s->fft_size); + s->w = AUBIO_ARRAY(smpl_t, s->fft_size); + s->ip[0] = 0; +#endif /* using OOURA */ + + return s; + +beach: + AUBIO_FREE(s); + return NULL; +} + +void del_aubio_fft(aubio_fft_t * s) { + /* destroy data */ +#ifdef HAVE_FFTW3 // using FFTW3 + pthread_mutex_lock(&aubio_fftw_mutex); + fftw_destroy_plan(s->pfw); + fftw_destroy_plan(s->pbw); + fftw_free(s->specdata); + pthread_mutex_unlock(&aubio_fftw_mutex); + +#elif defined HAVE_ACCELERATE // using ACCELERATE + AUBIO_FREE(s->spec.realp); + AUBIO_FREE(s->spec.imagp); + aubio_vDSP_DFT_DestroySetup(s->fftSetupBwd); + aubio_vDSP_DFT_DestroySetup(s->fftSetupFwd); + +#elif defined HAVE_INTEL_IPP // using Intel IPP + ippFree(s->memSpec); + ippFree(s->memInit); + ippFree(s->memBuffer); + ippFree(s->complexOut); + +#else // using OOURA + AUBIO_FREE(s->w); + AUBIO_FREE(s->ip); +#endif + + del_fvec(s->compspec); + AUBIO_FREE(s->in); + AUBIO_FREE(s->out); + AUBIO_FREE(s); +} + +void aubio_fft_do(aubio_fft_t * s, const fvec_t * input, cvec_t * spectrum) { + aubio_fft_do_complex(s, input, s->compspec); + aubio_fft_get_spectrum(s->compspec, spectrum); +} + +void aubio_fft_rdo(aubio_fft_t * s, const cvec_t * spectrum, fvec_t * output) { + aubio_fft_get_realimag(spectrum, s->compspec); + aubio_fft_rdo_complex(s, s->compspec, output); +} + +void aubio_fft_do_complex(aubio_fft_t * s, const fvec_t * input, fvec_t * compspec) { + uint_t i; +#ifndef HAVE_MEMCPY_HACKS + for (i=0; i < s->winsize; i++) { + s->in[i] = input->data[i]; + } +#else + memcpy(s->in, input->data, s->winsize * sizeof(smpl_t)); +#endif /* HAVE_MEMCPY_HACKS */ + +#ifdef HAVE_FFTW3 // using FFTW3 + fftw_execute(s->pfw); +#ifdef HAVE_COMPLEX_H + compspec->data[0] = REAL(s->specdata[0]); + for (i = 1; i < s->fft_size -1 ; i++) { + compspec->data[i] = REAL(s->specdata[i]); + compspec->data[compspec->length - i] = IMAG(s->specdata[i]); + } + compspec->data[s->fft_size-1] = REAL(s->specdata[s->fft_size-1]); +#else /* HAVE_COMPLEX_H */ + for (i = 0; i < s->fft_size; i++) { + compspec->data[i] = s->specdata[i]; + } +#endif /* HAVE_COMPLEX_H */ + +#elif defined HAVE_ACCELERATE // using ACCELERATE + // convert real data to even/odd format used in vDSP + aubio_vDSP_ctoz((aubio_DSPComplex*)s->in, 2, &s->spec, 1, s->fft_size/2); + // compute the FFT + aubio_vDSP_DFT_Execute(s->fftSetupFwd, s->spec.realp, s->spec.imagp, + s->spec.realp, s->spec.imagp); + // convert from vDSP complex split to [ r0, r1, ..., rN, iN-1, .., i2, i1] + compspec->data[0] = s->spec.realp[0]; + compspec->data[s->fft_size / 2] = s->spec.imagp[0]; + for (i = 1; i < s->fft_size / 2; i++) { + compspec->data[i] = s->spec.realp[i]; + compspec->data[s->fft_size - i] = s->spec.imagp[i]; + } + // apply scaling + smpl_t scale = 1./2.; + aubio_vDSP_vsmul(compspec->data, 1, &scale, compspec->data, 1, s->fft_size); + +#elif defined HAVE_INTEL_IPP // using Intel IPP + + // apply fft + aubio_ippsFFTFwd_RToCCS(s->in, (aubio_IppFloat*)s->complexOut, s->fftSpec, s->memBuffer); + // convert complex buffer to [ r0, r1, ..., rN, iN-1, .., i2, i1] + compspec->data[0] = s->complexOut[0].re; + compspec->data[s->fft_size / 2] = s->complexOut[s->fft_size / 2].re; + for (i = 1; i < s->fft_size / 2; i++) { + compspec->data[i] = s->complexOut[i].re; + compspec->data[s->fft_size - i] = s->complexOut[i].im; + } + +#else // using OOURA + aubio_ooura_rdft(s->winsize, 1, s->in, s->ip, s->w); + compspec->data[0] = s->in[0]; + compspec->data[s->winsize / 2] = s->in[1]; + for (i = 1; i < s->fft_size - 1; i++) { + compspec->data[i] = s->in[2 * i]; + compspec->data[s->winsize - i] = - s->in[2 * i + 1]; + } +#endif /* using OOURA */ +} + +void aubio_fft_rdo_complex(aubio_fft_t * s, const fvec_t * compspec, fvec_t * output) { + uint_t i; +#ifdef HAVE_FFTW3 + const smpl_t renorm = 1./(smpl_t)s->winsize; +#ifdef HAVE_COMPLEX_H + s->specdata[0] = compspec->data[0]; + for (i=1; i < s->fft_size - 1; i++) { + s->specdata[i] = compspec->data[i] + + I * compspec->data[compspec->length - i]; + } + s->specdata[s->fft_size - 1] = compspec->data[s->fft_size - 1]; +#else + for (i=0; i < s->fft_size; i++) { + s->specdata[i] = compspec->data[i]; + } +#endif + fftw_execute(s->pbw); + for (i = 0; i < output->length; i++) { + output->data[i] = s->out[i]*renorm; + } + +#elif defined HAVE_ACCELERATE // using ACCELERATE + // convert from real imag [ r0, r1, ..., rN, iN-1, .., i2, i1] + // to vDSP packed format [ r0, rN, r1, i1, ..., rN-1, iN-1 ] + s->out[0] = compspec->data[0]; + s->out[1] = compspec->data[s->winsize / 2]; + for (i = 1; i < s->fft_size / 2; i++) { + s->out[2 * i] = compspec->data[i]; + s->out[2 * i + 1] = compspec->data[s->winsize - i]; + } + // convert to split complex format used in vDSP + aubio_vDSP_ctoz((aubio_DSPComplex*)s->out, 2, &s->spec, 1, s->fft_size/2); + // compute the FFT + aubio_vDSP_DFT_Execute(s->fftSetupBwd, s->spec.realp, s->spec.imagp, + s->spec.realp, s->spec.imagp); + // convert result to real output + aubio_vDSP_ztoc(&s->spec, 1, (aubio_DSPComplex*)output->data, 2, s->fft_size/2); + // apply scaling + smpl_t scale = 1.0 / s->winsize; + aubio_vDSP_vsmul(output->data, 1, &scale, output->data, 1, s->fft_size); + +#elif defined HAVE_INTEL_IPP // using Intel IPP + + // convert from real imag [ r0, 0, ..., rN, iN-1, .., i2, i1, iN-1] to complex format + s->complexOut[0].re = compspec->data[0]; + s->complexOut[0].im = 0; + s->complexOut[s->fft_size / 2].re = compspec->data[s->fft_size / 2]; + s->complexOut[s->fft_size / 2].im = 0.0; + for (i = 1; i < s->fft_size / 2; i++) { + s->complexOut[i].re = compspec->data[i]; + s->complexOut[i].im = compspec->data[s->fft_size - i]; + } + // apply fft + aubio_ippsFFTInv_CCSToR((const aubio_IppFloat *)s->complexOut, output->data, s->fftSpec, s->memBuffer); + // apply scaling + aubio_ippsMulC(output->data, 1.0 / s->winsize, output->data, s->fft_size); + +#else // using OOURA + smpl_t scale = 2.0 / s->winsize; + s->out[0] = compspec->data[0]; + s->out[1] = compspec->data[s->winsize / 2]; + for (i = 1; i < s->fft_size - 1; i++) { + s->out[2 * i] = compspec->data[i]; + s->out[2 * i + 1] = - compspec->data[s->winsize - i]; + } + aubio_ooura_rdft(s->winsize, -1, s->out, s->ip, s->w); + for (i=0; i < s->winsize; i++) { + output->data[i] = s->out[i] * scale; + } +#endif +} + +void aubio_fft_get_spectrum(const fvec_t * compspec, cvec_t * spectrum) { + aubio_fft_get_phas(compspec, spectrum); + aubio_fft_get_norm(compspec, spectrum); +} + +void aubio_fft_get_realimag(const cvec_t * spectrum, fvec_t * compspec) { + aubio_fft_get_imag(spectrum, compspec); + aubio_fft_get_real(spectrum, compspec); +} + +void aubio_fft_get_phas(const fvec_t * compspec, cvec_t * spectrum) { + uint_t i; + if (compspec->data[0] < 0) { + spectrum->phas[0] = PI; + } else { + spectrum->phas[0] = 0.; + } +#if defined(HAVE_INTEL_IPP) + // convert from real imag [ r0, r1, ..., rN, iN-1, ..., i2, i1, i0] + // to [ r0, r1, ..., rN, i0, i1, i2, ..., iN-1] + for (i = 1; i < spectrum->length / 2; i++) { + ELEM_SWAP(compspec->data[compspec->length - i], + compspec->data[spectrum->length + i - 1]); + } + aubio_ippsAtan2(compspec->data + spectrum->length, + compspec->data + 1, spectrum->phas + 1, spectrum->length - 1); + // revert the imaginary part back again + for (i = 1; i < spectrum->length / 2; i++) { + ELEM_SWAP(compspec->data[spectrum->length + i - 1], + compspec->data[compspec->length - i]); + } +#else + for (i=1; i < spectrum->length - 1; i++) { + spectrum->phas[i] = ATAN2(compspec->data[compspec->length-i], + compspec->data[i]); + } +#endif +#ifdef HAVE_FFTW3 + // for even length only, make sure last element is 0 or PI + if (2 * (compspec->length / 2) == compspec->length) { +#endif + if (compspec->data[compspec->length/2] < 0) { + spectrum->phas[spectrum->length - 1] = PI; + } else { + spectrum->phas[spectrum->length - 1] = 0.; + } +#ifdef HAVE_FFTW3 + } else { + i = spectrum->length - 1; + spectrum->phas[i] = ATAN2(compspec->data[compspec->length-i], + compspec->data[i]); + } +#endif +} + +void aubio_fft_get_norm(const fvec_t * compspec, cvec_t * spectrum) { + uint_t i = 0; + spectrum->norm[0] = ABS(compspec->data[0]); + for (i=1; i < spectrum->length - 1; i++) { + spectrum->norm[i] = SQRT(SQR(compspec->data[i]) + + SQR(compspec->data[compspec->length - i]) ); + } +#ifdef HAVE_FFTW3 + // for even length, make sure last element is > 0 + if (2 * (compspec->length / 2) == compspec->length) { +#endif + spectrum->norm[spectrum->length-1] = + ABS(compspec->data[compspec->length/2]); +#ifdef HAVE_FFTW3 + } else { + i = spectrum->length - 1; + spectrum->norm[i] = SQRT(SQR(compspec->data[i]) + + SQR(compspec->data[compspec->length - i]) ); + } +#endif +} + +void aubio_fft_get_imag(const cvec_t * spectrum, fvec_t * compspec) { + uint_t i; + for (i = 1; i < ( compspec->length + 1 ) / 2 /*- 1 + 1*/; i++) { + compspec->data[compspec->length - i] = + spectrum->norm[i]*SIN(spectrum->phas[i]); + } +} + +void aubio_fft_get_real(const cvec_t * spectrum, fvec_t * compspec) { + uint_t i; + for (i = 0; i < compspec->length / 2 + 1; i++) { + compspec->data[i] = + spectrum->norm[i]*COS(spectrum->phas[i]); + } +} diff --git a/aubio/src/spectral/fft.h b/aubio/src/spectral/fft.h new file mode 100644 index 00000000..21072c8b --- /dev/null +++ b/aubio/src/spectral/fft.h @@ -0,0 +1,144 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Fast Fourier Transform + + Depending on how aubio was compiled, FFT are computed using one of: + - [Ooura](http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html) + - [FFTW3](http://www.fftw.org) + - [vDSP](https://developer.apple.com/library/mac/#documentation/Accelerate/Reference/vDSPRef/Reference/reference.html) + + \example spectral/test-fft.c + +*/ + +#ifndef AUBIO_FFT_H +#define AUBIO_FFT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** FFT object + + This object computes forward and backward FFTs. + +*/ +typedef struct _aubio_fft_t aubio_fft_t; + +/** create new FFT computation object + + \param size length of the FFT + +*/ +aubio_fft_t * new_aubio_fft (uint_t size); +/** delete FFT object + + \param s fft object as returned by new_aubio_fft + +*/ +void del_aubio_fft(aubio_fft_t * s); + +/** compute forward FFT + + \param s fft object as returned by new_aubio_fft + \param input input signal + \param spectrum output spectrum + +*/ +void aubio_fft_do (aubio_fft_t *s, const fvec_t * input, cvec_t * spectrum); +/** compute backward (inverse) FFT + + \param s fft object as returned by new_aubio_fft + \param spectrum input spectrum + \param output output signal + +*/ +void aubio_fft_rdo (aubio_fft_t *s, const cvec_t * spectrum, fvec_t * output); + +/** compute forward FFT + + \param s fft object as returned by new_aubio_fft + \param input real input signal + \param compspec complex output fft real/imag + +*/ +void aubio_fft_do_complex (aubio_fft_t *s, const fvec_t * input, fvec_t * compspec); +/** compute backward (inverse) FFT from real/imag + + \param s fft object as returned by new_aubio_fft + \param compspec real/imag input fft array + \param output real output array + +*/ +void aubio_fft_rdo_complex (aubio_fft_t *s, const fvec_t * compspec, fvec_t * output); + +/** convert real/imag spectrum to norm/phas spectrum + + \param compspec real/imag input fft array + \param spectrum cvec norm/phas output array + +*/ +void aubio_fft_get_spectrum(const fvec_t * compspec, cvec_t * spectrum); +/** convert real/imag spectrum to norm/phas spectrum + + \param compspec real/imag input fft array + \param spectrum cvec norm/phas output array + +*/ +void aubio_fft_get_realimag(const cvec_t * spectrum, fvec_t * compspec); + +/** compute phas spectrum from real/imag parts + + \param compspec real/imag input fft array + \param spectrum cvec norm/phas output array + +*/ +void aubio_fft_get_phas(const fvec_t * compspec, cvec_t * spectrum); +/** compute imaginary part from the norm/phas cvec + + \param spectrum norm/phas input array + \param compspec real/imag output fft array + +*/ +void aubio_fft_get_imag(const cvec_t * spectrum, fvec_t * compspec); + +/** compute norm component from real/imag parts + + \param compspec real/imag input fft array + \param spectrum cvec norm/phas output array + +*/ +void aubio_fft_get_norm(const fvec_t * compspec, cvec_t * spectrum); +/** compute real part from norm/phas components + + \param spectrum norm/phas input array + \param compspec real/imag output fft array + +*/ +void aubio_fft_get_real(const cvec_t * spectrum, fvec_t * compspec); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FFT_H */ diff --git a/aubio/src/spectral/filterbank.c b/aubio/src/spectral/filterbank.c new file mode 100644 index 00000000..e82d93dd --- /dev/null +++ b/aubio/src/spectral/filterbank.c @@ -0,0 +1,134 @@ +/* + Copyright (C) 2007-2009 Paul Brossier + and Amaury Hazan + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "fmat.h" +#include "cvec.h" +#include "vecutils.h" +#include "spectral/filterbank.h" +#include "mathutils.h" + +/** \brief A structure to store a set of n_filters filters of lenghts win_s */ +struct _aubio_filterbank_t +{ + uint_t win_s; + uint_t n_filters; + fmat_t *filters; + smpl_t norm; + smpl_t power; +}; + +aubio_filterbank_t * +new_aubio_filterbank (uint_t n_filters, uint_t win_s) +{ + /* allocate space for filterbank object */ + aubio_filterbank_t *fb = AUBIO_NEW (aubio_filterbank_t); + + if ((sint_t)n_filters <= 0) { + AUBIO_ERR("filterbank: n_filters should be > 0, got %d\n", n_filters); + goto fail; + } + if ((sint_t)win_s <= 0) { + AUBIO_ERR("filterbank: win_s should be > 0, got %d\n", win_s); + goto fail; + } + fb->win_s = win_s; + fb->n_filters = n_filters; + + /* allocate filter tables, a matrix of length win_s and of height n_filters */ + fb->filters = new_fmat (n_filters, win_s / 2 + 1); + + fb->norm = 1; + + fb->power = 1; + + return fb; +fail: + AUBIO_FREE (fb); + return NULL; +} + +void +del_aubio_filterbank (aubio_filterbank_t * fb) +{ + del_fmat (fb->filters); + AUBIO_FREE (fb); +} + +void +aubio_filterbank_do (aubio_filterbank_t * f, const cvec_t * in, fvec_t * out) +{ + /* apply filter to all input channel, provided out has enough channels */ + //uint_t max_filters = MIN (f->n_filters, out->length); + //uint_t max_length = MIN (in->length, f->filters->length); + + // view cvec->norm as fvec->data + fvec_t tmp; + tmp.length = in->length; + tmp.data = in->norm; + + if (f->power != 1.) fvec_pow(&tmp, f->power); + + fmat_vecmul(f->filters, &tmp, out); + + return; +} + +fmat_t * +aubio_filterbank_get_coeffs (const aubio_filterbank_t * f) +{ + return f->filters; +} + +uint_t +aubio_filterbank_set_coeffs (aubio_filterbank_t * f, const fmat_t * filter_coeffs) +{ + fmat_copy(filter_coeffs, f->filters); + return 0; +} + +uint_t +aubio_filterbank_set_norm (aubio_filterbank_t *f, smpl_t norm) +{ + if (norm != 0 && norm != 1) return AUBIO_FAIL; + f->norm = norm; + return AUBIO_OK; +} + +smpl_t +aubio_filterbank_get_norm (aubio_filterbank_t *f) +{ + return f->norm; +} + +uint_t +aubio_filterbank_set_power (aubio_filterbank_t *f, smpl_t power) +{ + f->power = power; + return AUBIO_OK; +} + +smpl_t +aubio_filterbank_get_power (aubio_filterbank_t *f) +{ + return f->power; +} diff --git a/aubio/src/spectral/filterbank.h b/aubio/src/spectral/filterbank.h new file mode 100644 index 00000000..876b14d3 --- /dev/null +++ b/aubio/src/spectral/filterbank.h @@ -0,0 +1,131 @@ +/* + Copyright (C) 2007-2013 Paul Brossier + and Amaury Hazan + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Filterbank object + + General-purpose spectral filterbank object. + + \example spectral/test-filterbank.c + +*/ + +#ifndef AUBIO_FILTERBANK_H +#define AUBIO_FILTERBANK_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** filterbank object + + This object stores a matrix of spectral filter coefficients. + + */ +typedef struct _aubio_filterbank_t aubio_filterbank_t; + +/** create filterbank object + + \param n_filters number of filters to create + \param win_s size of analysis buffer (and length the FFT transform) + +*/ +aubio_filterbank_t *new_aubio_filterbank (uint_t n_filters, uint_t win_s); + +/** destroy filterbank object + + \param f filterbank object, as returned by new_aubio_filterbank() + +*/ +void del_aubio_filterbank (aubio_filterbank_t * f); + +/** compute filterbank + + \param f filterbank object, as returned by new_aubio_filterbank() + \param in input spectrum containing an input spectrum of length `win_s` + \param out output vector containing the energy found in each band, `nfilt` output values + +*/ +void aubio_filterbank_do (aubio_filterbank_t * f, const cvec_t * in, fvec_t * out); + +/** return a pointer to the matrix object containing all filter coefficients + + \param f filterbank object, as returned by new_aubio_filterbank() + + */ +fmat_t *aubio_filterbank_get_coeffs (const aubio_filterbank_t * f); + +/** copy filter coefficients to the filterbank + + \param f filterbank object, as returned by new_aubio_filterbank() + \param filters filter bank coefficients to copy from + + */ +uint_t aubio_filterbank_set_coeffs (aubio_filterbank_t * f, const fmat_t * filters); + +/** set norm parameter + + \param f filterbank object, as returned by new_aubio_filterbank() + \param norm `1` to norm the filters, `0` otherwise. + + If set to `0`, the filters will not be normalized. If set to `1`, + each filter will be normalized to one. Defaults to `1`. + + This function should be called *before* setting the filters with one of + aubio_filterbank_set_triangle_bands(), aubio_filterbank_set_mel_coeffs(), + aubio_filterbank_set_mel_coeffs_htk(), or + aubio_filterbank_set_mel_coeffs_slaney(). + + */ +uint_t aubio_filterbank_set_norm (aubio_filterbank_t *f, smpl_t norm); + +/** get norm parameter + + \param f filterbank object, as returned by new_aubio_filterbank() + \returns `1` if norm is set, `0` otherwise. Defaults to `1`. + + */ +smpl_t aubio_filterbank_get_norm (aubio_filterbank_t *f); + +/** set power parameter + + \param f filterbank object, as returned by new_aubio_filterbank() + \param power Raise norm of the input spectrum norm to this power before + computing filterbank. Defaults to `1`. + + */ +uint_t aubio_filterbank_set_power (aubio_filterbank_t *f, smpl_t power); + +/** get power parameter + + \param f filterbank object, as returned by new_aubio_filterbank() + \return current power parameter. Defaults to `1`. + + */ +smpl_t aubio_filterbank_get_power (aubio_filterbank_t *f); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FILTERBANK_H */ diff --git a/aubio/src/spectral/filterbank_mel.c b/aubio/src/spectral/filterbank_mel.c new file mode 100644 index 00000000..37713a3a --- /dev/null +++ b/aubio/src/spectral/filterbank_mel.c @@ -0,0 +1,298 @@ +/* + Copyright (C) 2007-2009 Paul Brossier + and Amaury Hazan + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fmat.h" +#include "fvec.h" +#include "cvec.h" +#include "spectral/filterbank.h" +#include "spectral/filterbank_mel.h" +#include "mathutils.h" + +uint_t +aubio_filterbank_set_triangle_bands (aubio_filterbank_t * fb, + const fvec_t * freqs, smpl_t samplerate) +{ + + fmat_t *filters = aubio_filterbank_get_coeffs (fb); + uint_t n_filters = filters->height, win_s = filters->length; + fvec_t *lower_freqs, *upper_freqs, *center_freqs; + fvec_t *triangle_heights, *fft_freqs; + + uint_t fn; /* filter counter */ + uint_t bin; /* bin counter */ + + smpl_t riseInc, downInc; + + /* freqs define the bands of triangular overlapping windows. + throw a warning if filterbank object fb is too short. */ + if (freqs->length - 2 > n_filters) { + AUBIO_WRN ("not enough filters, %d allocated but %d requested\n", + n_filters, freqs->length - 2); + } + + if (freqs->length - 2 < n_filters) { + AUBIO_WRN ("too many filters, %d allocated but %d requested\n", + n_filters, freqs->length - 2); + } + + for (fn = 0; fn < freqs->length; fn++) { + if (freqs->data[fn] < 0) { + AUBIO_ERR("filterbank_mel: freqs must contain only positive values.\n"); + return AUBIO_FAIL; + } else if (freqs->data[fn] > samplerate / 2) { + AUBIO_WRN("filterbank_mel: freqs should contain only " + "values < samplerate / 2.\n"); + } else if (fn > 0 && freqs->data[fn] < freqs->data[fn-1]) { + AUBIO_ERR("filterbank_mel: freqs should be a list of frequencies " + "sorted from low to high, but freq[%d] < freq[%d-1]\n", fn, fn); + return AUBIO_FAIL; + } else if (fn > 0 && freqs->data[fn] == freqs->data[fn-1]) { + AUBIO_WRN("filterbank_mel: set_triangle_bands received a list " + "with twice the frequency %f\n", freqs->data[fn]); + } + } + + /* convenience reference to lower/center/upper frequency for each triangle */ + lower_freqs = new_fvec (n_filters); + upper_freqs = new_fvec (n_filters); + center_freqs = new_fvec (n_filters); + + /* height of each triangle */ + triangle_heights = new_fvec (n_filters); + + /* lookup table of each bin frequency in hz */ + fft_freqs = new_fvec (win_s); + + /* fill up the lower/center/upper */ + for (fn = 0; fn < n_filters; fn++) { + lower_freqs->data[fn] = freqs->data[fn]; + center_freqs->data[fn] = freqs->data[fn + 1]; + upper_freqs->data[fn] = freqs->data[fn + 2]; + } + + /* compute triangle heights so that each triangle has unit area */ + if (aubio_filterbank_get_norm(fb)) { + for (fn = 0; fn < n_filters; fn++) { + triangle_heights->data[fn] = + 2. / (upper_freqs->data[fn] - lower_freqs->data[fn]); + } + } else { + fvec_ones (triangle_heights); + } + + /* fill fft_freqs lookup table, which assigns the frequency in hz to each bin */ + for (bin = 0; bin < win_s; bin++) { + fft_freqs->data[bin] = + aubio_bintofreq (bin, samplerate, (win_s - 1) * 2); + } + + /* zeroing of all filters */ + fmat_zeros (filters); + + /* building each filter table */ + for (fn = 0; fn < n_filters; fn++) { + + /* skip first elements */ + for (bin = 0; bin < win_s - 1; bin++) { + if (fft_freqs->data[bin] <= lower_freqs->data[fn] && + fft_freqs->data[bin + 1] > lower_freqs->data[fn]) { + bin++; + break; + } + } + + /* compute positive slope step size */ + riseInc = triangle_heights->data[fn] + / (center_freqs->data[fn] - lower_freqs->data[fn]); + + /* compute coefficients in positive slope */ + for (; bin < win_s - 1; bin++) { + filters->data[fn][bin] = + (fft_freqs->data[bin] - lower_freqs->data[fn]) * riseInc; + + if (fft_freqs->data[bin + 1] >= center_freqs->data[fn]) { + bin++; + break; + } + } + + /* compute negative slope step size */ + downInc = triangle_heights->data[fn] + / (upper_freqs->data[fn] - center_freqs->data[fn]); + + /* compute coefficents in negative slope */ + for (; bin < win_s - 1; bin++) { + filters->data[fn][bin] += + (upper_freqs->data[fn] - fft_freqs->data[bin]) * downInc; + + if (filters->data[fn][bin] < 0.) { + filters->data[fn][bin] = 0.; + } + + if (fft_freqs->data[bin + 1] >= upper_freqs->data[fn]) + break; + } + /* nothing else to do */ + + } + + /* destroy temporarly allocated vectors */ + del_fvec (lower_freqs); + del_fvec (upper_freqs); + del_fvec (center_freqs); + + del_fvec (triangle_heights); + del_fvec (fft_freqs); + + return AUBIO_OK; +} + +uint_t +aubio_filterbank_set_mel_coeffs_slaney (aubio_filterbank_t * fb, + smpl_t samplerate) +{ + /* Malcolm Slaney parameters */ + const smpl_t lowestFrequency = 133.3333; + const smpl_t linearSpacing = 66.66666666; + const smpl_t logSpacing = 1.0711703; + + const uint_t linearFilters = 13; + const uint_t logFilters = 27; + const uint_t n_filters = linearFilters + logFilters; + + uint_t fn, retval; + smpl_t lastlinearCF; + + /* buffers to compute filter frequencies */ + fvec_t *freqs; + + if (samplerate <= 0) { + AUBIO_ERR("filterbank: set_mel_coeffs_slaney samplerate should be > 0\n"); + return AUBIO_FAIL; + } + + freqs = new_fvec (n_filters + 2); + + /* first step: fill all the linear filter frequencies */ + for (fn = 0; fn < linearFilters; fn++) { + freqs->data[fn] = lowestFrequency + fn * linearSpacing; + } + lastlinearCF = freqs->data[fn - 1]; + + /* second step: fill all the log filter frequencies */ + for (fn = 0; fn < logFilters + 2; fn++) { + freqs->data[fn + linearFilters] = + lastlinearCF * (POW (logSpacing, fn + 1)); + } + + /* now compute the actual coefficients */ + retval = aubio_filterbank_set_triangle_bands (fb, freqs, samplerate); + + /* destroy vector used to store frequency limits */ + del_fvec (freqs); + + return retval; +} + +static uint_t aubio_filterbank_check_freqs (aubio_filterbank_t *fb UNUSED, + smpl_t samplerate, smpl_t *freq_min, smpl_t *freq_max) +{ + if (samplerate <= 0) { + AUBIO_ERR("filterbank: set_mel_coeffs samplerate should be > 0\n"); + return AUBIO_FAIL; + } + if (*freq_max < 0) { + AUBIO_ERR("filterbank: set_mel_coeffs freq_max should be > 0\n"); + return AUBIO_FAIL; + } else if (*freq_max == 0) { + *freq_max = samplerate / 2.; + } + if (*freq_min < 0) { + AUBIO_ERR("filterbank: set_mel_coeffs freq_min should be > 0\n"); + return AUBIO_FAIL; + } + return AUBIO_OK; +} + +uint_t +aubio_filterbank_set_mel_coeffs (aubio_filterbank_t * fb, smpl_t samplerate, + smpl_t freq_min, smpl_t freq_max) +{ + uint_t m, retval; + smpl_t start = freq_min, end = freq_max, step; + fvec_t *freqs; + fmat_t *coeffs = aubio_filterbank_get_coeffs(fb); + uint_t n_bands = coeffs->height; + + if (aubio_filterbank_check_freqs(fb, samplerate, &start, &end)) { + return AUBIO_FAIL; + } + + start = aubio_hztomel(start); + end = aubio_hztomel(end); + + freqs = new_fvec(n_bands + 2); + step = (end - start) / (n_bands + 1); + + for (m = 0; m < n_bands + 2; m++) + { + freqs->data[m] = MIN(aubio_meltohz(start + step * m), samplerate/2.); + } + + retval = aubio_filterbank_set_triangle_bands (fb, freqs, samplerate); + + /* destroy vector used to store frequency limits */ + del_fvec (freqs); + return retval; +} + +uint_t +aubio_filterbank_set_mel_coeffs_htk (aubio_filterbank_t * fb, smpl_t samplerate, + smpl_t freq_min, smpl_t freq_max) +{ + uint_t m, retval; + smpl_t start = freq_min, end = freq_max, step; + fvec_t *freqs; + fmat_t *coeffs = aubio_filterbank_get_coeffs(fb); + uint_t n_bands = coeffs->height; + + if (aubio_filterbank_check_freqs(fb, samplerate, &start, &end)) { + return AUBIO_FAIL; + } + + start = aubio_hztomel_htk(start); + end = aubio_hztomel_htk(end); + + freqs = new_fvec (n_bands + 2); + step = (end - start) / (n_bands + 1); + + for (m = 0; m < n_bands + 2; m++) + { + freqs->data[m] = MIN(aubio_meltohz_htk(start + step * m), samplerate/2.); + } + + retval = aubio_filterbank_set_triangle_bands (fb, freqs, samplerate); + + /* destroy vector used to store frequency limits */ + del_fvec (freqs); + return retval; +} diff --git a/aubio/src/spectral/filterbank_mel.h b/aubio/src/spectral/filterbank_mel.h new file mode 100644 index 00000000..06bbf6c6 --- /dev/null +++ b/aubio/src/spectral/filterbank_mel.h @@ -0,0 +1,119 @@ +/* + Copyright (C) 2007-2013 Paul Brossier + and Amaury Hazan + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Filterbank object coefficients initialization + + Functions to create set the ::aubio_filterbank_t coefficients to + - ::aubio_filterbank_set_triangle_bands: overlapping triangular bands, + - ::aubio_filterbank_set_mel_coeffs_slaney: Mel frequency bands. + + \example spectral/test-filterbank_mel.c + +*/ + +#ifndef AUBIO_FILTERBANK_MEL_H +#define AUBIO_FILTERBANK_MEL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** filterbank initialization with triangular and overlapping bands + + \param fb filterbank object + \param freqs arbitrary array of boundary frequencies + \param samplerate audio sampling rate + + This function computes the coefficients of the filterbank based on the + boundaries found in freqs, in Hz, and using triangular overlapping bands. + +*/ +uint_t aubio_filterbank_set_triangle_bands (aubio_filterbank_t * fb, + const fvec_t * freqs, smpl_t samplerate); + +/** filterbank initialization for Mel filters using Slaney's coefficients + + \param fb filterbank object + \param samplerate audio sampling rate, in Hz + + The filter coefficients are built to match exactly Malcolm Slaney's Auditory + Toolbox implementation (see file mfcc.m). The number of filters should be 40. + + References + ---------- + + Malcolm Slaney, *Auditory Toolbox Version 2, Technical Report #1998-010* + https://engineering.purdue.edu/~malcolm/interval/1998-010/ + +*/ +uint_t aubio_filterbank_set_mel_coeffs_slaney (aubio_filterbank_t * fb, + smpl_t samplerate); + +/** Mel filterbank initialization + + \param fb filterbank object + \param samplerate audio sampling rate + \param fmin start frequency, in Hz + \param fmax end frequency, in Hz + + The filterbank will be initialized with bands linearly spaced in the mel + scale, from `fmin` to `fmax`. + + References + ---------- + + Malcolm Slaney, *Auditory Toolbox Version 2, Technical Report #1998-010* + https://engineering.purdue.edu/~malcolm/interval/1998-010/ + +*/ +uint_t aubio_filterbank_set_mel_coeffs(aubio_filterbank_t * fb, + smpl_t samplerate, smpl_t fmin, smpl_t fmax); + +/** Mel filterbank initialization + + \param fb filterbank object + \param samplerate audio sampling rate + \param fmin start frequency, in Hz + \param fmax end frequency, in Hz + + The bank of filters will be initalized to to cover linearly spaced bands in + the Htk mel scale, from `fmin` to `fmax`. + + References + ---------- + + Douglas O'Shaughnessy (1987). *Speech communication: human and machine*. + Addison-Wesley. p. 150. ISBN 978-0-201-16520-3. + + HTK Speech Recognition Toolkit: http://htk.eng.cam.ac.uk/ + +*/ +uint_t aubio_filterbank_set_mel_coeffs_htk(aubio_filterbank_t * fb, + smpl_t samplerate, smpl_t fmin, smpl_t fmax); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FILTERBANK_MEL_H */ diff --git a/aubio/src/spectral/mfcc.h b/aubio/src/spectral/mfcc.h new file mode 100644 index 00000000..46fd979e --- /dev/null +++ b/aubio/src/spectral/mfcc.h @@ -0,0 +1,173 @@ +/* + Copyright (C) 2007-2013 Paul Brossier + and Amaury Hazan + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Mel-Frequency Cepstrum Coefficients object + + This object computes MFCC coefficients on an input cvec_t. + + The implementation follows the specifications established by Malcolm Slaney + in its Auditory Toolbox, available online at the following address (see + file mfcc.m): + + https://engineering.purdue.edu/~malcolm/interval/1998-010/ + + \example spectral/test-mfcc.c + +*/ + +#ifndef AUBIO_MFCC_H +#define AUBIO_MFCC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** mfcc object */ +typedef struct _aubio_mfcc_t aubio_mfcc_t; + +/** create mfcc object + + \param buf_size size of analysis buffer (and length the FFT transform) + \param samplerate audio sampling rate + \param n_coeffs number of desired coefficients + \param n_filters number of desired filters + +*/ +aubio_mfcc_t *new_aubio_mfcc (uint_t buf_size, + uint_t n_filters, uint_t n_coeffs, uint_t samplerate); + +/** delete mfcc object + + \param mf mfcc object as returned by new_aubio_mfcc + +*/ +void del_aubio_mfcc (aubio_mfcc_t * mf); + +/** mfcc object processing + + \param mf mfcc object as returned by new_aubio_mfcc + \param in input spectrum (buf_size long) + \param out output mel coefficients buffer (n_coeffs long) + +*/ +void aubio_mfcc_do (aubio_mfcc_t * mf, const cvec_t * in, fvec_t * out); + +/** set power parameter + + \param mf mfcc object, as returned by new_aubio_mfcc() + \param power Raise norm of the input spectrum norm to this power before + computing filterbank. Defaults to `1`. + + See aubio_filterbank_set_power(). + + */ +uint_t aubio_mfcc_set_power (aubio_mfcc_t *mf, smpl_t power); + +/** get power parameter + + \param mf mfcc object, as returned by new_aubio_mfcc() + \return current power parameter. Defaults to `1`. + + See aubio_filterbank_get_power(). + + */ +smpl_t aubio_mfcc_get_power (aubio_mfcc_t *mf); + +/** set scaling parameter + + \param mf mfcc object, as returned by new_aubio_mfcc() + \param scale Scaling value to apply. + + Scales the output of the filterbank after taking its logarithm and before + computing the DCT. Defaults to `1`. + +*/ +uint_t aubio_mfcc_set_scale (aubio_mfcc_t *mf, smpl_t scale); + +/** get scaling parameter + + \param mf mfcc object, as returned by new_aubio_mfcc() + \return current scaling parameter. Defaults to `1`. + + */ +smpl_t aubio_mfcc_get_scale (aubio_mfcc_t *mf); + +/** Mel filterbank initialization + + \param mf mfcc object + \param fmin start frequency, in Hz + \param fmax end frequency, in Hz + + The filterbank will be initialized with bands linearly spaced in the mel + scale, from `fmin` to `fmax`. + + See also + -------- + + aubio_filterbank_set_mel_coeffs() + +*/ +uint_t aubio_mfcc_set_mel_coeffs (aubio_mfcc_t *mf, + smpl_t fmin, smpl_t fmax); + +/** Mel filterbank initialization + + \param mf mfcc object + \param fmin start frequency, in Hz + \param fmax end frequency, in Hz + + The bank of filters will be initalized to to cover linearly spaced bands in + the Htk mel scale, from `fmin` to `fmax`. + + See also + -------- + + aubio_filterbank_set_mel_coeffs_htk() + +*/ +uint_t aubio_mfcc_set_mel_coeffs_htk (aubio_mfcc_t *mf, + smpl_t fmin, smpl_t fmax); + +/** Mel filterbank initialization (Auditory Toolbox's parameters) + + \param mf mfcc object + + The filter coefficients are built to match exactly Malcolm Slaney's Auditory + Toolbox implementation. The number of filters should be 40. + + This is the default filterbank when `mf` was created with `n_filters = 40`. + + See also + -------- + + aubio_filterbank_set_mel_coeffs_slaney() + +*/ +uint_t aubio_mfcc_set_mel_coeffs_slaney (aubio_mfcc_t *mf); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_MFCC_H */ diff --git a/aubio/src/spectral/ooura_fft8g.c b/aubio/src/spectral/ooura_fft8g.c new file mode 100644 index 00000000..394bea08 --- /dev/null +++ b/aubio/src/spectral/ooura_fft8g.c @@ -0,0 +1,1672 @@ +// modifications made for aubio: +// - replace all 'double' with 'smpl_t' +// - include "aubio_priv.h" (for config.h and types.h) +// - add missing prototypes +// - use COS, SIN, and ATAN macros +// - add cast to (smpl_t) to avoid float conversion warnings +// - declare initialization as static +// - prefix public function with aubio_ooura_ + +#include "aubio_priv.h" + +void aubio_ooura_cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w); +void aubio_ooura_dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w); +void aubio_ooura_dfst(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w); +static void makewt(int nw, int *ip, smpl_t *w); +static void makect(int nc, int *ip, smpl_t *c); +static void bitrv2(int n, int *ip, smpl_t *a); +static void bitrv2conj(int n, int *ip, smpl_t *a); +static void cftfsub(int n, smpl_t *a, smpl_t *w); +static void cftbsub(int n, smpl_t *a, smpl_t *w); +static void cft1st(int n, smpl_t *a, smpl_t *w); +static void cftmdl(int n, int l, smpl_t *a, smpl_t *w); +static void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); +static void rftbsub(int n, smpl_t *a, int nc, smpl_t *c); +static void dctsub(int n, smpl_t *a, int nc, smpl_t *c); +static void dstsub(int n, smpl_t *a, int nc, smpl_t *c); + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :8, 4, 2 + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, smpl_t *, int *, smpl_t *); + void rdft(int, int, smpl_t *, int *, smpl_t *); + void ddct(int, int, smpl_t *, int *, smpl_t *); + void ddst(int, int, smpl_t *, int *, smpl_t *); + void dfct(int, smpl_t *, smpl_t *, int *, smpl_t *); + void dfst(int, smpl_t *, smpl_t *, int *, smpl_t *); + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (smpl_t *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (smpl_t *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (smpl_t *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (smpl_t *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (smpl_t *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (smpl_t *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (smpl_t *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (smpl_t *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (smpl_t *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (smpl_t *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (smpl_t *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (smpl_t *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (smpl_t *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + + +void aubio_ooura_cdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +{ + void makewt(int nw, int *ip, smpl_t *w); + void bitrv2(int n, int *ip, smpl_t *a); + void bitrv2conj(int n, int *ip, smpl_t *a); + void cftfsub(int n, smpl_t *a, smpl_t *w); + void cftbsub(int n, smpl_t *a, smpl_t *w); + + if (n > (ip[0] << 2)) { + makewt(n >> 2, ip, w); + } + if (n > 4) { + if (isgn >= 0) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + } else { + bitrv2conj(n, ip + 2, a); + cftbsub(n, a, w); + } + } else if (n == 4) { + cftfsub(n, a, w); + } +} + + +void aubio_ooura_rdft(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +{ + void makewt(int nw, int *ip, smpl_t *w); + void makect(int nc, int *ip, smpl_t *c); + void bitrv2(int n, int *ip, smpl_t *a); + void cftfsub(int n, smpl_t *a, smpl_t *w); + void cftbsub(int n, smpl_t *a, smpl_t *w); + void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); + void rftbsub(int n, smpl_t *a, int nc, smpl_t *c); + int nw, nc; + smpl_t xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = (smpl_t)0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } +} + + +void aubio_ooura_ddct(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +{ + void makewt(int nw, int *ip, smpl_t *w); + void makect(int nc, int *ip, smpl_t *c); + void bitrv2(int n, int *ip, smpl_t *a); + void cftfsub(int n, smpl_t *a, smpl_t *w); + void cftbsub(int n, smpl_t *a, smpl_t *w); + void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); + void rftbsub(int n, smpl_t *a, int nc, smpl_t *c); + void dctsub(int n, smpl_t *a, int nc, smpl_t *c); + int j, nw, nc; + smpl_t xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = a[j] - a[j - 1]; + a[j] += a[j - 1]; + } + a[1] = a[0] - xr; + a[0] += xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } + dctsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = a[j] - a[j + 1]; + a[j] += a[j + 1]; + } + a[n - 1] = xr; + } +} + + +void aubio_ooura_ddst(int n, int isgn, smpl_t *a, int *ip, smpl_t *w) +{ + void makewt(int nw, int *ip, smpl_t *w); + void makect(int nc, int *ip, smpl_t *c); + void bitrv2(int n, int *ip, smpl_t *a); + void cftfsub(int n, smpl_t *a, smpl_t *w); + void cftbsub(int n, smpl_t *a, smpl_t *w); + void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); + void rftbsub(int n, smpl_t *a, int nc, smpl_t *c); + void dstsub(int n, smpl_t *a, int nc, smpl_t *c); + int j, nw, nc; + smpl_t xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = -a[j] - a[j - 1]; + a[j] -= a[j - 1]; + } + a[1] = a[0] + xr; + a[0] -= xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } + dstsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = -a[j] - a[j + 1]; + a[j] -= a[j + 1]; + } + a[n - 1] = -xr; + } +} + + +void aubio_ooura_dfct(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) +{ + void makewt(int nw, int *ip, smpl_t *w); + void makect(int nc, int *ip, smpl_t *c); + void bitrv2(int n, int *ip, smpl_t *a); + void cftfsub(int n, smpl_t *a, smpl_t *w); + void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); + void dctsub(int n, smpl_t *a, int nc, smpl_t *c); + int j, k, l, m, mh, nw, nc; + smpl_t xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + m = n >> 1; + yi = a[m]; + xi = a[0] + a[n]; + a[0] -= a[n]; + t[0] = xi - yi; + t[m] = xi + yi; + if (n > 2) { + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] - a[n - j]; + xi = a[j] + a[n - j]; + yr = a[k] - a[n - k]; + yi = a[k] + a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi - yi; + t[k] = xi + yi; + } + t[mh] = a[mh] + a[n - mh]; + a[mh] -= a[n - mh]; + dctsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[0] - a[1]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] + a[j + 1]; + a[2 * j - 1] = a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dctsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[0] - t[1]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = t[j] - t[j + 1]; + a[k + l] = t[j] + t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 0; j < mh; j++) { + k = m - j; + t[j] = t[m + k] - t[m + j]; + t[k] = t[m + k] + t[m + j]; + } + t[mh] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + a[n] = t[2] - t[1]; + a[0] = t[2] + t[1]; + } else { + a[1] = a[0]; + a[2] = t[0]; + a[0] = t[1]; + } +} + + +void aubio_ooura_dfst(int n, smpl_t *a, smpl_t *t, int *ip, smpl_t *w) +{ + void makewt(int nw, int *ip, smpl_t *w); + void makect(int nc, int *ip, smpl_t *c); + void bitrv2(int n, int *ip, smpl_t *a); + void cftfsub(int n, smpl_t *a, smpl_t *w); + void rftfsub(int n, smpl_t *a, int nc, smpl_t *c); + void dstsub(int n, smpl_t *a, int nc, smpl_t *c); + int j, k, l, m, mh, nw, nc; + smpl_t xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + if (n > 2) { + m = n >> 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] + a[n - j]; + xi = a[j] - a[n - j]; + yr = a[k] + a[n - k]; + yi = a[k] - a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi + yi; + t[k] = xi - yi; + } + t[0] = a[mh] - a[n - mh]; + a[mh] += a[n - mh]; + a[0] = a[m]; + dstsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[1] - a[0]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] - a[j + 1]; + a[2 * j - 1] = -a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dstsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[1] - t[0]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = -t[j] - t[j + 1]; + a[k + l] = t[j] - t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + t[j] = t[m + k] + t[m + j]; + t[k] = t[m + k] - t[m + j]; + } + t[0] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + } + a[0] = 0; +} + + +/* -------- initializing routines -------- */ + + +#include + +void makewt(int nw, int *ip, smpl_t *w) +{ + void bitrv2(int n, int *ip, smpl_t *a); + int j, nwh; + smpl_t delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = ATAN(1.0) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = COS(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = COS(delta * j); + y = SIN(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + for (j = nwh - 2; j >= 2; j -= 2) { + x = w[2 * j]; + y = w[2 * j + 1]; + w[nwh + j] = x; + w[nwh + j + 1] = y; + } + bitrv2(nw, ip + 2, w); + } + } +} + + +void makect(int nc, int *ip, smpl_t *c) +{ + int j, nch; + smpl_t delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = ATAN(1.0) / nch; + c[0] = COS(delta * nch); + c[nch] = (smpl_t)0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = (smpl_t)0.5 * COS(delta * j); + c[nc - j] = (smpl_t)0.5 * SIN(delta * j); + } + } +} + + +/* -------- child routines -------- */ + + +void bitrv2(int n, int *ip, smpl_t *a) +{ + int j, j1, k, k1, l, m, m2; + smpl_t xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + + +void bitrv2conj(int n, int *ip, smpl_t *a) +{ + int j, j1, k, k1, l, m, m2; + smpl_t xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + j1 = k1 + m2; + k1 = j1 + m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + k1 += m2; + a[k1 + 1] = -a[k1 + 1]; + } + } else { + a[1] = -a[1]; + a[m2 + 1] = -a[m2 + 1]; + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + a[k1 + m2 + 1] = -a[k1 + m2 + 1]; + } + } +} + + +void cftfsub(int n, smpl_t *a, smpl_t *w) +{ + void cft1st(int n, smpl_t *a, smpl_t *w); + void cftmdl(int n, int l, smpl_t *a, smpl_t *w); + int j, j1, j2, j3, l; + smpl_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n >= 16) { + cft1st(n, a, w); + l = 16; + while ((l << 3) <= n) { + cftmdl(n, l, a, w); + l <<= 3; + } + } + if ((l << 1) < n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } else if ((l << 1) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cftbsub(int n, smpl_t *a, smpl_t *w) +{ + void cft1st(int n, smpl_t *a, smpl_t *w); + void cftmdl(int n, int l, smpl_t *a, smpl_t *w); + int j, j1, j2, j3, j4, j5, j6, j7, l; + smpl_t wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + l = 2; + if (n > 16) { + cft1st(n, a, w); + l = 16; + while ((l << 3) < n) { + cftmdl(n, l, a, w); + l <<= 3; + } + } + if ((l << 2) < n) { + wn4r = w[2]; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + j4 = j3 + l; + j5 = j4 + l; + j6 = j5 + l; + j7 = j6 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + y0r = x0r + x2r; + y0i = x0i - x2i; + y2r = x0r - x2r; + y2i = x0i + x2i; + y1r = x1r - x3i; + y1i = x1i - x3r; + y3r = x1r + x3i; + y3i = x1i + x3r; + x0r = a[j4] + a[j5]; + x0i = a[j4 + 1] + a[j5 + 1]; + x1r = a[j4] - a[j5]; + x1i = a[j4 + 1] - a[j5 + 1]; + x2r = a[j6] + a[j7]; + x2i = a[j6 + 1] + a[j7 + 1]; + x3r = a[j6] - a[j7]; + x3i = a[j6 + 1] - a[j7 + 1]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + a[j1] = y1r + y5r; + a[j1 + 1] = y1i - y5i; + a[j5] = y1r - y5r; + a[j5 + 1] = y1i + y5i; + a[j3] = y3r - y7i; + a[j3 + 1] = y3i - y7r; + a[j7] = y3r + y7i; + a[j7 + 1] = y3i + y7r; + a[j] = y0r + y4r; + a[j + 1] = y0i - y4i; + a[j4] = y0r - y4r; + a[j4 + 1] = y0i + y4i; + a[j2] = y2r - y6i; + a[j2 + 1] = y2i - y6r; + a[j6] = y2r + y6i; + a[j6 + 1] = y2i + y6r; + } + } else if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cft1st(int n, smpl_t *a, smpl_t *w) +{ + int j, k1; + smpl_t wn4r, wtmp, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, + wk4r, wk4i, wk5r, wk5i, wk6r, wk6i, wk7r, wk7i; + smpl_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[2]; + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + a[2] = y1r + y5r; + a[3] = y1i + y5i; + a[10] = y1r - y5r; + a[11] = y1i - y5i; + a[6] = y3r - y7i; + a[7] = y3i + y7r; + a[14] = y3r + y7i; + a[15] = y3i - y7r; + a[0] = y0r + y4r; + a[1] = y0i + y4i; + a[8] = y0r - y4r; + a[9] = y0i - y4i; + a[4] = y2r - y6i; + a[5] = y2i + y6r; + a[12] = y2r + y6i; + a[13] = y2i - y6r; + if (n > 16) { + wk1r = w[4]; + wk1i = w[5]; + x0r = a[16] + a[18]; + x0i = a[17] + a[19]; + x1r = a[16] - a[18]; + x1i = a[17] - a[19]; + x2r = a[20] + a[22]; + x2i = a[21] + a[23]; + x3r = a[20] - a[22]; + x3i = a[21] - a[23]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[24] + a[26]; + x0i = a[25] + a[27]; + x1r = a[24] - a[26]; + x1i = a[25] - a[27]; + x2r = a[28] + a[30]; + x2i = a[29] + a[31]; + x3r = a[28] - a[30]; + x3i = a[29] - a[31]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x3r - x1i; + y5r = wk1i * x0r - wk1r * x0i; + y5i = wk1i * x0i + wk1r * x0r; + y7r = wk1r * x2r + wk1i * x2i; + y7i = wk1r * x2i - wk1i * x2r; + x0r = wk1r * y1r - wk1i * y1i; + x0i = wk1r * y1i + wk1i * y1r; + a[18] = x0r + y5r; + a[19] = x0i + y5i; + a[26] = y5i - x0i; + a[27] = x0r - y5r; + x0r = wk1i * y3r - wk1r * y3i; + x0i = wk1i * y3i + wk1r * y3r; + a[22] = x0r - y7r; + a[23] = x0i + y7i; + a[30] = y7i - x0i; + a[31] = x0r + y7r; + a[16] = y0r + y4r; + a[17] = y0i + y4i; + a[24] = y4i - y0i; + a[25] = y0r - y4r; + x0r = y2r - y6i; + x0i = y2i + y6r; + a[20] = wn4r * (x0r - x0i); + a[21] = wn4r * (x0i + x0r); + x0r = y6r - y2i; + x0i = y2r + y6i; + a[28] = wn4r * (x0r - x0i); + a[29] = wn4r * (x0i + x0r); + k1 = 4; + for (j = 32; j < n; j += 16) { + k1 += 4; + wk1r = w[k1]; + wk1i = w[k1 + 1]; + wk2r = w[k1 + 2]; + wk2i = w[k1 + 3]; + wtmp = 2 * wk2i; + wk3r = wk1r - wtmp * wk1i; + wk3i = wtmp * wk1r - wk1i; + wk4r = 1 - wtmp * wk2i; + wk4i = wtmp * wk2r; + wtmp = 2 * wk4i; + wk5r = wk3r - wtmp * wk1i; + wk5i = wtmp * wk1r - wk3i; + wk6r = wk2r - wtmp * wk2i; + wk6i = wtmp * wk2r - wk2i; + wk7r = wk1r - wtmp * wk3i; + wk7i = wtmp * wk3r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + x0r = y1r + y5r; + x0i = y1i + y5i; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = y1r - y5r; + x0i = y1i - y5i; + a[j + 10] = wk5r * x0r - wk5i * x0i; + a[j + 11] = wk5r * x0i + wk5i * x0r; + x0r = y3r - y7i; + x0i = y3i + y7r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + x0r = y3r + y7i; + x0i = y3i - y7r; + a[j + 14] = wk7r * x0r - wk7i * x0i; + a[j + 15] = wk7r * x0i + wk7i * x0r; + a[j] = y0r + y4r; + a[j + 1] = y0i + y4i; + x0r = y0r - y4r; + x0i = y0i - y4i; + a[j + 8] = wk4r * x0r - wk4i * x0i; + a[j + 9] = wk4r * x0i + wk4i * x0r; + x0r = y2r - y6i; + x0i = y2i + y6r; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = y2r + y6i; + x0i = y2i - y6r; + a[j + 12] = wk6r * x0r - wk6i * x0i; + a[j + 13] = wk6r * x0i + wk6i * x0r; + } + } +} + + +void cftmdl(int n, int l, smpl_t *a, smpl_t *w) +{ + int j, j1, j2, j3, j4, j5, j6, j7, k, k1, m; + smpl_t wn4r, wtmp, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, + wk4r, wk4i, wk5r, wk5i, wk6r, wk6i, wk7r, wk7i; + smpl_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + m = l << 3; + wn4r = w[2]; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + j4 = j3 + l; + j5 = j4 + l; + j6 = j5 + l; + j7 = j6 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[j4] + a[j5]; + x0i = a[j4 + 1] + a[j5 + 1]; + x1r = a[j4] - a[j5]; + x1i = a[j4 + 1] - a[j5 + 1]; + x2r = a[j6] + a[j7]; + x2i = a[j6 + 1] + a[j7 + 1]; + x3r = a[j6] - a[j7]; + x3i = a[j6 + 1] - a[j7 + 1]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + a[j1] = y1r + y5r; + a[j1 + 1] = y1i + y5i; + a[j5] = y1r - y5r; + a[j5 + 1] = y1i - y5i; + a[j3] = y3r - y7i; + a[j3 + 1] = y3i + y7r; + a[j7] = y3r + y7i; + a[j7 + 1] = y3i - y7r; + a[j] = y0r + y4r; + a[j + 1] = y0i + y4i; + a[j4] = y0r - y4r; + a[j4 + 1] = y0i - y4i; + a[j2] = y2r - y6i; + a[j2 + 1] = y2i + y6r; + a[j6] = y2r + y6i; + a[j6 + 1] = y2i - y6r; + } + if (m < n) { + wk1r = w[4]; + wk1i = w[5]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + j4 = j3 + l; + j5 = j4 + l; + j6 = j5 + l; + j7 = j6 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[j4] + a[j5]; + x0i = a[j4 + 1] + a[j5 + 1]; + x1r = a[j4] - a[j5]; + x1i = a[j4 + 1] - a[j5 + 1]; + x2r = a[j6] + a[j7]; + x2i = a[j6 + 1] + a[j7 + 1]; + x3r = a[j6] - a[j7]; + x3i = a[j6 + 1] - a[j7 + 1]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x3r - x1i; + y5r = wk1i * x0r - wk1r * x0i; + y5i = wk1i * x0i + wk1r * x0r; + y7r = wk1r * x2r + wk1i * x2i; + y7i = wk1r * x2i - wk1i * x2r; + x0r = wk1r * y1r - wk1i * y1i; + x0i = wk1r * y1i + wk1i * y1r; + a[j1] = x0r + y5r; + a[j1 + 1] = x0i + y5i; + a[j5] = y5i - x0i; + a[j5 + 1] = x0r - y5r; + x0r = wk1i * y3r - wk1r * y3i; + x0i = wk1i * y3i + wk1r * y3r; + a[j3] = x0r - y7r; + a[j3 + 1] = x0i + y7i; + a[j7] = y7i - x0i; + a[j7 + 1] = x0r + y7r; + a[j] = y0r + y4r; + a[j + 1] = y0i + y4i; + a[j4] = y4i - y0i; + a[j4 + 1] = y0r - y4r; + x0r = y2r - y6i; + x0i = y2i + y6r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = y6r - y2i; + x0i = y2r + y6i; + a[j6] = wn4r * (x0r - x0i); + a[j6 + 1] = wn4r * (x0i + x0r); + } + k1 = 4; + for (k = 2 * m; k < n; k += m) { + k1 += 4; + wk1r = w[k1]; + wk1i = w[k1 + 1]; + wk2r = w[k1 + 2]; + wk2i = w[k1 + 3]; + wtmp = 2 * wk2i; + wk3r = wk1r - wtmp * wk1i; + wk3i = wtmp * wk1r - wk1i; + wk4r = 1 - wtmp * wk2i; + wk4i = wtmp * wk2r; + wtmp = 2 * wk4i; + wk5r = wk3r - wtmp * wk1i; + wk5i = wtmp * wk1r - wk3i; + wk6r = wk2r - wtmp * wk2i; + wk6i = wtmp * wk2r - wk2i; + wk7r = wk1r - wtmp * wk3i; + wk7i = wtmp * wk3r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + j4 = j3 + l; + j5 = j4 + l; + j6 = j5 + l; + j7 = j6 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[j4] + a[j5]; + x0i = a[j4 + 1] + a[j5 + 1]; + x1r = a[j4] - a[j5]; + x1i = a[j4 + 1] - a[j5 + 1]; + x2r = a[j6] + a[j7]; + x2i = a[j6 + 1] + a[j7 + 1]; + x3r = a[j6] - a[j7]; + x3i = a[j6 + 1] - a[j7 + 1]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + x0r = y1r + y5r; + x0i = y1i + y5i; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r - y5r; + x0i = y1i - y5i; + a[j5] = wk5r * x0r - wk5i * x0i; + a[j5 + 1] = wk5r * x0i + wk5i * x0r; + x0r = y3r - y7i; + x0i = y3i + y7r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + x0r = y3r + y7i; + x0i = y3i - y7r; + a[j7] = wk7r * x0r - wk7i * x0i; + a[j7 + 1] = wk7r * x0i + wk7i * x0r; + a[j] = y0r + y4r; + a[j + 1] = y0i + y4i; + x0r = y0r - y4r; + x0i = y0i - y4i; + a[j4] = wk4r * x0r - wk4i * x0i; + a[j4 + 1] = wk4r * x0i + wk4i * x0r; + x0r = y2r - y6i; + x0i = y2i + y6r; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = y2r + y6i; + x0i = y2i - y6r; + a[j6] = wk6r * x0r - wk6i * x0i; + a[j6 + 1] = wk6r * x0i + wk6i * x0r; + } + } + } +} + + +void rftfsub(int n, smpl_t *a, int nc, smpl_t *c) +{ + int j, k, kk, ks, m; + smpl_t wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = (smpl_t)0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void rftbsub(int n, smpl_t *a, int nc, smpl_t *c) +{ + int j, k, kk, ks, m; + smpl_t wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = (smpl_t)0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} + + +void dctsub(int n, smpl_t *a, int nc, smpl_t *c) +{ + int j, k, kk, ks, m; + smpl_t wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[j] - wkr * a[k]; + a[j] = wkr * a[j] + wki * a[k]; + a[k] = xr; + } + a[m] *= c[0]; +} + + +void dstsub(int n, smpl_t *a, int nc, smpl_t *c) +{ + int j, k, kk, ks, m; + smpl_t wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[k] - wkr * a[j]; + a[k] = wkr * a[k] + wki * a[j]; + a[j] = xr; + } + a[m] *= c[0]; +} + diff --git a/aubio/src/spectral/phasevoc.c b/aubio/src/spectral/phasevoc.c new file mode 100644 index 00000000..05ebdb06 --- /dev/null +++ b/aubio/src/spectral/phasevoc.c @@ -0,0 +1,224 @@ +/* + Copyright (C) 2003-2014 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "cvec.h" +#include "mathutils.h" +#include "spectral/fft.h" +#include "spectral/phasevoc.h" + +/** phasevocoder internal object */ +struct _aubio_pvoc_t { + uint_t win_s; /** grain length */ + uint_t hop_s; /** overlap step */ + aubio_fft_t * fft; /** fft object */ + fvec_t * data; /** current input grain, [win_s] frames */ + fvec_t * dataold; /** memory of past grain, [win_s-hop_s] frames */ + fvec_t * synth; /** current output grain, [win_s] frames */ + fvec_t * synthold; /** memory of past grain, [win_s-hop_s] frames */ + fvec_t * w; /** grain window [win_s] */ + uint_t start; /** where to start additive synthesis */ + uint_t end; /** where to end it */ + smpl_t scale; /** scaling factor for synthesis */ + uint_t end_datasize; /** size of memory to end */ + uint_t hop_datasize; /** size of memory to hop_s */ +}; + + +/** returns data and dataold slided by hop_s */ +static void aubio_pvoc_swapbuffers(aubio_pvoc_t *pv, const fvec_t *new); + +/** do additive synthesis from 'old' and 'cur' */ +static void aubio_pvoc_addsynth(aubio_pvoc_t *pv, fvec_t * synthnew); + +void aubio_pvoc_do(aubio_pvoc_t *pv, const fvec_t * datanew, cvec_t *fftgrain) { + /* slide */ + aubio_pvoc_swapbuffers(pv, datanew); + /* windowing */ + fvec_weight(pv->data, pv->w); + /* shift */ + fvec_shift(pv->data); + /* calculate fft */ + aubio_fft_do (pv->fft,pv->data,fftgrain); +} + +void aubio_pvoc_rdo(aubio_pvoc_t *pv,cvec_t * fftgrain, fvec_t * synthnew) { + /* calculate rfft */ + aubio_fft_rdo(pv->fft,fftgrain,pv->synth); + /* unshift */ + fvec_ishift(pv->synth); + /* windowing */ + // if overlap = 50%, do not apply window (identity) + if (pv->hop_s * 2 < pv->win_s) { + fvec_weight(pv->synth, pv->w); + } + /* additive synthesis */ + aubio_pvoc_addsynth(pv, synthnew); +} + +aubio_pvoc_t * new_aubio_pvoc (uint_t win_s, uint_t hop_s) { + aubio_pvoc_t * pv = AUBIO_NEW(aubio_pvoc_t); + + /* if (win_s < 2*hop_s) { + AUBIO_WRN("Hop size bigger than half the window size!\n"); + } */ + + if ((sint_t)hop_s < 1) { + AUBIO_ERR("pvoc: got hop_size %d, but can not be < 1\n", hop_s); + goto beach; + } else if ((sint_t)win_s < 2) { + AUBIO_ERR("pvoc: got buffer_size %d, but can not be < 2\n", win_s); + goto beach; + } else if (win_s < hop_s) { + AUBIO_ERR("pvoc: hop size (%d) is larger than win size (%d)\n", hop_s, win_s); + goto beach; + } + + pv->fft = new_aubio_fft (win_s); + if (pv->fft == NULL) { + goto beach; + } + + /* remember old */ + pv->data = new_fvec (win_s); + pv->synth = new_fvec (win_s); + + /* new input output */ + if (win_s > hop_s) { + pv->dataold = new_fvec (win_s-hop_s); + pv->synthold = new_fvec (win_s-hop_s); + } else { + pv->dataold = new_fvec (1); + pv->synthold = new_fvec (1); + } + pv->w = new_aubio_window ("hanningz", win_s); + + pv->hop_s = hop_s; + pv->win_s = win_s; + + /* more than 50% overlap, overlap anyway */ + if (win_s < 2 * hop_s) pv->start = 0; + /* less than 50% overlap, reset latest grain trail */ + else pv->start = win_s - hop_s - hop_s; + + if (win_s > hop_s) pv->end = win_s - hop_s; + else pv->end = 0; + + pv->end_datasize = pv->end * sizeof(smpl_t); + pv->hop_datasize = pv->hop_s * sizeof(smpl_t); + + // for reconstruction with 75% overlap + if (win_s == hop_s * 4) { + pv->scale = 2./3.; + } else if (win_s == hop_s * 8) { + pv->scale = 1./3.; + } else if (win_s == hop_s * 2) { + pv->scale = 1.; + } else { + pv->scale = .5; + } + + return pv; + +beach: + AUBIO_FREE (pv); + return NULL; +} + +uint_t aubio_pvoc_set_window(aubio_pvoc_t *pv, const char_t *window) { + return fvec_set_window(pv->w, (char_t*)window); +} + +void del_aubio_pvoc(aubio_pvoc_t *pv) { + del_fvec(pv->data); + del_fvec(pv->synth); + del_fvec(pv->dataold); + del_fvec(pv->synthold); + del_fvec(pv->w); + del_aubio_fft(pv->fft); + AUBIO_FREE(pv); +} + +static void aubio_pvoc_swapbuffers(aubio_pvoc_t *pv, const fvec_t *new) +{ + /* some convenience pointers */ + smpl_t * data = pv->data->data; + smpl_t * dataold = pv->dataold->data; + smpl_t * datanew = new->data; +#ifndef HAVE_MEMCPY_HACKS + uint_t i; + for (i = 0; i < pv->end; i++) + data[i] = dataold[i]; + for (i = 0; i < pv->hop_s; i++) + data[pv->end + i] = datanew[i]; + for (i = 0; i < pv->end; i++) + dataold[i] = data[i + pv->hop_s]; +#else + memcpy(data, dataold, pv->end_datasize); + data += pv->end; + memcpy(data, datanew, pv->hop_datasize); + data -= pv->end; + data += pv->hop_s; + memcpy(dataold, data, pv->end_datasize); +#endif +} + +static void aubio_pvoc_addsynth(aubio_pvoc_t *pv, fvec_t *synth_new) +{ + uint_t i; + /* some convenience pointers */ + smpl_t * synth = pv->synth->data; + smpl_t * synthold = pv->synthold->data; + smpl_t * synthnew = synth_new->data; + + /* put new result in synthnew */ + for (i = 0; i < pv->hop_s; i++) + synthnew[i] = synth[i] * pv->scale; + + /* no overlap, nothing else to do */ + if (pv->end == 0) return; + + /* add new synth to old one */ + for (i = 0; i < pv->hop_s; i++) + synthnew[i] += synthold[i]; + + /* shift synthold */ + for (i = 0; i < pv->start; i++) + synthold[i] = synthold[i + pv->hop_s]; + + /* erase last frame in synthold */ + for (i = pv->start; i < pv->end; i++) + synthold[i] = 0.; + + /* additive synth */ + for (i = 0; i < pv->end; i++) + synthold[i] += synth[i + pv->hop_s] * pv->scale; +} + +uint_t aubio_pvoc_get_win(aubio_pvoc_t* pv) +{ + return pv->win_s; +} + +uint_t aubio_pvoc_get_hop(aubio_pvoc_t* pv) +{ + return pv->hop_s; +} diff --git a/aubio/src/spectral/phasevoc.h b/aubio/src/spectral/phasevoc.h new file mode 100644 index 00000000..e3caf2de --- /dev/null +++ b/aubio/src/spectral/phasevoc.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Phase vocoder object + + This object implements a phase vocoder. The spectral frames are computed + using a HanningZ window and a swapped version of the signal to simplify the + phase relationships across frames. The window sizes and overlap are specified + at creation time. + + \example spectral/test-phasevoc.c + +*/ + +#ifndef AUBIO_PHASEVOC_H +#define AUBIO_PHASEVOC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** phasevocoder object */ +typedef struct _aubio_pvoc_t aubio_pvoc_t; + +/** create phase vocoder object + + \param win_s size of analysis buffer (and length the FFT transform) + \param hop_s step size between two consecutive analysis + +*/ +aubio_pvoc_t * new_aubio_pvoc (uint_t win_s, uint_t hop_s); +/** delete phase vocoder object + + \param pv phase vocoder object as returned by new_aubio_pvoc + +*/ +void del_aubio_pvoc(aubio_pvoc_t *pv); + +/** compute spectral frame + + This function accepts an input vector of size [hop_s]. The + analysis buffer is rotated and filled with the new data. After windowing of + this signal window, the Fourier transform is computed and returned in + fftgrain as two vectors, magnitude and phase. + + \param pv phase vocoder object as returned by new_aubio_pvoc + \param in new input signal (hop_s long) + \param fftgrain output spectral frame + +*/ +void aubio_pvoc_do(aubio_pvoc_t *pv, const fvec_t *in, cvec_t * fftgrain); +/** compute signal from spectral frame + + This function takes an input spectral frame fftgrain of size + [buf_s] and computes its inverse Fourier transform. Overlap-add + synthesis is then computed using the previously synthetised frames, and the + output stored in out. + + \param pv phase vocoder object as returned by new_aubio_pvoc + \param fftgrain input spectral frame + \param out output signal (hop_s long) + +*/ +void aubio_pvoc_rdo(aubio_pvoc_t *pv, cvec_t * fftgrain, fvec_t *out); + +/** get window size + + \param pv phase vocoder to get the window size from + +*/ +uint_t aubio_pvoc_get_win(aubio_pvoc_t* pv); + +/** get hop size + + \param pv phase vocoder to get the hop size from + +*/ +uint_t aubio_pvoc_get_hop(aubio_pvoc_t* pv); + +/** set window type + + \param pv phase vocoder to set the window type + \param window_type a string representing a window + + \return 0 if successful, non-zero otherwise + + */ +uint_t aubio_pvoc_set_window(aubio_pvoc_t *pv, const char_t *window_type); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PHASEVOC_H */ diff --git a/aubio/src/spectral/specdesc.c b/aubio/src/spectral/specdesc.c new file mode 100644 index 00000000..cc1665a3 --- /dev/null +++ b/aubio/src/spectral/specdesc.c @@ -0,0 +1,429 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "cvec.h" +#include "spectral/fft.h" +#include "spectral/specdesc.h" +#include "mathutils.h" +#include "utils/hist.h" + +void aubio_specdesc_energy(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_hfc(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_complex(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_phase(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_wphase(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_specdiff(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_kl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_mkl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); +void aubio_specdesc_specflux(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset); + +extern void aubio_specdesc_centroid (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_spread (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_skewness (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_kurtosis (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_slope (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_decrease (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +extern void aubio_specdesc_rolloff (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); + +/** onsetdetection types */ +typedef enum { + aubio_onset_energy, /**< energy based */ + aubio_onset_specdiff, /**< spectral diff */ + aubio_onset_hfc, /**< high frequency content */ + aubio_onset_complex, /**< complex domain */ + aubio_onset_phase, /**< phase fast */ + aubio_onset_wphase, /**< weighted phase */ + aubio_onset_kl, /**< Kullback Liebler */ + aubio_onset_mkl, /**< modified Kullback Liebler */ + aubio_onset_specflux, /**< spectral flux */ + aubio_specmethod_centroid, /**< spectral centroid */ + aubio_specmethod_spread, /**< spectral spread */ + aubio_specmethod_skewness, /**< spectral skewness */ + aubio_specmethod_kurtosis, /**< spectral kurtosis */ + aubio_specmethod_slope, /**< spectral kurtosis */ + aubio_specmethod_decrease, /**< spectral decrease */ + aubio_specmethod_rolloff, /**< spectral rolloff */ + aubio_onset_default = aubio_onset_hfc, /**< default mode, set to hfc */ +} aubio_specdesc_type; + +/** structure to store object state */ +struct _aubio_specdesc_t { + aubio_specdesc_type onset_type; /**< onset detection type */ + /** Pointer to aubio_specdesc_ function */ + void (*funcpointer)(aubio_specdesc_t *o, + const cvec_t * fftgrain, fvec_t * onset); + smpl_t threshold; /**< minimum norm threshold for phase and specdiff */ + fvec_t *oldmag; /**< previous norm vector */ + fvec_t *dev1 ; /**< current onset detection measure vector */ + fvec_t *theta1; /**< previous phase vector, one frame behind */ + fvec_t *theta2; /**< previous phase vector, two frames behind */ + aubio_hist_t * histog; /**< histogram */ +}; + + +/* Energy based onset detection function */ +void aubio_specdesc_energy (aubio_specdesc_t *o UNUSED, + const cvec_t * fftgrain, fvec_t * onset) { + uint_t j; + onset->data[0] = 0.; + for (j=0;jlength;j++) { + onset->data[0] += SQR(fftgrain->norm[j]); + } +} + +/* High Frequency Content onset detection function */ +void aubio_specdesc_hfc(aubio_specdesc_t *o UNUSED, + const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;jlength;j++) { + onset->data[0] += (j+1)*fftgrain->norm[j]; + } +} + + +/* Complex Domain Method onset detection function */ +void aubio_specdesc_complex (aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset) { + uint_t j; + uint_t nbins = fftgrain->length; + onset->data[0] = 0.; + for (j=0;jdev1->data[j] = 2. * o->theta1->data[j] - o->theta2->data[j]; + // compute the euclidean distance in the complex domain + // sqrt ( r_1^2 + r_2^2 - 2 * r_1 * r_2 * \cos ( \phi_1 - \phi_2 ) ) + onset->data[0] += + SQRT (ABS (SQR (o->oldmag->data[j]) + SQR (fftgrain->norm[j]) + - 2 * o->oldmag->data[j] * fftgrain->norm[j] + * COS (o->dev1->data[j] - fftgrain->phas[j]))); + /* swap old phase data (need to remember 2 frames behind)*/ + o->theta2->data[j] = o->theta1->data[j]; + o->theta1->data[j] = fftgrain->phas[j]; + /* swap old magnitude data (1 frame is enough) */ + o->oldmag->data[j] = fftgrain->norm[j]; + } +} + + +/* Phase Based Method onset detection function */ +void aubio_specdesc_phase(aubio_specdesc_t *o, + const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + uint_t nbins = fftgrain->length; + onset->data[0] = 0.0; + o->dev1->data[0]=0.; + for ( j=0;jdev1->data[j] = + aubio_unwrap2pi( + fftgrain->phas[j] + -2.0*o->theta1->data[j] + +o->theta2->data[j]); + if ( o->threshold < fftgrain->norm[j] ) + o->dev1->data[j] = ABS(o->dev1->data[j]); + else + o->dev1->data[j] = 0.0; + /* keep a track of the past frames */ + o->theta2->data[j] = o->theta1->data[j]; + o->theta1->data[j] = fftgrain->phas[j]; + } + /* apply o->histogram */ + aubio_hist_dyn_notnull(o->histog,o->dev1); + /* weight it */ + aubio_hist_weight(o->histog); + /* its mean is the result */ + onset->data[0] = aubio_hist_mean(o->histog); + //onset->data[0] = fvec_mean(o->dev1); +} + +/* weighted phase */ +void +aubio_specdesc_wphase(aubio_specdesc_t *o, + const cvec_t *fftgrain, fvec_t *onset) { + uint_t i; + aubio_specdesc_phase(o, fftgrain, onset); + for (i = 0; i < fftgrain->length; i++) { + o->dev1->data[i] *= fftgrain->norm[i]; + } + /* apply o->histogram */ + aubio_hist_dyn_notnull(o->histog,o->dev1); + /* weight it */ + aubio_hist_weight(o->histog); + /* its mean is the result */ + onset->data[0] = aubio_hist_mean(o->histog); +} + +/* Spectral difference method onset detection function */ +void aubio_specdesc_specdiff(aubio_specdesc_t *o, + const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + uint_t nbins = fftgrain->length; + onset->data[0] = 0.0; + for (j=0;jdev1->data[j] = SQRT( + ABS(SQR( fftgrain->norm[j]) + - SQR(o->oldmag->data[j]))); + if (o->threshold < fftgrain->norm[j] ) + o->dev1->data[j] = ABS(o->dev1->data[j]); + else + o->dev1->data[j] = 0.0; + o->oldmag->data[j] = fftgrain->norm[j]; + } + + /* apply o->histogram (act somewhat as a low pass on the + * overall function)*/ + aubio_hist_dyn_notnull(o->histog,o->dev1); + /* weight it */ + aubio_hist_weight(o->histog); + /* its mean is the result */ + onset->data[0] = aubio_hist_mean(o->histog); +} + +/* Kullback Liebler onset detection function + * note we use ln(1+Xn/(Xn-1+0.0001)) to avoid + * negative (1.+) and infinite values (+1.e-10) */ +void aubio_specdesc_kl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;jlength;j++) { + onset->data[0] += fftgrain->norm[j] + *LOG(1.+fftgrain->norm[j]/(o->oldmag->data[j]+1.e-1)); + o->oldmag->data[j] = fftgrain->norm[j]; + } + if (isnan(onset->data[0])) onset->data[0] = 0.; +} + +/* Modified Kullback Liebler onset detection function + * note we use ln(1+Xn/(Xn-1+0.0001)) to avoid + * negative (1.+) and infinite values (+1.e-10) */ +void aubio_specdesc_mkl(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;jlength;j++) { + onset->data[0] += LOG(1.+fftgrain->norm[j]/(o->oldmag->data[j]+1.e-1)); + o->oldmag->data[j] = fftgrain->norm[j]; + } + if (isnan(onset->data[0])) onset->data[0] = 0.; +} + +/* Spectral flux */ +void aubio_specdesc_specflux(aubio_specdesc_t *o, const cvec_t * fftgrain, fvec_t * onset){ + uint_t j; + onset->data[0] = 0.; + for (j=0;jlength;j++) { + if (fftgrain->norm[j] > o->oldmag->data[j]) + onset->data[0] += fftgrain->norm[j] - o->oldmag->data[j]; + o->oldmag->data[j] = fftgrain->norm[j]; + } +} + +/* Generic function pointing to the choosen one */ +void +aubio_specdesc_do (aubio_specdesc_t *o, const cvec_t * fftgrain, + fvec_t * onset) { + o->funcpointer(o,fftgrain,onset); +} + +/* Allocate memory for an onset detection + * depending on the choosen type, allocate memory as needed + */ +aubio_specdesc_t * +new_aubio_specdesc (const char_t * onset_mode, uint_t size){ + aubio_specdesc_t * o = AUBIO_NEW(aubio_specdesc_t); + uint_t rsize = size/2+1; + aubio_specdesc_type onset_type; + if (strcmp (onset_mode, "energy") == 0) + onset_type = aubio_onset_energy; + else if (strcmp (onset_mode, "specdiff") == 0) + onset_type = aubio_onset_specdiff; + else if (strcmp (onset_mode, "hfc") == 0) + onset_type = aubio_onset_hfc; + else if (strcmp (onset_mode, "complexdomain") == 0) + onset_type = aubio_onset_complex; + else if (strcmp (onset_mode, "complex") == 0) + onset_type = aubio_onset_complex; + else if (strcmp (onset_mode, "phase") == 0) + onset_type = aubio_onset_phase; + else if (strcmp (onset_mode, "wphase") == 0) + onset_type = aubio_onset_wphase; + else if (strcmp (onset_mode, "mkl") == 0) + onset_type = aubio_onset_mkl; + else if (strcmp (onset_mode, "kl") == 0) + onset_type = aubio_onset_kl; + else if (strcmp (onset_mode, "specflux") == 0) + onset_type = aubio_onset_specflux; + else if (strcmp (onset_mode, "centroid") == 0) + onset_type = aubio_specmethod_centroid; + else if (strcmp (onset_mode, "spread") == 0) + onset_type = aubio_specmethod_spread; + else if (strcmp (onset_mode, "skewness") == 0) + onset_type = aubio_specmethod_skewness; + else if (strcmp (onset_mode, "kurtosis") == 0) + onset_type = aubio_specmethod_kurtosis; + else if (strcmp (onset_mode, "slope") == 0) + onset_type = aubio_specmethod_slope; + else if (strcmp (onset_mode, "decrease") == 0) + onset_type = aubio_specmethod_decrease; + else if (strcmp (onset_mode, "rolloff") == 0) + onset_type = aubio_specmethod_rolloff; + else if (strcmp (onset_mode, "old_default") == 0) + onset_type = aubio_onset_default; + else if (strcmp (onset_mode, "default") == 0) + onset_type = aubio_onset_default; + else { + AUBIO_ERR("specdesc: unknown spectral descriptor type '%s'\n", + onset_mode); + AUBIO_FREE(o); + return NULL; + } + switch(onset_type) { + /* for both energy and hfc, only fftgrain->norm is required */ + case aubio_onset_energy: + break; + case aubio_onset_hfc: + break; + /* the other approaches will need some more memory spaces */ + case aubio_onset_complex: + o->oldmag = new_fvec(rsize); + o->dev1 = new_fvec(rsize); + o->theta1 = new_fvec(rsize); + o->theta2 = new_fvec(rsize); + break; + case aubio_onset_phase: + case aubio_onset_wphase: + o->dev1 = new_fvec(rsize); + o->theta1 = new_fvec(rsize); + o->theta2 = new_fvec(rsize); + o->histog = new_aubio_hist(0.0, PI, 10); + o->threshold = 0.1; + break; + case aubio_onset_specdiff: + o->oldmag = new_fvec(rsize); + o->dev1 = new_fvec(rsize); + o->histog = new_aubio_hist(0.0, PI, 10); + o->threshold = 0.1; + break; + case aubio_onset_kl: + case aubio_onset_mkl: + case aubio_onset_specflux: + o->oldmag = new_fvec(rsize); + break; + default: + break; + } + + switch(onset_type) { + case aubio_onset_energy: + o->funcpointer = aubio_specdesc_energy; + break; + case aubio_onset_hfc: + o->funcpointer = aubio_specdesc_hfc; + break; + case aubio_onset_complex: + o->funcpointer = aubio_specdesc_complex; + break; + case aubio_onset_phase: + o->funcpointer = aubio_specdesc_phase; + break; + case aubio_onset_wphase: + o->funcpointer = aubio_specdesc_wphase; + break; + case aubio_onset_specdiff: + o->funcpointer = aubio_specdesc_specdiff; + break; + case aubio_onset_kl: + o->funcpointer = aubio_specdesc_kl; + break; + case aubio_onset_mkl: + o->funcpointer = aubio_specdesc_mkl; + break; + case aubio_onset_specflux: + o->funcpointer = aubio_specdesc_specflux; + break; + case aubio_specmethod_centroid: + o->funcpointer = aubio_specdesc_centroid; + break; + case aubio_specmethod_spread: + o->funcpointer = aubio_specdesc_spread; + break; + case aubio_specmethod_skewness: + o->funcpointer = aubio_specdesc_skewness; + break; + case aubio_specmethod_kurtosis: + o->funcpointer = aubio_specdesc_kurtosis; + break; + case aubio_specmethod_slope: + o->funcpointer = aubio_specdesc_slope; + break; + case aubio_specmethod_decrease: + o->funcpointer = aubio_specdesc_decrease; + break; + case aubio_specmethod_rolloff: + o->funcpointer = aubio_specdesc_rolloff; + break; + default: + break; + } + o->onset_type = onset_type; + return o; +} + +void del_aubio_specdesc (aubio_specdesc_t *o){ + switch(o->onset_type) { + case aubio_onset_energy: + break; + case aubio_onset_hfc: + break; + case aubio_onset_complex: + del_fvec(o->oldmag); + del_fvec(o->dev1); + del_fvec(o->theta1); + del_fvec(o->theta2); + break; + case aubio_onset_phase: + case aubio_onset_wphase: + del_fvec(o->dev1); + del_fvec(o->theta1); + del_fvec(o->theta2); + del_aubio_hist(o->histog); + break; + case aubio_onset_specdiff: + del_fvec(o->oldmag); + del_fvec(o->dev1); + del_aubio_hist(o->histog); + break; + case aubio_onset_kl: + case aubio_onset_mkl: + case aubio_onset_specflux: + del_fvec(o->oldmag); + break; + default: + break; + } + AUBIO_FREE(o); +} diff --git a/aubio/src/spectral/specdesc.h b/aubio/src/spectral/specdesc.h new file mode 100644 index 00000000..0f688c14 --- /dev/null +++ b/aubio/src/spectral/specdesc.h @@ -0,0 +1,204 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Spectral description functions + + All of the following spectral description functions take as arguments the FFT + of a windowed signal (as created with aubio_pvoc). They output one smpl_t per + buffer (stored in a vector of size [1]). + + \section specdesc Spectral description functions + + A list of the spectral description methods currently available follows. + + \subsection onsetdesc Onset detection functions + + These functions are designed to raise at notes attacks in music signals. + + \b \p energy : Energy based onset detection function + + This function calculates the local energy of the input spectral frame. + + \b \p hfc : High Frequency Content onset detection function + + This method computes the High Frequency Content (HFC) of the input spectral + frame. The resulting function is efficient at detecting percussive onsets. + + Paul Masri. Computer modeling of Sound for Transformation and Synthesis of + Musical Signal. PhD dissertation, University of Bristol, UK, 1996. + + \b \p complex : Complex Domain Method onset detection function + + Christopher Duxbury, Mike E. Davies, and Mark B. Sandler. Complex domain + onset detection for musical signals. In Proceedings of the Digital Audio + Effects Conference, DAFx-03, pages 90-93, London, UK, 2003. + + \b \p phase : Phase Based Method onset detection function + + Juan-Pablo Bello, Mike P. Davies, and Mark B. Sandler. Phase-based note onset + detection for music signals. In Proceedings of the IEEE International + Conference on Acoustics Speech and Signal Processing, pages 441­444, + Hong-Kong, 2003. + + \b \p wphase : Weighted Phase Deviation onset detection function + + S. Dixon. Onset detection revisited. In Proceedings of the 9th International + Conference on Digital Audio Ef- fects (DAFx) , pages 133–137, 2006. + + http://www.eecs.qmul.ac.uk/~simond/pub/2006/dafx.pdf + + \b \p specdiff : Spectral difference method onset detection function + + Jonhatan Foote and Shingo Uchihashi. The beat spectrum: a new approach to + rhythm analysis. In IEEE International Conference on Multimedia and Expo + (ICME 2001), pages 881­884, Tokyo, Japan, August 2001. + + \b \p kl : Kullback-Liebler onset detection function + + Stephen Hainsworth and Malcom Macleod. Onset detection in music audio + signals. In Proceedings of the International Computer Music Conference + (ICMC), Singapore, 2003. + + \b \p mkl : Modified Kullback-Liebler onset detection function + + Paul Brossier, ``Automatic annotation of musical audio for interactive + systems'', Chapter 2, Temporal segmentation, PhD thesis, Centre for Digital + music, Queen Mary University of London, London, UK, 2006. + + \b \p specflux : Spectral Flux + + Simon Dixon, Onset Detection Revisited, in ``Proceedings of the 9th + International Conference on Digital Audio Effects'' (DAFx-06), Montreal, + Canada, 2006. + + \subsection shapedesc Spectral shape descriptors + + The following descriptors are described in: + + Geoffroy Peeters, A large set of audio features for sound description + (similarity and classification) in the CUIDADO project, CUIDADO I.S.T. + Project Report 2004 (pdf) + + \b \p centroid : Spectral centroid + + The spectral centroid represents the barycenter of the spectrum. + + \e Note: This function returns the result in bin. To get the spectral + centroid in Hz, aubio_bintofreq() should be used. + + \b \p spread : Spectral spread + + The spectral spread is the variance of the spectral distribution around its + centroid. + + See also Standard + deviation on Wikipedia. + + \b \p skewness : Spectral skewness + + Similarly, the skewness is computed from the third order moment of the + spectrum. A negative skewness indicates more energy on the lower part of the + spectrum. A positive skewness indicates more energy on the high frequency of + the spectrum. + + See also Skewness on + Wikipedia. + + \b \p kurtosis : Spectral kurtosis + + The kurtosis is a measure of the flatness of the spectrum, computed from the + fourth order moment. + + See also Kurtosis on + Wikipedia. + + \b \p slope : Spectral slope + + The spectral slope represents decreasing rate of the spectral amplitude, + computed using a linear regression. + + \b \p decrease : Spectral decrease + + The spectral decrease is another representation of the decreasing rate, + based on perceptual criteria. + + \b \p rolloff : Spectral roll-off + + This function returns the bin number below which 95% of the spectrum energy + is found. + + \example spectral/test-specdesc.c + +*/ + + +#ifndef AUBIO_SPECDESC_H +#define AUBIO_SPECDESC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** spectral description structure */ +typedef struct _aubio_specdesc_t aubio_specdesc_t; + +/** execute spectral description function on a spectral frame + + Generic function to compute spectral description. + + \param o spectral description object as returned by new_aubio_specdesc() + \param fftgrain input signal spectrum as computed by aubio_pvoc_do + \param desc output vector (one sample long, to send to the peak picking) + +*/ +void aubio_specdesc_do (aubio_specdesc_t * o, const cvec_t * fftgrain, + fvec_t * desc); + +/** creation of a spectral description object + + \param method spectral description method + \param buf_size length of the input spectrum frame + + The parameter \p method is a string that can be any of: + + - onset novelty functions: `complex`, `energy`, `hfc`, `kl`, `mkl`, + `phase`, `specdiff`, `specflux`, `wphase`, + + - spectral descriptors: `centroid`, `decrease`, `kurtosis`, `rolloff`, + `skewness`, `slope`, `spread`. + +*/ +aubio_specdesc_t *new_aubio_specdesc (const char_t * method, uint_t buf_size); + +/** deletion of a spectral descriptor + + \param o spectral descriptor object as returned by new_aubio_specdesc() + +*/ +void del_aubio_specdesc (aubio_specdesc_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SPECDESC_H */ diff --git a/aubio/src/spectral/statistics.c b/aubio/src/spectral/statistics.c new file mode 100644 index 00000000..d3033643 --- /dev/null +++ b/aubio/src/spectral/statistics.c @@ -0,0 +1,204 @@ +/* + Copyright (C) 2007-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "cvec.h" +#include "spectral/specdesc.h" + +void aubio_specdesc_centroid (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +void aubio_specdesc_spread (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +void aubio_specdesc_skewness (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +void aubio_specdesc_kurtosis (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +void aubio_specdesc_slope (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +void aubio_specdesc_decrease (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); +void aubio_specdesc_rolloff (aubio_specdesc_t * o, const cvec_t * spec, + fvec_t * desc); + + +smpl_t cvec_sum (const cvec_t * s); +smpl_t cvec_mean (const cvec_t * s); +smpl_t cvec_centroid (const cvec_t * s); +smpl_t cvec_moment (const cvec_t * s, uint_t moment); + +smpl_t +cvec_sum (const cvec_t * s) +{ + uint_t j; + smpl_t tmp = 0.0; + for (j = 0; j < s->length; j++) { + tmp += s->norm[j]; + } + return tmp; +} + +smpl_t +cvec_mean (const cvec_t * s) +{ + return cvec_sum (s) / (smpl_t) (s->length); +} + +smpl_t +cvec_centroid (const cvec_t * spec) +{ + smpl_t sum = 0., sc = 0.; + uint_t j; + sum = cvec_sum (spec); + if (sum == 0.) { + return 0.; + } else { + for (j = 0; j < spec->length; j++) { + sc += (smpl_t) j *spec->norm[j]; + } + return sc / sum; + } +} + +smpl_t +cvec_moment (const cvec_t * spec, uint_t order) +{ + smpl_t sum = 0., centroid = 0., sc = 0.; + uint_t j; + sum = cvec_sum (spec); + if (sum == 0.) { + return 0.; + } else { + centroid = cvec_centroid (spec); + for (j = 0; j < spec->length; j++) { + sc += (smpl_t) POW(j - centroid, order) * spec->norm[j]; + } + return sc / sum; + } +} + +void +aubio_specdesc_centroid (aubio_specdesc_t * o UNUSED, const cvec_t * spec, + fvec_t * desc) +{ + desc->data[0] = cvec_centroid (spec); +} + +void +aubio_specdesc_spread (aubio_specdesc_t * o UNUSED, const cvec_t * spec, + fvec_t * desc) +{ + desc->data[0] = cvec_moment (spec, 2); +} + +void +aubio_specdesc_skewness (aubio_specdesc_t * o UNUSED, const cvec_t * spec, + fvec_t * desc) +{ + smpl_t spread; + spread = cvec_moment (spec, 2); + if (spread == 0) { + desc->data[0] = 0.; + } else { + desc->data[0] = cvec_moment (spec, 3); + desc->data[0] /= POW ( SQRT (spread), 3); + } +} + +void +aubio_specdesc_kurtosis (aubio_specdesc_t * o UNUSED, const cvec_t * spec, + fvec_t * desc) +{ + smpl_t spread; + spread = cvec_moment (spec, 2); + if (spread == 0) { + desc->data[0] = 0.; + } else { + desc->data[0] = cvec_moment (spec, 4); + desc->data[0] /= SQR (spread); + } +} + +void +aubio_specdesc_slope (aubio_specdesc_t * o UNUSED, const cvec_t * spec, + fvec_t * desc) +{ + uint_t j; + smpl_t norm = 0, sum = 0.; + // compute N * sum(j**2) - sum(j)**2 + for (j = 0; j < spec->length; j++) { + norm += j*j; + } + norm *= spec->length; + // sum_0^N(j) = length * (length + 1) / 2 + norm -= SQR( (spec->length) * (spec->length - 1.) / 2. ); + sum = cvec_sum (spec); + desc->data[0] = 0.; + if (sum == 0.) { + return; + } else { + for (j = 0; j < spec->length; j++) { + desc->data[0] += j * spec->norm[j]; + } + desc->data[0] *= spec->length; + desc->data[0] -= sum * spec->length * (spec->length - 1) / 2.; + desc->data[0] /= norm; + desc->data[0] /= sum; + } +} + +void +aubio_specdesc_decrease (aubio_specdesc_t *o UNUSED, const cvec_t * spec, + fvec_t * desc) +{ + uint_t j; smpl_t sum; + sum = cvec_sum (spec); + desc->data[0] = 0; + if (sum == 0.) { + return; + } else { + sum -= spec->norm[0]; + for (j = 1; j < spec->length; j++) { + desc->data[0] += (spec->norm[j] - spec->norm[0]) / j; + } + desc->data[0] /= sum; + } +} + +void +aubio_specdesc_rolloff (aubio_specdesc_t *o UNUSED, const cvec_t * spec, + fvec_t *desc) +{ + uint_t j; smpl_t cumsum, rollsum; + cumsum = 0.; rollsum = 0.; + for (j = 0; j < spec->length; j++) { + cumsum += SQR (spec->norm[j]); + } + if (cumsum == 0) { + desc->data[0] = 0.; + } else { + cumsum *= 0.95; + j = 0; + while (rollsum < cumsum) { + rollsum += SQR (spec->norm[j]); + j++; + } + desc->data[0] = MAX (1, j) - 1.0f; + } +} diff --git a/aubio/src/spectral/tss.h b/aubio/src/spectral/tss.h new file mode 100644 index 00000000..a4f54a15 --- /dev/null +++ b/aubio/src/spectral/tss.h @@ -0,0 +1,103 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Transient / Steady-state Separation (TSS) + + This file implement a Transient / Steady-state Separation (TSS) as described + in: + + Christopher Duxbury, Mike E. Davies, and Mark B. Sandler. Separation of + transient information in musical audio using multiresolution analysis + techniques. In Proceedings of the Digital Audio Effects Conference, DAFx-01, + pages 1--5, Limerick, Ireland, 2001. + + Available at http://www.csis.ul.ie/dafx01/proceedings/papers/duxbury.pdf + + \example spectral/test-tss.c + +*/ + +#ifndef AUBIO_TSS_H +#define AUBIO_TSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Transient / Steady-state Separation object */ +typedef struct _aubio_tss_t aubio_tss_t; + +/** create tss object + + \param buf_size buffer size + \param hop_size step size + +*/ +aubio_tss_t *new_aubio_tss (uint_t buf_size, uint_t hop_size); + +/** delete tss object + + \param o tss object as returned by new_aubio_tss() + +*/ +void del_aubio_tss (aubio_tss_t * o); + +/** split input into transient and steady states components + + \param o tss object as returned by new_aubio_tss() + \param input input spectral frame + \param trans output transient components + \param stead output steady state components + +*/ +void aubio_tss_do (aubio_tss_t * o, const cvec_t * input, cvec_t * trans, + cvec_t * stead); + +/** set transient / steady state separation threshold + + \param o tss object as returned by new_aubio_tss() + \param thrs new threshold value + +*/ +uint_t aubio_tss_set_threshold (aubio_tss_t * o, smpl_t thrs); + +/** set parameter a, defaults to 3 + + \param o tss object as returned by new_aubio_tss() + \param alpha new value for alpha parameter + +*/ +uint_t aubio_tss_set_alpha (aubio_tss_t * o, smpl_t alpha); + +/** set parameter b, defaults to 3 + + \param o tss object as returned by new_aubio_tss() + \param beta new value for beta parameter + +*/ +uint_t aubio_tss_set_beta (aubio_tss_t * o, smpl_t beta); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_TSS_H */ diff --git a/aubio/src/synth/sampler.h b/aubio/src/synth/sampler.h new file mode 100644 index 00000000..fb1304f3 --- /dev/null +++ b/aubio/src/synth/sampler.h @@ -0,0 +1,140 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_SAMPLER_H +#define AUBIO_SAMPLER_H + +/** \file + + Load and play sound files. + + This file loads a sample and gets ready to play it. + + The `_do` function adds the new samples to the input, and write the result as + the output. + + \example synth/test-sampler.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** sampler object */ +typedef struct _aubio_sampler_t aubio_sampler_t; + +/** create new sampler object + + \param samplerate the sampling rate of the new sampler + \param hop_size the block size of the new sampler + + \return the newly created ::aubio_sampler_t + +*/ +aubio_sampler_t * new_aubio_sampler(uint_t samplerate, uint_t hop_size); + +/** load source in sampler + + \param o sampler, created by new_aubio_sampler() + \param uri the uri of the source to load + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_sampler_load( aubio_sampler_t * o, const char_t * uri ); + +/** process sampler function + + \param o sampler, created by new_aubio_sampler() + \param input input of the sampler, to be added to the output + \param output output of the sampler + +This function adds the new samples from the playing source to the output. + +If `input` is not NULL and different from `output`, then the samples from `input` +are added to the output. + +*/ +void aubio_sampler_do ( aubio_sampler_t * o, const fvec_t * input, fvec_t * output); + +/** process sampler function, multiple channels + + \param o sampler, created by new_aubio_sampler() + \param input input of the sampler, to be added to the output + \param output output of the sampler + +This function adds the new samples from the playing source to the output. + +If `input` is not NULL and different from `output`, then the samples from `input` +are added to the output. + +*/ +void aubio_sampler_do_multi ( aubio_sampler_t * o, const fmat_t * input, fmat_t * output); + +/** get current playing state + + \param o sampler, created by new_aubio_sampler() + + \return 0 if not playing, 1 if playing + +*/ +uint_t aubio_sampler_get_playing ( const aubio_sampler_t * o ); + +/** set current playing state + + \param o sampler, created by new_aubio_sampler() + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_set_playing ( aubio_sampler_t * o, uint_t playing ); + +/** play sample from start + + \param o sampler, created by new_aubio_sampler() + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_play ( aubio_sampler_t * o ); + +/** stop sample + + \param o sampler, created by new_aubio_sampler() + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_sampler_stop ( aubio_sampler_t * o ); + +/** destroy ::aubio_sampler_t object + + \param o sampler, created by new_aubio_sampler() + +*/ +void del_aubio_sampler( aubio_sampler_t * o ); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SAMPLER_H */ diff --git a/aubio/src/synth/wavetable.h b/aubio/src/synth/wavetable.h new file mode 100644 index 00000000..7607fbe5 --- /dev/null +++ b/aubio/src/synth/wavetable.h @@ -0,0 +1,180 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_WAVETABLE_H +#define AUBIO_WAVETABLE_H + +/** \file + + Wavetable synthesis. + + This file creates a wavetable and plays it at different frequency. + + The `_do` function adds the new samples to the input, and write the result as + the output. + + \example synth/test-wavetable.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** wavetable object */ +typedef struct _aubio_wavetable_t aubio_wavetable_t; + +/** create new wavetable object + + \param samplerate the sampling rate of the new wavetable + \param hop_size the block size of the new wavetable + + \return the newly created aubio_wavetable_t + +*/ +aubio_wavetable_t * new_aubio_wavetable(uint_t samplerate, uint_t hop_size); + +/** load source in wavetable + + TODO: This function is not implemented yet. See new_aubio_sampler() instead. + + \param o wavetable, created by new_aubio_wavetable() + \param uri the uri of the source to load + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_wavetable_load( aubio_wavetable_t * o, const char_t * uri ); + +/** process wavetable function + + \param o wavetable, created by new_aubio_wavetable() + \param input input of the wavetable, to be added to the output + \param output output of the wavetable + +This function adds the new samples from the playing wavetable to the output. + +If `input` is not NULL and different from `output`, then the samples from `input` +are added to the output. + +*/ +void aubio_wavetable_do ( aubio_wavetable_t * o, const fvec_t * input, fvec_t * output); + +/** process wavetable function, multiple channels + + \param o wavetable, created by new_aubio_wavetable() + \param input input of the wavetable, to be added to the output + \param output output of the wavetable + +This function adds the new samples from the playing wavetable to the output. + +If `input` is not NULL and different from `output`, then the samples from `input` +are added to the output. + +*/ +void aubio_wavetable_do_multi ( aubio_wavetable_t * o, const fmat_t * input, fmat_t * output); + +/** get current playing state + + \param o wavetable, created by new_aubio_wavetable() + + \return 0 if not playing, 1 if playing + +*/ +uint_t aubio_wavetable_get_playing ( const aubio_wavetable_t * o ); + +/** set current playing state + + \param o wavetable, created by new_aubio_wavetable() + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_set_playing ( aubio_wavetable_t * o, uint_t playing ); + +/** play sample from start + + \param o wavetable, created by new_aubio_wavetable() + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_play ( aubio_wavetable_t * o ); + +/** stop wavetable + + \param o wavetable, created by new_aubio_wavetable() + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_stop ( aubio_wavetable_t * o ); + +/** set wavetable frequency + + \param o wavetable, created by new_aubio_wavetable() + \param freq new frequency value for the wavetable + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_set_freq ( aubio_wavetable_t * o, smpl_t freq ); + +/** get wavetable frequency + + \param o wavetable, created by new_aubio_wavetable() + + \return current frequency, in Hz + +*/ +smpl_t aubio_wavetable_get_freq ( const aubio_wavetable_t * o); + +/** set wavetable amplitude + + \param o wavetable, created by new_aubio_wavetable() + \param amp new amplitude value for the wavetable + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_set_amp ( aubio_wavetable_t * o, smpl_t amp ); + +/** get wavetable amplitude + + \param o wavetable, created by new_aubio_wavetable() + + \return current amplitude + +*/ +smpl_t aubio_wavetable_get_amp ( const aubio_wavetable_t * o); + +/** destroy aubio_wavetable_t object + + \param o wavetable, created by new_aubio_wavetable() + +*/ +void del_aubio_wavetable( aubio_wavetable_t * o ); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_WAVETABLE_H */ diff --git a/aubio/src/tempo/beattracking.c b/aubio/src/tempo/beattracking.c new file mode 100644 index 00000000..96483f01 --- /dev/null +++ b/aubio/src/tempo/beattracking.c @@ -0,0 +1,449 @@ +/* + Copyright (C) 2005-2009 Matthew Davies and Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "mathutils.h" +#include "tempo/beattracking.h" + +/** define to 1 to print out tracking difficulties */ +#define AUBIO_BEAT_WARNINGS 0 + +uint_t fvec_gettimesig (fvec_t * acf, uint_t acflen, uint_t gp); +void aubio_beattracking_checkstate (aubio_beattracking_t * bt); + +struct _aubio_beattracking_t +{ + uint_t hop_size; /** length of one tempo detection function sample, in audio samples */ + uint_t samplerate; /** samplerate of the original signal */ + fvec_t *rwv; /** rayleigh weighting for beat period in general model */ + fvec_t *dfwv; /** exponential weighting for beat alignment in general model */ + fvec_t *gwv; /** gaussian weighting for beat period in context dependant model */ + fvec_t *phwv; /** gaussian weighting for beat alignment in context dependant model */ + fvec_t *dfrev; /** reversed onset detection function */ + fvec_t *acf; /** vector for autocorrelation function (of current detection function frame) */ + fvec_t *acfout; /** store result of passing acf through s.i.c.f.b. */ + fvec_t *phout; + uint_t timesig; /** time signature of input, set to zero until context dependent model activated */ + uint_t step; + uint_t rayparam; /** Rayleigh parameter */ + smpl_t lastbeat; + sint_t counter; + uint_t flagstep; + smpl_t g_var; + smpl_t gp; + smpl_t bp; + smpl_t rp; + smpl_t rp1; + smpl_t rp2; +}; + +aubio_beattracking_t * +new_aubio_beattracking (uint_t winlen, uint_t hop_size, uint_t samplerate) +{ + + aubio_beattracking_t *p = AUBIO_NEW (aubio_beattracking_t); + uint_t i = 0; + /* default value for rayleigh weighting - sets preferred tempo to 120bpm */ + smpl_t rayparam = 60. * samplerate / 120. / hop_size; + smpl_t dfwvnorm = EXP ((LOG (2.0) / rayparam) * (winlen + 2)); + /* length over which beat period is found [128] */ + uint_t laglen = winlen / 4; + /* step increment - both in detection function samples -i.e. 11.6ms or + * 1 onset frame [128] */ + uint_t step = winlen / 4; /* 1.5 seconds */ + + p->hop_size = hop_size; + p->samplerate = samplerate; + p->lastbeat = 0; + p->counter = 0; + p->flagstep = 0; + p->g_var = 3.901; // constthresh empirically derived! + p->rp = 1; + p->gp = 0; + + p->rayparam = rayparam; + p->step = step; + p->rwv = new_fvec (laglen); + p->gwv = new_fvec (laglen); + p->dfwv = new_fvec (winlen); + p->dfrev = new_fvec (winlen); + p->acf = new_fvec (winlen); + p->acfout = new_fvec (laglen); + p->phwv = new_fvec (2 * laglen); + p->phout = new_fvec (winlen); + + p->timesig = 0; + + /* exponential weighting, dfwv = 0.5 when i = 43 */ + for (i = 0; i < winlen; i++) { + p->dfwv->data[i] = (EXP ((LOG (2.0) / rayparam) * (i + 1))) + / dfwvnorm; + } + + for (i = 0; i < (laglen); i++) { + p->rwv->data[i] = ((smpl_t) (i + 1.) / SQR ((smpl_t) rayparam)) * + EXP ((-SQR ((smpl_t) (i + 1.)) / (2. * SQR ((smpl_t) rayparam)))); + } + + return p; + +} + +void +del_aubio_beattracking (aubio_beattracking_t * p) +{ + del_fvec (p->rwv); + del_fvec (p->gwv); + del_fvec (p->dfwv); + del_fvec (p->dfrev); + del_fvec (p->acf); + del_fvec (p->acfout); + del_fvec (p->phwv); + del_fvec (p->phout); + AUBIO_FREE (p); +} + + +void +aubio_beattracking_do (aubio_beattracking_t * bt, const fvec_t * dfframe, + fvec_t * output) +{ + + uint_t i, k; + uint_t step = bt->step; + uint_t laglen = bt->rwv->length; + uint_t winlen = bt->dfwv->length; + uint_t maxindex = 0; + //number of harmonics in shift invariant comb filterbank + uint_t numelem = 4; + + smpl_t phase; // beat alignment (step - lastbeat) + smpl_t beat; // beat position + smpl_t bp; // beat period + uint_t a, b; // used to build shift invariant comb filterbank + uint_t kmax; // number of elements used to find beat phase + + /* copy dfframe, apply detection function weighting, and revert */ + fvec_copy (dfframe, bt->dfrev); + fvec_weight (bt->dfrev, bt->dfwv); + fvec_rev (bt->dfrev); + + /* compute autocorrelation function */ + aubio_autocorr (dfframe, bt->acf); + + /* if timesig is unknown, use metrically unbiased version of filterbank */ + if (!bt->timesig) { + numelem = 4; + } else { + numelem = bt->timesig; + } + + /* first and last output values are left intentionally as zero */ + fvec_zeros (bt->acfout); + + /* compute shift invariant comb filterbank */ + for (i = 1; i < laglen - 1; i++) { + for (a = 1; a <= numelem; a++) { + for (b = 1; b < 2 * a; b++) { + bt->acfout->data[i] += bt->acf->data[i * a + b - 1] + * 1. / (2. * a - 1.); + } + } + } + /* apply Rayleigh weight */ + fvec_weight (bt->acfout, bt->rwv); + + /* find non-zero Rayleigh period */ + maxindex = fvec_max_elem (bt->acfout); + if (maxindex > 0 && maxindex < bt->acfout->length - 1) { + bt->rp = fvec_quadratic_peak_pos (bt->acfout, maxindex); + } else { + bt->rp = bt->rayparam; + } + + /* activate biased filterbank */ + aubio_beattracking_checkstate (bt); +#if 0 // debug metronome mode + bt->bp = 36.9142; +#endif + bp = bt->bp; + /* end of biased filterbank */ + + if (bp == 0) { + fvec_zeros(output); + return; + } + + /* deliberate integer operation, could be set to 3 max eventually */ + kmax = FLOOR (winlen / bp); + + /* initialize output */ + fvec_zeros (bt->phout); + for (i = 0; i < bp; i++) { + for (k = 0; k < kmax; k++) { + uint_t idx = i + (uint_t) ROUND (bp * k); + if (idx < bt->dfrev->length) + bt->phout->data[i] += bt->dfrev->data[idx]; +#if AUBIO_BEAT_WARNINGS + else + AUBIO_WRN ("[tempo] out of bounds index %d", idx); +#endif + } + } + fvec_weight (bt->phout, bt->phwv); + + /* find Rayleigh period */ + maxindex = fvec_max_elem (bt->phout); + if (maxindex >= winlen - 1) { +#if AUBIO_BEAT_WARNINGS + AUBIO_WRN ("no idea what this groove's phase is\n"); +#endif /* AUBIO_BEAT_WARNINGS */ + phase = step - bt->lastbeat; + } else { + phase = fvec_quadratic_peak_pos (bt->phout, maxindex); + } + /* take back one frame delay */ + phase += 1.; +#if 0 // debug metronome mode + phase = step - bt->lastbeat; +#endif + + /* reset output */ + fvec_zeros (output); + + i = 1; + beat = bp - phase; + + // AUBIO_DBG ("bp: %f, phase: %f, lastbeat: %f, step: %d, winlen: %d\n", + // bp, phase, bt->lastbeat, step, winlen); + + /* the next beat will be earlier than 60% of the tempo period + skip this one */ + if ( ( step - bt->lastbeat - phase ) < -0.40 * bp ) { +#if AUBIO_BEAT_WARNINGS + AUBIO_WRN ("back off-beat error, skipping this beat\n"); +#endif /* AUBIO_BEAT_WARNINGS */ + beat += bp; + } + + /* start counting the beats */ + while (beat + bp < 0) { + beat += bp; + } + + if (beat >= 0) { + //AUBIO_DBG ("beat: %d, %f, %f\n", i, bp, beat); + output->data[i] = beat; + i++; + } + + while (beat + bp <= step) { + beat += bp; + //AUBIO_DBG ("beat: %d, %f, %f\n", i, bp, beat); + output->data[i] = beat; + i++; + } + + bt->lastbeat = beat; + /* store the number of beats in this frame as the first element */ + output->data[0] = i; +} + +uint_t +fvec_gettimesig (fvec_t * acf, uint_t acflen, uint_t gp) +{ + sint_t k = 0; + smpl_t three_energy = 0., four_energy = 0.; + if (gp < 2) return 4; + if (acflen > 6 * gp + 2) { + for (k = -2; k < 2; k++) { + three_energy += acf->data[3 * gp + k]; + four_energy += acf->data[4 * gp + k]; + } + } else { + /*Expanded to be more accurate in time sig estimation */ + for (k = -2; k < 2; k++) { + three_energy += acf->data[3 * gp + k] + acf->data[6 * gp + k]; + four_energy += acf->data[4 * gp + k] + acf->data[2 * gp + k]; + } + } + return (three_energy > four_energy) ? 3 : 4; +} + +void +aubio_beattracking_checkstate (aubio_beattracking_t * bt) +{ + uint_t i, j, a, b; + uint_t flagconst = 0; + sint_t counter = bt->counter; + uint_t flagstep = bt->flagstep; + smpl_t gp = bt->gp; + smpl_t bp = bt->bp; + smpl_t rp = bt->rp; + smpl_t rp1 = bt->rp1; + smpl_t rp2 = bt->rp2; + uint_t laglen = bt->rwv->length; + uint_t acflen = bt->acf->length; + uint_t step = bt->step; + fvec_t *acf = bt->acf; + fvec_t *acfout = bt->acfout; + + if (gp) { + // compute shift invariant comb filterbank + fvec_zeros (acfout); + for (i = 1; i < laglen - 1; i++) { + for (a = 1; a <= bt->timesig; a++) { + for (b = 1; b < 2 * a; b++) { + acfout->data[i] += acf->data[i * a + b - 1]; + } + } + } + // since gp is set, gwv has been computed in previous checkstate + fvec_weight (acfout, bt->gwv); + gp = fvec_quadratic_peak_pos (acfout, fvec_max_elem (acfout)); + } else { + //still only using general model + gp = 0; + } + + //now look for step change - i.e. a difference between gp and rp that + // is greater than 2*constthresh - always true in first case, since gp = 0 + if (counter == 0) { + if (ABS (gp - rp) > 2. * bt->g_var) { + flagstep = 1; // have observed step change. + counter = 3; // setup 3 frame counter + } else { + flagstep = 0; + } + } + //i.e. 3rd frame after flagstep initially set + if (counter == 1 && flagstep == 1) { + //check for consistency between previous beatperiod values + if (ABS (2 * rp - rp1 - rp2) < bt->g_var) { + //if true, can activate context dependent model + flagconst = 1; + counter = 0; // reset counter and flagstep + } else { + //if not consistent, then don't flag consistency! + flagconst = 0; + counter = 2; // let it look next time + } + } else if (counter > 0) { + //if counter doesn't = 1, + counter = counter - 1; + } + + rp2 = rp1; + rp1 = rp; + + if (flagconst) { + /* first run of new hypothesis */ + gp = rp; + bt->timesig = fvec_gettimesig (acf, acflen, gp); + for (j = 0; j < laglen; j++) + bt->gwv->data[j] = + EXP (-.5 * SQR ((smpl_t) (j + 1. - gp)) / SQR (bt->g_var)); + flagconst = 0; + bp = gp; + /* flat phase weighting */ + fvec_ones (bt->phwv); + } else if (bt->timesig) { + /* context dependant model */ + bp = gp; + /* gaussian phase weighting */ + if (step > bt->lastbeat) { + for (j = 0; j < 2 * laglen; j++) { + bt->phwv->data[j] = + EXP (-.5 * SQR ((smpl_t) (1. + j - step + + bt->lastbeat)) / (bp / 8.)); + } + } else { + //AUBIO_DBG("NOT using phase weighting as step is %d and lastbeat %d \n", + // step,bt->lastbeat); + fvec_ones (bt->phwv); + } + } else { + /* initial state */ + bp = rp; + /* flat phase weighting */ + fvec_ones (bt->phwv); + } + + /* do some further checks on the final bp value */ + + /* if tempo is > 206 bpm, half it */ + while (0 < bp && bp < 25) { +#if AUBIO_BEAT_WARNINGS + AUBIO_WRN ("doubling from %f (%f bpm) to %f (%f bpm)\n", + bp, 60.*44100./512./bp, bp/2., 60.*44100./512./bp/2. ); + //AUBIO_DBG("warning, halving the tempo from %f\n", 60.*samplerate/hopsize/bp); +#endif /* AUBIO_BEAT_WARNINGS */ + bp = bp * 2; + } + + //AUBIO_DBG("tempo:\t%3.5f bpm | ", 5168./bp); + + /* smoothing */ + //bp = (uint_t) (0.8 * (smpl_t)bp + 0.2 * (smpl_t)bp2); + //AUBIO_DBG("tempo:\t%3.5f bpm smoothed | bp2 %d | bp %d | ", 5168./bp, bp2, bp); + //bp2 = bp; + //AUBIO_DBG("time signature: %d \n", bt->timesig); + bt->counter = counter; + bt->flagstep = flagstep; + bt->gp = gp; + bt->bp = bp; + bt->rp1 = rp1; + bt->rp2 = rp2; +} + +smpl_t +aubio_beattracking_get_period (const aubio_beattracking_t * bt) +{ + return bt->hop_size * bt->bp; +} + +smpl_t +aubio_beattracking_get_period_s (const aubio_beattracking_t * bt) +{ + return aubio_beattracking_get_period(bt) / (smpl_t) bt->samplerate; +} + +smpl_t +aubio_beattracking_get_bpm (const aubio_beattracking_t * bt) +{ + if (bt->bp != 0) { + return 60. / aubio_beattracking_get_period_s(bt); + } else { + return 0.; + } +} + +smpl_t +aubio_beattracking_get_confidence (const aubio_beattracking_t * bt) +{ + if (bt->gp) { + smpl_t acf_sum = fvec_sum(bt->acfout); + if (acf_sum != 0.) { + return fvec_quadratic_peak_mag (bt->acfout, bt->gp) / acf_sum; + } + } + return 0.; +} diff --git a/aubio/src/tempo/beattracking.h b/aubio/src/tempo/beattracking.h new file mode 100644 index 00000000..bc95a738 --- /dev/null +++ b/aubio/src/tempo/beattracking.h @@ -0,0 +1,121 @@ +/* + Copyright (C) 2003-2015 Matthew Davies and Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Beat tracking using a context dependant model + + This file implements the causal beat tracking algorithm designed by Matthew + Davies and described in the following articles: + + Matthew E. P. Davies and Mark D. Plumbley. Causal tempo tracking of audio. + In Proceedings of the International Symposium on Music Information Retrieval + (ISMIR), pages 164­169, Barcelona, Spain, 2004. + + Matthew E. P. Davies, Paul Brossier, and Mark D. Plumbley. Beat tracking + towards automatic musical accompaniment. In Proceedings of the Audio + Engineering Society 118th Convention, Barcelona, Spain, May 2005. + + \example tempo/test-beattracking.c + +*/ +#ifndef AUBIO_BEATTRACKING_H +#define AUBIO_BEATTRACKING_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** beat tracking object */ +typedef struct _aubio_beattracking_t aubio_beattracking_t; + +/** create beat tracking object + + \param winlen length of the onset detection window + \param hop_size number of onset detection samples [512] + \param samplerate samplerate of the input signal + +*/ +aubio_beattracking_t * new_aubio_beattracking(uint_t winlen, uint_t hop_size, + uint_t samplerate); + +/** track the beat + + \param bt beat tracking object + \param dfframes current input detection function frame, smoothed by + adaptive median threshold. + \param out stored detected beat locations + +*/ +void aubio_beattracking_do (aubio_beattracking_t * bt, const fvec_t * dfframes, + fvec_t * out); + +/** get current beat period in samples + + \param bt beat tracking object + + Returns the currently observed period, in samples, or 0 if no consistent + value is found. + +*/ +smpl_t aubio_beattracking_get_period (const aubio_beattracking_t * bt); + +/** get current beat period in seconds + + \param bt beat tracking object + + Returns the currently observed period, in seconds, or 0 if no consistent + value is found. + +*/ +smpl_t aubio_beattracking_get_period_s (const aubio_beattracking_t * bt); + +/** get current tempo in bpm + + \param bt beat tracking object + + Returns the currently observed tempo, in beats per minutes, or 0 if no + consistent value is found. + +*/ +smpl_t aubio_beattracking_get_bpm(const aubio_beattracking_t * bt); + +/** get current tempo confidence + + \param bt beat tracking object + + Returns the confidence with which the tempo has been observed, 0 if no + consistent value is found. + +*/ +smpl_t aubio_beattracking_get_confidence(const aubio_beattracking_t * bt); + +/** delete beat tracking object + + \param p beat tracking object + +*/ +void del_aubio_beattracking(aubio_beattracking_t * p); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_BEATTRACKING_H */ diff --git a/aubio/src/tempo/tempo.c b/aubio/src/tempo/tempo.c new file mode 100644 index 00000000..5698b2bb --- /dev/null +++ b/aubio/src/tempo/tempo.c @@ -0,0 +1,304 @@ +/* + Copyright (C) 2006-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "cvec.h" +#include "spectral/specdesc.h" +#include "tempo/beattracking.h" +#include "spectral/phasevoc.h" +#include "onset/peakpicker.h" +#include "mathutils.h" +#include "tempo/tempo.h" + +/* structure to store object state */ +struct _aubio_tempo_t { + aubio_specdesc_t * od; /** onset detection */ + aubio_pvoc_t * pv; /** phase vocoder */ + aubio_peakpicker_t * pp; /** peak picker */ + aubio_beattracking_t * bt; /** beat tracking */ + cvec_t * fftgrain; /** spectral frame */ + fvec_t * of; /** onset detection function value */ + fvec_t * dfframe; /** peak picked detection function buffer */ + fvec_t * out; /** beat tactus candidates */ + fvec_t * onset; /** onset results */ + smpl_t silence; /** silence parameter */ + smpl_t threshold; /** peak picking threshold */ + sint_t blockpos; /** current position in dfframe */ + uint_t winlen; /** dfframe bufsize */ + uint_t step; /** dfframe hopsize */ + uint_t samplerate; /** sampling rate of the signal */ + uint_t hop_size; /** get hop_size */ + uint_t total_frames; /** total frames since beginning */ + uint_t last_beat; /** time of latest detected beat, in samples */ + sint_t delay; /** delay to remove to last beat, in samples */ + uint_t last_tatum; /** time of latest detected tatum, in samples */ + uint_t tatum_signature; /** number of tatum between each beats */ +}; + +/* execute tempo detection function on iput buffer */ +void aubio_tempo_do(aubio_tempo_t *o, const fvec_t * input, fvec_t * tempo) +{ + uint_t i; + uint_t winlen = o->winlen; + uint_t step = o->step; + fvec_t * thresholded; + aubio_pvoc_do (o->pv, input, o->fftgrain); + aubio_specdesc_do (o->od, o->fftgrain, o->of); + /*if (usedoubled) { + aubio_specdesc_do(o2,fftgrain, onset2); + onset->data[0] *= onset2->data[0]; + }*/ + /* execute every overlap_size*step */ + if (o->blockpos == (signed)step -1 ) { + /* check dfframe */ + aubio_beattracking_do(o->bt,o->dfframe,o->out); + /* rotate dfframe */ + for (i = 0 ; i < winlen - step; i++ ) + o->dfframe->data[i] = o->dfframe->data[i+step]; + for (i = winlen - step ; i < winlen; i++ ) + o->dfframe->data[i] = 0.; + o->blockpos = -1; + } + o->blockpos++; + aubio_peakpicker_do (o->pp, o->of, o->onset); + // store onset detection function in second sample of vector + //tempo->data[1] = o->onset->data[0]; + thresholded = aubio_peakpicker_get_thresholded_input(o->pp); + o->dfframe->data[winlen - step + o->blockpos] = thresholded->data[0]; + /* end of second level loop */ + tempo->data[0] = 0; /* reset tactus */ + //i=0; + for (i = 1; i < o->out->data[0]; i++ ) { + /* if current frame is a predicted tactus */ + if (o->blockpos == FLOOR(o->out->data[i])) { + tempo->data[0] = o->out->data[i] - FLOOR(o->out->data[i]); /* set tactus */ + /* test for silence */ + if (aubio_silence_detection(input, o->silence)==1) { + tempo->data[0] = 0; // unset beat if silent + } + o->last_beat = o->total_frames + (uint_t)ROUND(tempo->data[0] * o->hop_size); + o->last_tatum = o->last_beat; + } + } + o->total_frames += o->hop_size; + return; +} + +uint_t aubio_tempo_get_last (aubio_tempo_t *o) +{ + return o->last_beat + o->delay; +} + +smpl_t aubio_tempo_get_last_s (aubio_tempo_t *o) +{ + return aubio_tempo_get_last (o) / (smpl_t) (o->samplerate); +} + +smpl_t aubio_tempo_get_last_ms (aubio_tempo_t *o) +{ + return aubio_tempo_get_last_s (o) * 1000.; +} + +uint_t aubio_tempo_set_delay(aubio_tempo_t * o, sint_t delay) { + o->delay = delay; + return AUBIO_OK; +} + +uint_t aubio_tempo_set_delay_s(aubio_tempo_t * o, smpl_t delay) { + o->delay = delay * o->samplerate; + return AUBIO_OK; +} + +uint_t aubio_tempo_set_delay_ms(aubio_tempo_t * o, smpl_t delay) { + return aubio_tempo_set_delay_s(o, delay / 1000.); +} + +uint_t aubio_tempo_get_delay(aubio_tempo_t * o) { + return o->delay; +} + +smpl_t aubio_tempo_get_delay_s(aubio_tempo_t * o) { + return o->delay / (smpl_t)(o->samplerate); +} + +smpl_t aubio_tempo_get_delay_ms(aubio_tempo_t * o) { + return aubio_tempo_get_delay_s(o) * 1000.; +} + +uint_t aubio_tempo_set_silence(aubio_tempo_t * o, smpl_t silence) { + o->silence = silence; + return AUBIO_OK; +} + +smpl_t aubio_tempo_get_silence(aubio_tempo_t * o) { + return o->silence; +} + +uint_t aubio_tempo_set_threshold(aubio_tempo_t * o, smpl_t threshold) { + o->threshold = threshold; + aubio_peakpicker_set_threshold(o->pp, o->threshold); + return AUBIO_OK; +} + +smpl_t aubio_tempo_get_threshold(aubio_tempo_t * o) { + return o->threshold; +} + +/* Allocate memory for an tempo detection */ +aubio_tempo_t * new_aubio_tempo (const char_t * tempo_mode, + uint_t buf_size, uint_t hop_size, uint_t samplerate) +{ + aubio_tempo_t * o = AUBIO_NEW(aubio_tempo_t); + char_t specdesc_func[PATH_MAX]; + o->samplerate = samplerate; + // check parameters are valid + if ((sint_t)hop_size < 1) { + AUBIO_ERR("tempo: got hop size %d, but can not be < 1\n", hop_size); + goto beach; + } else if ((sint_t)buf_size < 2) { + AUBIO_ERR("tempo: got window size %d, but can not be < 2\n", buf_size); + goto beach; + } else if (buf_size < hop_size) { + AUBIO_ERR("tempo: hop size (%d) is larger than window size (%d)\n", buf_size, hop_size); + goto beach; + } else if ((sint_t)samplerate < 1) { + AUBIO_ERR("tempo: samplerate (%d) can not be < 1\n", samplerate); + goto beach; + } + + /* length of observations, worth about 6 seconds */ + o->winlen = aubio_next_power_of_two(5.8 * samplerate / hop_size); + if (o->winlen < 4) o->winlen = 4; + o->step = o->winlen/4; + o->blockpos = 0; + o->threshold = 0.3; + o->silence = -90.; + o->total_frames = 0; + o->last_beat = 0; + o->delay = 0; + o->hop_size = hop_size; + o->dfframe = new_fvec(o->winlen); + o->fftgrain = new_cvec(buf_size); + o->out = new_fvec(o->step); + o->pv = new_aubio_pvoc(buf_size, hop_size); + o->pp = new_aubio_peakpicker(); + aubio_peakpicker_set_threshold (o->pp, o->threshold); + if ( strcmp(tempo_mode, "default") == 0 ) { + strncpy(specdesc_func, "specflux", PATH_MAX - 1); + } else { + strncpy(specdesc_func, tempo_mode, PATH_MAX - 1); + specdesc_func[PATH_MAX - 1] = '\0'; + } + o->od = new_aubio_specdesc(specdesc_func,buf_size); + o->of = new_fvec(1); + o->bt = new_aubio_beattracking(o->winlen, o->hop_size, o->samplerate); + o->onset = new_fvec(1); + /*if (usedoubled) { + o2 = new_aubio_specdesc(type_onset2,buffer_size); + onset2 = new_fvec(1); + }*/ + if (!o->dfframe || !o->fftgrain || !o->out || !o->pv || + !o->pp || !o->od || !o->of || !o->bt || !o->onset) { + AUBIO_ERR("tempo: failed creating tempo object\n"); + goto beach; + } + o->last_tatum = 0; + o->tatum_signature = 4; + return o; + +beach: + del_aubio_tempo(o); + return NULL; +} + +smpl_t aubio_tempo_get_bpm(aubio_tempo_t *o) { + return aubio_beattracking_get_bpm(o->bt); +} + +smpl_t aubio_tempo_get_period (aubio_tempo_t *o) +{ + return aubio_beattracking_get_period (o->bt); +} + +smpl_t aubio_tempo_get_period_s (aubio_tempo_t *o) +{ + return aubio_beattracking_get_period_s (o->bt); +} + +smpl_t aubio_tempo_get_confidence(aubio_tempo_t *o) { + return aubio_beattracking_get_confidence(o->bt); +} + +uint_t aubio_tempo_was_tatum (aubio_tempo_t *o) +{ + uint_t last_tatum_distance = o->total_frames - o->last_tatum; + smpl_t beat_period = aubio_tempo_get_period(o); + smpl_t tatum_period = beat_period / o->tatum_signature; + if (last_tatum_distance < o->hop_size) { + o->last_tatum = o->last_beat; + return 2; + } + else if (last_tatum_distance > tatum_period) { + if ( last_tatum_distance + o->hop_size > beat_period ) { + // next beat is too close, pass + return 0; + } + o->last_tatum = o->total_frames; + return 1; + } + return 0; +} + +smpl_t aubio_tempo_get_last_tatum (aubio_tempo_t *o) { + return (smpl_t)o->last_tatum - o->delay; +} + +uint_t aubio_tempo_set_tatum_signature (aubio_tempo_t *o, uint_t signature) { + if (signature < 1 || signature > 64) { + return AUBIO_FAIL; + } else { + o->tatum_signature = signature; + return AUBIO_OK; + } +} + +void del_aubio_tempo (aubio_tempo_t *o) +{ + if (o->od) + del_aubio_specdesc(o->od); + if (o->bt) + del_aubio_beattracking(o->bt); + if (o->pp) + del_aubio_peakpicker(o->pp); + if (o->pv) + del_aubio_pvoc(o->pv); + if (o->out) + del_fvec(o->out); + if (o->of) + del_fvec(o->of); + if (o->fftgrain) + del_cvec(o->fftgrain); + if (o->dfframe) + del_fvec(o->dfframe); + if (o->onset) + del_fvec(o->onset); + AUBIO_FREE(o); +} diff --git a/aubio/src/tempo/tempo.h b/aubio/src/tempo/tempo.h new file mode 100644 index 00000000..e2afe99c --- /dev/null +++ b/aubio/src/tempo/tempo.h @@ -0,0 +1,255 @@ +/* + Copyright (C) 2006-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Tempo detection object + + This object stores all the memory required for tempo detection algorithm + and returns the estimated beat locations. + + \example tempo/test-tempo.c + \example examples/aubiotrack.c + +*/ + +#ifndef AUBIO_TEMPO_H +#define AUBIO_TEMPO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** tempo detection structure */ +typedef struct _aubio_tempo_t aubio_tempo_t; + +/** create tempo detection object + + \param method beat tracking method, unused for now (use "default") + \param buf_size length of FFT + \param hop_size number of frames between two consecutive runs + \param samplerate sampling rate of the signal to analyze + + \return newly created ::aubio_tempo_t if successful, `NULL` otherwise + +*/ +aubio_tempo_t * new_aubio_tempo (const char_t * method, + uint_t buf_size, uint_t hop_size, uint_t samplerate); + +/** execute tempo detection + + \param o beat tracking object + \param input new samples + \param tempo output beats + +*/ +void aubio_tempo_do (aubio_tempo_t *o, const fvec_t * input, fvec_t * tempo); + +/** get the time of the latest beat detected, in samples + + \param o tempo detection object as returned by ::new_aubio_tempo + +*/ +uint_t aubio_tempo_get_last (aubio_tempo_t *o); + +/** get the time of the latest beat detected, in seconds + + \param o tempo detection object as returned by ::new_aubio_tempo + +*/ +smpl_t aubio_tempo_get_last_s (aubio_tempo_t *o); + +/** get the time of the latest beat detected, in milliseconds + + \param o tempo detection object as returned by ::new_aubio_tempo + +*/ +smpl_t aubio_tempo_get_last_ms (aubio_tempo_t *o); + +/** set tempo detection silence threshold + + \param o beat tracking object + \param silence new silence threshold, in dB + + \return `0` if successful, non-zero otherwise + +*/ +uint_t aubio_tempo_set_silence(aubio_tempo_t * o, smpl_t silence); + +/** get tempo detection silence threshold + + \param o tempo detection object as returned by new_aubio_tempo() + + \return current silence threshold + +*/ +smpl_t aubio_tempo_get_silence(aubio_tempo_t * o); + +/** set tempo detection peak picking threshold + + \param o beat tracking object + \param threshold new threshold + + \return `0` if successful, non-zero otherwise + +*/ +uint_t aubio_tempo_set_threshold(aubio_tempo_t * o, smpl_t threshold); + +/** get tempo peak picking threshold + + \param o tempo detection object as returned by new_aubio_tempo() + + \return current tempo detection threshold + +*/ +smpl_t aubio_tempo_get_threshold(aubio_tempo_t * o); + +/** get current beat period in samples + + \param bt beat tracking object + + Returns the currently observed period, in samples, or 0 if no consistent + value is found. + +*/ +smpl_t aubio_tempo_get_period (aubio_tempo_t * bt); + +/** get current beat period in seconds + + \param bt beat tracking object + + Returns the currently observed period, in seconds, or 0 if no consistent + value is found. + +*/ +smpl_t aubio_tempo_get_period_s (aubio_tempo_t * bt); + +/** get current tempo + + \param o beat tracking object + + \return the currently observed tempo, or `0` if no consistent value is found + +*/ +smpl_t aubio_tempo_get_bpm(aubio_tempo_t * o); + +/** get current tempo confidence + + \param o beat tracking object + + \return confidence with which the tempo has been observed, the higher the + more confidence, `0` if no consistent value is found. + +*/ +smpl_t aubio_tempo_get_confidence(aubio_tempo_t * o); + +/** set number of tatum per beat + + \param o beat tracking object + \param signature number of tatum per beat (between 1 and 64) + +*/ +uint_t aubio_tempo_set_tatum_signature(aubio_tempo_t *o, uint_t signature); + +/** check whether a tatum was detected in the current frame + + \param o beat tracking object + + \return 2 if a beat was detected, 1 if a tatum was detected, 0 otherwise + +*/ +uint_t aubio_tempo_was_tatum(aubio_tempo_t *o); + +/** get position of last_tatum, in samples + + \param o beat tracking object + +*/ +smpl_t aubio_tempo_get_last_tatum(aubio_tempo_t *o); + +/** get current delay + + \param o beat tracking object + + \return current delay, in samples + + */ +uint_t aubio_tempo_get_delay(aubio_tempo_t * o); + +/** get current delay in seconds + + \param o beat tracking object + + \return current delay, in seconds + + */ +smpl_t aubio_tempo_get_delay_s(aubio_tempo_t * o); + +/** get current delay in ms + + \param o beat tracking object + + \return current delay, in milliseconds + + */ +smpl_t aubio_tempo_get_delay_ms(aubio_tempo_t * o); + +/** set current delay + + \param o beat tracking object + \param delay delay to set tempo to, in samples + + \return `0` if successful, non-zero otherwise + + */ +uint_t aubio_tempo_set_delay(aubio_tempo_t * o, sint_t delay); + +/** set current delay in seconds + + \param o beat tracking object + \param delay delay to set tempo to, in seconds + + \return `0` if successful, non-zero otherwise + + */ +uint_t aubio_tempo_set_delay_s(aubio_tempo_t * o, smpl_t delay); + +/** set current delay + + \param o beat tracking object + \param delay delay to set tempo to, in samples + + \return `0` if successful, non-zero otherwise + + */ +uint_t aubio_tempo_set_delay_ms(aubio_tempo_t * o, smpl_t delay); + +/** delete tempo detection object + + \param o beat tracking object + +*/ +void del_aubio_tempo(aubio_tempo_t * o); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_TEMPO_H */ diff --git a/aubio/src/temporal/a_weighting.c b/aubio/src/temporal/a_weighting.c new file mode 100644 index 00000000..f19f566f --- /dev/null +++ b/aubio/src/temporal/a_weighting.c @@ -0,0 +1,262 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "types.h" +#include "fvec.h" +#include "lvec.h" +#include "temporal/filter.h" +#include "temporal/a_weighting.h" + +uint_t +aubio_filter_set_a_weighting (aubio_filter_t * f, uint_t samplerate) +{ + uint_t order; lsmp_t *a, *b; lvec_t *as, *bs; + + if ((sint_t)samplerate <= 0) { + AUBIO_ERROR("aubio_filter: failed setting A-weighting with samplerate %d\n", samplerate); + return AUBIO_FAIL; + } + if (f == NULL) { + AUBIO_ERROR("aubio_filter: failed setting A-weighting with filter NULL\n"); + return AUBIO_FAIL; + } + + order = aubio_filter_get_order (f); + if (order != 7) { + AUBIO_ERROR ("aubio_filter: order of A-weighting filter must be 7, not %d\n", order); + return 1; + } + + aubio_filter_set_samplerate (f, samplerate); + bs = aubio_filter_get_feedforward (f); + as = aubio_filter_get_feedback (f); + b = bs->data, a = as->data; + + /* select coefficients according to sampling frequency */ + switch (samplerate) { + + case 8000: + b[0] = 6.306209468238731519207362907764036208391189575195312500e-01; + b[1] = -1.261241893647746525886077506584115326404571533203125000e+00; + b[2] = -6.306209468238730408984338282607495784759521484375000000e-01; + b[3] = 2.522483787295493051772155013168230652809143066406250000e+00; + b[4] = -6.306209468238730408984338282607495784759521484375000000e-01; + b[5] = -1.261241893647746525886077506584115326404571533203125000e+00; + b[6] = 6.306209468238731519207362907764036208391189575195312500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.128467193009123015201566886389628052711486816406250000e+00; + a[2] = 2.948668980101234460278192273108288645744323730468750000e-01; + a[3] = 1.824183830735050637628091863007284700870513916015625000e+00; + a[4] = -8.056628943119792385374466903158463537693023681640625000e-01; + a[5] = -3.947497982842933517133587884018197655677795410156250000e-01; + a[6] = 2.098548546080332977137317129745497368276119232177734375e-01; + break; + + case 11025: + b[0] = 6.014684165832374640459079273568931967020034790039062500e-01; + b[1] = -1.202936833166475150136420779745094478130340576171875000e+00; + b[2] = -6.014684165832373530236054648412391543388366699218750000e-01; + b[3] = 2.405873666332950300272841559490188956260681152343750000e+00; + b[4] = -6.014684165832373530236054648412391543388366699218750000e-01; + b[5] = -1.202936833166475150136420779745094478130340576171875000e+00; + b[6] = 6.014684165832374640459079273568931967020034790039062500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.463578747722854345170162559952586889266967773437500000e+00; + a[2] = 1.096799662705210121060872552334330976009368896484375000e+00; + a[3] = 1.381222210556041218865175324026495218276977539062500000e+00; + a[4] = -1.013875696476876031582037285261321812868118286132812500e+00; + a[5] = -1.839132734476921215982514468123554252088069915771484375e-01; + a[6] = 1.833526393172056623281918064094497822225093841552734375e-01; + break; + + case 16000: + b[0] = 5.314898298235570806014038680586963891983032226562500000e-01; + b[1] = -1.062979659647114161202807736117392778396606445312500000e+00; + b[2] = -5.314898298235570806014038680586963891983032226562500000e-01; + b[3] = 2.125959319294228322405615472234785556793212890625000000e+00; + b[4] = -5.314898298235570806014038680586963891983032226562500000e-01; + b[5] = -1.062979659647114161202807736117392778396606445312500000e+00; + b[6] = 5.314898298235570806014038680586963891983032226562500000e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.867832572992162987191022693878039717674255371093750000e+00; + a[2] = 2.221144410202312347024644623161293566226959228515625000e+00; + a[3] = 4.552683347886614662058946123579517006874084472656250000e-01; + a[4] = -9.833868636162828025248927588108927011489868164062500000e-01; + a[5] = 5.592994142413361402521587706360151059925556182861328125e-02; + a[6] = 1.188781038285612462468421313133148942142724990844726562e-01; + break; + + case 22050: + b[0] = 4.492998504299193784916610638902056962251663208007812500e-01; + b[1] = -8.985997008598388680056245902960654348134994506835937500e-01; + b[2] = -4.492998504299192674693586013745516538619995117187500000e-01; + b[3] = 1.797199401719677958055854105623438954353332519531250000e+00; + b[4] = -4.492998504299192674693586013745516538619995117187500000e-01; + b[5] = -8.985997008598388680056245902960654348134994506835937500e-01; + b[6] = 4.492998504299193784916610638902056962251663208007812500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -3.229078805225074955131958631682209670543670654296875000e+00; + a[2] = 3.354494881236033787530459449044428765773773193359375000e+00; + a[3] = -7.317843680657351024265722116979304701089859008789062500e-01; + a[4] = -6.271627581807257545420952737913466989994049072265625000e-01; + a[5] = 1.772142005020879151899748649157118052244186401367187500e-01; + a[6] = 5.631716697383508385410522123493137769401073455810546875e-02; + break; + + case 24000: + b[0] = 4.256263892891054001488271296693710610270500183105468750e-01; + b[1] = -8.512527785782106892753517968230880796909332275390625000e-01; + b[2] = -4.256263892891054556599783609271980822086334228515625000e-01; + b[3] = 1.702505557156421378550703593646176159381866455078125000e+00; + b[4] = -4.256263892891054556599783609271980822086334228515625000e-01; + b[5] = -8.512527785782106892753517968230880796909332275390625000e-01; + b[6] = 4.256263892891054001488271296693710610270500183105468750e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -3.325996004241962733516402295208536088466644287109375000e+00; + a[2] = 3.677161079286316969216841243905946612358093261718750000e+00; + a[3] = -1.106476076828482035807610373012721538543701171875000000e+00; + a[4] = -4.726706734908718843257702246773988008499145507812500000e-01; + a[5] = 1.861941760230954034938122276798821985721588134765625000e-01; + a[6] = 4.178771337829546850262119050967157818377017974853515625e-02; + break; + + case 32000: + b[0] = 3.434583386824304196416335344110848382115364074707031250e-01; + b[1] = -6.869166773648609503055695313378237187862396240234375000e-01; + b[2] = -3.434583386824303641304823031532578170299530029296875000e-01; + b[3] = 1.373833354729721900611139062675647437572479248046875000e+00; + b[4] = -3.434583386824303641304823031532578170299530029296875000e-01; + b[5] = -6.869166773648609503055695313378237187862396240234375000e-01; + b[6] = 3.434583386824304196416335344110848382115364074707031250e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -3.656446043233668063976438133977353572845458984375000000e+00; + a[2] = 4.831468450652579349480220116674900054931640625000000000e+00; + a[3] = -2.557597496581567764195597192156128585338592529296875000e+00; + a[4] = 2.533680394205302666144064005493419244885444641113281250e-01; + a[5] = 1.224430322452567110325105659285327419638633728027343750e-01; + a[6] = 6.764072168342137418572956875095769646577537059783935547e-03; + break; + + case 44100: + b[0] = 2.557411252042575133813784304948057979345321655273437500e-01; + b[1] = -5.114822504085150267627568609896115958690643310546875000e-01; + b[2] = -2.557411252042575133813784304948057979345321655273437500e-01; + b[3] = 1.022964500817030053525513721979223191738128662109375000e+00; + b[4] = -2.557411252042575133813784304948057979345321655273437500e-01; + b[5] = -5.114822504085150267627568609896115958690643310546875000e-01; + b[6] = 2.557411252042575133813784304948057979345321655273437500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -4.019576181115832369528106937650591135025024414062500000e+00; + a[2] = 6.189406442920693862674852425698190927505493164062500000e+00; + a[3] = -4.453198903544116404873420833609998226165771484375000000e+00; + a[4] = 1.420842949621876627475103305187076330184936523437500000e+00; + a[5] = -1.418254738303044160119270600262098014354705810546875000e-01; + a[6] = 4.351177233495117681327801761881346465088427066802978516e-03; + break; + + case 48000: + b[0] = 2.343017922995132285013397677175817079842090606689453125e-01; + b[1] = -4.686035845990265125138307666929904371500015258789062500e-01; + b[2] = -2.343017922995132007457641520886681973934173583984375000e-01; + b[3] = 9.372071691980530250276615333859808743000030517578125000e-01; + b[4] = -2.343017922995132007457641520886681973934173583984375000e-01; + b[5] = -4.686035845990265125138307666929904371500015258789062500e-01; + b[6] = 2.343017922995132285013397677175817079842090606689453125e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -4.113043408775872045168853219365701079368591308593750000e+00; + a[2] = 6.553121752655050258340452273841947317123413085937500000e+00; + a[3] = -4.990849294163385074796224216697737574577331542968750000e+00; + a[4] = 1.785737302937575599059982778271660208702087402343750000e+00; + a[5] = -2.461905953194876706113802811159985139966011047363281250e-01; + a[6] = 1.122425003323123879339640041052916785702109336853027344e-02; + break; + + case 88200: + b[0] = 1.118876366882113199130444058937428053468465805053710938e-01; + b[1] = -2.237752733764226120705131961585721001029014587402343750e-01; + b[2] = -1.118876366882113337908322137081995606422424316406250000e-01; + b[3] = 4.475505467528452241410263923171442002058029174804687500e-01; + b[4] = -1.118876366882113337908322137081995606422424316406250000e-01; + b[5] = -2.237752733764226120705131961585721001029014587402343750e-01; + b[6] = 1.118876366882113199130444058937428053468465805053710938e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -4.726938565651158441482948546763509511947631835937500000e+00; + a[2] = 9.076897983832765248735086061060428619384765625000000000e+00; + a[3] = -9.014855113464800950850985827855765819549560546875000000e+00; + a[4] = 4.852772261031594425162438710685819387435913085937500000e+00; + a[5] = -1.333877820398965186043938047077972441911697387695312500e+00; + a[6] = 1.460012549591642450064199465487035922706127166748046875e-01; + break; + + case 96000: + b[0] = 9.951898975972744976203898659150581806898117065429687500e-02; + b[1] = -1.990379795194548995240779731830116361379623413085937500e-01; + b[2] = -9.951898975972744976203898659150581806898117065429687500e-02; + b[3] = 3.980759590389097990481559463660232722759246826171875000e-01; + b[4] = -9.951898975972744976203898659150581806898117065429687500e-02; + b[5] = -1.990379795194548995240779731830116361379623413085937500e-01; + b[6] = 9.951898975972744976203898659150581806898117065429687500e-02; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -4.802203044225376693532325589330866932868957519531250000e+00; + a[2] = 9.401807218627226347962277941405773162841796875000000000e+00; + a[3] = -9.566143943569164420637207513209432363510131835937500000e+00; + a[4] = 5.309775930392619081032989925006404519081115722656250000e+00; + a[5] = -1.517333360452622237346531619550660252571105957031250000e+00; + a[6] = 1.740971994228911745583587844521389342844486236572265625e-01; + break; + + case 192000: + b[0] = 3.433213424548713782469278044118254911154508590698242188e-02; + b[1] = -6.866426849097426177159775306790834292769432067871093750e-02; + b[2] = -3.433213424548714476358668434841092675924301147460937500e-02; + b[3] = 1.373285369819485235431955061358166858553886413574218750e-01; + b[4] = -3.433213424548714476358668434841092675924301147460937500e-02; + b[5] = -6.866426849097426177159775306790834292769432067871093750e-02; + b[6] = 3.433213424548713782469278044118254911154508590698242188e-02; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -5.305923689674640009172890131594613194465637207031250000e+00; + a[2] = 1.165952437466175695135461864992976188659667968750000000e+01; + a[3] = -1.357560092700591525272102444432675838470458984375000000e+01; + a[4] = 8.828906932824192921316353022120893001556396484375000000e+00; + a[5] = -3.039490120988216581565666274400427937507629394531250000e+00; + a[6] = 4.325834301870381537469256727490574121475219726562500000e-01; + break; + + default: + AUBIO_ERROR ("sampling rate of A-weighting filter is %d, should be one of\ + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 192000.\n", samplerate); + return 1; + + } + + return 0; +} + +aubio_filter_t * +new_aubio_filter_a_weighting (uint_t samplerate) +{ + aubio_filter_t *f = new_aubio_filter (7); + if (aubio_filter_set_a_weighting(f,samplerate) != AUBIO_OK) { + del_aubio_filter(f); + return NULL; + } + return f; +} diff --git a/aubio/src/temporal/a_weighting.h b/aubio/src/temporal/a_weighting.h new file mode 100644 index 00000000..49dd4d5c --- /dev/null +++ b/aubio/src/temporal/a_weighting.h @@ -0,0 +1,88 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_FILTER_A_DESIGN_H +#define AUBIO_FILTER_A_DESIGN_H + +/** \file + + A-weighting filter coefficients + + This file creates an A-weighting digital filter, which reduces low and high + frequencies and enhance the middle ones to reflect the ability of the human + hearing. + + The implementation is based on the following standard: + + - IEC/CD 1672: Electroacoustics-Sound Level Meters, IEC, Geneva, Nov. 1996, + for A- and C-weighting filters. + + See also: + + - A-Weighting on + Wikipedia + - Weighting filter on + Wikipedia + - Christophe + Couvreur's 'octave' toolbox + + The coefficients in this file have been computed using Christophe Couvreur's + scripts in octave 3.0 (debian package 1:3.0.5-6+b2 with octave-signal + 1.0.9-1+b1 on i386), with
 [b, a] = adsign(1/Fs) 
for various + sampling frequencies (8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, and 192000 Hz). + + The sampling frequency should normally be higher than 20kHz, but most common + file sampling rates have been included for completeness. + + \example temporal/test-a_weighting.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** create new A-design filter + + \param samplerate sampling frequency of the signal to filter. Should be one of + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, and + 192000 Hz + + \return a new filter object + +*/ +aubio_filter_t *new_aubio_filter_a_weighting (uint_t samplerate); + +/** set feedback and feedforward coefficients of a A-weighting filter + + \param f filter object to get coefficients from + \param samplerate sampling frequency of the signal to filter. Should be one of + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, and + 192000 Hz + +*/ +uint_t aubio_filter_set_a_weighting (aubio_filter_t * f, uint_t samplerate); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FILTER_A_DESIGN_H */ diff --git a/aubio/src/temporal/biquad.c b/aubio/src/temporal/biquad.c new file mode 100644 index 00000000..426b64fb --- /dev/null +++ b/aubio/src/temporal/biquad.c @@ -0,0 +1,54 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "lvec.h" +#include "temporal/filter.h" +#include "temporal/biquad.h" + +uint_t +aubio_filter_set_biquad (aubio_filter_t * f, lsmp_t b0, lsmp_t b1, lsmp_t b2, + lsmp_t a1, lsmp_t a2) +{ + uint_t order = aubio_filter_get_order (f); + lvec_t *bs = aubio_filter_get_feedforward (f); + lvec_t *as = aubio_filter_get_feedback (f); + + if (order != 3) { + AUBIO_ERROR ("order of biquad filter must be 3, not %d\n", order); + return AUBIO_FAIL; + } + bs->data[0] = b0; + bs->data[1] = b1; + bs->data[2] = b2; + as->data[0] = 1.; + as->data[1] = a1; + as->data[2] = a2; + return AUBIO_OK; +} + +aubio_filter_t * +new_aubio_filter_biquad (lsmp_t b0, lsmp_t b1, lsmp_t b2, lsmp_t a1, lsmp_t a2) +{ + aubio_filter_t *f = new_aubio_filter (3); + aubio_filter_set_biquad (f, b0, b1, b2, a1, a2); + return f; +} diff --git a/aubio/src/temporal/biquad.h b/aubio/src/temporal/biquad.h new file mode 100644 index 00000000..1d19d942 --- /dev/null +++ b/aubio/src/temporal/biquad.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_FILTER_BIQUAD_H +#define AUBIO_FILTER_BIQUAD_H + +/** \file + + Second order Infinite Impulse Response filter + + This file implements a normalised biquad filter (second order IIR): + + \f$ y[n] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2] \f$ + + The filtfilt version runs the filter twice, forward and backward, to + compensate the phase shifting of the forward operation. + + See also Digital + biquad filter on wikipedia. + + \example temporal/test-biquad.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** set coefficients of a biquad filter + + \param f filter object as returned by new_aubio_filter() + \param b0 forward filter coefficient + \param b1 forward filter coefficient + \param b2 forward filter coefficient + \param a1 feedback filter coefficient + \param a2 feedback filter coefficient + +*/ +uint_t aubio_filter_set_biquad (aubio_filter_t * f, lsmp_t b0, lsmp_t b1, + lsmp_t b2, lsmp_t a1, lsmp_t a2); + +/** create biquad filter with `b0`, `b1`, `b2`, `a1`, `a2` coeffs + + \param b0 forward filter coefficient + \param b1 forward filter coefficient + \param b2 forward filter coefficient + \param a1 feedback filter coefficient + \param a2 feedback filter coefficient + +*/ +aubio_filter_t *new_aubio_filter_biquad (lsmp_t b0, lsmp_t b1, lsmp_t b2, + lsmp_t a1, lsmp_t a2); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FILTER_BIQUAD_H */ diff --git a/aubio/src/temporal/c_weighting.c b/aubio/src/temporal/c_weighting.c new file mode 100644 index 00000000..91ada717 --- /dev/null +++ b/aubio/src/temporal/c_weighting.c @@ -0,0 +1,217 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "types.h" +#include "fvec.h" +#include "lvec.h" +#include "temporal/filter.h" +#include "temporal/c_weighting.h" + +uint_t +aubio_filter_set_c_weighting (aubio_filter_t * f, uint_t samplerate) +{ + uint_t order; lsmp_t *a, *b; lvec_t *as, *bs; + + if ((sint_t)samplerate <= 0) { + AUBIO_ERROR("aubio_filter: failed setting C-weighting with samplerate %d\n", samplerate); + return AUBIO_FAIL; + } + if (f == NULL) { + AUBIO_ERROR("aubio_filter: failed setting C-weighting with filter NULL\n"); + return AUBIO_FAIL; + } + + order = aubio_filter_get_order (f); + if ( order != 5 ) { + AUBIO_ERROR ("aubio_filter: order of C-weighting filter must be 5, not %d\n", order); + return 1; + } + + aubio_filter_set_samplerate (f, samplerate); + bs = aubio_filter_get_feedforward (f); + as = aubio_filter_get_feedback (f); + b = bs->data, a = as->data; + + /* select coefficients according to sampling frequency */ + switch (samplerate) { + + case 8000: + b[0] = 6.782173932405135552414776611840352416038513183593750000e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -1.356434786481027110482955322368070483207702636718750000e+00; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 6.782173932405135552414776611840352416038513183593750000e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -6.589092811505605773447769024642184376716613769531250000e-01; + a[2] = -1.179445664897062595599663836765103042125701904296875000e+00; + a[3] = 4.243329729632123736848825501510873436927795410156250000e-01; + a[4] = 4.147270002091348328754349950031610205769538879394531250e-01; + break; + + case 11025: + b[0] = 6.002357155402652244546857218665536493062973022460937500e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -1.200471431080530448909371443733107298612594604492187500e+00; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 6.002357155402652244546857218665536493062973022460937500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -8.705602141280316397242700077185872942209243774414062500e-01; + a[2] = -9.037199507150940336330791069485712796449661254882812500e-01; + a[3] = 4.758433040929530011275971901341108605265617370605468750e-01; + a[4] = 2.987653956523212417373258631414500996470451354980468750e-01; + break; + + case 16000: + b[0] = 4.971057193673903418229542694461997598409652709960937500e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -9.942114387347806836459085388923995196819305419921875000e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 4.971057193673903418229542694461997598409652709960937500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -1.162322939286873690889478893950581550598144531250000000e+00; + a[2] = -4.771961355734982701548574368644040077924728393554687500e-01; + a[3] = 4.736145114694704227886745684372726827859878540039062500e-01; + a[4] = 1.660337524309875301131711466950946487486362457275390625e-01; + break; + + case 22050: + b[0] = 4.033381299002754549754001800465630367398262023925781250e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -8.066762598005509099508003600931260734796524047851562500e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 4.033381299002754549754001800465630367398262023925781250e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -1.449545371157945350404361306573264300823211669921875000e+00; + a[2] = -1.030104190885922088583015465701464563608169555664062500e-02; + a[3] = 3.881857047554073680828423675848171114921569824218750000e-01; + a[4] = 7.171589940116777917022972133054281584918498992919921875e-02; + break; + + case 24000: + b[0] = 3.786678621924967069745093795063439756631851196289062500e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -7.573357243849934139490187590126879513263702392578125000e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 3.786678621924967069745093795063439756631851196289062500e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -1.529945307555420797029910318087786436080932617187500000e+00; + a[2] = 1.283553182116208835061854642844991758465766906738281250e-01; + a[3] = 3.494608072385725350272878131363540887832641601562500000e-01; + a[4] = 5.217291949300089520802359288609295617789030075073242188e-02; + break; + + case 32000: + b[0] = 2.977986488230693340462096330156782642006874084472656250e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -5.955972976461386680924192660313565284013748168945312500e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 2.977986488230693340462096330156782642006874084472656250e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -1.812455387128179218336754274787381291389465332031250000e+00; + a[2] = 6.425013281155662614452239722595550119876861572265625000e-01; + a[3] = 1.619857574578579817448087396769551560282707214355468750e-01; + a[4] = 7.987649713547682189807019881300220731645822525024414062e-03; + break; + + case 44100: + b[0] = 2.170085619492190254220531642204150557518005371093750000e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -4.340171238984380508441063284408301115036010742187500000e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 2.170085619492190254220531642204150557518005371093750000e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.134674963687040794013682898366823792457580566406250000e+00; + a[2] = 1.279333533236062692139967111870646476745605468750000000e+00; + a[3] = -1.495598460893957093453821016737492755055427551269531250e-01; + a[4] = 4.908700174624683852664386307651511742733418941497802734e-03; + break; + + case 48000: + b[0] = 1.978871200263932761398422144338837824761867523193359375e-01; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -3.957742400527865522796844288677675649523735046386718750e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 1.978871200263932761398422144338837824761867523193359375e-01; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.219172914052801814932536217384040355682373046875000000e+00; + a[2] = 1.455135878947171557129536267893854528665542602539062500e+00; + a[3] = -2.484960738877830532800317087094299495220184326171875000e-01; + a[4] = 1.253882314727246607977129144728678511455655097961425781e-02; + break; + + case 88200: + b[0] = 9.221909851156021020734954163344809785485267639160156250e-02; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -1.844381970231204204146990832668961957097053527832031250e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 9.221909851156021020734954163344809785485267639160156250e-02; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.785795902923448696952846148633398115634918212890625000e+00; + a[2] = 2.727736758747444145711824603495188057422637939453125000e+00; + a[3] = -1.097007502819661528548067508381791412830352783203125000e+00; + a[4] = 1.550674356752141103132913713125162757933139801025390625e-01; + break; + + case 96000: + b[0] = 8.182864044979756834585771230194950476288795471191406250e-02; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -1.636572808995951366917154246038990095257759094238281250e-01; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 8.182864044979756834585771230194950476288795471191406250e-02; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -2.856378516857566829401093855267390608787536621093750000e+00; + a[2] = 2.897640237559524045707348705036565661430358886718750000e+00; + a[3] = -1.225265958339703198376469117647502571344375610351562500e+00; + a[4] = 1.840048283551226071530493300087982788681983947753906250e-01; + break; + + case 192000: + b[0] = 2.784755468532278815940728122768632601946592330932617188e-02; + b[1] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[2] = -5.569510937064557631881456245537265203893184661865234375e-02; + b[3] = 0.000000000000000000000000000000000000000000000000000000e+00; + b[4] = 2.784755468532278815940728122768632601946592330932617188e-02; + a[0] = 1.000000000000000000000000000000000000000000000000000000e+00; + a[1] = -3.333298856144166322224009491037577390670776367187500000e+00; + a[2] = 4.111467536240339448738723149290308356285095214843750000e+00; + a[3] = -2.222889041651291641699117462849244475364685058593750000e+00; + a[4] = 4.447204118126878991112960193277103826403617858886718750e-01; + break; + + default: + AUBIO_ERROR ( "sampling rate of C-weighting filter is %d, should be one of\ + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 192000.\n", + samplerate ); + return 1; + + } + + return 0; +} + +aubio_filter_t * new_aubio_filter_c_weighting (uint_t samplerate) { + aubio_filter_t * f = new_aubio_filter(5); + if (aubio_filter_set_c_weighting(f,samplerate) != AUBIO_OK) { + del_aubio_filter(f); + return NULL; + } + return f; +} diff --git a/aubio/src/temporal/c_weighting.h b/aubio/src/temporal/c_weighting.h new file mode 100644 index 00000000..d87f61fa --- /dev/null +++ b/aubio/src/temporal/c_weighting.h @@ -0,0 +1,88 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_FILTER_C_DESIGN_H +#define AUBIO_FILTER_C_DESIGN_H + +/** \file + + C-weighting filter coefficients + + This file creates a C-weighting digital filter, which reduces low and high + frequencies and enhance the middle ones to reflect the ability of the human + hearing. + + The implementation is based on the following standard: + + - IEC/CD 1672: Electroacoustics-Sound Level Meters, IEC, Geneva, Nov. 1996, + for A- and C-weighting filters. + + See also: + + - A-Weighting on + Wikipedia + - Weighting filter on + Wikipedia + - Christophe + Couvreur's 'octave' toolbox + + The coefficients in this file have been computed using Christophe Couvreur's + scripts in octave 3.0 (debian package 1:3.0.5-6+b2 with octave-signal + 1.0.9-1+b1 on i386), with
 [b, a] = cdsign(1/Fs) 
for various + sampling frequencies (8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, + 88200, 96000, and 192000 Hz). + + The sampling frequency should normally be higher than 20kHz, but most common + file sampling rates have been included for completeness. + + \example temporal/test-c_weighting.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** create new C-design filter + + \param samplerate sampling frequency of the signal to filter. Should be one of + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, and + 192000 Hz + + \return a new filter object + +*/ +aubio_filter_t *new_aubio_filter_c_weighting (uint_t samplerate); + +/** set feedback and feedforward coefficients of a C-weighting filter + + \param f filter object to get coefficients from + \param samplerate sampling frequency of the signal to filter. Should be one of + 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, and + 192000 Hz + +*/ +uint_t aubio_filter_set_c_weighting (aubio_filter_t * f, uint_t samplerate); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FILTER_C_DESIGN_H */ diff --git a/aubio/src/temporal/filter.c b/aubio/src/temporal/filter.c new file mode 100644 index 00000000..776d2e6f --- /dev/null +++ b/aubio/src/temporal/filter.c @@ -0,0 +1,163 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + + +/* Requires lsmp_t to be long or double. float will NOT give reliable + * results */ + +#include "aubio_priv.h" +#include "fvec.h" +#include "lvec.h" +#include "mathutils.h" +#include "temporal/filter.h" + +struct _aubio_filter_t +{ + uint_t order; + uint_t samplerate; + lvec_t *a; + lvec_t *b; + lvec_t *y; + lvec_t *x; +}; + +void +aubio_filter_do_outplace (aubio_filter_t * f, const fvec_t * in, fvec_t * out) +{ + fvec_copy (in, out); + aubio_filter_do (f, out); +} + +void +aubio_filter_do (aubio_filter_t * f, fvec_t * in) +{ + uint_t j, l, order = f->order; + lsmp_t *x = f->x->data; + lsmp_t *y = f->y->data; + lsmp_t *a = f->a->data; + lsmp_t *b = f->b->data; + + for (j = 0; j < in->length; j++) { + /* new input */ + x[0] = KILL_DENORMAL (in->data[j]); + y[0] = b[0] * x[0]; + for (l = 1; l < order; l++) { + y[0] += b[l] * x[l]; + y[0] -= a[l] * y[l]; + } + /* new output */ + in->data[j] = y[0]; + /* store for next sample */ + for (l = order - 1; l > 0; l--) { + x[l] = x[l - 1]; + y[l] = y[l - 1]; + } + } +} + +/* The rough way: reset memory of filter between each run to avoid end effects. */ +void +aubio_filter_do_filtfilt (aubio_filter_t * f, fvec_t * in, fvec_t * tmp) +{ + uint_t j; + uint_t length = in->length; + /* apply filtering */ + aubio_filter_do (f, in); + aubio_filter_do_reset (f); + /* mirror */ + for (j = 0; j < length; j++) + tmp->data[length - j - 1] = in->data[j]; + /* apply filtering on mirrored */ + aubio_filter_do (f, tmp); + aubio_filter_do_reset (f); + /* invert back */ + for (j = 0; j < length; j++) + in->data[j] = tmp->data[length - j - 1]; +} + +lvec_t * +aubio_filter_get_feedback (const aubio_filter_t * f) +{ + return f->a; +} + +lvec_t * +aubio_filter_get_feedforward (const aubio_filter_t * f) +{ + return f->b; +} + +uint_t +aubio_filter_get_order (const aubio_filter_t * f) +{ + return f->order; +} + +uint_t +aubio_filter_get_samplerate (const aubio_filter_t * f) +{ + return f->samplerate; +} + +uint_t +aubio_filter_set_samplerate (aubio_filter_t * f, uint_t samplerate) +{ + f->samplerate = samplerate; + return AUBIO_OK; +} + +void +aubio_filter_do_reset (aubio_filter_t * f) +{ + lvec_zeros (f->x); + lvec_zeros (f->y); +} + +aubio_filter_t * +new_aubio_filter (uint_t order) +{ + aubio_filter_t *f = AUBIO_NEW (aubio_filter_t); + if ((sint_t)order < 1) { + AUBIO_FREE(f); + return NULL; + } + f->x = new_lvec (order); + f->y = new_lvec (order); + f->a = new_lvec (order); + f->b = new_lvec (order); + /* by default, samplerate is not set */ + f->samplerate = 0; + f->order = order; + /* set default to identity */ + f->a->data[0] = 1.; + f->b->data[0] = 1.; + return f; +} + +void +del_aubio_filter (aubio_filter_t * f) +{ + del_lvec (f->a); + del_lvec (f->b); + del_lvec (f->x); + del_lvec (f->y); + AUBIO_FREE (f); + return; +} diff --git a/aubio/src/temporal/filter.h b/aubio/src/temporal/filter.h new file mode 100644 index 00000000..b8b678e8 --- /dev/null +++ b/aubio/src/temporal/filter.h @@ -0,0 +1,176 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_FILTER_H +#define AUBIO_FILTER_H + +/** \file + + Digital filter + + This object stores a digital filter of order \f$n\f$. + It contains the following data: + - \f$ n*1 b_i \f$ feedforward coefficients + - \f$ n*1 a_i \f$ feedback coefficients + - \f$ n*c x_i \f$ input signal + - \f$ n*c y_i \f$ output signal + + For convenience, the samplerate of the input signal is also stored in the + object. + + Feedforward and feedback parameters can be modified using + aubio_filter_get_feedback() and aubio_filter_get_feedforward(). + + The function aubio_filter_do_outplace() computes the following output signal + \f$ y[n] \f$ from the input signal \f$ x[n] \f$: + + \f{eqnarray*}{ + y[n] = b_0 x[n] & + & b_1 x[n-1] + b_2 x[n-2] + ... + b_P x[n-P] \\ + & - & a_1 y[n-1] - a_2 y[n-2] - ... - a_P y[n-P] \\ + \f} + + The function aubio_filter_do() executes the same computation but modifies + directly the input signal (in-place). + + The function aubio_filter_do_filtfilt() version runs the filter twice, first + forward then backward, to compensate with the phase shifting of the forward + operation. + + Some convenience functions are provided: + - new_aubio_filter_a_weighting() and aubio_filter_set_a_weighting(), + - new_aubio_filter_c_weighting() and aubio_filter_set_c_weighting(). + - new_aubio_filter_biquad() and aubio_filter_set_biquad(). + + \example temporal/test-filter.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Digital filter + +*/ +typedef struct _aubio_filter_t aubio_filter_t; + +/** filter input vector (in-place) + + \param f filter object as returned by new_aubio_filter() + \param in input vector to filter + +*/ +void aubio_filter_do (aubio_filter_t * f, fvec_t * in); + +/** filter input vector (out-of-place) + + \param f filter object as returned by new_aubio_filter() + \param in input vector to filter + \param out output vector to store filtered input + +*/ +void aubio_filter_do_outplace (aubio_filter_t * f, const fvec_t * in, fvec_t * out); + +/** filter input vector forward and backward + + \param f ::aubio_filter_t object as returned by new_aubio_filter() + \param in ::fvec_t input vector to filter + \param tmp memory space to use for computation + +*/ +void aubio_filter_do_filtfilt (aubio_filter_t * f, fvec_t * in, fvec_t * tmp); + +/** returns a pointer to feedback coefficients \f$ a_i \f$ + + \param f filter object to get parameters from + + \return a pointer to the \f$ a_0 ... a_i ... a_P \f$ coefficients + +*/ +lvec_t *aubio_filter_get_feedback (const aubio_filter_t * f); + +/** returns a pointer to feedforward coefficients \f$ b_i \f$ + + \param f filter object to get coefficients from + + \return a pointer to the \f$ b_0 ... b_i ... b_P \f$ coefficients + +*/ +lvec_t *aubio_filter_get_feedforward (const aubio_filter_t * f); + +/** get order of the filter + + \param f filter to get order from + + \return the order of the filter + +*/ +uint_t aubio_filter_get_order (const aubio_filter_t * f); + +/** get sampling rate of the filter + + \param f filter to get sampling rate from + + \return the sampling rate of the filter, in Hz + +*/ +uint_t aubio_filter_get_samplerate (const aubio_filter_t * f); + +/** get sampling rate of the filter + + \param f filter to get sampling rate from + \param samplerate sample rate to set the filter to + + \return the sampling rate of the filter, in Hz + +*/ +uint_t aubio_filter_set_samplerate (aubio_filter_t * f, uint_t samplerate); + +/** reset filter memory + + \param f filter object as returned by new_aubio_filter() + +*/ +void aubio_filter_do_reset (aubio_filter_t * f); + +/** create new filter object + + This function creates a new ::aubio_filter_t object, given the order of the + filter. + + \param order order of the filter (number of coefficients) + + \return the newly created filter object + +*/ +aubio_filter_t *new_aubio_filter (uint_t order); + +/** delete a filter object + + \param f filter object to delete + +*/ +void del_aubio_filter (aubio_filter_t * f); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_FILTER_H */ diff --git a/aubio/src/temporal/resampler.h b/aubio/src/temporal/resampler.h new file mode 100644 index 00000000..7528e8ac --- /dev/null +++ b/aubio/src/temporal/resampler.h @@ -0,0 +1,65 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_RESAMPLER_H +#define AUBIO_RESAMPLER_H + +/** \file + + Resampling object + + This object resamples an input vector into an output vector using + libsamplerate. See http://www.mega-nerd.com/SRC/ + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** resampler object */ +typedef struct _aubio_resampler_t aubio_resampler_t; + +/** create resampler object + + \param ratio output_sample_rate / input_sample_rate + \param type libsamplerate resampling type, see http://www.mega-nerd.com/SRC/api_misc.html#Converters + +*/ +aubio_resampler_t *new_aubio_resampler (smpl_t ratio, uint_t type); + +/** delete resampler object */ +void del_aubio_resampler (aubio_resampler_t * s); + +/** resample input in output + + \param s resampler object + \param input input buffer of size N + \param output output buffer of size N*ratio + +*/ +void aubio_resampler_do (aubio_resampler_t * s, const fvec_t * input, + fvec_t * output); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_RESAMPLER_H */ diff --git a/aubio/src/types.h b/aubio/src/types.h new file mode 100644 index 00000000..57df2e0d --- /dev/null +++ b/aubio/src/types.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2003-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_TYPES_H +#define AUBIO_TYPES_H + +/** \file + + Definition of data types used in aubio + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_AUBIO_DOUBLE +/** defined to 1 if aubio is compiled in double precision */ +#define HAVE_AUBIO_DOUBLE 0 +#endif + +/** short sample format (32 or 64 bits) */ +#if !HAVE_AUBIO_DOUBLE +typedef float smpl_t; +/** print format for sample in single precision */ +#define AUBIO_SMPL_FMT "%f" +#else +typedef double smpl_t; +/** print format for double in single precision */ +#define AUBIO_SMPL_FMT "%lf" +#endif +/** long sample format (64 bits or more) */ +#if !HAVE_AUBIO_DOUBLE +typedef double lsmp_t; +/** print format for sample in double precision */ +#define AUBIO_LSMP_FMT "%lf" +#else +typedef long double lsmp_t; +/** print format for double in double precision */ +#define AUBIO_LSMP_FMT "%Lf" +#endif +/** unsigned integer */ +typedef unsigned int uint_t; +/** signed integer */ +typedef int sint_t; +/** character */ +typedef char char_t; + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_TYPES_H */ diff --git a/aubio/src/utils/hist.c b/aubio/src/utils/hist.c new file mode 100644 index 00000000..2dcc4432 --- /dev/null +++ b/aubio/src/utils/hist.c @@ -0,0 +1,151 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "utils/scale.h" +#include "mathutils.h" //fvec_min fvec_max +#include "utils/hist.h" + +/******** + * Object Structure + */ + +struct _aubio_hist_t { + fvec_t * hist; + uint_t nelems; + fvec_t * cent; + aubio_scale_t *scaler; +}; + +/** + * Object creation/deletion calls + */ +aubio_hist_t * new_aubio_hist (smpl_t flow, smpl_t fhig, uint_t nelems){ + aubio_hist_t * s = AUBIO_NEW(aubio_hist_t); + smpl_t step = (fhig-flow)/(smpl_t)(nelems); + smpl_t accum = step; + uint_t i; + if ((sint_t)nelems <= 0) { + AUBIO_FREE(s); + return NULL; + } + s->nelems = nelems; + s->hist = new_fvec(nelems); + s->cent = new_fvec(nelems); + + /* use scale to map flow/fhig -> 0/nelems */ + s->scaler = new_aubio_scale(flow,fhig,0,nelems); + /* calculate centers now once */ + s->cent->data[0] = flow + 0.5 * step; + for (i=1; i < s->nelems; i++, accum+=step ) + s->cent->data[i] = s->cent->data[0] + accum; + + return s; +} + +void del_aubio_hist(aubio_hist_t *s) { + del_fvec(s->hist); + del_fvec(s->cent); + del_aubio_scale(s->scaler); + AUBIO_FREE(s); +} + +/*** + * do it + */ +void aubio_hist_do (aubio_hist_t *s, fvec_t *input) { + uint_t j; + sint_t tmp = 0; + aubio_scale_do(s->scaler, input); + /* reset data */ + fvec_zeros(s->hist); + /* run accum */ + for (j=0; j < input->length; j++) + { + tmp = (sint_t)FLOOR(input->data[j]); + if ((tmp >= 0) && (tmp < (sint_t)s->nelems)) { + s->hist->data[tmp] += 1; + } + } +} + +void aubio_hist_do_notnull (aubio_hist_t *s, fvec_t *input) { + uint_t j; + sint_t tmp = 0; + aubio_scale_do(s->scaler, input); + /* reset data */ + fvec_zeros(s->hist); + /* run accum */ + for (j=0; j < input->length; j++) { + if (input->data[j] != 0) { + tmp = (sint_t)FLOOR(input->data[j]); + if ((tmp >= 0) && (tmp < (sint_t)s->nelems)) + s->hist->data[tmp] += 1; + } + } +} + + +void aubio_hist_dyn_notnull (aubio_hist_t *s, fvec_t *input) { + uint_t i; + sint_t tmp = 0; + smpl_t ilow = fvec_min(input); + smpl_t ihig = fvec_max(input); + smpl_t step = (ihig-ilow)/(smpl_t)(s->nelems); + + /* readapt */ + aubio_scale_set_limits (s->scaler, ilow, ihig, 0, s->nelems); + + /* recalculate centers */ + s->cent->data[0] = ilow + 0.5f * step; + for (i=1; i < s->nelems; i++) + s->cent->data[i] = s->cent->data[0] + i * step; + + /* scale */ + aubio_scale_do(s->scaler, input); + + /* reset data */ + fvec_zeros(s->hist); + /* run accum */ + for (i=0; i < input->length; i++) { + if (input->data[i] != 0) { + tmp = (sint_t)FLOOR(input->data[i]); + if ((tmp >= 0) && (tmp < (sint_t)s->nelems)) + s->hist->data[tmp] += 1; + } + } +} + +void aubio_hist_weight (aubio_hist_t *s) { + uint_t j; + for (j=0; j < s->nelems; j++) { + s->hist->data[j] *= s->cent->data[j]; + } +} + +smpl_t aubio_hist_mean (const aubio_hist_t *s) { + uint_t j; + smpl_t tmp = 0.0; + for (j=0; j < s->nelems; j++) + tmp += s->hist->data[j]; + return tmp/(smpl_t)(s->nelems); +} + diff --git a/aubio/src/utils/hist.h b/aubio/src/utils/hist.h new file mode 100644 index 00000000..d39091a4 --- /dev/null +++ b/aubio/src/utils/hist.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** @file + * + * Histogram function + * + * Big hacks to implement an histogram + */ + +#ifndef AUBIO_HIST_H +#define AUBIO_HIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** histogram object */ +typedef struct _aubio_hist_t aubio_hist_t; + +/** histogram creation + + \param flow minimum input + \param fhig maximum input + \param nelems number of histogram columns + +*/ +aubio_hist_t * new_aubio_hist(smpl_t flow, smpl_t fhig, uint_t nelems); +/** histogram deletion */ +void del_aubio_hist(aubio_hist_t *s); +/** compute the histogram */ +void aubio_hist_do(aubio_hist_t *s, fvec_t * input); +/** compute the histogram ignoring null elements */ +void aubio_hist_do_notnull(aubio_hist_t *s, fvec_t * input); +/** compute the mean of the histogram */ +smpl_t aubio_hist_mean(const aubio_hist_t *s); +/** weight the histogram */ +void aubio_hist_weight(aubio_hist_t *s); +/** compute dynamic histogram for non-null elements */ +void aubio_hist_dyn_notnull (aubio_hist_t *s, fvec_t *input); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_HIST_H */ diff --git a/aubio/src/utils/log.c b/aubio/src/utils/log.c new file mode 100644 index 00000000..967c2d6c --- /dev/null +++ b/aubio/src/utils/log.c @@ -0,0 +1,92 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "log.h" + +/** array of pointers to logging functions, one per level */ +static aubio_log_function_t aubio_log_function[AUBIO_LOG_LAST_LEVEL]; +/** array of pointers to closure passed to logging functions, one per level */ +static void* aubio_log_user_data[AUBIO_LOG_LAST_LEVEL]; +/** buffer for logging messages */ +static char aubio_log_buffer[512]; + +/** private function used by default by logging functions */ +void +aubio_default_log(sint_t level, const char_t *message, void * data UNUSED) +{ + FILE *out; + out = stdout; + if (level == AUBIO_LOG_ERR || level == AUBIO_LOG_DBG || level == AUBIO_LOG_WRN) { + out = stderr; + } + fprintf(out, "%s", message); + //fflush(out); +} + +uint_t +aubio_log(sint_t level, const char_t *fmt, ...) +{ + aubio_log_function_t fun = NULL; + + va_list args; + va_start(args, fmt); + vsnprintf(aubio_log_buffer, sizeof(aubio_log_buffer), fmt, args); + va_end(args); + + if ((level >= 0) && (level < AUBIO_LOG_LAST_LEVEL)) { + fun = aubio_log_function[level]; + if (fun != NULL) { + (*fun)(level, aubio_log_buffer, aubio_log_user_data[level]); + } else { + aubio_default_log(level, aubio_log_buffer, NULL); + } + } + return AUBIO_FAIL; +} + +void +aubio_log_reset(void) +{ + uint_t i = 0; + for (i = 0; i < AUBIO_LOG_LAST_LEVEL; i++) { + aubio_log_set_level_function(i, aubio_default_log, NULL); + } +} + +aubio_log_function_t +aubio_log_set_level_function(sint_t level, aubio_log_function_t fun, void * data) +{ + aubio_log_function_t old = NULL; + if ((level >= 0) && (level < AUBIO_LOG_LAST_LEVEL)) { + old = aubio_log_function[level]; + aubio_log_function[level] = fun; + aubio_log_user_data[level] = data; + } + return old; +} + +void +aubio_log_set_function(aubio_log_function_t fun, void * data) { + uint_t i = 0; + for (i = 0; i < AUBIO_LOG_LAST_LEVEL; i++) { + aubio_log_set_level_function(i, fun, data); + } +} diff --git a/aubio/src/utils/log.h b/aubio/src/utils/log.h new file mode 100644 index 00000000..091e91d4 --- /dev/null +++ b/aubio/src/utils/log.h @@ -0,0 +1,99 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_LOG_H +#define AUBIO_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file + + Logging features + + This file specifies ::aubio_log_set_function and + ::aubio_log_set_level_function, which let you define one or several custom + logging functions to redirect warnings and errors from aubio to your + application. The custom function should have the prototype defined in + ::aubio_log_function_t. + + After a call to ::aubio_log_set_level_function, ::aubio_log_reset can be used + to reset each logging functions to the default ones. + + \example utils/test-log.c + +*/ + +/** list of logging levels */ +enum aubio_log_level { + AUBIO_LOG_ERR, /**< critical errors */ + AUBIO_LOG_INF, /**< infos */ + AUBIO_LOG_MSG, /**< general messages */ + AUBIO_LOG_DBG, /**< debug messages */ + AUBIO_LOG_WRN, /**< warnings */ + AUBIO_LOG_LAST_LEVEL, /**< number of valid levels */ +}; + +/** Logging function prototype, to be passed to ::aubio_log_set_function + + \param level log level + \param message text to log + \param data optional closure used by the callback + + See @ref utils/test-log.c for an example of logging function. + + */ +typedef void (*aubio_log_function_t)(sint_t level, const char_t *message, void + *data); + +/** Set logging function for all levels + + \param fun the function to be used to log, of type ::aubio_log_function_t + \param data optional closure to be passed to the function (can be NULL if + nothing to pass) + + */ +void aubio_log_set_function(aubio_log_function_t fun, void* data); + +/** Set logging function for a given level + + \param level the level for which to set the logging function + \param fun the function to be used to log, of type ::aubio_log_function_t + \param data optional closure to be passed to the function (can be NULL if + nothing to pass) + +*/ +aubio_log_function_t aubio_log_set_level_function(sint_t level, + aubio_log_function_t fun, void* data); + +/** Reset all logging functions to the default one + + After calling this function, the default logging function will be used to + print error, warning, normal, and debug messages to `stdout` or `stderr`. + + */ +void aubio_log_reset(void); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_LOG_H */ diff --git a/aubio/src/utils/parameter.c b/aubio/src/utils/parameter.c new file mode 100644 index 00000000..00b39799 --- /dev/null +++ b/aubio/src/utils/parameter.c @@ -0,0 +1,141 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "parameter.h" + +#define AUBIO_PARAM_MAX_STEPS 2000 +#define AUBIO_PARAM_MIN_STEPS 1 + +struct _aubio_parameter_t { + smpl_t current_value; + smpl_t target_value; + smpl_t increment; + + smpl_t max_value; + smpl_t min_value; + + uint_t steps; +}; + +aubio_parameter_t * new_aubio_parameter (smpl_t min_value, smpl_t max_value, uint_t steps ) +{ + aubio_parameter_t * param = AUBIO_NEW(aubio_parameter_t); + + param->min_value = min_value; + param->max_value = max_value; + param->steps = steps; + + param->current_value = param->min_value; + param->target_value = param->current_value; + param->increment = 0.; + + return param; +} + +uint_t aubio_parameter_set_target_value ( aubio_parameter_t * param, smpl_t value ) +{ + uint_t err = AUBIO_OK; + if (value < param->min_value ) { + param->target_value = param->min_value; + err = AUBIO_FAIL; + } else if ( value > param->max_value ) { + param->target_value = param->max_value; + err = AUBIO_FAIL; + } else { + param->target_value = value; + } + param->increment = ( param->target_value - param->current_value ) / param->steps; + return err; +} + +uint_t aubio_parameter_set_current_value ( aubio_parameter_t * param, smpl_t value ) +{ + uint_t err = AUBIO_OK; + if (value < param->min_value ) { + param->current_value = param->min_value; + err = AUBIO_FAIL; + } else if ( value > param->max_value ) { + param->current_value = param->max_value; + err = AUBIO_FAIL; + } else { + param->current_value = value; + } + param->target_value = param->current_value; + param->increment = 0; + return err; +} + +smpl_t aubio_parameter_get_current_value ( const aubio_parameter_t * s ) +{ + return s->current_value; +} + +smpl_t aubio_parameter_get_next_value ( aubio_parameter_t * s ) +{ + if ( ABS(s->current_value - s->target_value) > ABS(s->increment) ) { + s->current_value += s->increment; + } else { + s->current_value = s->target_value; + } + return s->current_value; +} + +uint_t aubio_parameter_set_steps ( aubio_parameter_t * param, uint_t steps ) +{ + if (steps < AUBIO_PARAM_MIN_STEPS || steps > AUBIO_PARAM_MAX_STEPS) { + return AUBIO_FAIL; + } + param->steps = steps; + param->increment = ( param->target_value - param->current_value ) / param->steps; + return AUBIO_OK; +} + +uint_t aubio_parameter_get_steps ( const aubio_parameter_t * param ) +{ + return param->steps; +} + +uint_t aubio_parameter_set_min_value ( aubio_parameter_t * param, smpl_t min_value ) +{ + param->min_value = min_value; + return AUBIO_OK; +} + +smpl_t aubio_parameter_get_min_value ( const aubio_parameter_t * param ) +{ + return param->min_value; +} + +uint_t aubio_parameter_set_max_value ( aubio_parameter_t * param, smpl_t max_value ) +{ + param->max_value = max_value; + return AUBIO_OK; +} + +smpl_t aubio_parameter_get_max_value ( const aubio_parameter_t * param ) +{ + return param->max_value; +} + +void del_aubio_parameter ( aubio_parameter_t * param ) +{ + AUBIO_FREE(param); +} diff --git a/aubio/src/utils/parameter.h b/aubio/src/utils/parameter.h new file mode 100644 index 00000000..9649eb8c --- /dev/null +++ b/aubio/src/utils/parameter.h @@ -0,0 +1,159 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#ifndef AUBIO_PARAMETER_H +#define AUBIO_PARAMETER_H + +/** \file + + Parameter with linear interpolation + + This object manages a parameter, with minimum and maximum values, and a + number of steps to compute linear interpolation between two values. + + \example utils/test-parameter.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** parameter object */ +typedef struct _aubio_parameter_t aubio_parameter_t; + +/** create new parameter object + + \param min_value the minimum value of the new parameter + \param max_value the maximum value of the new parameter + \param steps the number of steps to interpolate from the old value to the target value + + \return the newly created ::aubio_parameter_t + +*/ +aubio_parameter_t * new_aubio_parameter(smpl_t min_value, smpl_t max_value, uint_t steps); + +/** set target value of the parameter + + \param param parameter, created by ::new_aubio_parameter + \param value new target value + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_parameter_set_target_value ( aubio_parameter_t * param, smpl_t value ); + +/** get next parameter + + \param param parameter, created by ::new_aubio_parameter + + \return new interpolated parameter value + +*/ +smpl_t aubio_parameter_get_next_value ( aubio_parameter_t * param ); + +/** get current parameter value, without interpolation + + \param param parameter, created by ::new_aubio_parameter + + \return current value + +*/ +smpl_t aubio_parameter_get_current_value ( const aubio_parameter_t * param ); + +/** set current parameter value, skipping interpolation + + \param param parameter, created by ::new_aubio_parameter + \param value new parameter value + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_parameter_set_current_value ( aubio_parameter_t * param, smpl_t value ); + +/** set number of steps used for interpolation + + \param param parameter, created by ::new_aubio_parameter + \param steps new number of steps + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_parameter_set_steps ( aubio_parameter_t * param, uint_t steps ); + +/** get number of steps of this parameter + + \param param parameter, created by ::new_aubio_parameter + + \return number of steps + +*/ +uint_t aubio_parameter_get_steps ( const aubio_parameter_t * param); + +/** set minimum value of this parameter + + \param param parameter, created by ::new_aubio_parameter + \param min_value new minimum value + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_parameter_set_min_value ( aubio_parameter_t * param, smpl_t min_value ); + +/** get minimum value of this parameter + + \param param parameter, created by ::new_aubio_parameter + + \return minimum value + +*/ +smpl_t aubio_parameter_get_min_value ( const aubio_parameter_t * param ); + +/** set maximum value of this parameter + + \param param parameter, created by ::new_aubio_parameter + \param max_value new maximum value + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_parameter_set_max_value ( aubio_parameter_t * param, smpl_t max_value ); + +/** get maximum value of this parameter + + \param param parameter, created by ::new_aubio_parameter + + \return maximum value + +*/ +smpl_t aubio_parameter_get_max_value ( const aubio_parameter_t * param ); + +/** destroy ::aubio_parameter_t object + + \param param parameter, created by ::new_aubio_parameter + +*/ +void del_aubio_parameter( aubio_parameter_t * param ); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_PARAMETER_H */ diff --git a/aubio/src/utils/scale.c b/aubio/src/utils/scale.c new file mode 100644 index 00000000..3bd116c8 --- /dev/null +++ b/aubio/src/utils/scale.c @@ -0,0 +1,79 @@ +/* + Copyright (C) 2003-2009 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" +#include "fvec.h" +#include "utils/scale.h" + +struct _aubio_scale_t { + smpl_t ilow; + smpl_t ihig; + smpl_t olow; + smpl_t ohig; + + smpl_t scaler; + smpl_t irange; + + /* not implemented yet : type in/out data + bool inint; + bool outint; + */ +}; + +aubio_scale_t * new_aubio_scale (smpl_t ilow, smpl_t ihig, + smpl_t olow, smpl_t ohig) { + aubio_scale_t * s = AUBIO_NEW(aubio_scale_t); + aubio_scale_set_limits (s, ilow, ihig, olow, ohig); + return s; +} + +void del_aubio_scale(aubio_scale_t *s) { + AUBIO_FREE(s); +} + +uint_t aubio_scale_set_limits (aubio_scale_t *s, smpl_t ilow, smpl_t ihig, + smpl_t olow, smpl_t ohig) { + smpl_t inputrange = ihig - ilow; + smpl_t outputrange= ohig - olow; + s->ilow = ilow; + s->ihig = ihig; + s->olow = olow; + s->ohig = ohig; + if (inputrange == 0) { + s->scaler = 0.0; + } else { + s->scaler = outputrange/inputrange; + if (inputrange < 0) { + inputrange = inputrange * -1.0f; + } + } + return AUBIO_OK; +} + +void aubio_scale_do (aubio_scale_t *s, fvec_t *input) +{ + uint_t j; + for (j=0; j < input->length; j++){ + input->data[j] -= s->ilow; + input->data[j] *= s->scaler; + input->data[j] += s->olow; + } +} + diff --git a/aubio/src/utils/scale.h b/aubio/src/utils/scale.h new file mode 100644 index 00000000..da311140 --- /dev/null +++ b/aubio/src/utils/scale.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Vector scaling function + + This object, inspired from the scale object in FTS, the jMax engine, scales + the values of a vector according to an affine function defined as follow: + + \f$ y = (x - ilow)*(ohig-olow)/(ihig-ilow) + olow \f$ + +*/ +#ifndef AUBIO_SCALE_H +#define AUBIO_SCALE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** scale object */ +typedef struct _aubio_scale_t aubio_scale_t; + +/** create a scale object + + \param flow lower value of output function + \param fhig higher value of output function + \param ilow lower value of input function + \param ihig higher value of output function + +*/ +aubio_scale_t * new_aubio_scale(smpl_t flow, smpl_t fhig, + smpl_t ilow, smpl_t ihig); +/** delete a scale object + + \param s scale object as returned by new_aubio_scale + +*/ +void del_aubio_scale(aubio_scale_t *s); +/** scale input vector + + \param s scale object as returned by new_aubio_scale + \param input vector to scale + +*/ +void aubio_scale_do(aubio_scale_t *s, fvec_t * input); +/** modify scale parameters after object creation + + \param s scale object as returned by new_aubio_scale + \param olow lower value of output function + \param ohig higher value of output function + \param ilow lower value of input function + \param ihig higher value of output function + +*/ +uint_t aubio_scale_set_limits (aubio_scale_t *s, smpl_t ilow, smpl_t ihig, + smpl_t olow, smpl_t ohig); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_SCALE_H */ diff --git a/aubio/src/utils/strutils.c b/aubio/src/utils/strutils.c new file mode 100644 index 00000000..8b695ff4 --- /dev/null +++ b/aubio/src/utils/strutils.c @@ -0,0 +1,47 @@ +/* + Copyright (C) 2018 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +#include "aubio_priv.h" + +#ifdef HAVE_WIN_HACKS +#define strncasecmp _strnicmp +#endif + +const char_t *aubio_str_get_extension(const char_t *filename) +{ + // find last occurence of dot character + const char_t *ext; + if (!filename) return NULL; + ext = strrchr(filename, '.'); + if (!ext || ext == filename) return ""; + else return ext + 1; +} + +uint_t aubio_str_extension_matches(const char_t *ext, const char_t *pattern) +{ + return ext && pattern && (strncasecmp(ext, pattern, PATH_MAX) == 0); +} + +uint_t aubio_str_path_has_extension(const char_t *filename, + const char_t *pattern) +{ + const char_t *ext = aubio_str_get_extension(filename); + return aubio_str_extension_matches(ext, pattern); +} diff --git a/aubio/src/utils/windll.c b/aubio/src/utils/windll.c new file mode 100644 index 00000000..cad04439 --- /dev/null +++ b/aubio/src/utils/windll.c @@ -0,0 +1,59 @@ +/* + Copyright (C) 2016 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** @file + + Windows dll entry point. + +*/ + +#include "aubio_priv.h" + +#ifdef HAVE_WIN_HACKS + +#ifndef __GNUC__ // do not include msvc headers when using gcc/mingw32 + +// latest version +#include +// for earlier versions, include WinSDKVer.h and set _WIN32_WINNT macro + +#endif /* __GNUC__ */ + +#define WIN32_LEAN_AND_MEAN +#include + +#include "aubio.h" + +BOOL APIENTRY DllMain( HMODULE hModule UNUSED, + DWORD ul_reason_for_call, + LPVOID lpReserved UNUSED) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +#endif diff --git a/aubio/src/vecutils.c b/aubio/src/vecutils.c new file mode 100644 index 00000000..8907b5ec --- /dev/null +++ b/aubio/src/vecutils.c @@ -0,0 +1,36 @@ +#include "aubio_priv.h" +#include "types.h" +#include "fvec.h" +#include "cvec.h" +#include "vecutils.h" + +#define AUBIO_OP(OPNAME, OP, TYPE, OBJ) \ +void TYPE ## _ ## OPNAME (TYPE ## _t *o) \ +{ \ + uint_t j; \ + for (j = 0; j < o->length; j++) { \ + o->OBJ[j] = OP (o->OBJ[j]); \ + } \ +} + +#define AUBIO_OP_C(OPNAME, OP) \ + AUBIO_OP(OPNAME, OP, fvec, data) + +AUBIO_OP_C(exp, EXP) +AUBIO_OP_C(cos, COS) +AUBIO_OP_C(sin, SIN) +AUBIO_OP_C(abs, ABS) +AUBIO_OP_C(sqrt, SQRT) +AUBIO_OP_C(log10, SAFE_LOG10) +AUBIO_OP_C(log, SAFE_LOG) +AUBIO_OP_C(floor, FLOOR) +AUBIO_OP_C(ceil, CEIL) +AUBIO_OP_C(round, ROUND) + +void fvec_pow (fvec_t *s, smpl_t power) +{ + uint_t j; + for (j = 0; j < s->length; j++) { + s->data[j] = POW(s->data[j], power); + } +} diff --git a/aubio/src/vecutils.h b/aubio/src/vecutils.h new file mode 100644 index 00000000..f0ed96e5 --- /dev/null +++ b/aubio/src/vecutils.h @@ -0,0 +1,116 @@ +/* + Copyright (C) 2009-2015 Paul Brossier + + This file is part of aubio. + + aubio 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. + + aubio 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 aubio. If not, see . + +*/ + +/** \file + + Utility functions for ::fvec_t + + */ + +#ifndef AUBIO_VECUTILS_H +#define AUBIO_VECUTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** compute \f$e^x\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_exp (fvec_t *s); + +/** compute \f$cos(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_cos (fvec_t *s); + +/** compute \f$sin(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_sin (fvec_t *s); + +/** compute the \f$abs(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_abs (fvec_t *s); + +/** compute the \f$sqrt(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_sqrt (fvec_t *s); + +/** compute the \f$log10(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_log10 (fvec_t *s); + +/** compute the \f$log(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_log (fvec_t *s); + +/** compute the \f$floor(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_floor (fvec_t *s); + +/** compute the \f$ceil(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_ceil (fvec_t *s); + +/** compute the \f$round(x)\f$ of each vector elements + + \param s vector to modify + +*/ +void fvec_round (fvec_t *s); + +/** raise each vector elements to the power pow + + \param s vector to modify + \param pow power to raise to + +*/ +void fvec_pow (fvec_t *s, smpl_t pow); + +#ifdef __cplusplus +} +#endif + +#endif /* AUBIO_VECUTILS_H */ diff --git a/client_generic/Client/AudioAnalyzer.cpp b/client_generic/Client/AudioAnalyzer.cpp new file mode 100644 index 00000000..17a1ef74 --- /dev/null +++ b/client_generic/Client/AudioAnalyzer.cpp @@ -0,0 +1,462 @@ +#include "AudioAnalyzer.h" + +extern "C" { +#include "aubio.h" +} + +#include "BeatDetektor.h" + +#include +#include +#include +#include +#include +#include +#include +#include "Settings.h" + +namespace +{ +constexpr uint_t kSampleRate = 44100; +constexpr uint_t kWinSize = 2048; +constexpr uint_t kHopSize = 512; +constexpr uint_t kNumBins = kWinSize / 2 + 1; + +float Clamp01(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; } + +float SmoothAttackRelease(float previous, float current, float attack, float release) +{ + const float a = current > previous ? attack : release; + return previous * (1.0f - a) + current * a; +} + +float DbToLinear(float db) { return std::pow(10.0f, db / 20.0f); } + +} // namespace + +constexpr uint_t kMelBands = 40; + +struct AubioState +{ + aubio_pvoc_t* pvoc = nullptr; + cvec_t* fftgrain = nullptr; + fvec_t* hopIn = nullptr; + fvec_t* tempoOut = nullptr; + aubio_tempo_t* tempo = nullptr; + aubio_filterbank_t* filterbank = nullptr; + fvec_t* melOut = nullptr; + bool ready = false; + + void Init() + { + hopIn = new_fvec(kHopSize); + fftgrain = new_cvec(kWinSize); + tempoOut = new_fvec(2); + melOut = new_fvec(kMelBands); + + pvoc = new_aubio_pvoc(kWinSize, kHopSize); + tempo = new_aubio_tempo("complex", kWinSize, kHopSize, kSampleRate); + filterbank = new_aubio_filterbank(kMelBands, kWinSize); + if (filterbank) + aubio_filterbank_set_mel_coeffs(filterbank, (smpl_t)kSampleRate, 20.0f, 11000.0f); + + if (pvoc && tempo && filterbank) + ready = true; + } + + ~AubioState() + { + if (pvoc) del_aubio_pvoc(pvoc); + if (fftgrain) del_cvec(fftgrain); + if (hopIn) del_fvec(hopIn); + if (tempoOut) del_fvec(tempoOut); + if (tempo) del_aubio_tempo(tempo); + if (filterbank) del_aubio_filterbank(filterbank); + if (melOut) del_fvec(melOut); + } +}; + +// ── Manual BPM control ──────────────────────────────────────────────────────── +static std::atomic s_bpmOverride{0.0f}; +static std::atomic s_liveBpm{120.0f}; +static std::atomic s_liveBeatPhase{0.0f}; +static std::atomic s_liveBeatCount{0}; +static std::atomic s_resetBeatPhase{false}; + +void AudioAnalyzer_SetBpmOverride(float bpm) +{ + s_bpmOverride.store(bpm, std::memory_order_relaxed); +} + +float AudioAnalyzer_GetCurrentBpm() +{ + return s_liveBpm.load(std::memory_order_relaxed); +} + +float AudioAnalyzer_GetBeatPhase() +{ + return s_liveBeatPhase.load(std::memory_order_relaxed); +} + +uint32_t AudioAnalyzer_GetBeatCount() +{ + return s_liveBeatCount.load(std::memory_order_relaxed); +} + +void AudioAnalyzer_ResetBeatPhase() +{ + s_resetBeatPhase.store(true, std::memory_order_relaxed); + s_liveBeatCount.store(0, std::memory_order_relaxed); +} + +float AudioAnalyzer_TapTempo() +{ + using Clock = std::chrono::steady_clock; + using Sec = std::chrono::duration; + + static constexpr int kMaxTaps = 8; + static Clock::time_point s_taps[kMaxTaps]; + static int s_tapCount = 0; + + const auto now = Clock::now(); + + if (s_tapCount > 0) + { + if (Sec(now - s_taps[(s_tapCount - 1) % kMaxTaps]).count() > 3.0) + s_tapCount = 0; + } + + s_taps[s_tapCount % kMaxTaps] = now; + ++s_tapCount; + + if (s_tapCount < 2) + return 0.0f; + + const int n = std::min(s_tapCount, kMaxTaps); + const int oldest = (s_tapCount - n) % kMaxTaps; + const int newest = (s_tapCount - 1) % kMaxTaps; + const double totalSec = Sec(s_taps[newest] - s_taps[oldest]).count(); + if (totalSec <= 0.0) + return 0.0f; + + const float bpm = static_cast(60.0 * (n - 1) / totalSec); + return (bpm >= 40.0f && bpm <= 250.0f) ? bpm : 0.0f; +} + +// ── Background thread ───────────────────────────────────────────────────────── + +void AudioAnalyzer::Start() +{ + if (m_Running.exchange(true)) + return; // already running + m_Thread = std::thread(&AudioAnalyzer::ThreadProc, this); +} + +void AudioAnalyzer::Stop() +{ + if (!m_Running.exchange(false)) + return; // already stopped + if (m_Thread.joinable()) + m_Thread.join(); +} + +AudioFeatures AudioAnalyzer::GetFeaturesSnapshot() const +{ + std::lock_guard lock(m_FeaturesMutex); + return m_Features; +} + +void AudioAnalyzer::ThreadProc() +{ + using Clock = std::chrono::steady_clock; + + std::deque sampleBuffer; + AubioState aubio; + + // Per-thread state (was static locals in Update()) + float bassPeak = 0.001f, midPeak = 0.001f, highPeak = 0.001f; + int warmup = 0; + float s_prevBass = 0.0f, s_prevMid = 0.0f, s_prevHigh = 0.0f; + float s_acfBpm = 120.0f; + uint_t s_totalHops = 0; + BeatDetektor bd_slow(60.0f, 119.0f); // covers 60–119 BPM + BeatDetektor bd_fast(120.0f, 239.0f); // covers 120–239 BPM + std::vector bdFftVec(kNumBins, 0.0f); + float s_beatPhase = 0.0f; + float s_beatInterval = 60.0f / 120.0f; + int s_beatCount = 0; + float volume = 0.0f; + float spectralCentroid = 0.0f; + float kick = 0.0f, snare = 0.0f, transient = 0.0f; + + // Silence tracking uses wall clock since audio may be sparse + auto silenceStart = Clock::now(); + bool inSilence = false; + + if (!m_AudioInput.IsRunning()) + m_AudioInput.Start(); + + aubio.Init(); + + while (m_Running.load(std::memory_order_relaxed)) + { + // Pull new samples from WASAPI + std::vector newSamples = m_AudioInput.GetSamples(); + for (float s : newSamples) + sampleBuffer.push_back(s); + + // Bound the backlog (~200 ms) so start-up after a stall stays smooth + constexpr size_t kMaxBufferSamples = kHopSize * 18; + if (sampleBuffer.size() > kMaxBufferSamples) + sampleBuffer.erase(sampleBuffer.begin(), + sampleBuffer.begin() + + static_cast(sampleBuffer.size() - kMaxBufferSamples)); + + if (sampleBuffer.size() < kHopSize) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + continue; + } + + if (!aubio.ready) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + continue; + } + + // Process all available hops — thread runs continuously so no per-call cap needed + float bassEnergyAcc = 0.0f, midEnergyAcc = 0.0f, highEnergyAcc = 0.0f; + float centroidNum = 0.0f, centroidDen = 0.0f; + float rmsAcc = 0.0f; + int hops = 0; + bool beatFired = false; + + while (sampleBuffer.size() >= kHopSize) + { + for (uint_t i = 0; i < kHopSize; ++i) + { + const float s = sampleBuffer[i]; + aubio.hopIn->data[i] = s; + rmsAcc += s * s; + } + for (uint_t i = 0; i < kHopSize; ++i) + sampleBuffer.pop_front(); + + aubio_pvoc_do(aubio.pvoc, aubio.hopIn, aubio.fftgrain); + + for (uint_t bin = 1; bin < kNumBins; ++bin) + { + const float mag = aubio.fftgrain->norm[bin]; + const float f = (float)kSampleRate * bin / (float)kWinSize; + if (f >= 20.0f && f < 20000.0f) + { + centroidNum += f * mag; + centroidDen += mag; + } + } + + aubio_filterbank_do(aubio.filterbank, aubio.fftgrain, aubio.melOut); + float bassEnergy = 0.0f, midEnergy = 0.0f, highEnergy = 0.0f; + for (uint_t b = 0; b < 8; ++b) bassEnergy += aubio.melOut->data[b]; + for (uint_t b = 8; b < 28; ++b) midEnergy += aubio.melOut->data[b]; + for (uint_t b = 28; b < 40; ++b) highEnergy += aubio.melOut->data[b]; + bassEnergy /= 8.0f; + midEnergy /= 20.0f; + highEnergy /= 12.0f; + + bassEnergyAcc += bassEnergy; + midEnergyAcc += midEnergy; + highEnergyAcc += highEnergy; + + aubio_tempo_do(aubio.tempo, aubio.hopIn, aubio.tempoOut); + if (aubio.tempoOut->data[0] > 0.0f) beatFired = true; + + // Feed BeatDetektor — reuse aubio's per-hop FFT magnitudes, zero extra FFT cost + ++s_totalHops; + { + const float hopTimeSec = float(s_totalHops) * float(kHopSize) / float(kSampleRate); + for (uint_t bin = 0; bin < kNumBins; ++bin) + bdFftVec[bin] = aubio.fftgrain->norm[bin]; + bd_slow.process(hopTimeSec, bdFftVec); + bd_fast.process(hopTimeSec, bdFftVec); + } + + ++hops; + } + + if (hops == 0) + continue; + + const float bassEnergy = bassEnergyAcc / hops; + const float midEnergy = midEnergyAcc / hops; + const float highEnergy = highEnergyAcc / hops; + const float rawLevel = std::sqrt(rmsAcc / (hops * (int)kHopSize)); + + // RMS volume + float clampedLevel = rawLevel; + if (clampedLevel < DbToLinear(-60.0f)) + clampedLevel = 0.0f; + clampedLevel = Clamp01(clampedLevel * 3.0f); + volume = SmoothAttackRelease(volume, clampedLevel, 0.35f, 0.08f); + + // Spectral centroid + float rawCentroid = 0.0f; + if (centroidDen > 0.0f) + rawCentroid = centroidNum / centroidDen; + const float logMin = std::log(20.0f); + const float logMax = std::log(20000.0f); + float logCentroid = 0.0f; + if (rawCentroid > 0.0f) + logCentroid = (std::log(rawCentroid) - logMin) / (logMax - logMin); + spectralCentroid = SmoothAttackRelease(spectralCentroid, Clamp01(logCentroid), 0.3f, 0.1f); + + // Adaptive normalisation — read settings once per thread iteration, not per hop + const float kPeakDecay = g_Settings()->Get("settings.player.audio_peak_decay", 0.999f); + const float kPeakFloor = g_Settings()->Get("settings.player.audio_peak_floor", 0.01f); + const float bassMult = g_Settings()->Get("settings.player.audio_bass_mult", 0.7f); + const float midMult = g_Settings()->Get("settings.player.audio_mid_mult", 0.8f); + const float highMult = g_Settings()->Get("settings.player.audio_high_mult", 0.7f); + + bassPeak = std::max({bassEnergy, bassPeak * kPeakDecay, kPeakFloor}); + midPeak = std::max({midEnergy, midPeak * kPeakDecay, kPeakFloor}); + highPeak = std::max({highEnergy, highPeak * kPeakDecay, kPeakFloor}); + + // gamma=2: squares the normalised ratio so quiet passages (e.g. 20% of peak) + // read ~0.04 instead of 0.20. Peaks still reach 1.0; only the curve changes. + constexpr float kGamma = 2.0f; + + float normBass, normMid, normHigh; + if (warmup < 90) + { + ++warmup; + normBass = Clamp01(std::pow(bassEnergy * 120.0f, kGamma)); + normMid = Clamp01(std::pow(midEnergy * 80.0f, kGamma)); + normHigh = Clamp01(std::pow(highEnergy * 60.0f, kGamma)); + } + else + { + normBass = Clamp01(std::pow(bassEnergy / bassPeak, kGamma) * bassMult); + normMid = Clamp01(std::pow(midEnergy / midPeak, kGamma) * midMult); + normHigh = Clamp01(std::pow(highEnergy / highPeak, kGamma) * highMult); + } + + // Kick / snare / transient + // volume is RMS-based and doesn't adapt away like normBass/normMid, so it's a reliable + // gate against ambient content in quiet sections where the adaptive peak has decayed to floor. + const float bassDelta = normBass - s_prevBass; + const float midDelta = normMid - s_prevMid; + const float highDelta = normHigh - s_prevHigh; + // Require onset from a low prior level: percussion attacks from near-silence; + // sustained bass/mid content (guitar, synth) keeps s_prevBass/s_prevMid elevated. + const bool kickFired = volume > 0.30f && normBass > 0.35f && bassDelta > 0.22f && s_prevBass < 0.25f; + const bool snareFired = volume > 0.30f && normMid > 0.25f && midDelta > 0.22f && midDelta > bassDelta * 1.5f && s_prevMid < 0.20f; + const bool transientFired = volume > 0.25f && std::max({normBass, normMid, normHigh}) > 0.20f && + (bassDelta + midDelta + highDelta) / 3.0f > 0.26f; + s_prevBass = normBass; s_prevMid = normMid; s_prevHigh = normHigh; + + // BPM — BeatDetektor primary; aubio onset confidence still drives phase correction below + const float overrideBpm = s_bpmOverride.load(std::memory_order_relaxed); + const float aubioConf = aubio_tempo_get_confidence(aubio.tempo); + if (overrideBpm > 0.0f) + { + s_acfBpm = overrideBpm; + } + else + { + const bool slowWins = (bd_slow.win_val >= bd_fast.win_val); + const float bdPeriod = slowWins ? bd_slow.winning_bpm : bd_fast.winning_bpm; // seconds per beat + const float bdConf = slowWins ? bd_slow.win_val : bd_fast.win_val; + const float bdBpm = (bdPeriod > 0.0f) ? (60.0f / bdPeriod) : 0.0f; + + // win_val >= 20 is the minimum reliable reading; 60 = finish line (high confidence) + if (bdConf >= 20.0f && bdBpm >= 40.0f && bdBpm <= 250.0f) + { + // Fold octave errors: BeatDetektor sometimes locks onto 2x or 0.5x the true tempo + float candidate = bdBpm; + if (s_acfBpm > 0.0f) + { + const float r = candidate / s_acfBpm; + if (r > 1.8f && r < 2.2f) candidate *= 0.5f; + else if (r > 0.45f && r < 0.55f) candidate *= 2.0f; + } + // Scale alpha by proximity: full rate within 8% of current estimate, zero at 20%+ + // This prevents a bad detection frame from yanking the estimate far off track. + const float baseAlpha = Clamp01((bdConf - 20.0f) / 40.0f) * 0.05f; + const float deviation = (s_acfBpm > 0.0f) ? std::abs(candidate / s_acfBpm - 1.0f) : 0.0f; + const float alpha = baseAlpha * Clamp01(1.0f - deviation / 0.15f); + s_acfBpm = s_acfBpm * (1.0f - alpha) + candidate * alpha; + s_acfBpm = std::max(40.0f, std::min(250.0f, s_acfBpm)); + } + } + s_liveBpm.store(s_acfBpm, std::memory_order_relaxed); + + // Beat phase — advance driven by BeatDetektor BPM; aubio onsets correct phase + if (s_resetBeatPhase.exchange(false, std::memory_order_relaxed)) + { + s_beatPhase = 0.0f; + s_beatCount = 0; + } + + if (beatFired && aubioConf > 0.12f) ++s_beatCount; + + s_beatInterval = 60.0f / s_acfBpm; + + const float audioDt = float(hops) * float(kHopSize) / float(kSampleRate); + s_beatPhase += audioDt / s_beatInterval; + + if (s_beatPhase >= 1.0f) + { + const uint32_t wraps = static_cast(s_beatPhase); + s_beatPhase = std::fmod(s_beatPhase, 1.0f); + s_liveBeatCount.fetch_add(wraps, std::memory_order_relaxed); + } + + const bool bpmLocked = (overrideBpm > 0.0f); + if (!bpmLocked && beatFired && aubioConf > 0.12f && s_beatCount >= 4) + { + const float err = s_beatPhase > 0.5f ? s_beatPhase - 1.0f : s_beatPhase; + const float gain = 0.5f + aubioConf * 0.4f; + s_beatPhase -= err * gain; + if (s_beatPhase < 0.0f) s_beatPhase += 1.0f; + } + + s_liveBeatPhase.store(s_beatPhase, std::memory_order_relaxed); + + // Silence gate — use wall clock (audio thread runs even when signal is absent) + bool hasSignal; + if (clampedLevel > 0.005f) + { + silenceStart = Clock::now(); + inSilence = false; + hasSignal = true; + } + else + { + using FSec = std::chrono::duration; + const float silenceSec = FSec(Clock::now() - silenceStart).count(); + inSilence = (silenceSec > 1.5f); + hasSignal = !inSilence; + } + + // Publish features — brief lock, render thread gets a copy + kick = SmoothAttackRelease(kick, kickFired ? 1.0f : 0.0f, 0.90f, 0.06f); + snare = SmoothAttackRelease(snare, snareFired ? 1.0f : 0.0f, 0.85f, 0.07f); + transient = SmoothAttackRelease(transient, transientFired ? 1.0f : 0.0f, 0.80f, 0.08f); + + { + std::lock_guard lock(m_FeaturesMutex); + m_Features.volume = volume; + m_Features.bass = normBass; + m_Features.mid = normMid; + m_Features.high = normHigh; + m_Features.spectralCentroid = spectralCentroid; + m_Features.kick = kick; + m_Features.snare = snare; + m_Features.transient = transient; + m_Features.beatPhase = s_beatPhase; + m_Features.bpm = s_acfBpm; + m_Features.hasSignal = hasSignal; + m_Features.sampleCount = static_cast(sampleBuffer.size()); + } + } +} diff --git a/client_generic/Client/AudioAnalyzer.h b/client_generic/Client/AudioAnalyzer.h new file mode 100644 index 00000000..307ba668 --- /dev/null +++ b/client_generic/Client/AudioAnalyzer.h @@ -0,0 +1,56 @@ +#ifndef AUDIO_ANALYZER_H_INCLUDED +#define AUDIO_ANALYZER_H_INCLUDED + +#include +#include +#include +#include + +// Manual BPM control — callable from any thread/UI +void AudioAnalyzer_SetBpmOverride(float bpm); // 0 = release override, resume auto-detect +float AudioAnalyzer_TapTempo(); // call on each tap; returns new BPM (0 if < 2 taps yet) +float AudioAnalyzer_GetCurrentBpm(); // live read of the current BPM estimate +float AudioAnalyzer_GetBeatPhase(); // 0..1 position within current beat cycle +uint32_t AudioAnalyzer_GetBeatCount(); // monotonically increasing beat counter (wraps at 4B beats) +void AudioAnalyzer_ResetBeatPhase(); // snap beat phase to 0 (beat 1) on next process loop + +#include "AudioInputWasapi.h" + +struct AudioFeatures +{ + float volume = 0.0f; + float bass = 0.0f; + float mid = 0.0f; + float high = 0.0f; + float spectralCentroid = 0.0f; + float kick = 0.0f; // narrowband onset pulse: 30–200 Hz + float snare = 0.0f; // narrowband onset pulse: 200–800 Hz + float transient = 0.0f; // broadband onset pulse: any transient + float beatPhase = 0.0f; // 0..1 position within current beat cycle + float bpm = 120.0f; + bool hasSignal = false; + int sampleCount = 0; +}; + +class AudioAnalyzer +{ + public: + ~AudioAnalyzer() { Stop(); } + + void Start(); // launch background processing thread + void Stop(); // stop thread and join (safe to call multiple times) + + // Thread-safe snapshot for the render loop — never blocks for more than a mutex acquire. + AudioFeatures GetFeaturesSnapshot() const; + + private: + void ThreadProc(); + + AudioInputWasapi m_AudioInput; + AudioFeatures m_Features; + mutable std::mutex m_FeaturesMutex; + std::thread m_Thread; + std::atomic m_Running{false}; +}; + +#endif diff --git a/client_generic/Client/AudioInputWasapi.cpp b/client_generic/Client/AudioInputWasapi.cpp new file mode 100644 index 00000000..6e8a98aa --- /dev/null +++ b/client_generic/Client/AudioInputWasapi.cpp @@ -0,0 +1,244 @@ +#include "AudioInputWasapi.h" + +#include +#include +#include +#include + +#pragma comment(lib, "Ole32.lib") + +AudioInputWasapi::AudioInputWasapi() {} + +AudioInputWasapi::~AudioInputWasapi() { Stop(); } + +bool AudioInputWasapi::Start() +{ + HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) + { + return false; + } + + IMMDeviceEnumerator* enumerator = nullptr; + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, + __uuidof(IMMDeviceEnumerator), + reinterpret_cast(&enumerator)); + + if (FAILED(hr) || !enumerator) + { + return false; + } + + hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_Device); + enumerator->Release(); + + if (FAILED(hr) || !m_Device) + { + return false; + } + + hr = m_Device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, + reinterpret_cast(&m_AudioClient)); + + if (FAILED(hr) || !m_AudioClient) + { + return false; + } + + WAVEFORMATEX* mixFormat = nullptr; + hr = m_AudioClient->GetMixFormat(&mixFormat); + + if (FAILED(hr) || !mixFormat) + { + return false; + } + m_ChannelCount = mixFormat->nChannels; + if (m_ChannelCount <= 0) + m_ChannelCount = 2; + + hr = m_AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, + mixFormat, nullptr); + + CoTaskMemFree(mixFormat); + + if (FAILED(hr)) + { + return false; + } + + hr = m_AudioClient->GetService(__uuidof(IAudioCaptureClient), + reinterpret_cast(&m_CaptureClient)); + + if (FAILED(hr) || !m_CaptureClient) + { + return false; + } + + hr = m_AudioClient->Start(); + + if (FAILED(hr)) + { + return false; + } + + m_Running = true; + m_Level = 0.0f; + + return true; +} + +void AudioInputWasapi::Stop() +{ + if (m_AudioClient) + { + m_AudioClient->Stop(); + } + + if (m_CaptureClient) + { + m_CaptureClient->Release(); + m_CaptureClient = nullptr; + } + + if (m_AudioClient) + { + m_AudioClient->Release(); + m_AudioClient = nullptr; + } + + if (m_Device) + { + m_Device->Release(); + m_Device = nullptr; + } + + m_Running = false; + m_Level = 0.0f; +} + +bool AudioInputWasapi::IsRunning() const { return m_Running; } + +float AudioInputWasapi::GetLevel() const +{ + if (!m_CaptureClient) + return 0.0f; + + UINT32 packetLength = 0; + HRESULT hr = m_CaptureClient->GetNextPacketSize(&packetLength); + + float level = 0.0f; + int sampleCount = 0; + + while (SUCCEEDED(hr) && packetLength > 0) + { + BYTE* data = nullptr; + UINT32 numFrames = 0; + DWORD flags = 0; + + hr = m_CaptureClient->GetBuffer(&data, &numFrames, &flags, nullptr, + nullptr); + + if (FAILED(hr)) + break; + + // assume float samples (common for WASAPI mix format) + float* samples = reinterpret_cast(data); + + for (UINT32 i = 0; i < numFrames; ++i) + { + float s = samples[i]; + level += s * s; + sampleCount++; + } + + m_CaptureClient->ReleaseBuffer(numFrames); + + hr = m_CaptureClient->GetNextPacketSize(&packetLength); + } + + if (sampleCount > 0) + { + level = std::sqrtf(level / sampleCount); + } + + return level; +} +std::vector AudioInputWasapi::GetSamples() +{ + std::vector samplesOut; + + if (!m_CaptureClient) + return samplesOut; + + UINT32 packetLength = 0; + HRESULT hr = m_CaptureClient->GetNextPacketSize(&packetLength); + + while (SUCCEEDED(hr) && packetLength > 0) + { + BYTE* data = nullptr; + UINT32 numFrames = 0; + DWORD flags = 0; + + hr = m_CaptureClient->GetBuffer(&data, &numFrames, &flags, nullptr, + nullptr); + + if (FAILED(hr)) + break; + + float* samples = reinterpret_cast(data); + + for (UINT32 frame = 0; frame < numFrames; ++frame) + { + float mono = 0.0f; + + for (int ch = 0; ch < m_ChannelCount; ++ch) + { + mono += samples[(frame * m_ChannelCount) + ch]; + } + + mono /= static_cast(m_ChannelCount); + samplesOut.push_back(mono); + } + + m_CaptureClient->ReleaseBuffer(numFrames); + + hr = m_CaptureClient->GetNextPacketSize(&packetLength); + } + + return samplesOut; +} +StereoSamples AudioInputWasapi::GetStereoSamples() +{ + StereoSamples out; + + if (!m_CaptureClient || m_ChannelCount < 2) + return out; + + UINT32 packetLength = 0; + HRESULT hr = m_CaptureClient->GetNextPacketSize(&packetLength); + + while (SUCCEEDED(hr) && packetLength > 0) + { + BYTE* data = nullptr; + UINT32 numFrames = 0; + DWORD flags = 0; + + hr = m_CaptureClient->GetBuffer(&data, &numFrames, &flags, nullptr, + nullptr); + if (FAILED(hr)) + break; + + float* samples = reinterpret_cast(data); + for (UINT32 frame = 0; frame < numFrames; ++frame) + { + out.left.push_back(samples[(frame * m_ChannelCount) + 0]); + out.right.push_back(samples[(frame * m_ChannelCount) + 1]); + } + + m_CaptureClient->ReleaseBuffer(numFrames); + hr = m_CaptureClient->GetNextPacketSize(&packetLength); + } + + return out; +} \ No newline at end of file diff --git a/client_generic/Client/AudioInputWasapi.h b/client_generic/Client/AudioInputWasapi.h new file mode 100644 index 00000000..c4f5ac2b --- /dev/null +++ b/client_generic/Client/AudioInputWasapi.h @@ -0,0 +1,38 @@ +#ifndef AUDIO_INPUT_WASAPI_H_INCLUDED +#define AUDIO_INPUT_WASAPI_H_INCLUDED + +#include +#include +#include + +struct StereoSamples +{ + std::vector left; + std::vector right; +}; + +class AudioInputWasapi +{ + public: + AudioInputWasapi(); + ~AudioInputWasapi(); + + bool Start(); + void Stop(); + + bool IsRunning() const; + float GetLevel() const; + std::vector GetSamples(); + StereoSamples GetStereoSamples(); + + private: + bool m_Running = false; + float m_Level = 0.0f; + int m_ChannelCount = 2; + + IAudioClient* m_AudioClient = nullptr; + IAudioCaptureClient* m_CaptureClient = nullptr; + IMMDevice* m_Device = nullptr; +}; + +#endif \ No newline at end of file diff --git a/client_generic/Client/AudioPanelWin32.cpp b/client_generic/Client/AudioPanelWin32.cpp new file mode 100644 index 00000000..7ebb2686 --- /dev/null +++ b/client_generic/Client/AudioPanelWin32.cpp @@ -0,0 +1,457 @@ +#ifdef WIN32 + +#include "AudioPanelWin32.h" +#include "AudioAnalyzer.h" +#include "Settings.h" + +#include "imgui.h" +#include "imgui_impl_dx11.h" +#include "imgui_impl_win32.h" +#include "../DisplayOutput/D3D11/DisplayDX11.h" +#include "Player.h" +#include "SettingsDialogWin32.h" + +#include +#include + + + +// ── Panel state ─────────────────────────────────────────────────────────────── +static std::atomic g_audioPanelVisible{false}; +static bool g_imguiInitialized = false; + +void AudioPanelWin32_SetVisible(bool visible) +{ + g_audioPanelVisible.store(visible, std::memory_order_release); +} + +bool AudioPanelWin32_IsVisible() +{ + return g_audioPanelVisible.load(std::memory_order_acquire); +} + +// ── ImGui init/shutdown ─────────────────────────────────────────────────────── +static void TryInitImGui(ID3D11Device* device, ID3D11DeviceContext* ctx) +{ + if (g_imguiInitialized) + return; + auto* dx = + dynamic_cast(g_Player().Display().get()); + if (!dx) + return; + HWND hwnd = dx->GetWindowHandle(); + if (!hwnd) + return; + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGui::GetIO().IniFilename = nullptr; + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplDX11_Init(device, ctx); + g_imguiInitialized = true; +} + +static void ShutdownImGui() +{ + if (!g_imguiInitialized) + return; + if (g_Settings()->Storage()) + g_Settings()->Storage()->Commit(); + ImGui_ImplDX11_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + g_imguiInitialized = false; +} + +bool AudioPanelWin32_TryConsumeWndProc(HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam, LRESULT* outResult) +{ + if (!g_audioPanelVisible.load(std::memory_order_acquire) || + !g_imguiInitialized) + return false; + + extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler( + HWND, UINT, WPARAM, LPARAM); + const LRESULT r = ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam); + + // Unlike modal dialogs, the audio panel is a non-modal overlay. Only consume + // input when ImGui actually wants it — otherwise native chrome interactions + // (close/min/max buttons) are swallowed even when the cursor isn't near the panel. + const ImGuiIO& io = ImGui::GetIO(); + const bool isMouse = (msg == WM_MOUSEMOVE || msg == WM_LBUTTONDOWN || + msg == WM_LBUTTONUP || msg == WM_RBUTTONDOWN || + msg == WM_RBUTTONUP || msg == WM_MBUTTONDOWN || + msg == WM_MBUTTONUP || msg == WM_MOUSEWHEEL || + msg == WM_MOUSEHWHEEL); + const bool isKey = (msg == WM_KEYDOWN || msg == WM_KEYUP || + msg == WM_SYSKEYDOWN || msg == WM_SYSKEYUP || msg == WM_CHAR); + if ((isMouse && !io.WantCaptureMouse) || (isKey && !io.WantCaptureKeyboard)) + return false; + + if (r != 0) + { + if (outResult) + *outResult = r; + return true; + } + return false; +} + +// ── Main render function ────────────────────────────────────────────────────── +bool AudioPanelWin32_RenderIfNeeded(ID3D11Device* device, + ID3D11DeviceContext* ctx, + ID3D11RenderTargetView* rtv, + float viewportW, float viewportH) +{ + const bool visible = g_audioPanelVisible.load(std::memory_order_acquire); + + if (!visible) + { + if (g_imguiInitialized) + ShutdownImGui(); + return false; + } + + TryInitImGui(device, ctx); + if (!g_imguiInitialized) + return false; + + ctx->OMSetRenderTargets(1, &rtv, nullptr); + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + const float margin = 16.0f; + const float panelW = 300.0f; + ImGui::SetNextWindowPos(ImVec2(margin, margin), ImGuiCond_Always); + ImGui::SetNextWindowSizeConstraints(ImVec2(panelW, 0), ImVec2(panelW, viewportH - margin * 2)); + ImGui::SetNextWindowBgAlpha(0.6f); + + ImGuiWindowFlags flags = + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_AlwaysAutoResize; + + ImGui::Begin("##AudioPanel", nullptr, flags); + + ImGui::TextUnformatted("Audio Reactive"); + ImGui::Separator(); + + // ── Helpers ─────────────────────────────────────────────────────────────── + + static const char* kCutStyles[] = {"Hard (0.05s)", "Fast (0.2s)", "Slow (2.0s)"}; + static const char* kMixSources[] = {"Bass", "Mid", "High", "Centroid", "Kick", "Snare", "Transient", "Beat Phase"}; + + const auto tip = [](const char* text) { + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", text); + }; + + const auto drawSlider = [](const char* label, float* val, float vmin, float vmax, + const char* key, const char* fmt = "%.2f") + { + ImGui::TextUnformatted(label); + ImGui::SameLine(110.0f); + ImGui::PushItemWidth(-1); + if (ImGui::SliderFloat((std::string("##") + label).c_str(), val, vmin, vmax, fmt)) + g_Settings()->Set(key, *val); + ImGui::PopItemWidth(); + }; + + const auto drawSliderInt = [](const char* label, int* val, int vmin, int vmax, const char* key) + { + ImGui::TextUnformatted(label); + ImGui::SameLine(110.0f); + ImGui::PushItemWidth(-1); + if (ImGui::SliderInt((std::string("##") + label).c_str(), val, vmin, vmax)) + g_Settings()->Set(key, *val); + ImGui::PopItemWidth(); + }; + + const auto drawCombo = [](const char* label, int* val, + const char* const* items, int n, const char* key) + { + ImGui::TextUnformatted(label); + ImGui::SameLine(110.0f); + ImGui::PushItemWidth(-1); + if (ImGui::Combo((std::string("##") + label).c_str(), val, items, n)) + g_Settings()->Set(key, *val); + ImGui::PopItemWidth(); + }; + + const auto drawCheck = [](const char* label, bool* val, const char* key) + { + if (ImGui::Checkbox(label, val)) + g_Settings()->Set(key, *val); + }; + + // Checkbox + slider on one row; slider is greyed when checkbox is off. + // checkTip shown on checkbox hover, sliderTip shown on slider hover (works even when disabled). + const auto drawWeightRow = [&](const char* checkId, bool* use, const char* useKey, + const char* label, float* val, const char* valKey, + const char* checkTip, const char* sliderTip) + { + if (ImGui::Checkbox(checkId, use)) + g_Settings()->Set(useKey, *use); + if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", checkTip); + ImGui::SameLine(0, 2); + ImGui::BeginDisabled(!(*use)); + ImGui::TextUnformatted(label); + ImGui::SameLine(110.0f); + ImGui::PushItemWidth(-1); + if (ImGui::SliderFloat((std::string("##") + label).c_str(), val, 0.0f, 1.0f, "%.2f")) + g_Settings()->Set(valKey, *val); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("%s", sliderTip); + ImGui::PopItemWidth(); + ImGui::EndDisabled(); + }; + + // ── Analyser ────────────────────────────────────────────────────────────── + if (ImGui::CollapsingHeader("Analyser", ImGuiTreeNodeFlags_DefaultOpen)) + { + drawSlider("Bass", &g_audioBassMult, 0.5f, 2.0f, "settings.player.audio_bass_mult"); + tip("Multiplier on normalised bass energy (20-200 Hz)."); + drawSlider("Mid", &g_audioMidMult, 0.5f, 2.0f, "settings.player.audio_mid_mult"); + tip("Multiplier on normalised mid energy (200-4000 Hz)."); + drawSlider("High", &g_audioHighMult, 0.5f, 2.0f, "settings.player.audio_high_mult"); + tip("Multiplier on normalised high energy (4-11 kHz)."); + drawSlider("Peak decay", &g_audioPeakDecay, 0.95f, 0.99999f,"settings.player.audio_peak_decay", "%.5f"); + tip("How slowly the per-band peak normalisation decays. Higher = longer memory, slower adaptation."); + drawSlider("Peak floor", &g_audioPeakFloor, 0.0f, 0.1f, "settings.player.audio_peak_floor", "%.4f"); + tip("Minimum peak value — prevents normalisation from chasing the noise floor during quiet passages.\n" + "Raise this if bands read too high during silence or quiet sections."); + drawSlider("Dark brt", &g_audioDarkBrightness,-1.0f, 0.0f, "settings.player.audio_dark_brightness"); + tip("Brightness floor when no audio signal is present."); + } + + // ── Beat ────────────────────────────────────────────────────────────────── + if (ImGui::CollapsingHeader("Beat", ImGuiTreeNodeFlags_DefaultOpen)) + { + static bool s_bpmLocked = false; + static float s_lockedBpm = 120.0f; + + // Beat count is monotonically incremented by the audio thread on every beat wrap. + // Deriving beatIndex from it (rather than detecting phase wraps per frame) handles + // multiple beats per render frame correctly after a dream change drains audio backlog. + const uint32_t beatCount = AudioAnalyzer_GetBeatCount(); + const float phase = AudioAnalyzer_GetBeatPhase(); + const int s_beatIndex = static_cast(beatCount % 4); + + // BPM readout + Tap + Reset on one row + const float liveBpm = AudioAnalyzer_GetCurrentBpm(); + ImGui::Text("Live BPM: %.1f", liveBpm); + ImGui::SameLine(110.0f); + const float halfW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; + if (ImGui::Button("Tap##bpm", ImVec2(halfW, 0))) + { + const float tapped = AudioAnalyzer_TapTempo(); + if (tapped > 0.0f) + { + s_lockedBpm = tapped; + s_bpmLocked = true; + } + } + tip("Tap to the beat. Locks BPM automatically once two or more taps are recorded.\n" + "Resets if you stop tapping for more than 3 seconds."); + ImGui::SameLine(); + if (ImGui::Button("Reset##beat", ImVec2(-1, 0))) + AudioAnalyzer_ResetBeatPhase(); + tip("Snap beat phase to beat 1 immediately."); + + // 4-box beat visualizer — box 1 is beat 1, pulses on each beat + { + const float avail = ImGui::GetContentRegionAvail().x; + const float gap = 4.0f; + const float boxW = (avail - gap * 3.0f) / 4.0f; + const float boxH = 22.0f; + + ImDrawList* dl = ImGui::GetWindowDrawList(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + + for (int i = 0; i < 4; ++i) + { + const float x = pos.x + i * (boxW + gap); + const bool active = (i == s_beatIndex); + // Pulse: bright at phase=0, half-bright at phase=1 + const float pulse = active ? (1.0f - phase * 0.5f) : 0.0f; + const int alpha = active ? (int)(pulse * 200.0f) + 40 : 110; + + ImU32 fillCol; + if (i == 0) + fillCol = active ? IM_COL32(255, 210, 40, alpha) // beat 1: gold + : IM_COL32(80, 65, 15, alpha); + else + fillCol = active ? IM_COL32(140, 140, 255, alpha) // beats 2-4: blue-white + : IM_COL32(45, 45, 45, alpha); + + dl->AddRectFilled(ImVec2(x, pos.y), ImVec2(x + boxW, pos.y + boxH), + fillCol, 3.0f); + + char num[4]; + snprintf(num, sizeof(num), "%d", i + 1); + const ImVec2 tsz = ImGui::CalcTextSize(num); + dl->AddText(ImVec2(x + (boxW - tsz.x) * 0.5f, pos.y + (boxH - tsz.y) * 0.5f), + active ? IM_COL32(255, 255, 255, 255) : IM_COL32(160, 160, 160, 180), + num); + } + ImGui::Dummy(ImVec2(avail, boxH)); + } + + // Tempo nudge — hold to temporarily shift tempo ±5%; release restores locked BPM exactly. + // Auto-locks at current BPM on first press if not already locked. + float nudgeFactor = 0.0f; + { + const float nudgeW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; + ImGui::Button("< Slow##nudge", ImVec2(nudgeW, 0)); + if (ImGui::IsItemActivated() && !s_bpmLocked) + { + s_lockedBpm = AudioAnalyzer_GetCurrentBpm(); + s_bpmLocked = true; + } + if (ImGui::IsItemActive()) nudgeFactor = -0.05f; + tip("Hold to slow tempo 5% — release to restore.\nUse to drift beat phase back into sync."); + ImGui::SameLine(); + ImGui::Button("Fast >##nudge", ImVec2(-1, 0)); + if (ImGui::IsItemActivated() && !s_bpmLocked) + { + s_lockedBpm = AudioAnalyzer_GetCurrentBpm(); + s_bpmLocked = true; + } + if (ImGui::IsItemActive()) nudgeFactor = +0.05f; + tip("Hold to speed tempo 5% — release to restore.\nUse to drift beat phase forward into sync."); + } + + // Lock BPM row + if (ImGui::Checkbox("Lock BPM", &s_bpmLocked) && !s_bpmLocked) + AudioAnalyzer_SetBpmOverride(0.0f); + ImGui::SameLine(110.0f); + ImGui::PushItemWidth(-1); + ImGui::BeginDisabled(!s_bpmLocked); + if (ImGui::InputFloat("##lockedbpm", &s_lockedBpm, 1.0f, 5.0f, "%.1f")) + s_lockedBpm = std::max(40.0f, std::min(250.0f, s_lockedBpm)); + ImGui::EndDisabled(); + ImGui::PopItemWidth(); + tip("BPM used when locked. Drag or use +/- to nudge."); + + // nudgeFactor temporarily scales the override while held; s_lockedBpm is never mutated + AudioAnalyzer_SetBpmOverride(s_bpmLocked ? s_lockedBpm * (1.0f + nudgeFactor) : 0.0f); + } + + // ── FPS ─────────────────────────────────────────────────────────────────── + if (ImGui::CollapsingHeader("FPS", ImGuiTreeNodeFlags_DefaultOpen)) + { + drawCheck("Enable##fps", &g_audioFpsEnabled, "settings.player.audio_fps_enabled"); + tip("Vary playback frame rate with audio intensity."); + drawSlider("Min FPS", &g_audioFpsMin, 1.0f, 240.0f, "settings.player.audio_fps_min"); + tip("Frame rate when audio is quiet or absent."); + drawSlider("Max FPS", &g_audioFpsMax, 1.0f, 240.0f, "settings.player.audio_fps_max"); + tip("Frame rate at peak audio intensity."); + drawWeightRow("##useC", &g_audioFpsUseCentroid, "settings.player.audio_fps_use_centroid", + "W centroid", &g_audioFpsWeightCentroid, "settings.player.audio_fps_weight_centroid", + "Tick to include spectral centroid in the FPS calculation.\n" + "Centroid is the weighted average frequency of the spectrum -a measure of perceptual brightness.\n" + "Unlike fixed bands, it shifts continuously as the tonal balance changes.", + "Relative influence of spectral centroid vs other active bands.\n" + "Centroid rises as music gets brighter (more treble) and falls for bass-heavy passages.\n" + "Good for tracking tonal shifts rather than raw energy level."); + drawWeightRow("##useM", &g_audioFpsUseMid, "settings.player.audio_fps_use_mid", + "W mid", &g_audioFpsWeightMid, "settings.player.audio_fps_weight_mid", + "Tick to include mid-range energy (200-3500 Hz) in the FPS calculation.\n" + "Covers vocals, guitars, synths, and most melodic content.", + "Relative influence of mid-range energy (200-3500 Hz) vs other active bands.\n" + "Mid is the most presence-heavy band -high during busy sections, lower during breakdowns.\n" + "Differs from centroid in that it measures raw energy in a fixed range, not tonal balance."); + drawWeightRow("##useH", &g_audioFpsUseHigh, "settings.player.audio_fps_use_high", + "W high", &g_audioFpsWeightHigh, "settings.player.audio_fps_weight_high", + "Tick to include high-frequency energy (3.5-20 kHz) in the FPS calculation.\n" + "Covers cymbals, hi-hats, sibilance, and air.", + "Relative influence of high-frequency energy (3.5-20 kHz) vs other active bands.\n" + "High tracks cymbal work and brightness -useful for driving FPS with rhythmic hi-hat patterns.\n" + "Quieter than mid or bass in most music, so weight often needs to be higher to compensate."); + drawWeightRow("##useB", &g_audioFpsUseBass, "settings.player.audio_fps_use_bass", + "W bass", &g_audioFpsWeightBass, "settings.player.audio_fps_weight_bass", + "Tick to include bass energy (30-200 Hz) in the FPS calculation.\n" + "Covers kick drums, bass guitar, and sub-bass.", + "Relative influence of bass energy (30-200 Hz) vs other active bands.\n" + "Bass is typically the most stable band -it provides a steady baseline FPS.\n" + "Less reactive to melodic dynamics than centroid or mid."); + drawSlider("Attack", &g_audioFpsAttack, 0.01f, 1.0f, "settings.player.audio_fps_attack"); + tip("How quickly FPS rises toward the target (0 = never, 1 = instant)."); + drawSlider("Release", &g_audioFpsRelease, 0.01f, 1.0f, "settings.player.audio_fps_release"); + tip("How quickly FPS falls back toward minimum when audio quietens."); + } + + // ── Cuts ────────────────────────────────────────────────────────────────── + if (ImGui::CollapsingHeader("Cuts")) + { + drawSlider("Global cooldown", &g_audioCutGlobalCooldown, 0.1f, 10.0f, "settings.player.audio_cut_global_cooldown"); + tip("Minimum seconds between any two audio-triggered cuts."); + drawSlider("Min volume", &g_audioCutMinVolume, 0.0f, 1.0f, "settings.player.audio_cut_min_volume"); + tip("Cuts only fire when overall volume is at or above this level."); + ImGui::Spacing(); + + drawCheck("Transient", &g_audioCutTransientEnabled, "settings.player.audio_cut_transient_enabled"); + tip("Trigger a cut on broadband transients (sudden energy across all bands)."); + drawSlider("Trans thresh", &g_audioCutTransientThreshold, 0.1f, 1.0f, "settings.player.audio_cut_transient_threshold"); + tip("Smoothed transient level that triggers the cut."); + drawCombo("Trans style", &g_audioCutTransientStyle, kCutStyles, 3, "settings.player.audio_cut_transient_style"); + tip("Transition style applied when a transient cut fires."); + ImGui::Spacing(); + + drawCheck("Kick", &g_audioCutKickEnabled, "settings.player.audio_cut_kick_enabled"); + tip("Trigger a cut on bass kicks (sharp bass energy rise)."); + drawSlider("Kick thresh",&g_audioCutKickThreshold, 0.1f, 1.0f, "settings.player.audio_cut_kick_threshold"); + tip("Smoothed kick level that triggers the cut."); + drawCombo("Kick style", &g_audioCutKickStyle, kCutStyles, 3, "settings.player.audio_cut_kick_style"); + tip("Transition style applied when a kick cut fires."); + ImGui::Spacing(); + + drawCheck("Snare", &g_audioCutSnareEnabled, "settings.player.audio_cut_snare_enabled"); + tip("Trigger a cut on snare hits (mid-dominant transient, more mid than bass rise)."); + drawSlider("Snare thresh",&g_audioCutSnareThreshold, 0.1f, 1.0f, "settings.player.audio_cut_snare_threshold"); + tip("Smoothed snare level that triggers the cut."); + drawCombo("Snare style", &g_audioCutSnareStyle, kCutStyles, 3, "settings.player.audio_cut_snare_style"); + tip("Transition style applied when a snare cut fires."); + ImGui::Spacing(); + + drawCheck("Beat trigger", &g_audioCutBeatEnabled, "settings.player.audio_cut_beat_enabled"); + tip("Trigger a cut every N bars on beat 1."); + drawSliderInt("Bars", &g_audioCutBeatN, 1, 8, "settings.player.audio_cut_beat_n"); + tip("Number of bars between beat-triggered cuts. Cuts only fire on beat 1."); + drawCombo("Beat style", &g_audioCutBeatStyle, kCutStyles, 3, "settings.player.audio_cut_beat_style"); + tip("Transition style applied when a beat cut fires."); + ImGui::Spacing(); + + drawCheck("Volume trigger", &g_audioCutVolumeEnabled, "settings.player.audio_cut_volume_enabled"); + tip("Trigger a cut when overall volume crosses a threshold."); + drawSlider("Vol thresh", &g_audioCutVolumeThreshold, 0.1f, 1.0f, "settings.player.audio_cut_volume_threshold"); + tip("Volume level that triggers the cut."); + drawCombo("Vol style", &g_audioCutVolumeStyle, kCutStyles, 3, "settings.player.audio_cut_volume_style"); + tip("Transition style applied when a volume cut fires."); + } + + // ── Mixing ──────────────────────────────────────────────────────────────── + if (ImGui::CollapsingHeader("Mixing")) + { + drawCheck("Enable##mix", &g_audioMixEnabled, "settings.player.audio_mix_enabled"); + tip("Control the blend between two dream sequences using an audio signal."); + drawCombo("Source", &g_audioMixSource, kMixSources, 8, "settings.player.audio_mix_source"); + tip("Audio signal that drives the mix position."); + drawSlider("Mix min", &g_audioMixMin, 0.0f, 1.0f, "settings.player.audio_mix_min"); + tip("Blend position when the audio source is at zero."); + drawSlider("Mix max", &g_audioMixMax, 0.0f, 1.0f, "settings.player.audio_mix_max"); + tip("Blend position when the audio source is at peak."); + drawSlider("Smooth", &g_audioMixSmooth, 0.01f, 1.0f, "settings.player.audio_mix_smooth"); + tip("IIR smoothing applied to the mix position. Lower = smoother but slower to react."); + } + + ImGui::End(); + + ImGui::Render(); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + + return true; +} + +#endif diff --git a/client_generic/Client/AudioPanelWin32.h b/client_generic/Client/AudioPanelWin32.h new file mode 100644 index 00000000..2393b56d --- /dev/null +++ b/client_generic/Client/AudioPanelWin32.h @@ -0,0 +1,17 @@ +#pragma once +#ifdef WIN32 +#include +#include + +/// Called from RendererDX11::EndFrame — renders ImGui audio sliders when F3 is open. +bool AudioPanelWin32_RenderIfNeeded(ID3D11Device* device, + ID3D11DeviceContext* ctx, + ID3D11RenderTargetView* rtv, + float viewportW, float viewportH); + +/// Called from client.h when F3 is toggled. +void AudioPanelWin32_SetVisible(bool visible); +bool AudioPanelWin32_IsVisible(); +bool AudioPanelWin32_TryConsumeWndProc(HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam, LRESULT* outResult); +#endif \ No newline at end of file diff --git a/client_generic/Client/BeatDetektor.cpp b/client_generic/Client/BeatDetektor.cpp new file mode 100644 index 00000000..1b04d4b3 --- /dev/null +++ b/client_generic/Client/BeatDetektor.cpp @@ -0,0 +1,296 @@ +/* + * BeatDetektor.cpp + * + * BeatDetektor - CubicFX Visualizer Beat Detection & Analysis Algorithm + * + * Copyright (c) 2009 Charles J. Cliffe. + * MIT License — http://opensource.org/licenses/MIT + */ + +#include "BeatDetektor.h" + +void BeatDetektor::process(float timer_seconds, std::vector &fft_data) +{ + if (!last_timer) { last_timer = timer_seconds; return; } + + if (timer_seconds < last_timer) { reset(); return; } + + float timestamp = timer_seconds; + + last_update = timer_seconds - last_timer; + last_timer = timer_seconds; + total_time += last_update; + + unsigned int range_step = (unsigned int)(fft_data.size() / BD_DETECTION_RANGES); + if (range_step == 0) return; + + unsigned int range = 0; + int i, x; + float v; + + float bpm_floor = 60.0f / BPM_MAX; + float bpm_ceil = 60.0f / BPM_MIN; + + if (current_bpm != current_bpm) current_bpm = 0.0f; // NaN guard + + for (x = 0; x < (int)fft_data.size() && range < BD_DETECTION_RANGES; x += range_step) + { + if (!src) + { + a_freq_range[range] = 0.0f; + for (i = x; i < x + (int)range_step; i++) + { + v = fabs(fft_data[i]); + a_freq_range[range] += v; + } + a_freq_range[range] /= range_step; + + ma_freq_range[range] -= (ma_freq_range[range] - a_freq_range[range]) * last_update * detection_rate; + maa_freq_range[range] -= (maa_freq_range[range] - ma_freq_range[range]) * last_update * detection_rate; + } + else + { + a_freq_range[range] = src->a_freq_range[range]; + ma_freq_range[range] = src->ma_freq_range[range]; + maa_freq_range[range] = src->maa_freq_range[range]; + } + + bool det = (ma_freq_range[range] * detection_factor >= maa_freq_range[range]); + + if (ma_bpm_range[range] > bpm_ceil) ma_bpm_range[range] = bpm_ceil; + if (ma_bpm_range[range] < bpm_floor) ma_bpm_range[range] = bpm_floor; + if (maa_bpm_range[range] > bpm_ceil) maa_bpm_range[range] = bpm_ceil; + if (maa_bpm_range[range] < bpm_floor) maa_bpm_range[range] = bpm_floor; + + bool rewarded = false; + + if (!detection[range] && det) + { + float trigger_gap = timestamp - last_detection[range]; + +#define REWARD_VALS 7 + float reward_tolerances[REWARD_VALS] = { 0.001f, 0.005f, 0.01f, 0.02f, 0.04f, 0.08f, 0.10f }; + float reward_multipliers[REWARD_VALS] = { 20.0f, 10.0f, 8.0f, 1.0f, 0.5f, 0.25f, 0.125f }; + + if (trigger_gap < bpm_ceil && trigger_gap > bpm_floor) + { + for (i = 0; i < REWARD_VALS; i++) + { + if (fabs(ma_bpm_range[range] - trigger_gap) < ma_bpm_range[range] * reward_tolerances[i]) + { + detection_quality[range] += quality_reward * reward_multipliers[i]; + rewarded = true; + } + } + if (rewarded) + last_detection[range] = timestamp; + } + else if (trigger_gap >= bpm_ceil) + { + trigger_gap /= 2.0f; + if (trigger_gap < bpm_ceil && trigger_gap > bpm_floor) + { + for (i = 0; i < REWARD_VALS; i++) + { + if (fabs(ma_bpm_range[range] - trigger_gap) < ma_bpm_range[range] * reward_tolerances[i]) + { + detection_quality[range] += quality_reward * reward_multipliers[i]; + rewarded = true; + } + } + } + if (!rewarded) trigger_gap *= 2.0f; + last_detection[range] = timestamp; + } + + float qmp = (detection_quality[range] / quality_avg) * BD_QUALITY_STEP; + if (qmp > 1.0f) qmp = 1.0f; + + if (rewarded) + { + ma_bpm_range[range] -= (ma_bpm_range[range] - trigger_gap) * qmp; + maa_bpm_range[range] -= (maa_bpm_range[range] - ma_bpm_range[range]) * qmp; + } + else if (trigger_gap >= bpm_floor && trigger_gap <= bpm_ceil) + { + if (detection_quality[range] < quality_avg * BD_QUALITY_TOLERANCE && current_bpm) + { + ma_bpm_range[range] -= (ma_bpm_range[range] - trigger_gap) * BD_QUALITY_STEP; + maa_bpm_range[range] -= (maa_bpm_range[range] - ma_bpm_range[range]) * BD_QUALITY_STEP; + } + detection_quality[range] -= BD_QUALITY_STEP; + } + else if (trigger_gap >= bpm_ceil) + { + if (detection_quality[range] < quality_avg * BD_QUALITY_TOLERANCE && current_bpm) + { + ma_bpm_range[range] -= (ma_bpm_range[range] - current_bpm) * 0.5f; + maa_bpm_range[range] -= (maa_bpm_range[range] - ma_bpm_range[range]) * 0.5f; + } + detection_quality[range] -= quality_reward * BD_QUALITY_STEP; + } + } + + if ((!rewarded && timestamp - last_detection[range] > bpm_ceil) || + (det && fabs(ma_bpm_range[range] - current_bpm) > bpm_offset)) + { + detection_quality[range] -= detection_quality[range] * BD_QUALITY_STEP * quality_decay * last_update; + } + + if (detection_quality[range] <= 0.0f) detection_quality[range] = 0.001f; + + detection[range] = det; + range++; + } + + quality_total = 0.0f; + float bpm_total = 0.0f; + int bpm_contributions = 0; + + for (x = 0; x < BD_DETECTION_RANGES; x++) + quality_total += detection_quality[x]; + + quality_avg = quality_total / (float)BD_DETECTION_RANGES; + + ma_quality_avg += (quality_avg - ma_quality_avg) * last_update * detection_rate / 2.0f; + maa_quality_avg += (ma_quality_avg - maa_quality_avg) * last_update; + ma_quality_total += (quality_total - ma_quality_total) * last_update * detection_rate / 2.0f; + + ma_quality_avg -= 0.98f * ma_quality_avg * last_update * 3.0f; + + if (ma_quality_total <= 0.0f) ma_quality_total = 1.0f; + if (ma_quality_avg <= 0.0f) ma_quality_avg = 1.0f; + + float avg_bpm_offset = 0.0f; + float offset_test_bpm = current_bpm; + std::map draft; + + for (x = 0; x < BD_DETECTION_RANGES; x++) + { + if (detection_quality[x] * BD_QUALITY_TOLERANCE >= ma_quality_avg) + { + if (maa_bpm_range[x] < bpm_ceil && maa_bpm_range[x] > bpm_floor) + { + bpm_total += maa_bpm_range[x]; + + float draft_float = roundf((60.0f / maa_bpm_range[x]) * 1000.0f); + draft_float = (fabsf(ceilf(draft_float) - (60.0f / current_bpm) * 1000.0f) < + fabsf(floorf(draft_float) - (60.0f / current_bpm) * 1000.0f)) + ? ceilf(draft_float / 10.0f) : floorf(draft_float / 10.0f); + int draft_int = (int)(draft_float / 10.0f); + + draft[draft_int] += (detection_quality[x] / quality_avg); + bpm_contributions++; + + if (offset_test_bpm == 0.0f) offset_test_bpm = maa_bpm_range[x]; + else avg_bpm_offset += fabsf(offset_test_bpm - maa_bpm_range[x]); + } + } + } + + bool has_prediction = (bpm_contributions >= minimum_contributions); + + if (has_prediction) + { + int draft_winner = 0; + float win_max = 0.0f; + + for (auto &kv : draft) + { + if (kv.second > win_max) + { + win_max = kv.second; + draft_winner = kv.first; + } + } + + if (draft_winner != 0) + bpm_predict = 60.0f / (float)(draft_winner / 10.0f); + + avg_bpm_offset /= (float)bpm_contributions; + bpm_offset = avg_bpm_offset; + + if (!current_bpm) current_bpm = bpm_predict; + + if (current_bpm && bpm_predict) + current_bpm -= (current_bpm - bpm_predict) * last_update; + if (current_bpm != current_bpm || current_bpm < 0.0f) current_bpm = 0.0f; + + float contest_max = 0.0f; + for (auto &kv : bpm_contest) + { + if (contest_max < kv.second) contest_max = kv.second; + if (kv.second > BD_FINISH_LINE / 2.0f) + bpm_contest_lo[(int)roundf((float)kv.first / 10.0f)] += (kv.second / 10.0f) * last_update; + } + + if (contest_max > finish_line) + { + for (auto &kv : bpm_contest) + kv.second = (kv.second / contest_max) * finish_line; + } + + contest_max = 0.0f; + for (auto &kv : bpm_contest_lo) + if (contest_max < kv.second) contest_max = kv.second; + + if (contest_max > finish_line) + { + for (auto &kv : bpm_contest_lo) + kv.second = (kv.second / contest_max) * finish_line; + } + + for (auto &kv : bpm_contest) + kv.second -= kv.second * (last_update / detection_rate); + for (auto &kv : bpm_contest_lo) + kv.second -= kv.second * (last_update / detection_rate); + + bpm_timer += last_update; + + int winner = 0; + int winner_lo = 0; + + if (bpm_timer > winning_bpm / 4.0f && current_bpm) + { + if (winning_bpm) + while (bpm_timer > winning_bpm / 4.0f) + bpm_timer -= winning_bpm / 4.0f; + + quarter_counter++; + half_counter = quarter_counter / 2; + beat_counter = quarter_counter / 4; + + bpm_contest[(int)roundf((60.0f / current_bpm) * 10.0f)] += quality_reward; + + win_val = 0.0f; + for (auto &kv : bpm_contest) + { + if (win_val < kv.second) + { + winner = kv.first; + win_val = kv.second; + } + } + if (winner) + { + win_bpm_int = winner; + winning_bpm = 60.0f / (float)(winner / 10.0f); + } + + win_val_lo = 0.0f; + for (auto &kv : bpm_contest_lo) + { + if (win_val_lo < kv.second) + { + winner_lo = kv.first; + win_val_lo = kv.second; + } + } + if (winner_lo) + { + win_bpm_int_lo = winner_lo; + winning_bpm_lo = 60.0f / (float)(winner_lo); + } + } + } +} diff --git a/client_generic/Client/BeatDetektor.h b/client_generic/Client/BeatDetektor.h new file mode 100644 index 00000000..cb7242cb --- /dev/null +++ b/client_generic/Client/BeatDetektor.h @@ -0,0 +1,135 @@ +#pragma once + +/* + * BeatDetektor.h + * + * BeatDetektor - CubicFX Visualizer Beat Detection & Analysis Algorithm + * + * Copyright (c) 2009 Charles J. Cliffe. + * MIT License — http://opensource.org/licenses/MIT + */ + +#include +#include +#include + +#define BD_DETECTION_RANGES 128 +#define BD_DETECTION_RATE 12.0 +#define BD_DETECTION_FACTOR 0.925 + +#define BD_QUALITY_TOLERANCE 0.96 +#define BD_QUALITY_DECAY 0.95 +#define BD_QUALITY_REWARD 7.0 +#define BD_QUALITY_STEP 0.1 +#define BD_FINISH_LINE 60.0 +#define BD_MINIMUM_CONTRIBUTIONS 6 + +class BeatDetektor +{ +public: + float BPM_MIN; + float BPM_MAX; + + BeatDetektor *src; + + float current_bpm; + float winning_bpm; + float winning_bpm_lo; + float win_val; + int win_bpm_int; + float win_val_lo; + int win_bpm_int_lo; + + float bpm_predict; + + bool is_erratic; + float bpm_offset; + float last_timer; + float last_update; + float total_time; + + float bpm_timer; + int beat_counter; + int half_counter; + int quarter_counter; + float detection_factor; + float quality_reward, quality_decay, detection_rate, finish_line; + int minimum_contributions; + float quality_total, quality_avg, ma_quality_lo, ma_quality_total, ma_quality_avg, maa_quality_avg; + + float a_freq_range[BD_DETECTION_RANGES]; + float ma_freq_range[BD_DETECTION_RANGES]; + float maa_freq_range[BD_DETECTION_RANGES]; + float last_detection[BD_DETECTION_RANGES]; + + float ma_bpm_range[BD_DETECTION_RANGES]; + float maa_bpm_range[BD_DETECTION_RANGES]; + + float detection_quality[BD_DETECTION_RANGES]; + + bool detection[BD_DETECTION_RANGES]; + + std::map bpm_contest; + std::map bpm_contest_lo; + + BeatDetektor(float BPM_MIN_in = 100.0f, float BPM_MAX_in = 200.0f, BeatDetektor *link_src = nullptr) : + current_bpm(0.0f), + winning_bpm(0.0f), + winning_bpm_lo(0.0f), + win_val(0.0f), + win_bpm_int(0), + win_val_lo(0.0f), + win_bpm_int_lo(0), + bpm_predict(0.0f), + is_erratic(false), + bpm_offset(0.0f), + last_timer(0.0f), + last_update(0.0f), + total_time(0.0f), + bpm_timer(0.0f), + beat_counter(0), + half_counter(0), + quarter_counter(0), + src(link_src), + quality_reward(BD_QUALITY_REWARD), + detection_rate(BD_DETECTION_RATE), + finish_line(BD_FINISH_LINE), + minimum_contributions(BD_MINIMUM_CONTRIBUTIONS), + detection_factor(BD_DETECTION_FACTOR), + quality_total(1.0f), + quality_avg(1.0f), + quality_decay(BD_QUALITY_DECAY), + ma_quality_avg(0.001f), + ma_quality_lo(1.0f), + ma_quality_total(1.0f) + { + BPM_MIN = BPM_MIN_in; + BPM_MAX = BPM_MAX_in; + reset(); + } + + void reset(bool reset_freq = true) + { + for (int i = 0; i < BD_DETECTION_RANGES; i++) + { + ma_bpm_range[i] = maa_bpm_range[i] = + 60.0f / (float)(BPM_MIN + 5) + + ((60.0f / (float)(BPM_MAX - 5) - 60.0f / (float)(BPM_MIN + 5)) * + ((float)i / (float)BD_DETECTION_RANGES)); + if (reset_freq) + a_freq_range[i] = ma_freq_range[i] = maa_freq_range[i] = 0.0f; + last_detection[i] = 0.0f; + detection_quality[i] = 0.0f; + detection[i] = false; + } + total_time = 0.0f; + maa_quality_avg = 500.0f; + bpm_offset = bpm_timer = last_update = last_timer = + winning_bpm = current_bpm = win_val = 0.0f; + win_bpm_int = 0; + bpm_contest.clear(); + bpm_contest_lo.clear(); + } + + void process(float timer_seconds, std::vector &fft_data); +}; diff --git a/client_generic/Client/Player.cpp b/client_generic/Client/Player.cpp index 11510374..04a726af 100644 --- a/client_generic/Client/Player.cpp +++ b/client_generic/Client/Player.cpp @@ -707,9 +707,12 @@ bool CPlayer::Update(uint32_t displayUnit) const bool freezePlayback = m_bPaused || IsFirstRunWizardPlaybackHold(); + if (m_blendClip && m_audioBlendAlpha >= 0.0f) + m_blendClip->Update(m_TimelineTime, freezePlayback); + if (m_currentClip) { m_currentClip->Update(m_TimelineTime, freezePlayback); - + // Only check for transition if we're not buffering anything if (!freezePlayback && !IsAnyClipBuffering()) { // Check if we need to prepare for transition @@ -851,13 +854,16 @@ void CPlayer::RenderFrame(DisplayOutput::spCRenderer renderer) { } else { g_Log->Error("Render frame has null nextClip despite checking for it earlier"); } + } else if (m_audioBlendAlpha >= 0.0f && m_currentClip && m_blendClip) { + m_currentClip->DrawFrame(renderer, 1.0f - m_audioBlendAlpha); + m_blendClip->DrawFrame(renderer, m_audioBlendAlpha); } else if (m_currentClip) { if (m_currentClip->IsBuffering()) { // We're still buffering, show appropriate UI /*g_Log->Info("Buffering clip %s, frame queue: %d", m_currentClip->GetClipMetadata().dreamData.uuid.c_str(), m_currentClip->GetDecoder()->QueueLength());*/ - + // Still call DrawFrame which will handle buffering visualization m_currentClip->DrawFrame(renderer); } else { @@ -1102,6 +1108,14 @@ void CPlayer::SetPerceptualFPS(const double _fps){ } } +void CPlayer::TriggerAudioCut(float transitionDuration) +{ + if (m_isTransitioning || m_pendingSeekCrossfade) + return; + m_transitionDuration = std::max(0.05f, transitionDuration); + PlayNextDream(true); +} + // Getters for both perceptual FPS and true decoder FPS double CPlayer::GetPerceptualFPS() { return m_PerceptualFPS; @@ -1453,13 +1467,17 @@ void CPlayer::UpdateTransition(double currentTime) m_PreloadingNextClip = false; m_PreloadingDreamUUID = ""; - // Start the asynchronous destruction of the current clip + // Keep the outgoing clip as the frozen blend source when audio mixing is active; + // otherwise destroy it immediately. if (m_currentClip) { - destroyClipAsync(std::move(m_currentClip)); + if (m_audioBlendAlpha >= 0.0f) + m_blendClip = std::move(m_currentClip); + else + destroyClipAsync(std::move(m_currentClip)); } - + m_currentClip = m_nextClip; - + if (m_currentClip) { // Transition already handled the fade-in visually, so disable the clip's internal // fade-in to avoid a brief dark flash caused by the clip's secondsIn calculation diff --git a/client_generic/Client/Player.h b/client_generic/Client/Player.h index 944b9f27..75b597e6 100644 --- a/client_generic/Client/Player.h +++ b/client_generic/Client/Player.h @@ -52,7 +52,10 @@ class CPlayer : public Base::CSingleton ContentDecoder::spCClip m_currentClip; ContentDecoder::spCClip m_nextClip; - + ContentDecoder::spCClip m_blendClip; // frozen prev clip for audio-driven blending + + float m_audioBlendAlpha = -1.0f; // < 0 = disabled; set by SetAudioBlendAlpha() + bool m_isTransitioning; double m_transitionStartTime; float m_transitionDuration; @@ -205,6 +208,9 @@ class CPlayer : public Base::CSingleton void SetPerceptualFPS(const double _fps); double GetPerceptualFPS(); double GetDecoderFPS(); + + void SetAudioBlendAlpha(float a) { m_audioBlendAlpha = a; } + void TriggerAudioCut(float transitionDuration); inline void Fullscreen(const bool _bState) { m_bFullscreen = _bState; }; inline bool Stopped() { return !m_bStarted; }; diff --git a/client_generic/Client/SettingsDialogWin32.cpp b/client_generic/Client/SettingsDialogWin32.cpp index 2971fc3c..0d5e76c9 100644 --- a/client_generic/Client/SettingsDialogWin32.cpp +++ b/client_generic/Client/SettingsDialogWin32.cpp @@ -2,6 +2,7 @@ #include "SettingsDialogWin32.h" #include "AboutDialogWin32.h" +#include "AudioPanelWin32.h" #include #include @@ -184,6 +185,8 @@ static void OnShowPreferencesRequested() if (!g_overlayAllowed.load(std::memory_order_acquire)) return; AboutDialogWin32_DismissWithoutSaveForExternalOverlay(); + // Audio panel and settings share the same HWND ImGui context — close panel first. + AudioPanelWin32_SetVisible(false); g_pendingImGuiShutdown.store(false, std::memory_order_release); g_showRequested.store(true, std::memory_order_release); } @@ -540,6 +543,11 @@ static void DrawAdvancedTab() #include "SettingsDialogWin32/SettingsDialogWin32.TabAdvanced.inl" } +static void DrawAudioReactiveTab() +{ +#include "SettingsDialogWin32/SettingsDialogWin32.TabAudioReactive.inl" +} + static void DrawSettingsDialog(float viewportW, float viewportH) { const float targetWidth = S(541.f); // Match macOS settings dialog content width. @@ -624,6 +632,7 @@ static void DrawSettingsDialog(float viewportW, float viewportH) drawTabItem("Disk", DrawDiskTab); drawTabItem("Display", DrawDisplayTab); drawTabItem("Advanced", DrawAdvancedTab); + drawTabItem("Audio", DrawAudioReactiveTab); ImGui::EndTabBar(); } @@ -689,6 +698,57 @@ static void DrawSettingsDialog(float viewportW, float viewportH) } // namespace +// Audio reactive settings - defined here, shared with AudioPanelWin32 +bool g_audioReactive = true; +float g_audioBassMult = 0.7f; +float g_audioMidMult = 0.8f; +float g_audioHighMult = 0.7f; +float g_audioPeakDecay = 0.999f; +float g_audioPeakFloor = 0.01f; +float g_audioDarkBrightness = -0.5f; +float g_audioOnsetThreshold = 0.001f; + +// FPS +bool g_audioFpsEnabled = true; +float g_audioFpsWeightCentroid = 0.8f; +float g_audioFpsWeightMid = 0.5f; +float g_audioFpsWeightHigh = 0.3f; +float g_audioFpsWeightBass = 0.1f; +float g_audioFpsMin = 4.0f; +float g_audioFpsMax = 60.0f; +float g_audioFpsAttack = 0.3f; +float g_audioFpsRelease = 0.1f; +bool g_audioFpsUseCentroid = true; +bool g_audioFpsUseMid = true; +bool g_audioFpsUseHigh = true; +bool g_audioFpsUseBass = true; + +// Cuts +bool g_audioCutTransientEnabled = false; +float g_audioCutTransientThreshold = 0.5f; +int g_audioCutTransientStyle = 0; +bool g_audioCutKickEnabled = false; +float g_audioCutKickThreshold = 0.5f; +int g_audioCutKickStyle = 0; +bool g_audioCutSnareEnabled = false; +float g_audioCutSnareThreshold = 0.5f; +int g_audioCutSnareStyle = 1; +bool g_audioCutBeatEnabled = false; +int g_audioCutBeatN = 4; +int g_audioCutBeatStyle = 1; +bool g_audioCutVolumeEnabled = false; +float g_audioCutVolumeThreshold = 0.8f; +int g_audioCutVolumeStyle = 2; +float g_audioCutGlobalCooldown = 2.0f; +float g_audioCutMinVolume = 0.8f; + +// Mixing +bool g_audioMixEnabled = false; +int g_audioMixSource = 0; +float g_audioMixMin = 0.0f; +float g_audioMixMax = 1.0f; +float g_audioMixSmooth = 0.15f; + void SettingsDialogWin32_DismissWithoutSaveForExternalOverlay() { if (!g_overlayAllowed.load(std::memory_order_acquire)) @@ -820,4 +880,9 @@ bool SettingsDialogWin32_RenderIfNeeded(ID3D11Device* device, ID3D11DeviceContex return true; } +void SettingsDialogWin32_LoadAudioSettings() +{ + LoadSettingsForShow(); +} + #endif // WIN32 diff --git a/client_generic/Client/SettingsDialogWin32.h b/client_generic/Client/SettingsDialogWin32.h index 7f3c1158..c980c025 100644 --- a/client_generic/Client/SettingsDialogWin32.h +++ b/client_generic/Client/SettingsDialogWin32.h @@ -7,6 +7,9 @@ void SettingsDialogWin32_Register(); void SettingsDialogWin32_SetOverlayAllowed(bool allow); +/// Populate all audio-reactive globals from the settings file. +/// Call this whenever a panel that uses those globals becomes visible. +void SettingsDialogWin32_LoadAudioSettings(); bool SettingsDialogWin32_HasPendingOrVisible(); bool SettingsDialogWin32_IsVisible(); void SettingsDialogWin32_Toggle(); @@ -29,4 +32,55 @@ bool SettingsDialogWin32_RenderIfNeeded(ID3D11Device* device, ID3D11DeviceContex constexpr UINT kSettingsDialogWin32SnapAfterCloseMsg = WM_APP + 1; void SettingsDialogWin32_HandleDeferredSnap(); +// Audio reactive settings - shared with AudioPanelWin32 +extern bool g_audioReactive; +extern float g_audioBassMult; +extern float g_audioMidMult; +extern float g_audioHighMult; +extern float g_audioPeakDecay; +extern float g_audioPeakFloor; +extern float g_audioDarkBrightness; +extern float g_audioOnsetThreshold; + +// FPS +extern bool g_audioFpsEnabled; +extern float g_audioFpsWeightCentroid; +extern float g_audioFpsWeightMid; +extern float g_audioFpsWeightHigh; +extern float g_audioFpsWeightBass; +extern float g_audioFpsMin; +extern float g_audioFpsMax; +extern float g_audioFpsAttack; +extern float g_audioFpsRelease; +extern bool g_audioFpsUseCentroid; +extern bool g_audioFpsUseMid; +extern bool g_audioFpsUseHigh; +extern bool g_audioFpsUseBass; + +// Cuts +extern bool g_audioCutTransientEnabled; +extern float g_audioCutTransientThreshold; +extern int g_audioCutTransientStyle; +extern bool g_audioCutKickEnabled; +extern float g_audioCutKickThreshold; +extern int g_audioCutKickStyle; +extern bool g_audioCutSnareEnabled; +extern float g_audioCutSnareThreshold; +extern int g_audioCutSnareStyle; +extern bool g_audioCutBeatEnabled; +extern int g_audioCutBeatN; +extern int g_audioCutBeatStyle; +extern bool g_audioCutVolumeEnabled; +extern float g_audioCutVolumeThreshold; +extern int g_audioCutVolumeStyle; +extern float g_audioCutGlobalCooldown; +extern float g_audioCutMinVolume; + +// Mixing +extern bool g_audioMixEnabled; +extern int g_audioMixSource; +extern float g_audioMixMin; +extern float g_audioMixMax; +extern float g_audioMixSmooth; + #endif diff --git a/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.CommonSettingsState.inl b/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.CommonSettingsState.inl index 7e643d79..d505f37b 100644 --- a/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.CommonSettingsState.inl +++ b/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.CommonSettingsState.inl @@ -42,6 +42,53 @@ static void LoadSettingsForShow() g_keepScreensaverEnabled = g_Settings()->Get("settings.app.keep_screensaver_enabled", true); + g_audioReactive = g_Settings()->Get("settings.player.audio_reactive", true); + g_audioBassMult = g_Settings()->Get("settings.player.audio_bass_mult", 0.7f); + g_audioMidMult = g_Settings()->Get("settings.player.audio_mid_mult", 0.8f); + g_audioHighMult = g_Settings()->Get("settings.player.audio_high_mult", 0.7f); + g_audioPeakDecay = g_Settings()->Get("settings.player.audio_peak_decay", 0.999f); + g_audioPeakFloor = g_Settings()->Get("settings.player.audio_peak_floor", 0.01f); + g_audioDarkBrightness = g_Settings()->Get("settings.player.audio_dark_brightness", -0.5f); + g_audioOnsetThreshold = g_Settings()->Get("settings.player.audio_onset_threshold", 0.001f); + + g_audioFpsEnabled = g_Settings()->Get("settings.player.audio_fps_enabled", true); + g_audioFpsWeightCentroid = g_Settings()->Get("settings.player.audio_fps_weight_centroid", 0.8f); + g_audioFpsWeightMid = g_Settings()->Get("settings.player.audio_fps_weight_mid", 0.5f); + g_audioFpsWeightHigh = g_Settings()->Get("settings.player.audio_fps_weight_high", 0.3f); + g_audioFpsWeightBass = g_Settings()->Get("settings.player.audio_fps_weight_bass", 0.1f); + g_audioFpsMin = g_Settings()->Get("settings.player.audio_fps_min", 4.0f); + g_audioFpsMax = g_Settings()->Get("settings.player.audio_fps_max", 60.0f); + g_audioFpsAttack = g_Settings()->Get("settings.player.audio_fps_attack", 0.3f); + g_audioFpsRelease = g_Settings()->Get("settings.player.audio_fps_release", 0.1f); + g_audioFpsUseCentroid = g_Settings()->Get("settings.player.audio_fps_use_centroid", true); + g_audioFpsUseMid = g_Settings()->Get("settings.player.audio_fps_use_mid", true); + g_audioFpsUseHigh = g_Settings()->Get("settings.player.audio_fps_use_high", true); + g_audioFpsUseBass = g_Settings()->Get("settings.player.audio_fps_use_bass", true); + + g_audioCutTransientEnabled = g_Settings()->Get("settings.player.audio_cut_transient_enabled", false); + g_audioCutTransientThreshold = g_Settings()->Get("settings.player.audio_cut_transient_threshold", 0.5f); + g_audioCutTransientStyle = g_Settings()->Get("settings.player.audio_cut_transient_style", 0); + g_audioCutKickEnabled = g_Settings()->Get("settings.player.audio_cut_kick_enabled", false); + g_audioCutKickThreshold = g_Settings()->Get("settings.player.audio_cut_kick_threshold", 0.5f); + g_audioCutKickStyle = g_Settings()->Get("settings.player.audio_cut_kick_style", 0); + g_audioCutSnareEnabled = g_Settings()->Get("settings.player.audio_cut_snare_enabled", false); + g_audioCutSnareThreshold = g_Settings()->Get("settings.player.audio_cut_snare_threshold", 0.5f); + g_audioCutSnareStyle = g_Settings()->Get("settings.player.audio_cut_snare_style", 1); + g_audioCutBeatEnabled = g_Settings()->Get("settings.player.audio_cut_beat_enabled", false); + g_audioCutBeatN = g_Settings()->Get("settings.player.audio_cut_beat_n", 4); + g_audioCutBeatStyle = g_Settings()->Get("settings.player.audio_cut_beat_style", 1); + g_audioCutVolumeEnabled = g_Settings()->Get("settings.player.audio_cut_volume_enabled", false); + g_audioCutVolumeThreshold= g_Settings()->Get("settings.player.audio_cut_volume_threshold",0.8f); + g_audioCutVolumeStyle = g_Settings()->Get("settings.player.audio_cut_volume_style", 2); + g_audioCutGlobalCooldown = g_Settings()->Get("settings.player.audio_cut_global_cooldown", 2.0f); + g_audioCutMinVolume = g_Settings()->Get("settings.player.audio_cut_min_volume", 0.8f); + + g_audioMixEnabled = g_Settings()->Get("settings.player.audio_mix_enabled", false); + g_audioMixSource = g_Settings()->Get("settings.player.audio_mix_source", 0); + g_audioMixMin = g_Settings()->Get("settings.player.audio_mix_min", 0.0f); + g_audioMixMax = g_Settings()->Get("settings.player.audio_mix_max", 1.0f); + g_audioMixSmooth = g_Settings()->Get("settings.player.audio_mix_smooth", 0.15f); + g_statusBuf[0] = '\0'; } @@ -76,6 +123,54 @@ static void SaveSettings() g_Settings()->Set("settings.app.keep_screensaver_enabled", g_keepScreensaverEnabled); g_Settings()->Set("settings.generator.nickname", std::string(g_nicknameBuf)); + + g_Settings()->Set("settings.player.audio_reactive", g_audioReactive); + g_Settings()->Set("settings.player.audio_bass_mult", g_audioBassMult); + g_Settings()->Set("settings.player.audio_mid_mult", g_audioMidMult); + g_Settings()->Set("settings.player.audio_high_mult", g_audioHighMult); + g_Settings()->Set("settings.player.audio_peak_decay", g_audioPeakDecay); + g_Settings()->Set("settings.player.audio_peak_floor", g_audioPeakFloor); + g_Settings()->Set("settings.player.audio_dark_brightness", g_audioDarkBrightness); + g_Settings()->Set("settings.player.audio_onset_threshold", g_audioOnsetThreshold); + + g_Settings()->Set("settings.player.audio_fps_enabled", g_audioFpsEnabled); + g_Settings()->Set("settings.player.audio_fps_weight_centroid", g_audioFpsWeightCentroid); + g_Settings()->Set("settings.player.audio_fps_weight_mid", g_audioFpsWeightMid); + g_Settings()->Set("settings.player.audio_fps_weight_high", g_audioFpsWeightHigh); + g_Settings()->Set("settings.player.audio_fps_weight_bass", g_audioFpsWeightBass); + g_Settings()->Set("settings.player.audio_fps_min", g_audioFpsMin); + g_Settings()->Set("settings.player.audio_fps_max", g_audioFpsMax); + g_Settings()->Set("settings.player.audio_fps_attack", g_audioFpsAttack); + g_Settings()->Set("settings.player.audio_fps_release", g_audioFpsRelease); + g_Settings()->Set("settings.player.audio_fps_use_centroid", g_audioFpsUseCentroid); + g_Settings()->Set("settings.player.audio_fps_use_mid", g_audioFpsUseMid); + g_Settings()->Set("settings.player.audio_fps_use_high", g_audioFpsUseHigh); + g_Settings()->Set("settings.player.audio_fps_use_bass", g_audioFpsUseBass); + + g_Settings()->Set("settings.player.audio_cut_transient_enabled", g_audioCutTransientEnabled); + g_Settings()->Set("settings.player.audio_cut_transient_threshold",g_audioCutTransientThreshold); + g_Settings()->Set("settings.player.audio_cut_transient_style", g_audioCutTransientStyle); + g_Settings()->Set("settings.player.audio_cut_kick_enabled", g_audioCutKickEnabled); + g_Settings()->Set("settings.player.audio_cut_kick_threshold", g_audioCutKickThreshold); + g_Settings()->Set("settings.player.audio_cut_kick_style", g_audioCutKickStyle); + g_Settings()->Set("settings.player.audio_cut_snare_enabled", g_audioCutSnareEnabled); + g_Settings()->Set("settings.player.audio_cut_snare_threshold", g_audioCutSnareThreshold); + g_Settings()->Set("settings.player.audio_cut_snare_style", g_audioCutSnareStyle); + g_Settings()->Set("settings.player.audio_cut_beat_enabled", g_audioCutBeatEnabled); + g_Settings()->Set("settings.player.audio_cut_beat_n", g_audioCutBeatN); + g_Settings()->Set("settings.player.audio_cut_beat_style", g_audioCutBeatStyle); + g_Settings()->Set("settings.player.audio_cut_volume_enabled", g_audioCutVolumeEnabled); + g_Settings()->Set("settings.player.audio_cut_volume_threshold", g_audioCutVolumeThreshold); + g_Settings()->Set("settings.player.audio_cut_volume_style", g_audioCutVolumeStyle); + g_Settings()->Set("settings.player.audio_cut_global_cooldown", g_audioCutGlobalCooldown); + g_Settings()->Set("settings.player.audio_cut_min_volume", g_audioCutMinVolume); + + g_Settings()->Set("settings.player.audio_mix_enabled", g_audioMixEnabled); + g_Settings()->Set("settings.player.audio_mix_source", g_audioMixSource); + g_Settings()->Set("settings.player.audio_mix_min", g_audioMixMin); + g_Settings()->Set("settings.player.audio_mix_max", g_audioMixMax); + g_Settings()->Set("settings.player.audio_mix_smooth", g_audioMixSmooth); + g_Settings()->Storage()->Commit(); if (!g_unlimitedCache) diff --git a/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.DialogLifecycle.inl b/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.DialogLifecycle.inl index 6a7f1654..b69be036 100644 --- a/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.DialogLifecycle.inl +++ b/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.DialogLifecycle.inl @@ -5,8 +5,9 @@ static void ApplyDialogPauseState(bool visible) const bool wasPaused = g_Player().IsPaused(); const bool wasUserPaused = g_Player().IsUserPaused(); g_wasPausedBeforeDialog.store(wasPaused, std::memory_order_release); - g_wasUserPausedBeforeDialog.store(wasUserPaused, std::memory_order_release); - g_Player().SetPaused(true, /*isUserInitiated=*/true); + g_wasUserPausedBeforeDialog.store(wasUserPaused, + std::memory_order_release); + // Don't pause - keep playback running so audio reactive visuals continue g_pausedBySettingsDialog.store(true, std::memory_order_release); return; } diff --git a/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.TabAudioReactive.inl b/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.TabAudioReactive.inl new file mode 100644 index 00000000..27075138 --- /dev/null +++ b/client_generic/Client/SettingsDialogWin32/SettingsDialogWin32.TabAudioReactive.inl @@ -0,0 +1,237 @@ +{ +const float leftInset = S(138.f); +const float availW = ImGui::GetContentRegionAvail().x; +const float sliderW = availW - leftInset - S(8.f); +const float topPad = S(12.f); +const float rowGap = S(6.f); + +static const char* kCutStyles[] = {"Hard cut (0.05s)", "Fast dissolve (0.2s)", "Slow dissolve (2.0s)"}; +static const char* kMixSources[] = {"Bass", "Mid", "High", "Centroid", "Kick", "Snare", "Transient", "Beat Phase"}; + +const auto drawSlider = [&](const char* label, float* val, float vmin, float vmax, + const char* key, const char* fmt = "%.2f") +{ + const float labelW = ImGui::CalcTextSize(label).x; + ImGui::SetCursorPosX(leftInset - labelW - S(8.f)); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(label); + ImGui::SameLine(0.f, S(8.f)); + ImGui::PushItemWidth(sliderW); + if (ImGui::SliderFloat((std::string("##") + label).c_str(), val, vmin, vmax, fmt)) + g_Settings()->Set(key, *val); + ImGui::PopItemWidth(); +}; + +const auto drawSliderInt = [&](const char* label, int* val, int vmin, int vmax, const char* key) +{ + const float labelW = ImGui::CalcTextSize(label).x; + ImGui::SetCursorPosX(leftInset - labelW - S(8.f)); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(label); + ImGui::SameLine(0.f, S(8.f)); + ImGui::PushItemWidth(sliderW); + if (ImGui::SliderInt((std::string("##") + label).c_str(), val, vmin, vmax)) + g_Settings()->Set(key, *val); + ImGui::PopItemWidth(); +}; + +const auto drawCombo = [&](const char* label, int* val, const char* const* items, int itemCount, const char* key) +{ + const float labelW = ImGui::CalcTextSize(label).x; + ImGui::SetCursorPosX(leftInset - labelW - S(8.f)); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(label); + ImGui::SameLine(0.f, S(8.f)); + ImGui::PushItemWidth(sliderW); + if (ImGui::Combo((std::string("##") + label).c_str(), val, items, itemCount)) + g_Settings()->Set(key, *val); + ImGui::PopItemWidth(); +}; + +const auto drawCheck = [&](const char* label, bool* val, const char* key) +{ + ImGui::SetCursorPosX(leftInset); + if (StyledCheckbox(label, val)) + g_Settings()->Set(key, *val); +}; + +const auto tip = [](const char* text) { + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", text); +}; + +// Checkbox to the left of a right-aligned label + slider (for FPS weight rows). +// checkTip shown on checkbox hover, sliderTip shown on slider hover (works even when disabled). +const auto drawWeightSlider = [&](const char* label, bool* use, float* val, + const char* useKey, const char* valKey, + const char* checkTip, const char* sliderTip) +{ + const float labelW = ImGui::CalcTextSize(label).x; + const float checkSz = ImGui::GetFrameHeight(); + ImGui::SetCursorPosX(leftInset - labelW - S(8.f) - checkSz - S(4.f)); + ImGui::AlignTextToFramePadding(); + if (StyledCheckbox((std::string("##w_") + label).c_str(), use)) + g_Settings()->Set(useKey, *use); + if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", checkTip); + ImGui::SameLine(0.f, S(4.f)); + ImGui::BeginDisabled(!(*use)); + ImGui::SetCursorPosX(leftInset - labelW - S(8.f)); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted(label); + ImGui::SameLine(0.f, S(8.f)); + ImGui::PushItemWidth(sliderW); + if (ImGui::SliderFloat((std::string("##") + label).c_str(), val, 0.0f, 1.0f, "%.2f")) + g_Settings()->Set(valKey, *val); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) + ImGui::SetTooltip("%s", sliderTip); + ImGui::PopItemWidth(); + ImGui::EndDisabled(); +}; + +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + topPad); +ImGui::SetCursorPosX(leftInset); +if (StyledCheckbox("Enable audio reactive", &g_audioReactive)) + g_Settings()->Set("settings.player.audio_reactive", g_audioReactive); + +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::Separator(); + +// ── Analyser ────────────────────────────────────────────────────────────────── +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::SetCursorPosX(S(8.f)); +ImGui::TextUnformatted("Analyser"); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawSlider("Bass mult", &g_audioBassMult, 0.5f, 2.0f, "settings.player.audio_bass_mult"); +tip("Multiplier on normalised bass energy (20-200 Hz)."); +drawSlider("Mid mult", &g_audioMidMult, 0.5f, 2.0f, "settings.player.audio_mid_mult"); +tip("Multiplier on normalised mid energy (200-4000 Hz)."); +drawSlider("High mult", &g_audioHighMult, 0.5f, 2.0f, "settings.player.audio_high_mult"); +tip("Multiplier on normalised high energy (4-11 kHz)."); +drawSlider("Peak decay", &g_audioPeakDecay, 0.95f, 0.99999f,"settings.player.audio_peak_decay", "%.5f"); +tip("How slowly per-band peak normalisation decays. Higher = longer memory, slower adaptation to volume changes."); +drawSlider("Dark brightness", &g_audioDarkBrightness,-1.0f, 0.0f, "settings.player.audio_dark_brightness"); +tip("Brightness floor applied when no audio signal is present."); + +// ── FPS ─────────────────────────────────────────────────────────────────────── +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::Separator(); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::SetCursorPosX(S(8.f)); +ImGui::TextUnformatted("FPS"); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Enable FPS control", &g_audioFpsEnabled, "settings.player.audio_fps_enabled"); +tip("Vary playback frame rate with audio intensity."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +drawSlider("Min FPS", &g_audioFpsMin, 1.0f, 240.0f, "settings.player.audio_fps_min"); +tip("Frame rate when audio is quiet or absent."); +drawSlider("Max FPS", &g_audioFpsMax, 1.0f, 240.0f, "settings.player.audio_fps_max"); +tip("Frame rate at peak audio intensity."); +drawWeightSlider("W centroid", &g_audioFpsUseCentroid, &g_audioFpsWeightCentroid, + "settings.player.audio_fps_use_centroid", "settings.player.audio_fps_weight_centroid", + "Tick to include spectral centroid in the FPS calculation.\n" + "Centroid is the weighted average frequency of the spectrum -a measure of perceptual brightness.\n" + "Unlike fixed bands, it shifts continuously as the tonal balance changes.", + "Relative influence of spectral centroid vs other active bands.\n" + "Centroid rises as music gets brighter (more treble) and falls for bass-heavy passages.\n" + "Good for tracking tonal shifts rather than raw energy level."); +drawWeightSlider("W mid", &g_audioFpsUseMid, &g_audioFpsWeightMid, + "settings.player.audio_fps_use_mid", "settings.player.audio_fps_weight_mid", + "Tick to include mid-range energy (200-3500 Hz) in the FPS calculation.\n" + "Covers vocals, guitars, synths, and most melodic content.", + "Relative influence of mid-range energy (200-3500 Hz) vs other active bands.\n" + "Mid is the most presence-heavy band -high during busy sections, lower during breakdowns.\n" + "Differs from centroid in that it measures raw energy in a fixed range, not tonal balance."); +drawWeightSlider("W high", &g_audioFpsUseHigh, &g_audioFpsWeightHigh, + "settings.player.audio_fps_use_high", "settings.player.audio_fps_weight_high", + "Tick to include high-frequency energy (3.5-20 kHz) in the FPS calculation.\n" + "Covers cymbals, hi-hats, sibilance, and air.", + "Relative influence of high-frequency energy (3.5-20 kHz) vs other active bands.\n" + "High tracks cymbal work and brightness -useful for driving FPS with rhythmic hi-hat patterns.\n" + "Quieter than mid or bass in most music, so weight often needs to be higher to compensate."); +drawWeightSlider("W bass", &g_audioFpsUseBass, &g_audioFpsWeightBass, + "settings.player.audio_fps_use_bass", "settings.player.audio_fps_weight_bass", + "Tick to include bass energy (30-200 Hz) in the FPS calculation.\n" + "Covers kick drums, bass guitar, and sub-bass.", + "Relative influence of bass energy (30-200 Hz) vs other active bands.\n" + "Bass is typically the most stable band -provides a steady FPS baseline.\n" + "Less reactive to melodic dynamics than centroid or mid."); +drawSlider("FPS attack", &g_audioFpsAttack, 0.01f, 1.0f, "settings.player.audio_fps_attack"); +tip("How quickly FPS rises toward the target (0 = never, 1 = instant)."); +drawSlider("FPS release", &g_audioFpsRelease, 0.01f, 1.0f, "settings.player.audio_fps_release"); +tip("How quickly FPS falls back toward minimum when audio quietens."); + +// ── Cuts ────────────────────────────────────────────────────────────────────── +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::Separator(); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::SetCursorPosX(S(8.f)); +ImGui::TextUnformatted("Cuts"); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawSlider("Global cooldown", &g_audioCutGlobalCooldown, 0.1f, 10.0f, "settings.player.audio_cut_global_cooldown"); +tip("Minimum seconds between any two audio-triggered cuts."); +drawSlider("Min volume", &g_audioCutMinVolume, 0.0f, 1.0f, "settings.player.audio_cut_min_volume"); +tip("Cuts only fire when overall volume is at or above this level. Prevents cuts during quiet or sparse passages."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Transient trigger", &g_audioCutTransientEnabled, "settings.player.audio_cut_transient_enabled"); +tip("Trigger a cut on broadband transients (sudden energy across all bands)."); +drawSlider("Transient threshold", &g_audioCutTransientThreshold, 0.1f, 1.0f, "settings.player.audio_cut_transient_threshold"); +tip("Smoothed transient level that triggers the cut."); +drawCombo("Transient style", &g_audioCutTransientStyle, kCutStyles, 3, "settings.player.audio_cut_transient_style"); +tip("Transition style applied when a transient cut fires."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Kick trigger", &g_audioCutKickEnabled, "settings.player.audio_cut_kick_enabled"); +tip("Trigger a cut on bass kicks (sharp bass energy rise)."); +drawSlider("Kick threshold",&g_audioCutKickThreshold, 0.1f, 1.0f, "settings.player.audio_cut_kick_threshold"); +tip("Smoothed kick level that triggers the cut."); +drawCombo("Kick style", &g_audioCutKickStyle, kCutStyles, 3, "settings.player.audio_cut_kick_style"); +tip("Transition style applied when a kick cut fires."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Snare trigger", &g_audioCutSnareEnabled, "settings.player.audio_cut_snare_enabled"); +tip("Trigger a cut on snare hits (mid-dominant transient -requires more mid than bass rise to filter out kicks)."); +drawSlider("Snare threshold",&g_audioCutSnareThreshold, 0.1f, 1.0f, "settings.player.audio_cut_snare_threshold"); +tip("Smoothed snare level that triggers the cut."); +drawCombo("Snare style", &g_audioCutSnareStyle, kCutStyles, 3, "settings.player.audio_cut_snare_style"); +tip("Transition style applied when a snare cut fires."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Beat trigger", &g_audioCutBeatEnabled, "settings.player.audio_cut_beat_enabled"); +tip("Trigger a cut every N bars on beat 1."); +drawSliderInt("Bars", &g_audioCutBeatN, 1, 8, "settings.player.audio_cut_beat_n"); +tip("Number of bars between beat-triggered cuts. Cuts only fire on beat 1."); +drawCombo("Beat style", &g_audioCutBeatStyle, kCutStyles, 3, "settings.player.audio_cut_beat_style"); +tip("Transition style applied when a beat cut fires."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Volume trigger", &g_audioCutVolumeEnabled, "settings.player.audio_cut_volume_enabled"); +tip("Trigger a cut when overall volume crosses a threshold."); +drawSlider("Volume threshold", &g_audioCutVolumeThreshold, 0.1f, 1.0f, "settings.player.audio_cut_volume_threshold"); +tip("Volume level that triggers the cut."); +drawCombo("Volume style", &g_audioCutVolumeStyle, kCutStyles, 3, "settings.player.audio_cut_volume_style"); +tip("Transition style applied when a volume cut fires."); + +// ── Mixing ──────────────────────────────────────────────────────────────────── +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::Separator(); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +ImGui::SetCursorPosX(S(8.f)); +ImGui::TextUnformatted("Mixing"); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); + +drawCheck("Enable mixing", &g_audioMixEnabled, "settings.player.audio_mix_enabled"); +tip("Control the blend between two dream sequences using an audio signal."); +ImGui::SetCursorPosY(ImGui::GetCursorPosY() + rowGap); +drawCombo("Mix source", &g_audioMixSource, kMixSources, 8, "settings.player.audio_mix_source"); +tip("Audio signal that drives the mix position."); +drawSlider("Mix min", &g_audioMixMin, 0.0f, 1.0f, "settings.player.audio_mix_min"); +tip("Blend position when the audio source is at zero."); +drawSlider("Mix max", &g_audioMixMax, 0.0f, 1.0f, "settings.player.audio_mix_max"); +tip("Blend position when the audio source is at peak."); +drawSlider("Smooth", &g_audioMixSmooth, 0.01f, 1.0f, "settings.player.audio_mix_smooth"); +tip("IIR smoothing applied to the mix position. Lower = smoother but slower to react."); +} diff --git a/client_generic/Client/client.h b/client_generic/Client/client.h index 14e2e6f5..6e066c8f 100644 --- a/client_generic/Client/client.h +++ b/client_generic/Client/client.h @@ -41,10 +41,14 @@ #include "TextureFlat.h" #include "Timer.h" #include "StringFormat.h" +#include "AudioAnalyzer.h" +#include +#include #if defined(WIN32) #include "FirstTimeSetupWin32.h" #include "SettingsDialogWin32.h" +#include "AudioPanelWin32.h" #endif #include "PlatformUtils.h" @@ -193,7 +197,8 @@ class CElectricSheep // We use perceptual FPS here which don't take into account per dream activity level double m_PerceptualFPS; - + AudioAnalyzer m_AudioAnalyzer; + // internal brightness counter int m_Brightness = 0; @@ -421,6 +426,8 @@ class CElectricSheep spStats->Add( new Hud::CStringStat("activityLevel", "Activity level: ", "1.00")); + spStats->Add( + new Hud::CStringStat("audioReactive", "Audio reactive: ", "off")); spStats->Add(new Hud::CStringStat("playHead", "", "00m00s/00m00s")); int32_t displayMode = @@ -447,6 +454,29 @@ class CElectricSheep 1.0)); spStats->Add(new Hud::CStringStat(std::string("zconnerror"), "", "")); } + void AddAudioStatsHud() + { + m_HudManager->Add("audiostats", std::make_shared( + Base::Math::CRect(1, 1), + m_HudFontName, m_HudFontSize)); + m_HudManager->Hide("audiostats"); + Hud::spCStatsConsole spStats = + std::dynamic_pointer_cast( + m_HudManager->Get("audiostats")); + spStats->Add(new Hud::CStringStat("audio-signal", "Signal: ", "off")); + spStats->Add(new Hud::CStringStat("audio-volume", "Volume: ", "")); + spStats->Add(new Hud::CStringStat("audio-kick", "Kick: ", "")); + spStats->Add(new Hud::CStringStat("audio-snare", "Snare: ", "")); + spStats->Add(new Hud::CStringStat("audio-transient", "Transient:", "")); + spStats->Add(new Hud::CStringStat("audio-bass", "Bass: ", "")); + spStats->Add(new Hud::CStringStat("audio-mid", "Mid: ", "")); + spStats->Add(new Hud::CStringStat("audio-high", "High: ", "")); + spStats->Add(new Hud::CStringStat("audio-centroid", "Centroid: ", "")); + spStats->Add(new Hud::CStringStat("audio-samples", "Samples: ", "")); + spStats->Add(new Hud::CStringStat("audio-bpm", "BPM: ", "")); + spStats->Add(new Hud::CStringStat("audio-beat", "Beat: ", "")); + spStats->Add(new Hud::CStringStat("audio-bar", "Bar: ", "")); + } void AddCreditsHud() { @@ -720,6 +750,11 @@ class CElectricSheep AddNetworkIndicatorHud(); AddHelpHud(); AddDreamStatsHud(); + AddAudioStatsHud(); +#ifdef WIN32 + SettingsDialogWin32_LoadAudioSettings(); +#endif + m_AudioAnalyzer.Start(); AddProgressHud(); AddSplashHud(); AddOSDHud(); @@ -829,6 +864,13 @@ class CElectricSheep { printf("CElectricSheep::Shutdown()\n"); + // Persist any settings changed via the audio panel (or elsewhere) before + // aggressive shutdown paths can call _exit() and bypass later cleanup. + if (g_Settings()->Storage()) + g_Settings()->Storage()->Commit(); + + m_AudioAnalyzer.Stop(); + // Reset cancellation flag - Startup() will set it to true if this is a restart s_shutdownCancelled.store(false); @@ -1278,7 +1320,179 @@ class CElectricSheep bool drawNoSheepIntro = false; bool drawn = g_Player().Update(displayUnit); // , drawNoSheepIntro); - + const bool audioReactiveEnabled = + g_Settings()->Get("settings.player.audio_reactive", true); + + const AudioFeatures audio = m_AudioAnalyzer.GetFeaturesSnapshot(); + + if (audioReactiveEnabled) + { + // ── Brightness ──────────────────────────────────────────────── + const float prescribedBrightness = TapsToBrightness(m_Brightness); + const float reactiveDarkBrightness = g_audioDarkBrightness; + const float bassAmount = audio.hasSignal ? audio.bass : 0.0f; + const float kickAmount = audio.hasSignal ? audio.kick : 0.0f; + + float reactiveBrightness = + reactiveDarkBrightness + + ((prescribedBrightness - reactiveDarkBrightness) * bassAmount) + + (kickAmount * (float)g_Settings()->Get( + "settings.player.audio_kick_contrib", 0.3f)); + + reactiveBrightness = std::max(reactiveDarkBrightness, + std::min(prescribedBrightness, reactiveBrightness)); + + if (auto spRenderer = g_Player().Renderer()) + spRenderer->SetBrightness(reactiveBrightness); + + // ── FPS ─────────────────────────────────────────────────────── + if (g_audioFpsEnabled) + { + const float centroid = audio.hasSignal ? audio.spectralCentroid : 0.0f; + const float mid = audio.hasSignal ? audio.mid : 0.0f; + const float high = audio.hasSignal ? audio.high : 0.0f; + const float bass = audio.hasSignal ? audio.bass : 0.0f; + + const float wC = g_audioFpsUseCentroid ? g_audioFpsWeightCentroid : 0.0f; + const float wM = g_audioFpsUseMid ? g_audioFpsWeightMid : 0.0f; + const float wH = g_audioFpsUseHigh ? g_audioFpsWeightHigh : 0.0f; + const float wB = g_audioFpsUseBass ? g_audioFpsWeightBass : 0.0f; + const float wSum = wC + wM + wH + wB; + const float norm = (wSum > 0.001f) ? 1.0f / wSum : 1.0f; + float signal = (centroid * wC + mid * wM + high * wH + bass * wB) * norm; + signal = std::max(0.0f, std::min(1.0f, signal)); + + const float targetFps = g_audioFpsMin + (g_audioFpsMax - g_audioFpsMin) * signal; + + static float s_smoothFps = 4.0f; + const float a = (targetFps > s_smoothFps) ? g_audioFpsAttack : g_audioFpsRelease; + s_smoothFps = s_smoothFps * (1.0f - a) + targetFps * a; + + g_Player().SetPerceptualFPS(static_cast(s_smoothFps)); + } + else + { + g_Player().SetPerceptualFPS( + g_Settings()->Get("settings.player.perceptual_fps", 16.0)); + } + + // ── Cuts ────────────────────────────────────────────────────── + { + static float s_cutCooldown = 0.0f; + static float s_prevTransient = 0.0f; + static float s_prevKick = 0.0f; + static float s_prevSnare = 0.0f; + static float s_prevVolume = 0.0f; + // s_beatCutBase tracks the bar-aligned beat count at which the last cut fired. + // UINT32_MAX = not yet initialised; set to currentBeat on first use to avoid + // a startup cascade if the beat count has already advanced. + static uint32_t s_beatCutBase = UINT32_MAX; + + const float dt = 1.0f / 60.0f; + s_cutCooldown = std::max(0.0f, s_cutCooldown - dt); + + const float curTransient = audio.hasSignal ? audio.transient : 0.0f; + const float curKick = audio.hasSignal ? audio.kick : 0.0f; + const float curSnare = audio.hasSignal ? audio.snare : 0.0f; + const float curVol = audio.hasSignal ? audio.volume : 0.0f; + + const bool transientEdge = (s_prevTransient < g_audioCutTransientThreshold && curTransient >= g_audioCutTransientThreshold); + const bool kickEdge = (s_prevKick < g_audioCutKickThreshold && curKick >= g_audioCutKickThreshold); + const bool snareEdge = (s_prevSnare < g_audioCutSnareThreshold && curSnare >= g_audioCutSnareThreshold); + const bool volEdge = (s_prevVolume < g_audioCutVolumeThreshold && curVol >= g_audioCutVolumeThreshold); + s_prevTransient = curTransient; + s_prevKick = curKick; + s_prevSnare = curSnare; + s_prevVolume = curVol; + + // Beat trigger: fires on beat 1 of every N bars. + // targetBarBeat is always a multiple of 4 (bar boundary), so the trigger + // stays aligned even if the render loop misses the exact beat-count sample + // when currentBeat % 4 == 0. After firing, base is set to targetBarBeat + // (not currentBeat) so subsequent targets are always bar-aligned too. + const uint32_t currentBeat = AudioAnalyzer_GetBeatCount(); + if (s_beatCutBase == UINT32_MAX) + s_beatCutBase = currentBeat; // align to current position on first use + + const uint32_t N = static_cast(std::max(1, g_audioCutBeatN)); + + // Detect beat count reset (user pressed Reset) — beat count jumped back to 0 + if (s_beatCutBase != UINT32_MAX && currentBeat < s_beatCutBase) + s_beatCutBase = UINT32_MAX; + + // Snap s_beatCutBase forward if volume gating silenced cuts for multiple bars, + // so we don't cascade-cut to "catch up" when audio becomes loud again. + if (s_beatCutBase != UINT32_MAX) + { + const uint32_t currentBar = currentBeat / 4; + const uint32_t baseBar = s_beatCutBase / 4; + if (currentBar > baseBar + N) + { + const uint32_t skipped = (currentBar - baseBar) / N; + s_beatCutBase = (baseBar + (skipped - 1) * N) * 4; + } + } + + const uint32_t targetBarBeat = ((s_beatCutBase / 4) + N) * 4; + const bool beatEdge = (currentBeat >= targetBarBeat); + + int cutStyle = -1; + if (s_cutCooldown <= 0.0f && !g_Player().IsTransitioning() && + curVol >= g_audioCutMinVolume) + { + if (g_audioCutTransientEnabled && transientEdge) + cutStyle = g_audioCutTransientStyle; + else if (g_audioCutKickEnabled && kickEdge) + cutStyle = g_audioCutKickStyle; + else if (g_audioCutSnareEnabled && snareEdge) + cutStyle = g_audioCutSnareStyle; + else if (g_audioCutBeatEnabled && beatEdge) + cutStyle = g_audioCutBeatStyle; + else if (g_audioCutVolumeEnabled && volEdge) + cutStyle = g_audioCutVolumeStyle; + } + + if (cutStyle >= 0) + { + s_cutCooldown = g_audioCutGlobalCooldown; + // Reset to the bar boundary we just crossed, not to currentBeat. + // This keeps every subsequent target aligned to beat 1 of a bar. + s_beatCutBase = (cutStyle == g_audioCutBeatStyle) ? targetBarBeat : currentBeat; + const float dur = (cutStyle == 0) ? 0.05f : (cutStyle == 1) ? 0.2f : 2.0f; + g_Player().TriggerAudioCut(dur); + } + } + + // ── Mixing ──────────────────────────────────────────────────── + if (g_audioMixEnabled) + { + const float sources[] = { + audio.bass, audio.mid, audio.high, + audio.spectralCentroid, audio.kick, audio.snare, + audio.transient, audio.beatPhase + }; + const int srcIdx = std::max(0, std::min(7, g_audioMixSource)); + const float raw = audio.hasSignal ? sources[srcIdx] : 0.0f; + const float target = g_audioMixMin + (g_audioMixMax - g_audioMixMin) * raw; + static float s_mixAlpha = 0.0f; + s_mixAlpha = s_mixAlpha * (1.0f - g_audioMixSmooth) + target * g_audioMixSmooth; + g_Player().SetAudioBlendAlpha(std::max(0.0f, std::min(1.0f, s_mixAlpha))); + } + else + { + g_Player().SetAudioBlendAlpha(-1.0f); + } + } + else + { + if (auto spRenderer = g_Player().Renderer()) + spRenderer->SetBrightness(TapsToBrightness(m_Brightness)); + + g_Player().SetPerceptualFPS( + g_Settings()->Get("settings.player.perceptual_fps", 16.0)); + g_Player().SetAudioBlendAlpha(-1.0f); + } + if ((!EDreamClient::IsLoggedIn() && !m_MultipleInstancesMode) || !g_Player().HasStarted()) { drawNoSheepIntro = true; } @@ -2070,6 +2284,84 @@ class CElectricSheep } } // if (spStats) - dreamstats + + // Update audio stats HUD (F3) + if (auto spAudioStats = + std::dynamic_pointer_cast( + m_HudManager->Get("audiostats"))) + { + static float dVolume = 0.0f, dBass = 0.0f, dMid = 0.0f; + static float dHigh = 0.0f, dCentroid = 0.0f; + static float dKick = 0.0f, dSnare = 0.0f, dTransient = 0.0f; + const float dSmooth = 0.12f; + dVolume = dVolume + (audio.volume - dVolume) * dSmooth; + dBass = dBass + (audio.bass - dBass) * dSmooth; + dMid = dMid + (audio.mid - dMid) * dSmooth; + dHigh = dHigh + (audio.high - dHigh) * dSmooth; + dCentroid = dCentroid + (audio.spectralCentroid - dCentroid) * dSmooth; + dKick = dKick + (audio.kick - dKick) * dSmooth; + dSnare = dSnare + (audio.snare - dSnare) * dSmooth; + dTransient= dTransient+ (audio.transient - dTransient)* dSmooth; + + auto bar = [](float value) + { + int count = static_cast(value * 20.0f); + if (count < 0) + count = 0; + if (count > 20) + count = 20; + std::string result; + for (int i = 0; i < count; ++i) + result += "|"; + return result; + }; + + ((Hud::CStringStat*)spAudioStats->Get("audio-signal")) + ->SetSample(dVolume > 0.01f ? "yes" : "no"); + ((Hud::CStringStat*)spAudioStats->Get("audio-volume")) + ->SetSample(string_format( + "%-20s %.3f", bar(dVolume).c_str(), dVolume)); + ((Hud::CStringStat*)spAudioStats->Get("audio-bass")) + ->SetSample(string_format("%-20s %.3f", + bar(dBass).c_str(), dBass)); + ((Hud::CStringStat*)spAudioStats->Get("audio-mid")) + ->SetSample(string_format("%-20s %.3f", + bar(dMid).c_str(), dMid)); + ((Hud::CStringStat*)spAudioStats->Get("audio-high")) + ->SetSample(string_format("%-20s %.3f", + bar(dHigh).c_str(), dHigh)); + ((Hud::CStringStat*)spAudioStats->Get("audio-centroid")) + ->SetSample(string_format( + "%-20s %.3f", bar(dCentroid).c_str(), dCentroid)); + ((Hud::CStringStat*)spAudioStats->Get("audio-kick")) + ->SetSample(string_format("%-20s %.3f", bar(dKick).c_str(), dKick)); + ((Hud::CStringStat*)spAudioStats->Get("audio-snare")) + ->SetSample(string_format("%-20s %.3f", bar(dSnare).c_str(), dSnare)); + ((Hud::CStringStat*)spAudioStats->Get("audio-transient")) + ->SetSample(string_format("%-20s %.3f", bar(dTransient).c_str(), dTransient)); + ((Hud::CStringStat*)spAudioStats->Get("audio-samples")) + ->SetSample(string_format("%d", audio.sampleCount)); + ((Hud::CStringStat*)spAudioStats->Get("audio-bpm")) + ->SetSample(string_format("%.1f", audio.bpm)); + { + const uint32_t beatCount = AudioAnalyzer_GetBeatCount(); + const int beatIdx = static_cast(beatCount % 4); + const uint32_t barCount = beatCount / 4; + const uint32_t beatN = static_cast(std::max(1, g_audioCutBeatN)); + const uint32_t barCycle = barCount % beatN; + char beatBuf[13]; + snprintf(beatBuf, sizeof(beatBuf), "%s%s%s%s", + beatIdx == 0 ? "[1]" : " 1 ", + beatIdx == 1 ? "[2]" : " 2 ", + beatIdx == 2 ? "[3]" : " 3 ", + beatIdx == 3 ? "[4]" : " 4 "); + ((Hud::CStringStat*)spAudioStats->Get("audio-beat")) + ->SetSample(beatBuf); + ((Hud::CStringStat*)spAudioStats->Get("audio-bar")) + ->SetSample(string_format("%u/%u", barCycle + 1, beatN)); + } + } + // Finally render hud. #if defined(WIN32) if (!FirstTimeSetupWin32_IsWizardVisible()) @@ -2100,6 +2392,7 @@ class CElectricSheep CLIENT_COMMAND_PLAYBACK_FASTER, CLIENT_COMMAND_F1, CLIENT_COMMAND_F2, + CLIENT_COMMAND_F3, CLIENT_COMMAND_SKIP_FW, CLIENT_COMMAND_SKIP_BW, CLIENT_COMMAND_PAUSE, @@ -2329,15 +2622,31 @@ class CElectricSheep case CLIENT_COMMAND_F1: m_F1F4Timer.Reset(); m_HudManager->Toggle("helpmessage"); +#ifdef WIN32 + AudioPanelWin32_SetVisible(false); +#endif g_Log->Info("HUD toggled (F1), syncing state to server"); EDreamClient::SendStateUpdate(); return true; case CLIENT_COMMAND_F2: m_F1F4Timer.Reset(); m_HudManager->Toggle("dreamstats"); +#ifdef WIN32 + AudioPanelWin32_SetVisible(false); +#endif g_Log->Info("HUD toggled (F2), syncing state to server"); EDreamClient::SendStateUpdate(); return true; + case CLIENT_COMMAND_F3: + m_HudManager->Toggle("audiostats"); +#ifdef WIN32 + // Settings and audio panel both own an ImGui context on the same HWND — + // close settings before opening the audio panel to avoid a context conflict. + if (!AudioPanelWin32_IsVisible()) + SettingsDialogWin32_DismissWithoutSaveForExternalOverlay(); + AudioPanelWin32_SetVisible(!AudioPanelWin32_IsVisible()); +#endif + return true; case CLIENT_COMMAND_SKIP_FW: if (!g_Player().IsJumpDisabled()) { popOSD(Hud::Forward10); @@ -2461,6 +2770,8 @@ class CElectricSheep return ExecuteCommand(CLIENT_COMMAND_F1); case DisplayOutput::CKeyEvent::KEY_F2: return ExecuteCommand(CLIENT_COMMAND_F2); + case DisplayOutput::CKeyEvent::KEY_F3: + return ExecuteCommand(CLIENT_COMMAND_F3); // Reset playlist case DisplayOutput::CKeyEvent::KEY_N: diff --git a/client_generic/Client/client_win32.h b/client_generic/Client/client_win32.h index ada619e5..27c4ff30 100644 --- a/client_generic/Client/client_win32.h +++ b/client_generic/Client/client_win32.h @@ -280,11 +280,21 @@ class CElectricSheep_Win32 : public CElectricSheep m_bAllowFKey = true; } } - + // Stops .scr /s running if .exe is open + if (m_ScrMode == eSaver && + g_SingleInstanceObj.IsAnotherInstanceRunning()) + { + g_Log->Info("Screensaver launch skipped because another " + "infinidream instance is already running."); + m_bConfigMode = true; + return false; + } // Check for multiple instances if we're not specifically asked not - // to. + // to. if (m_ScrMode != eWindowed_AllowMultipleInstances && - m_ScrMode != eSaver && m_ScrMode != eConfig && + m_ScrMode != eSaver && + m_ScrMode != eConfig && + m_ScrMode != ePreview && m_ScrMode != eFullScreenStandalone) { if (g_SingleInstanceObj.IsAnotherInstanceRunning()) diff --git a/client_generic/DisplayOutput/D3D11/DisplayDX11.cpp b/client_generic/DisplayOutput/D3D11/DisplayDX11.cpp index 5544ce26..8658002a 100644 --- a/client_generic/DisplayOutput/D3D11/DisplayDX11.cpp +++ b/client_generic/DisplayOutput/D3D11/DisplayDX11.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "AudioPanelWin32.h" #pragma comment(lib, "shcore.lib") #pragma comment(lib, "dwmapi.lib") extern void ESShowPreferences(); @@ -196,6 +197,7 @@ static bool HandleAppCommand(HWND hWnd, CDisplayDX11* self, UINT cmd) switch (cmd) { case ID_FILE_PREFERENCES: + AudioPanelWin32_SetVisible(false); SettingsDialogWin32_Toggle(); return true; case ID_FILE_EXIT: @@ -633,12 +635,77 @@ LRESULT CALLBACK CDisplayDX11::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARA } } + // Pre-handle all caption-button clicks when the audio panel (F3) is visible, + // mirroring the settings-dialog pre-handle above. ImGui's WndProcHandler runs + // later and would otherwise swallow WM_LBUTTONDOWN/Up before the normal + // WM_LBUTTONUP chrome handler in the switch below can fire WindowClose() etc. + if (self && AudioPanelWin32_IsVisible() && + !self->m_bFullScreen && !self->m_bEmbeddedSaverPreview && self->m_useCustomWindowChrome) + { + const LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE); + const bool hasNativeCaption = (style & WS_CAPTION) != 0; + if (!hasNativeCaption) + { + if (msg == WM_LBUTTONDOWN) + { + UpdateTitlebarChromeRects(self, hWnd); + const POINT ptClient{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + int pressed = 0; + if (PtInRectClient(self->m_captionBtnCloseRect, ptClient)) pressed = 3; + else if (PtInRectClient(self->m_captionBtnMaxRect, ptClient)) pressed = 2; + else if (PtInRectClient(self->m_captionBtnMinRect, ptClient)) pressed = 1; + else if (PtInRectClient(self->m_titlebarGearRect, ptClient)) pressed = 4; + else if (PtInRectClient(self->m_titlebarFullscreenRect, ptClient)) pressed = 5; + else if (PtInRectClient(self->m_titlebarMenuRect, ptClient)) pressed = 6; + if (pressed != 0) + { + self->m_captionBtnPressed = pressed; + SetCapture(hWnd); + return 0; + } + } + else if (msg == WM_LBUTTONUP) + { + const int pressed = (captionPressedSnapshot != 0) ? captionPressedSnapshot + : self->m_captionBtnPressed; + if (pressed != 0) + { + UpdateTitlebarChromeRects(self, hWnd); + const POINT ptClient{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + self->m_captionBtnPressed = 0; + if (GetCapture() == hWnd) ReleaseCapture(); + if (PtInRectClient(self->m_captionBtnCloseRect, ptClient) && pressed == 3) self->WindowClose(); + else if (PtInRectClient(self->m_captionBtnMaxRect, ptClient) && pressed == 2) self->WindowToggleMaximizeRestore(); + else if (PtInRectClient(self->m_captionBtnMinRect, ptClient) && pressed == 1) self->WindowMinimize(); + else if (PtInRectClient(self->m_titlebarGearRect, ptClient) && pressed == 4) + SendMessageW(hWnd, WM_COMMAND, MAKEWPARAM(ID_FILE_PREFERENCES, 0), 0); + else if (PtInRectClient(self->m_titlebarFullscreenRect, ptClient) && pressed == 5) + SendMessageW(hWnd, WM_COMMAND, MAKEWPARAM(ID_VIEW_FULLSCREEN, 0), 0); + else if (PtInRectClient(self->m_titlebarMenuRect, ptClient) && pressed == 6) + { + POINT anchorClient{self->m_titlebarMenuRect.left, self->m_titlebarMenuRect.bottom}; + POINT anchorScreen = anchorClient; + ClientToScreen(hWnd, &anchorScreen); + ShowTitlebarOverflowMenu(hWnd, anchorScreen); + } + return 0; + } + } + } + } + LRESULT imguiHandled = 0; - if (SettingsDialogWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, &imguiHandled)) + if (SettingsDialogWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, + &imguiHandled)) + return imguiHandled; + if (AboutDialogWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, + &imguiHandled)) return imguiHandled; - if (AboutDialogWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, &imguiHandled)) + if (FirstTimeSetupWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, + &imguiHandled)) return imguiHandled; - if (FirstTimeSetupWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, &imguiHandled)) + if (AudioPanelWin32_TryConsumeWndProc(hWnd, msg, wParam, lParam, + &imguiHandled)) return imguiHandled; #endif diff --git a/client_generic/DisplayOutput/D3D11/RendererDX11.cpp b/client_generic/DisplayOutput/D3D11/RendererDX11.cpp index 8444122c..acd67da3 100644 --- a/client_generic/DisplayOutput/D3D11/RendererDX11.cpp +++ b/client_generic/DisplayOutput/D3D11/RendererDX11.cpp @@ -14,6 +14,7 @@ #include "AboutDialogWin32.h" #include "FirstTimeSetupWin32.h" #include "SettingsDialogWin32.h" +#include "AudioPanelWin32.h" #include #include #endif @@ -1539,9 +1540,14 @@ bool CRendererDX11::EndFrame(bool drawn) { const float viewportW = static_cast(m_spDisplay->Width()); const float viewportH = static_cast(m_spDisplay->Height()); const bool settingsVisible = SettingsDialogWin32_RenderIfNeeded( - m_device.Get(), m_context.Get(), m_renderTargetView.Get(), viewportW, viewportH); + m_device.Get(), m_context.Get(), m_renderTargetView.Get(), + viewportW, viewportH); const bool aboutVisible = AboutDialogWin32_RenderIfNeeded( - m_device.Get(), m_context.Get(), m_renderTargetView.Get(), viewportW, viewportH); + m_device.Get(), m_context.Get(), m_renderTargetView.Get(), + viewportW, viewportH); + const bool audioPanelVisible = AudioPanelWin32_RenderIfNeeded( + m_device.Get(), m_context.Get(), m_renderTargetView.Get(), + viewportW, viewportH); // Custom titlebar strip + caption buttons: draw in DX so it never flashes. if (auto* dx = dynamic_cast(m_spDisplay.get())) @@ -1781,7 +1787,8 @@ bool CRendererDX11::EndFrame(bool drawn) { EndGpuTimingFrame(); - if (effectiveDrawn || settingsVisible || aboutVisible) + if (effectiveDrawn || settingsVisible || aboutVisible || + audioPanelVisible) { m_spDisplay->SwapBuffers(); return true; diff --git a/client_generic/MSVC/electricsheep.vcxproj b/client_generic/MSVC/electricsheep.vcxproj index dfeec362..16d3e29b 100644 --- a/client_generic/MSVC/electricsheep.vcxproj +++ b/client_generic/MSVC/electricsheep.vcxproj @@ -182,8 +182,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" Disabled - ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;WIN32;_DEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;D3D_DEBUG_INFO;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;%(PreprocessorDefinitions) + ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;WIN32;_DEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;D3D_DEBUG_INFO;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL @@ -219,8 +219,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" Disabled - ..\MacBuild;..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\TupleStorage;..\socket.io-client-cpp\include;..\websocketpp;..\;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;BOOST_BIND_GLOBAL_PLACEHOLDERS;WIN32;_WIN64;_DEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;D3D_DEBUG_INFO;COMPILE_MULTIMON_STUBS;%(PreprocessorDefinitions) + ..\MacBuild;..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\TupleStorage;..\socket.io-client-cpp\include;..\websocketpp;..\;C:\Users\Sanial\client\vcpkg_installed\x64-windows\include;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;BOOST_BIND_GLOBAL_PLACEHOLDERS;WIN32;_WIN64;_DEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;D3D_DEBUG_INFO;COMPILE_MULTIMON_STUBS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL @@ -237,7 +237,7 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" avcodec.lib;avformat.lib;swscale.lib;avdevice.lib;avutil.lib;sioclient.lib;libssl.lib;libcrypto.lib;libpng16d.lib;kernel32.lib;user32.lib;dwrite.lib;shlwapi.lib;libcurl.lib;d3d9.lib;d3d11.lib;d3d12.lib;dxgi.lib;d3dcompiler.lib;dxguid.lib;ws2_32.lib;wldap32.lib;psapi.lib;delayimp.lib;crypt32.lib;bcrypt.lib;pdh.lib;%(AdditionalDependencies) $(OutDir)$(ProjectName)d.exe - ..\socket.io-client-cpp\lib\Debug;%(AdditionalLibraryDirectories) + ..\socket.io-client-cpp\lib\Debug;C:\Users\Sanial\client\vcpkg_installed\x64-windows\lib;%(AdditionalLibraryDirectories) %(IgnoreSpecificDefaultLibraries) wldap32.dll;ws2_32.dll;%(DelayLoadDLLs) true @@ -266,8 +266,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" false true true - ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;%(PreprocessorDefinitions) + ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true MultiThreaded false @@ -332,8 +332,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" false true true - ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;%(PreprocessorDefinitions) + ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true MultiThreaded false @@ -398,8 +398,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" false true true - ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;%(PreprocessorDefinitions) + ..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\tinyXml;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;BOOST_NUMERIC_CONVERSION_RELAX_BUILT_IN_CAST_TRAITS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true MultiThreaded false @@ -464,8 +464,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" false true true - ..\;..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;..\MacBuild;..\socket.io-client-cpp\include;..\websocketpp;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;WIN32;_WIN64;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;%(PreprocessorDefinitions) + ..\;..\ContentDownloader;..\Client;..\Common;..\Common\Math;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\Networking;..\TupleStorage;..\DisplayOutput\DirectX;..\DisplayOutput\OpenGL;..\MacBuild;..\socket.io-client-cpp\include;..\websocketpp;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;WIN32;_WIN64;NDEBUG;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true MultiThreadedDLL false @@ -528,8 +528,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" false true false - ..\;..\Client;..\Common;..\Common\Math;..\ContentDownloader;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\DisplayOutput\D3D11;..\Networking;..\TupleStorage;..\MacBuild;..\socket.io-client-cpp\include;..\websocketpp;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;BOOST_BIND_GLOBAL_PLACEHOLDERS;DEBUG;WIN32;_WIN64;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;%(PreprocessorDefinitions) + ..\;..\Client;..\Common;..\Common\Math;..\ContentDownloader;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\DisplayOutput\D3D11;..\Networking;..\TupleStorage;..\MacBuild;..\socket.io-client-cpp\include;..\websocketpp;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;BOOST_BIND_GLOBAL_PLACEHOLDERS;DEBUG;WIN32;_WIN64;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true MultiThreadedDLL false @@ -593,8 +593,8 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" false true false - ..\;..\Client;..\Common;..\Common\Math;..\ContentDownloader;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\DisplayOutput\D3D11;..\Networking;..\TupleStorage;..\MacBuild;..\socket.io-client-cpp\include;..\websocketpp;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x0A00;BOOST_BIND_GLOBAL_PLACEHOLDERS;DEBUG;WIN32;_WIN64;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;%(PreprocessorDefinitions) + ..\;..\Client;..\Common;..\Common\Math;..\ContentDownloader;..\ContentDecoder;..\DisplayOutput\Renderer;..\DisplayOutput;..\DisplayOutput\D3D11;..\Networking;..\TupleStorage;..\MacBuild;..\socket.io-client-cpp\include;..\websocketpp;..\..\aubio\src;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0A00;BOOST_BIND_GLOBAL_PLACEHOLDERS;DEBUG;WIN32;_WIN64;_WINDOWS;NOMINMAX;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;COMPILE_MULTIMON_STUBS;_USE_MATH_DEFINES;HAVE_CONFIG_H;%(PreprocessorDefinitions) true MultiThreadedDebugDLL false @@ -646,6 +646,47 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" + + + + + + + + + + + + + + + $(IntDir)aubio_log.obj + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -695,6 +736,10 @@ copy /y "..\runtime\*.ttf" "$(OutDir)" + + + + diff --git a/client_generic/MSVC/electricsheep.vcxproj.filters b/client_generic/MSVC/electricsheep.vcxproj.filters index 9b5690bb..24bec41b 100644 --- a/client_generic/MSVC/electricsheep.vcxproj.filters +++ b/client_generic/MSVC/electricsheep.vcxproj.filters @@ -58,6 +58,9 @@ {55b5ee93-d357-4b3d-bc19-998a3c9c138d} + + {a1b2c3d4-e5f6-7890-abcd-ef1234567890} + @@ -186,6 +189,97 @@ DisplayOutput\D3D11 + + ContentDownloader + + + ContentDownloader + + + ContentDownloader + + + ContentDownloader + + + ContentDownloader + + + ContentDownloader + + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + audioFlux + + ContentDownloader + @@ -440,6 +534,36 @@ DisplayOutput\D3D11 + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + @@ -448,6 +572,7 @@ Common + diff --git a/client_generic/MSVC/electricsheep.vcxproj.user b/client_generic/MSVC/electricsheep.vcxproj.user index 627f9fc3..928a9781 100644 --- a/client_generic/MSVC/electricsheep.vcxproj.user +++ b/client_generic/MSVC/electricsheep.vcxproj.user @@ -16,4 +16,7 @@ $(OutDir) WindowsLocalDebugger + + true + \ No newline at end of file